The Battle for Wesnoth  1.15.11+dev
text.hpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2008 - 2018 by Mark de Wever <koraq@xs4all.nl>
3  Part of the Battle for Wesnoth Project https://www.wesnoth.org/
4 
5  This program is free software; you can redistribute it and/or modify
6  it under the terms of the GNU General Public License as published by
7  the Free Software Foundation; either version 2 of the License, or
8  (at your option) any later version.
9  This program is distributed in the hope that it will be useful,
10  but WITHOUT ANY WARRANTY.
11 
12  See the COPYING file for more details.
13 */
14 
15 #pragma once
16 
17 #include "font/font_options.hpp"
18 #include "color.hpp"
19 #include "sdl/surface.hpp"
21 
22 #include <pango/pango.h>
23 #include <pango/pangocairo.h>
24 
25 #include <functional>
26 #include <memory>
27 #include <string>
28 #include <vector>
29 
30 /***
31  * Note: This is the cairo-pango code path, not the SDL_TTF code path.
32  */
33 
34 struct language_def;
35 struct point;
36 
37 namespace font {
38 
39 // add background color and also font markup.
40 
41 /**
42  * Text class.
43  *
44  * This class represents text which is rendered using Pango.
45  *
46  * It takes text, as a utf-8 std::string, plus formatting options including
47  * font and color. It provides a surface object which holds the rendered text.
48  *
49  * Besides this, it can do some additional calculations using the font layout.
50  *
51  * It can take an index into the text, and convert it to pixel coordinates,
52  * so that if we want to draw a cursor in an editbox, we know where to draw it.
53  *
54  * It can also take a pixel coordinate with respect to the text layout, and
55  * translate it back to an index into the original text. This is useful if the
56  * user clicks on the text, and we want to know where to move the cursor.
57  *
58  * The get_token method takes a pixel coordinate, which we assume represents a
59  * click position, and gets the corresponding "token" from the string. The default
60  * token delimiters are whitespace " \n\r\t". So, this returns the "word" that the
61  * user clicked on.
62  *
63  * Finally, the get_link method represents special support for hyperlinks in text.
64  * A token "looks like a link" if it begins "http://" or "https://".
65  * If a text has link_aware enabled, then any such token is rendered with an
66  * underline and in a special color, see `link_color`.
67  * The get_link method calls get_token and further checks if the clicked token
68  * looks like a link.
69  *
70  * This class stores the text to draw and uses pango with the cairo backend to
71  * render the text. See http://pango.org for more info.
72  *
73  */
75 {
76 public:
77 
78  pango_text();
79 
80  pango_text(const pango_text &) = delete;
81  pango_text & operator = (const pango_text &) = delete;
82 
83  /**
84  * Returns the rendered text.
85  *
86  * Before rendering it tests whether a redraw is needed and if so it first
87  * redraws the surface before returning it.
88  */
89  surface& render();
90 
91  /** Returns the width needed for the text. */
92  int get_width() const;
93 
94  /** Returns the height needed for the text. */
95  int get_height() const;
96 
97  /** Returns the pixel size needed for the text. */
98  point get_size() const;
99 
100  /** Has the text been truncated? This happens if it exceeds max width or height. */
101  bool is_truncated() const;
102 
103  /**
104  * Inserts UTF-8 text.
105  *
106  * @param offset The position to insert the text.
107  * @param text The UTF-8 text to insert.
108  *
109  * @returns The number of characters inserted.
110  */
111  unsigned insert_text(const unsigned offset, const std::string& text);
112 
113  /***** ***** ***** ***** Font flags ***** ***** ***** *****/
114 
115  // NOTE: these values must be powers of 2 in order to be bit-unique
116  enum FONT_STYLE {
121  };
122 
123  /***** ***** ***** ***** Query details ***** ***** ***** *****/
124 
125  /**
126  * Returns the maximum glyph height of a font, in pixels.
127  *
128  * @returns The height of the tallest possible glyph for the selected
129  * font. More specifically, the result is the sum of the maximum
130  * ascent and descent lengths.
131  */
132  int get_max_glyph_height() const;
133 
134  /**
135  * Gets the location for the cursor.
136  *
137  * @param column The column offset of the cursor.
138  * @param line The line offset of the cursor.
139  *
140  * @returns The position of the top of the cursor. It the
141  * requested location is out of range 0,0 is
142  * returned.
143  */
145  const unsigned column, const unsigned line = 0) const;
146 
147  /**
148  * Get maximum length.
149  *
150  * @returns The maximum length of the text. The length of text
151  * should not exceed this value.
152  */
153  std::size_t get_maximum_length() const;
154 
155  /**
156  * Gets the largest collection of characters, including the token at position,
157  * and not including any characters from the delimiters set.
158  *
159  * @param position The pixel position in the text area.
160  * @param delimiters
161  *
162  * @returns The token containing position, and none of the
163  * delimiter characters. If position is out of bounds,
164  * it returns the empty string.
165  */
166  std::string get_token(const point & position, const char * delimiters = " \n\r\t") const;
167 
168  /**
169  * Checks if position points to a character in a link in the text, returns it
170  * if so, empty string otherwise. Link-awareness must be enabled to get results.
171  * @param position The pixel position in the text area.
172  *
173  * @returns The link if one is found, the empty string otherwise.
174  */
175  std::string get_link(const point & position) const;
176 
177  /**
178  * Gets the column of line of the character at the position.
179  *
180  * @param position The pixel position in the text area.
181  *
182  * @returns A point with the x value the column and the y
183  * value the line of the character found (or last
184  * character if not found.
185  */
186  point get_column_line(const point& position) const;
187 
188  /**
189  * Retrieves a list of strings with contents for each rendered line.
190  *
191  * This method is not const because it requires rendering the text.
192  *
193  * @note This is only intended for renderer implementation details. This
194  * is a rather expensive function because it copies everything at
195  * least once.
196  */
197  std::vector<std::string> get_lines() const;
198 
199  /**
200  * Gets the length of the text in bytes.
201  *
202  * The text set is UTF-8 so the length of the string might not be the length
203  * of the text.
204  */
205  std::size_t get_length() const { return length_; }
206 
207  /**
208  * Sets the text to render.
209  *
210  * @param text The text to render.
211  * @param markedup Should the text be rendered with pango
212  * markup. If the markup is invalid it's
213  * rendered as text without markup.
214  *
215  * @returns The status, if rendered as markup and the
216  * markup contains errors, false is returned
217  * else true.
218  */
219  bool set_text(const std::string& text, const bool markedup);
220 
221  /***** ***** ***** ***** Setters / getters ***** ***** ***** *****/
222 
223  const std::string& text() const { return text_; }
224 
226 
227  pango_text& set_font_size(const unsigned font_size);
228 
229  pango_text& set_font_style(const FONT_STYLE font_style);
230 
231  pango_text& set_foreground_color(const color_t& color);
232 
233  pango_text& set_maximum_width(int width);
234 
235  pango_text& set_characters_per_line(const unsigned characters_per_line);
236 
237  pango_text& set_maximum_height(int height, bool multiline);
238 
239  pango_text& set_ellipse_mode(const PangoEllipsizeMode ellipse_mode);
240 
241  pango_text& set_alignment(const PangoAlignment alignment);
242 
243  pango_text& set_maximum_length(const std::size_t maximum_length);
244 
245  bool link_aware() const { return link_aware_; }
246 
247  pango_text& set_link_aware(bool b);
248 
249  pango_text& set_link_color(const color_t& color);
250 
251  pango_text& set_add_outline(bool do_add);
252 
253 private:
254 
255  /***** ***** ***** ***** Pango variables ***** ***** ***** *****/
256  std::unique_ptr<PangoContext, std::function<void(void*)>> context_;
257  std::unique_ptr<PangoLayout, std::function<void(void*)>> layout_;
258  mutable PangoRectangle rect_;
259 
260  /** The SDL surface to render upon used as a cache. */
261  mutable surface surface_;
262 
263 
264  /** The text to draw (stored as UTF-8). */
265  std::string text_;
266 
267  /** Does the text contain pango markup? If different render routines must be used. */
269 
270  /** Are hyperlinks in the text marked-up, and will get_link return them. */
272 
273  /**
274  * The color to render links in.
275  *
276  * Links are formatted using pango &lt;span> as follows:
277  *
278  * &lt;span underline="single" color=" + link_color_ + ">
279  */
281 
282  /** The font family class used. */
284 
285  /** The font size to draw. */
286  unsigned font_size_;
287 
288  /** The style of the font, this is an orred mask of the font flags. */
290 
291  /** The foreground color. */
293 
294  /** Whether to add an outline effect. */
296 
297  /**
298  * The maximum width of the text.
299  *
300  * Values less or equal to 0 mean no maximum and are internally stored as
301  * -1, since that's the value pango uses for it.
302  *
303  * See @ref characters_per_line_.
304  */
306 
307  /**
308  * The number of characters per line.
309  *
310  * This can be used as an alternative of @ref maximum_width_. The user can
311  * select a number of characters on a line for wrapping. When the value is
312  * non-zero it determines the maximum width based on the average character
313  * width.
314  *
315  * If both @ref maximum_width_ and @ref characters_per_line_ are set the
316  * minimum of the two will be the maximum.
317  *
318  * @note Long lines are often harder to read, setting this value can
319  * automatically wrap on a number of characters regardless of the font
320  * size. Often 66 characters is considered the optimal value for a one
321  * column text.
322  */
324 
325  /**
326  * The maximum height of the text.
327  *
328  * Values less or equal to 0 mean no maximum and are internally stored as
329  * -1, since that's the value pango uses for it.
330  */
332 
333  /** The way too long text is shown depends on this mode. */
334  PangoEllipsizeMode ellipse_mode_;
335 
336  /** The alignment of the text. */
337  PangoAlignment alignment_;
338 
339  /** The maximum length of the text. */
340  std::size_t maximum_length_;
341 
342  /**
343  * The text has two dirty states:
344  * - The setting of the state and the size calculations.
345  * - The rendering of the surface.
346  */
347 
348  /** The dirty state of the calculations. */
349  mutable bool calculation_dirty_;
350 
351  /** Length of the text. */
352  mutable std::size_t length_;
353 
354  /**
355  * Recalculates the text layout.
356  */
357  void recalculate() const;
358 
359  /** Calculates surface size. */
360  PangoRectangle calculate_size(PangoLayout& layout) const;
361 
362  /** The dirty state of the surface. */
363  mutable bool surface_dirty_;
364 
365  /**
366  * Renders the text.
367  *
368  * It will do a recalculation first so no need to call both.
369  */
370  void rerender();
371 
372  void render(PangoLayout& layout, const PangoRectangle& rect,
373  const std::size_t surface_buffer_offset, const unsigned stride);
374 
375  /**
376  * Buffer to store the image on.
377  *
378  * We use a cairo surface to draw on this buffer and then use the buffer as
379  * data source for the SDL_Surface. This means the buffer needs to be stored
380  * in the object, since SDL_Surface doesn't own its buffer.
381  */
382  mutable std::vector<uint8_t> surface_buffer_;
383 
384  /**
385  * Sets the markup'ed text.
386  *
387  * It tries to set the text as markup. If the markup is invalid it will try
388  * a bit harder to recover from the errors and still set the markup.
389  *
390  * @param text The text to set as markup.
391  * @param layout
392  *
393  * @returns Whether the markup was set or an
394  * unrecoverable error occurred and the text is
395  * set as plain text with an error message.
396  */
397  bool set_markup(std::string_view text, PangoLayout& layout);
398 
399  bool validate_markup(std::string_view text, char** raw_text, std::string& semi_escaped) const;
400 
401  static void copy_layout_properties(PangoLayout& src, PangoLayout& dst);
402 
403  std::string format_links(std::string_view text) const;
404 };
405 
406 /**
407  * Returns a reference to a static pango_text object.
408  *
409  * Since the class is essentially a render pipeline, there's no need for individual
410  * areas of the game to own their own renderers. Not to mention it isn't a trivial
411  * class; constructing one is likely to be expensive.
412  */
414 
415 /**
416  * Returns the maximum glyph height of a font, in pixels.
417  *
418  * @param size Desired font size in pixels.
419  * @param fclass Font family to use for measurement.
420  * @param style Font style to select the correct variant for measurement.
421  *
422  * @returns The height of the tallest possible glyph for the selected
423  * font. More specifically, the result is the sum of the maximum
424  * ascent and descent lengths.
425  */
427 
428 } // namespace font
void recalculate() const
Recalculates the text layout.
Definition: text.cpp:491
unsigned font_size_
The font size to draw.
Definition: text.hpp:286
family_class
Font classes for get_font_families().
std::size_t get_length() const
Gets the length of the text in bytes.
Definition: text.hpp:205
bool set_text(const std::string &text, const bool markedup)
Sets the text to render.
Definition: text.cpp:274
Collection of helper functions relating to Pango formatting.
bool set_markup(std::string_view text, PangoLayout &layout)
Sets the markup&#39;ed text.
Definition: text.cpp:785
int get_width() const
Returns the width needed for the text.
Definition: text.cpp:99
int maximum_height_
The maximum height of the text.
Definition: text.hpp:331
unsigned characters_per_line_
The number of characters per line.
Definition: text.hpp:323
std::size_t length_
Length of the text.
Definition: text.hpp:352
std::vector< std::string > get_lines() const
Retrieves a list of strings with contents for each rendered line.
Definition: text.cpp:897
pango_text & set_link_aware(bool b)
Definition: text.cpp:439
bool link_aware() const
Definition: text.hpp:245
std::size_t maximum_length_
The maximum length of the text.
Definition: text.hpp:340
std::size_t get_maximum_length() const
Get maximum length.
Definition: text.cpp:188
font::family_class font_class_
The font family class used.
Definition: text.hpp:283
pango_text & set_font_style(const FONT_STYLE font_style)
Definition: text.cpp:334
pango_text & get_text_renderer()
Returns a reference to a static pango_text object.
Definition: text.cpp:923
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.
Definition: text.cpp:929
pango_text & set_ellipse_mode(const PangoEllipsizeMode ellipse_mode)
Definition: text.cpp:401
pango_text & set_maximum_length(const std::size_t maximum_length)
Definition: text.cpp:426
std::vector< uint8_t > surface_buffer_
Buffer to store the image on.
Definition: text.hpp:382
bool validate_markup(std::string_view text, char **raw_text, std::string &semi_escaped) const
Definition: text.cpp:852
pango_text & set_alignment(const PangoAlignment alignment)
Definition: text.cpp:415
#define b
point get_cursor_position(const unsigned column, const unsigned line=0) const
Gets the location for the cursor.
Definition: text.cpp:143
std::size_t size(const std::string &str)
Length in characters of a UTF-8 string.
Definition: unicode.cpp:86
std::string get_token(const point &position, const char *delimiters=" \\) const
Gets the largest collection of characters, including the token at position, and not including any cha...
Definition: text.cpp:193
point get_size() const
Returns the pixel size needed for the text.
Definition: text.cpp:109
std::unique_ptr< PangoLayout, std::function< void(void *)> > layout_
Definition: text.hpp:257
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...
Definition: text.cpp:814
bool is_truncated() const
Has the text been truncated? This happens if it exceeds max width or height.
Definition: text.cpp:116
int maximum_width_
The maximum width of the text.
Definition: text.hpp:305
color_t foreground_color_
The foreground color.
Definition: text.hpp:292
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...
Definition: text.cpp:225
pango_text & set_font_size(const unsigned font_size)
Definition: text.cpp:322
PangoEllipsizeMode ellipse_mode_
The way too long text is shown depends on this mode.
Definition: text.hpp:334
pango_text & set_characters_per_line(const unsigned characters_per_line)
Definition: text.cpp:370
bool markedup_text_
Does the text contain pango markup? If different render routines must be used.
Definition: text.hpp:268
PangoRectangle calculate_size(PangoLayout &layout) const
Calculates surface size.
Definition: text.cpp:503
bool surface_dirty_
The dirty state of the surface.
Definition: text.hpp:363
PangoRectangle rect_
Definition: text.hpp:258
bool calculation_dirty_
The text has two dirty states:
Definition: text.hpp:349
unsigned insert_text(const unsigned offset, const std::string &text)
Inserts UTF-8 text.
Definition: text.cpp:123
pango_text & set_family_class(font::family_class fclass)
Definition: text.cpp:311
std::unique_ptr< PangoContext, std::function< void(void *)> > context_
Definition: text.hpp:256
bool link_aware_
Are hyperlinks in the text marked-up, and will get_link return them.
Definition: text.hpp:271
point get_column_line(const point &position) const
Gets the column of line of the character at the position.
Definition: text.cpp:240
FONT_STYLE font_style_
The style of the font, this is an orred mask of the font flags.
Definition: text.hpp:289
int get_max_glyph_height() const
Returns the maximum glyph height of a font, in pixels.
Definition: text.cpp:471
bool add_outline_
Whether to add an outline effect.
Definition: text.hpp:295
Holds a 2D point.
Definition: point.hpp:23
pango_text & set_link_color(const color_t &color)
Definition: text.cpp:449
int get_height() const
Returns the height needed for the text.
Definition: text.cpp:104
void rerender()
Renders the text.
Definition: text.cpp:695
const std::string & text() const
Definition: text.hpp:223
color_t link_color_
The color to render links in.
Definition: text.hpp:280
Text class.
Definition: text.hpp:74
pango_text & set_maximum_width(int width)
Definition: text.cpp:355
surface surface_
The SDL surface to render upon used as a cache.
Definition: text.hpp:261
surface & render()
Returns the rendered text.
Definition: text.cpp:92
pango_text & set_maximum_height(int height, bool multiline)
Definition: text.cpp:382
std::string text_
The text to draw (stored as UTF-8).
Definition: text.hpp:265
pango_text & set_foreground_color(const color_t &color)
Definition: text.cpp:345
PangoAlignment alignment_
The alignment of the text.
Definition: text.hpp:337
pango_text & set_add_outline(bool do_add)
Definition: text.cpp:460
static void copy_layout_properties(PangoLayout &src, PangoLayout &dst)
Definition: text.cpp:890
pango_text & operator=(const pango_text &)=delete