16 #define GETTEXT_DOMAIN "wesnoth-lib"
41 #define DBG_FT LOG_STREAM(debug, log_font)
58 std::map<std::size_t, texture> rendered_cache{};
63 rendered_cache.clear();
67 : context_(pango_font_map_create_context(pango_cairo_font_map_get_default()), g_object_unref)
68 , layout_(pango_layout_new(context_.
get()), g_object_unref)
71 , markedup_text_(false)
76 , font_style_(STYLE_NORMAL)
80 , characters_per_line_(0)
82 , ellipse_mode_(PANGO_ELLIPSIZE_END)
83 , alignment_(PANGO_ALIGN_LEFT)
84 , maximum_length_(std::string::npos)
85 , calculation_dirty_(true)
87 , attribute_start_offset_(0)
88 , attribute_end_offset_(0)
97 pango_cairo_context_set_resolution(
context_.get(), 72.0);
101 pango_layout_set_wrap(
layout_.get(), PANGO_WRAP_WORD_CHAR);
104 cairo_font_options_t *fo = cairo_font_options_create();
105 cairo_font_options_set_hint_style(fo, CAIRO_HINT_STYLE_FULL);
106 cairo_font_options_set_hint_metrics(fo, CAIRO_HINT_METRICS_ON);
107 cairo_font_options_set_antialias(fo, CAIRO_ANTIALIAS_DEFAULT);
109 pango_cairo_context_set_font_options(
context_.get(), fo);
110 cairo_font_options_destroy(fo);
162 return (pango_layout_is_ellipsized(
layout_.get()) != 0);
179 std::string tmp =
text_;
190 std::unique_ptr<PangoLayoutIter, std::function<void(PangoLayoutIter*)>> itor(
191 pango_layout_get_iter(
layout_.get()), pango_layout_iter_free);
196 if(
static_cast<int>(
line) >= pango_layout_get_line_count(
layout_.get())) {
200 for(std::size_t
i = 0;
i <
line; ++
i) {
201 pango_layout_iter_next_line(itor.get());
206 for(std::size_t
i = 0;
i < column; ++
i) {
207 if(!pango_layout_iter_next_char(itor.get())) {
211 if(
i + 1 == column) {
220 const int offset = pango_layout_iter_get_index(itor.get());
229 pango_layout_get_cursor_pos(
layout_.get(), offset, &
rect,
nullptr);
245 if (!pango_layout_xy_to_index(
layout_.get(), position.x * PANGO_SCALE,
246 position.y * PANGO_SCALE, &
index, &trailing)) {
250 std::string txt = pango_layout_get_text(
layout_.get());
252 std::string
d(delim);
254 if (
index < 0 || (
static_cast<std::size_t
>(
index) >= txt.size()) ||
d.find(txt.at(
index)) != std::string::npos) {
258 std::size_t l =
index;
259 while (l > 0 && (
d.find(txt.at(l-1)) == std::string::npos)) {
263 std::size_t r =
index + 1;
264 while (r < txt.size() && (
d.find(txt.at(r)) == std::string::npos)) {
268 return txt.substr(l,r-l);
277 std::string tok =
get_token(position,
" \n\r\t");
292 pango_layout_xy_to_index(
layout_.get(), position.x * PANGO_SCALE,
293 position.y * PANGO_SCALE, &
index, &trailing);
297 pango_layout_index_to_line_x(
layout_.get(),
index, trailing, &
line, &offset);
298 offset = PANGO_PIXELS(offset);
311 for(std::size_t
i = 0; ; ++
i) {
326 pango_layout_xy_to_index(
layout_.get(), position.x * PANGO_SCALE,
327 position.y * PANGO_SCALE, &
index, &trailing);
339 PangoAttribute *attr = pango_attr_size_new_absolute(PANGO_SCALE *
size);
344 DBG_GUI_D <<
"attribute start: " << start_offset <<
" end : " << end_offset;
357 PangoAttribute *attr = pango_attr_weight_new(weight);
362 DBG_GUI_D <<
"attribute start: " << start_offset <<
" end : " << end_offset;
376 PangoAttribute *attr = pango_attr_style_new(style);
394 PangoAttribute *attr = pango_attr_underline_new(underline);
399 DBG_GUI_D <<
"attribute start: " << start_offset <<
" end : " << end_offset;
408 std::tuple<uint16_t, uint16_t, uint16_t> color_to_uint16(
const color_t& color)
411 color.r / 255.0 * std::numeric_limits<uint16_t>::max(),
412 color.g / 255.0 * std::numeric_limits<uint16_t>::max(),
413 color.b / 255.0 * std::numeric_limits<uint16_t>::max()
425 auto [col_r, col_g, col_b] = color_to_uint16(color);
426 PangoAttribute *attr = pango_attr_foreground_new(col_r, col_g, col_b);
427 attr->start_index = start_offset;
428 attr->end_index = end_offset;
432 DBG_GUI_D <<
"color: " << col_r <<
"," << col_g <<
"," << col_b;
445 PangoAttribute *attr = pango_attr_family_new(family.c_str());
450 DBG_GUI_D <<
"attribute start: " << start_offset <<
" end : " << end_offset;
469 DBG_GUI_D <<
"highlight color: " << col_r <<
"," << col_g <<
"," << col_b;
471 PangoAttribute *attr = pango_attr_background_new(col_r, col_g, col_b);
491 const std::u32string wide = unicode_cast<std::u32string>(
text);
492 const std::string narrow = unicode_cast<std::string>(wide);
495 <<
"pango_text::" << __func__
497 <<
"' contains invalid utf-8, trimmed the invalid parts.";
502 pango_layout_set_text(
layout_.get(), narrow.c_str(), narrow.size());
505 pango_layout_set_text(
layout_.get(), narrow.c_str(), narrow.size());
607 pango_layout_set_height(
layout_.get(), !multiline ? -1 : height * PANGO_SCALE);
620 pango_layout_set_ellipsize(
layout_.get(), ellipse_mode);
629 pango_layout_set_height(
layout_.get(), -1);
638 pango_layout_set_alignment(
layout_.get(), alignment);
650 std::string tmp =
text_;
691 PangoFont*
f = pango_font_map_load_font(
692 pango_cairo_font_map_get_default(),
696 PangoFontMetrics* m = pango_font_get_metrics(
f,
nullptr);
698 auto ascent = pango_font_metrics_get_ascent(m);
699 auto descent = pango_font_metrics_get_descent(m);
701 pango_font_metrics_unref(m);
704 return ceil(pango_units_to_double(ascent + descent) /
pixel_scale_);
747 pango_layout_set_font_description(&
layout,
font.get());
750 PangoAttrList *attribute_list = pango_attr_list_new();
751 pango_attr_list_insert(attribute_list
752 , pango_attr_underline_new(PANGO_UNDERLINE_SINGLE));
754 pango_layout_set_attributes(&
layout, attribute_list);
755 pango_attr_list_unref(attribute_list);
758 int maximum_width = 0;
760 PangoFont*
f = pango_font_map_load_font(
761 pango_cairo_font_map_get_default(),
765 PangoFontMetrics* m = pango_font_get_metrics(
f,
nullptr);
767 int w = pango_font_metrics_get_approximate_char_width(m);
770 maximum_width = ceil(pango_units_to_double(
w));
772 pango_font_metrics_unref(m);
782 pango_layout_set_width(&
layout, maximum_width == -1
784 : maximum_width * PANGO_SCALE);
785 pango_layout_get_pixel_extents(&
layout,
nullptr, &
size);
789 <<
"' maximum_width " << maximum_width
797 <<
" font_style " << std::hex <<
font_style_ << std::dec
798 <<
" maximum_width " << maximum_width
800 <<
" result " <<
size
803 if(maximum_width != -1 &&
size.x +
size.width > maximum_width) {
806 <<
" ' width " <<
size.x +
size.width
807 <<
" greater as the wanted maximum of " << maximum_width
815 <<
" ' height " <<
size.y +
size.height
836 for (
int i = 1;
i < 256; ++
i) {
851 unsigned temp = (value * div) / 256u;
859 value = std::min(255u, temp);
868 uint8_t a = (
c >> 24) & 0xff;
869 uint8_t r = (
c >> 16) & 0xff;
870 uint8_t
g = (
c >> 8) & 0xff;
871 uint8_t
b =
c & 0xff;
878 c = (
static_cast<uint32_t
>(a) << 24) | (
static_cast<uint32_t
>(r) << 16) | (
static_cast<uint32_t
>(
g) << 8) |
static_cast<uint32_t
>(
b);
883 cairo_format_t
format = CAIRO_FORMAT_ARGB32;
887 std::unique_ptr<cairo_surface_t, std::function<void(cairo_surface_t*)>> cairo_surface(
888 cairo_image_surface_create_for_data(buffer,
format, viewport.w, viewport.h, stride), cairo_surface_destroy);
889 std::unique_ptr<cairo_t, std::function<void(cairo_t*)>> cr(cairo_create(cairo_surface.get()), cairo_destroy);
891 if(cairo_status(cr.get()) == CAIRO_STATUS_INVALID_SIZE) {
892 throw std::length_error(
"Text is too long to render");
896 cairo_move_to(cr.get(), -viewport.x, -viewport.y);
909 pango_cairo_layout_path(cr.get(), &
layout);
912 cairo_set_source_rgba(cr.get(), 0.0, 0.0, 0.0, 1.0);
914 cairo_set_line_join(cr.get(), CAIRO_LINE_JOIN_ROUND);
915 cairo_set_line_width(cr.get(), 3.0);
918 cairo_stroke(cr.get());
922 cairo_set_source_rgba(cr.get(),
929 pango_cairo_show_layout(cr.get(), &
layout);
941 cairo_format_t
format = CAIRO_FORMAT_ARGB32;
942 const int stride = cairo_format_stride_for_width(
format, viewport.w);
946 if(stride <= 0 || viewport.h <= 0) {
951 DBG_FT <<
"creating new text surface";
956 if(viewport.h > std::numeric_limits<int>::max() / stride) {
957 throw std::length_error(
"Text is too long to render");
971 for(
int y = 0; y < viewport.h; ++y) {
972 uint32_t* pixels =
reinterpret_cast<uint32_t*
>(&
surface_buffer_[y * stride]);
973 for(
int x = 0; x < viewport.w; ++x) {
978 return SDL_CreateRGBSurfaceWithFormatFrom(
979 &
surface_buffer_[0], viewport.w, viewport.h, 32, stride, SDL_PIXELFORMAT_ARGB8888);
985 std::string semi_escaped;
987 if(!semi_escaped.empty()) {
994 pango_layout_set_markup(&
layout, formatted_text.c_str(), formatted_text.size());
1000 PangoAttrList* markup_list = pango_layout_get_attributes(&
layout);
1002 PangoAttribute* attr =
static_cast<PangoAttribute*
>(l->data);
1003 pango_attr_list_change(markup_list, attr);
1017 static const std::string delim =
" \n\r\t";
1018 std::ostringstream result;
1020 std::size_t tok_start = 0;
1021 for(std::size_t pos = 0; pos <
text.length(); ++pos) {
1022 if(delim.find(
text[pos]) == std::string::npos) {
1026 if(
const auto tok_length = pos - tok_start) {
1028 auto token =
text.substr(tok_start, tok_length);
1036 result <<
text[pos];
1037 tok_start = pos + 1;
1041 if(tok_start <
text.length()) {
1050 return result.str();
1055 if(pango_parse_markup(
text.data(),
text.size(),
1056 0,
nullptr, raw_text,
nullptr,
nullptr)) {
1075 if(
text.size() == semi_escaped.size()
1076 || !pango_parse_markup(semi_escaped.c_str(), semi_escaped.size()
1077 , 0,
nullptr, raw_text,
nullptr,
nullptr)) {
1085 <<
" text '" <<
text
1086 <<
"' has unescaped ampersands '&', escaped them.";
1093 pango_layout_set_alignment(&
dst, pango_layout_get_alignment(&
src));
1094 pango_layout_set_height(&
dst, pango_layout_get_height(&
src));
1095 pango_layout_set_ellipsize(&
dst, pango_layout_get_ellipsize(&
src));
1103 std::vector<std::string> res;
1104 int count = pango_layout_get_line_count(
layout);
1110 using layout_iterator = std::unique_ptr<PangoLayoutIter, std::function<void(PangoLayoutIter*)>>;
1111 layout_iterator
i{pango_layout_get_iter(
layout), pango_layout_iter_free};
1116 PangoLayoutLine* ll = pango_layout_iter_get_line_readonly(
i.get());
1117 const char* begin = &pango_layout_get_text(
layout)[ll->start_index];
1118 res.emplace_back(begin, ll->length);
1119 }
while(pango_layout_iter_next_line(
i.get()));
1126 return pango_layout_get_line_readonly(
layout_.get(),
index);
1132 pango_layout_index_to_line_x(
layout_.get(), offset, 0, &line_num,
nullptr);
1139 return text_renderer;
Small helper class to make sure the pango font object is destroyed properly.
int pixel_scale_
The pixel scale, used to render high-DPI text.
int xy_to_index(const point &position) const
pango_text & set_font_style(const FONT_STYLE font_style)
PangoEllipsizeMode ellipse_mode_
The way too long text is shown depends on this mode.
void add_attribute_weight(const unsigned start_offset, const unsigned end_offset, PangoWeight weight)
Add pango font weight attribute to a specific portion of text.
static void copy_layout_properties(PangoLayout &src, PangoLayout &dst)
bool add_outline_
Whether to add an outline effect.
bool validate_markup(std::string_view text, char **raw_text, std::string &semi_escaped) const
bool set_markup(std::string_view text, PangoLayout &layout)
Sets the markup'ed text.
pango_text & set_maximum_length(const std::size_t maximum_length)
void render(PangoLayout &layout, const SDL_Rect &viewport, const unsigned stride)
This is part of create_surface(viewport).
surface create_surface()
Equivalent to create_surface(viewport), where the viewport's top-left is at (0,0) and the area is lar...
PangoAlignment alignment_
The alignment of the text.
int maximum_height_
The maximum height of the text.
point get_size()
Returns the size of the text, in drawing coordinates.
pango_text & set_characters_per_line(const unsigned characters_per_line)
color_t link_color_
The color to render links in.
unsigned insert_text(const unsigned offset, const std::string &text, const bool use_markup=false)
Inserts UTF-8 text.
int get_line_num_from_offset(const unsigned offset)
Given a byte index, find out at which line the corresponding character is located.
void recalculate() const
Recalculates the text layout.
color_t foreground_color_
The foreground color.
point get_column_line(const point &position) const
Gets the column of line of the character at the position.
std::unique_ptr< PangoContext, std::function< void(void *)> > context_
bool link_aware_
Are hyperlinks in the text marked-up, and will get_link return them.
pango_text & set_foreground_color(const color_t &color)
int to_draw_scale(int s) const
Scale the given render-space size to draw-space, rounding up.
std::string format_links(std::string_view text) const
Replaces all instances of URLs in a given string with formatted links and returns the result.
PangoLayoutLine * get_line(int index)
Get a specific line from the pango layout.
unsigned characters_per_line_
The number of characters per line.
bool markedup_text_
Does the text contain pango markup? If different render routines must be used.
pango_text & set_family_class(font::family_class fclass)
font::family_class font_class_
The font family class used.
std::vector< std::string > get_lines() const
Retrieves a list of strings with contents for each rendered line.
void add_attribute_fg_color(const unsigned start_offset, const unsigned end_offset, const color_t &color)
Add pango fg color attribute to a specific portion of text.
void update_pixel_scale()
Update pixel scale, if necessary.
void add_attribute_font_family(const unsigned start_offset, const unsigned end_offset, const std::string &family)
Add pango font family attribute to a specific portion of text.
unsigned font_size_
The font size to draw.
void add_attribute_size(const unsigned start_offset, const unsigned end_offset, int size)
Add pango font size attribute to a specific portion of text.
texture render_texture(const SDL_Rect &viewport)
Wrapper around render_surface which sets texture::w() and texture::h() in the same way that render_an...
surface render_surface(const SDL_Rect &viewport)
Returns the rendered text.
std::vector< uint8_t > surface_buffer_
Buffer to store the image on.
pango_text & set_add_outline(bool do_add)
unsigned attribute_end_offset_
pango_text & set_ellipse_mode(const PangoEllipsizeMode ellipse_mode)
void add_attribute_bg_color(const unsigned start_offset, const unsigned end_offset, const color_t &color)
Mark a specific portion of text for highlighting.
PangoRectangle calculate_size(PangoLayout &layout) const
Calculates surface size.
void add_attribute_underline(const unsigned start_offset, const unsigned end_offset, PangoUnderline underline)
Add pango underline attribute to a specific portion of text.
pango_text & set_alignment(const PangoAlignment alignment)
std::string text_
The text to draw (stored as UTF-8).
point get_cursor_position(const unsigned column, const unsigned line=0) const
Gets the location for the cursor, in drawing coordinates.
bool calculation_dirty_
The text has two dirty states:
std::unique_ptr< PangoLayout, std::function< void(void *)> > layout_
pango_text & set_font_size(unsigned font_size)
pango_text & set_link_aware(bool b)
FONT_STYLE font_style_
The style of the font, this is an orred mask of the font flags.
unsigned attribute_start_offset_
std::string get_token(const point &position, const char *delimiters=" \n\r\t") const
Gets the largest collection of characters, including the token at position, and not including any cha...
bool set_text(const std::string &text, const bool markedup)
Sets the text to render.
bool is_truncated() const
Has the text been truncated? This happens if it exceeds max width or height.
void add_attribute_style(const unsigned start_offset, const unsigned end_offset, PangoStyle style)
Add pango font style attribute to a specific portion of text, used to set italic/oblique text.
texture with_draw_scale(const texture &t) const
Adjust a texture's draw-width and height according to pixel scale.
std::size_t maximum_length_
The maximum length of the text.
point get_cursor_pos_from_index(const unsigned offset) const
Gets the location for the cursor, in drawing coordinates.
pango_text & set_maximum_height(int height, bool multiline)
pango_text & set_maximum_width(int width)
std::size_t get_maximum_length() const
Get maximum length.
PangoAttrList * global_attribute_list_
Global pango attribute list.
texture render_and_get_texture()
Returns the cached texture, or creates a new one otherwise.
pango_text & set_link_color(const color_t &color)
std::string get_link(const point &position) const
Checks if position points to a character in a link in the text, returns it if so, empty string otherw...
void clear_attribute_list()
Clears all attributes from the global attribute list.
const std::string & text() const
std::size_t length_
Length of the text.
int get_max_glyph_height() const
Returns the maximum glyph height of a font, in drawing coordinates.
int maximum_width_
The maximum width of the text.
int font_scaled(int size)
Wrapper class to encapsulate creation and management of an SDL_Texture.
void set_draw_size(int w, int h)
Set the intended size of the texture, in draw-space.
Define the common log macros for the gui toolkit.
void point(int x, int y)
Draw a single point.
void rect(const SDL_Rect &rect)
Draw a rectangle.
void line(int from_x, int from_y, int to_x, int to_y)
Draw a line.
family_class
Font classes for get_font_families().
int get_max_height(unsigned size, font::family_class fclass, pango_text::FONT_STYLE style)
Returns the maximum glyph height of a font, in pixels.
pango_text & get_text_renderer()
Returns a reference to a static pango_text object.
constexpr float get_line_spacing_factor()
bool looks_like_url(std::string_view str)
std::string semi_escape_text(std::string_view text)
std::string format_as_link(const std::string &link, color_t color)
const t_string & get_font_families(family_class fclass)
Returns the currently defined fonts.
static void unpremultiply(uint8_t &value, const unsigned div)
static void from_cairo_format(uint32_t &c)
Converts from cairo-format ARGB32 premultiplied alpha to plain alpha.
void flush_texture_cache()
Flush the rendered text cache.
static constexpr inverse_table inverse_table_
std::string_view debug_truncate(std::string_view text)
Returns a truncated version of the text.
std::string & insert(std::string &str, const std::size_t pos, const std::string &insert)
Insert a UTF-8 string at the specified position.
std::size_t size(std::string_view str)
Length in characters of a UTF-8 string.
std::string & truncate(std::string &str, const std::size_t size)
Truncates a UTF-8 string to the specified number of characters.
std::size_t index(std::string_view str, const std::size_t index)
Codepoint index corresponding to the nth character in a UTF-8 string.
int get_pixel_scale()
Get the current active pixel scale multiplier.
rect dst
Location on the final composed sheet.
rect src
Non-transparent portion of the surface to compose.
The basic class for representing 8-bit RGB or RGBA colour values.
constexpr inverse_table()
unsigned operator[](uint8_t i) const
An abstract description of a rectangle with integer coordinates.
contains the current text being parsed as well as the token_type of what's being parsed.
static lg::log_domain log_font("font")