1 /* GG is a GUI for OpenGL.
2    Copyright (C) 2003-2008 T. Zachary Laine
3 
4    This library is free software; you can redistribute it and/or
5    modify it under the terms of the GNU Lesser General Public License
6    as published by the Free Software Foundation; either version 2.1
7    of the License, or (at your option) any later version.
8 
9    This library is distributed in the hope that it will be useful,
10    but WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12    Lesser General Public License for more details.
13 
14    You should have received a copy of the GNU Lesser General Public
15    License along with this library; if not, write to the Free
16    Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
17    02111-1307 USA
18 
19    If you do not wish to comply with the terms of the LGPL please
20    contact the author as other terms are available for a fee.
21 
22    Zach Laine
23    whatwasthataddress@gmail.com */
24 
25 #include <GG/Font.h>
26 
27 #include <GG/GUI.h>
28 #include <GG/Base.h>
29 #include <GG/StyleFactory.h>
30 #include <GG/utf8/checked.h>
31 #include <GG/GLClientAndServerBuffer.h>
32 
33 #include <ft2build.h>
34 #include FT_FREETYPE_H
35 
36 #include <boost/format.hpp>
37 #include <boost/lexical_cast.hpp>
38 #include <boost/xpressive/xpressive.hpp>
39 #include <boost/xpressive/regex_actions.hpp>
40 
41 #include <cctype>
42 #include <cmath>
43 #include <iterator>
44 #include <numeric>
45 #include <sstream>
46 #include <unordered_set>
47 
48 #define DEBUG_DETERMINELINES 0
49 
50 namespace GG { namespace detail {
51 
FTFaceWrapper()52     FTFaceWrapper::FTFaceWrapper()
53     {}
54 
~FTFaceWrapper()55     FTFaceWrapper::~FTFaceWrapper()
56     { if (m_face) FT_Done_Face(m_face); }
57 
58 } }
59 
60 using namespace GG;
61 
62 namespace {
63     const std::uint32_t WIDE_SPACE = ' ';
64     const std::uint32_t WIDE_NEWLINE = '\n';
65     const std::uint32_t WIDE_CR = '\r';
66     const std::uint32_t WIDE_FF = '\f';
67     const std::uint32_t WIDE_TAB = '\t';
68 
69     const std::string ITALIC_TAG = "i";
70     const std::string SHADOW_TAG = "s";
71     const std::string UNDERLINE_TAG = "u";
72     const std::string SUPERSCRIPT_TAG = "sup";
73     const std::string SUBSCRIPT_TAG = "sub";
74     const std::string RGBA_TAG = "rgba";
75     const std::string ALIGN_LEFT_TAG = "left";
76     const std::string ALIGN_CENTER_TAG = "center";
77     const std::string ALIGN_RIGHT_TAG = "right";
78     const std::string PRE_TAG = "pre";
79 
80     template <typename T>
NextPowerOfTwo(T input)81     T NextPowerOfTwo(T input)
82     {
83         T value(1);
84         while (value < input)
85             value *= 2;
86         return value;
87     }
88 
89     /** This is used to collect data on the glyphs as they are recorded into buffers, for use in creating Glyph objects at the end
90         of Font's constructor.*/
91     struct TempGlyphData
92     {
TempGlyphData__anon714a98a00111::TempGlyphData93         TempGlyphData() {}
TempGlyphData__anon714a98a00111::TempGlyphData94         TempGlyphData(const Pt& ul_, const Pt& lr_, Y y_ofs, X lb, X a) :
95             ul(ul_), lr(lr_), y_offset(y_ofs), left_b(lb), adv(a) {}
96         Pt          ul, lr;   ///< area of glyph subtexture within texture
97         Y           y_offset; ///< vertical offset to draw texture (may be negative!)
98         X           left_b;   ///< left bearing (see Glyph)
99         X           adv;      ///< advance of glyph (see Glyph)
100     };
101 
102     /// A two dimensional grid of pixels that expands to
103     /// fit any write much like an stl vector, but in 2d.
104     template<typename T>
105     class Buffer2d
106     {
107     public:
108         /// Create a new 2D buffer
109         /// \param initial_width Initial width to allocate
110         /// \param initial_height Initial height to allocate
111         /// \param default_value The value to fill empty space with whenever it appears
Buffer2d(X initial_width,Y initial_height,const T & default_value)112         Buffer2d(X initial_width, Y initial_height, const T& default_value):
113             m_capacity_width(initial_width),
114             m_capacity_height(initial_height),
115             m_data(Value(initial_width)*Value(initial_height), default_value),
116             m_current_width(initial_width),
117             m_current_height(initial_height),
118             m_default_value(default_value)
119         {}
120 
121         /// Access point \x,\y, expanding the buffer if it does not exist yet
at(X x,Y y)122         T& at(X x, Y y)
123         {
124             EnsureFit(x, y);
125             return this->get(x,y);
126         }
127 
128         /// Access point \x, \y without any checks
get(X x,Y y)129         T& get(X x, Y y)
130         { return m_data[Value(m_capacity_width)*Value(y) + Value(x)]; }
131 
132         /// Returns the current highest x the user has requested to exist
CurrentWidth() const133         X CurrentWidth() const
134         { return m_current_width; }
135 
136         /// Returns the current highest y the user has requested to exist
CurrentHeight() const137         Y CurrentHeight() const
138         { return m_capacity_height; }
139 
140         /// Returns the actual width of the storage area allocated so far
BufferWidth() const141         X BufferWidth() const
142         { return m_capacity_width; }
143 
144         /// Returns the actual height of the storage area allocated so far
BufferHeight() const145         Y BufferHeight() const
146         { return m_capacity_height; }
147 
148         /// Return a pointer to the storage buffer where the data is kept
Buffer()149         T* Buffer()
150         { return &*m_data.begin(); }
151 
152         /// Returns the size of the storage buffer where the data is kept
BufferSize() const153         std::size_t BufferSize() const
154         { return m_data.size(); }
155 
156         /// Makes the size of the underlying buffer the smallest power of power of two
157         /// rectangle that can accommodate CurrentWidth() and CurrentHeight()
MakePowerOfTwo()158         void MakePowerOfTwo()
159         { ResizeCapacity(NextPowerOfTwo(m_current_width), NextPowerOfTwo(m_current_height)); }
160 
161     private:
162         X m_capacity_width; // How wide the reserved buffer is
163         Y m_capacity_height; // How hight the reserved buffer is
164         std::vector<T> m_data;
165         X m_current_width; // The highest x coordinate written to
166         Y m_current_height; // The highest y coordinate written to
167         const T m_default_value; // The value with which to fill all emerging empty slots in the buffer
168 
EnsureFit(X x,Y y)169         void EnsureFit(X x, Y y)
170         {
171             X new_width = std::max(m_current_width, x + 1); // Zero indexed => width = max_x + 1
172             Y new_height = std::max(m_current_height, y + 1); // Also zero indexed
173             X new_capacity_width = m_capacity_width;
174             Y new_capacity_height = m_capacity_height;
175             while (new_width > new_capacity_width) {
176                 new_capacity_width *= 2;
177             }
178             while (new_height > new_capacity_height) {
179                 new_capacity_height *= 2;
180             }
181 
182             ResizeCapacity(new_capacity_width, new_capacity_height);
183             m_current_width = new_width;
184             m_current_height = new_height;
185         }
186 
ResizeCapacity(X new_capacity_width,Y new_capacity_height)187         void ResizeCapacity(X new_capacity_width, Y new_capacity_height)
188         {
189             // If there really was a change, we need to recreate our storage.
190             // This is expensive, but since we double the size every time,
191             // the cost of adding data here in some sane order is amortized constant
192             if (new_capacity_width != m_capacity_width || new_capacity_height != m_capacity_height) {
193                 // Create new storage and copy old data. A linear copy won't do, there
194                 // will be a new mapping from 2d indexes to 1d memory.
195                 std::vector<T> new_data(Value(new_capacity_width)*Value(new_capacity_height), m_default_value);
196                 for (Y y_i = Y0; y_i < m_current_height && y_i < new_capacity_height; ++y_i) {
197                     for (X x_i = X0; x_i < m_current_width && x_i < new_capacity_width; ++x_i) {
198                         unsigned pos = Value(new_capacity_width) * Value(y_i) + Value(x_i);
199                         new_data[pos] = get(x_i, y_i);
200                     }
201                 }
202                 std::swap(m_data, new_data);
203                 m_capacity_width = new_capacity_width;
204                 m_capacity_height = new_capacity_height;
205             }
206         }
207     };
208 
209     struct FTLibraryWrapper
210     {
FTLibraryWrapper__anon714a98a00111::FTLibraryWrapper211         FTLibraryWrapper()
212         {
213             if (!m_library && FT_Init_FreeType(&m_library)) // if no library exists and we can't create one...
214                 throw FailedFTLibraryInit("Unable to initialize FreeType font library object");
215         }
~FTLibraryWrapper__anon714a98a00111::FTLibraryWrapper216         ~FTLibraryWrapper() { FT_Done_FreeType(m_library); }
217         FT_Library m_library = nullptr;
218     } g_library;
219 
220     struct PushSubmatchOntoStackP
221     {
222         typedef void result_type;
operator ()__anon714a98a00111::PushSubmatchOntoStackP223         void operator()(const std::string* str,
224                         std::stack<Font::Substring>& tag_stack,
225                         bool& ignore_tags,
226                         const boost::xpressive::ssub_match& sub) const
227         {
228             tag_stack.push(Font::Substring(*str, sub));
229             if (tag_stack.top() == PRE_TAG)
230                 ignore_tags = true;
231         }
232     };
233     const boost::xpressive::function<PushSubmatchOntoStackP>::type PushP = {{}};
234 
SetJustification(bool & last_line_of_curr_just,Font::LineData & line_data,Alignment orig_just,Alignment prev_just)235     void SetJustification(bool& last_line_of_curr_just, Font::LineData& line_data, Alignment orig_just, Alignment prev_just)
236     {
237         if (last_line_of_curr_just) {
238             line_data.justification = orig_just;
239             last_line_of_curr_just = false;
240         } else {
241             line_data.justification = prev_just;
242         }
243     }
244 
245     const double ITALICS_SLANT_ANGLE = 12; // degrees
246     const double ITALICS_FACTOR = 1.0 / tan((90 - ITALICS_SLANT_ANGLE) * 3.1415926 / 180.0); // factor used to shear glyphs ITALICS_SLANT_ANGLE degrees CW from straight up
247 
248     const std::vector<std::pair<std::uint32_t, std::uint32_t>> PRINTABLE_ASCII_ALPHA_RANGES{
249         {0x41, 0x5B},
250         {0x61, 0x7B}};
251 
252     const std::vector<std::pair<std::uint32_t, std::uint32_t>> PRINTABLE_ASCII_NONALPHA_RANGES{
253         {0x09, 0x0D},
254         {0x20, 0x21},
255         {0x30, 0x3A},
256         {0x21, 0x30},
257         {0x3A, 0x41},
258         {0x5B, 0x61},
259         {0x7B, 0x7F}};
260 }
261 
262 ///////////////////////////////////////
263 // Constants
264 ///////////////////////////////////////
265 const StrSize GG::S0(0);
266 const StrSize GG::S1(1);
267 const StrSize GG::INVALID_STR_SIZE(std::numeric_limits<std::size_t>::max());
268 const CPSize GG::CP0(0);
269 const CPSize GG::CP1(1);
270 const CPSize GG::INVALID_CP_SIZE(std::numeric_limits<std::size_t>::max());
271 
272 
273 ///////////////////////////////////////
274 // function GG::RgbaTag
275 ///////////////////////////////////////
RgbaTag(const Clr & c)276 std::string GG::RgbaTag(const Clr& c)
277 {
278     std::stringstream stream;
279     stream << "<rgba "
280            << static_cast<int>(c.r) << " "
281            << static_cast<int>(c.g) << " "
282            << static_cast<int>(c.b) << " "
283            << static_cast<int>(c.a)
284            << ">";
285     return stream.str();
286 }
287 
288 
289 ///////////////////////////////////////
290 // TextFormat
291 ///////////////////////////////////////
292 const TextFormat GG::FORMAT_NONE         (0);
293 const TextFormat GG::FORMAT_VCENTER      (1 << 0);
294 const TextFormat GG::FORMAT_TOP          (1 << 1);
295 const TextFormat GG::FORMAT_BOTTOM       (1 << 2);
296 const TextFormat GG::FORMAT_CENTER       (1 << 3);
297 const TextFormat GG::FORMAT_LEFT         (1 << 4);
298 const TextFormat GG::FORMAT_RIGHT        (1 << 5);
299 const TextFormat GG::FORMAT_NOWRAP       (1 << 6);
300 const TextFormat GG::FORMAT_WORDBREAK    (1 << 7);
301 const TextFormat GG::FORMAT_LINEWRAP     (1 << 8);
302 const TextFormat GG::FORMAT_IGNORETAGS   (1 << 9);
303 
304 GG_FLAGSPEC_IMPL(TextFormat);
305 
306 namespace {
RegisterTextFormats()307     bool RegisterTextFormats()
308     {
309         FlagSpec<TextFormat>& spec = FlagSpec<TextFormat>::instance();
310         spec.insert(FORMAT_NONE,        "FORMAT_NONE",      true);
311         spec.insert(FORMAT_VCENTER,     "FORMAT_VCENTER",   true);
312         spec.insert(FORMAT_TOP,         "FORMAT_TOP",       true);
313         spec.insert(FORMAT_BOTTOM,      "FORMAT_BOTTOM",    true);
314         spec.insert(FORMAT_CENTER,      "FORMAT_CENTER",    true);
315         spec.insert(FORMAT_LEFT,        "FORMAT_LEFT",      true);
316         spec.insert(FORMAT_RIGHT,       "FORMAT_RIGHT",     true);
317         spec.insert(FORMAT_NOWRAP,      "FORMAT_NOWRAP",    true);
318         spec.insert(FORMAT_WORDBREAK,   "FORMAT_WORDBREAK", true);
319         spec.insert(FORMAT_LINEWRAP,    "FORMAT_LINEWRAP",  true);
320         spec.insert(FORMAT_IGNORETAGS,  "FORMAT_IGNORETAGS",true);
321         return true;
322     }
323     bool dummy = RegisterTextFormats();
324 }
325 
326 
327 ///////////////////////////////////////
328 // class GG::Font::Substring
329 ///////////////////////////////////////
330 const std::string Font::Substring::EMPTY_STRING;
331 
Substring()332 Font::Substring::Substring() :
333     str(&EMPTY_STRING),
334     first(0),
335     second(0)
336 {}
337 
Substring(const std::string & str_,std::string::const_iterator first_,std::string::const_iterator second_)338 Font::Substring::Substring(const std::string& str_,
339                            std::string::const_iterator first_,
340                            std::string::const_iterator second_) :
341     str(&str_),
342     first(0),
343     second(0)
344 {
345     assert(str->begin() <= first_);
346     assert(first_ <= second_);
347     assert(second_ <= str->end());
348     first = std::distance(str->begin(), first_);
349     second = std::distance(str->begin(), second_);
350 }
351 
Substring(const std::string & str_,const IterPair & pair)352 Font::Substring::Substring(const std::string& str_, const IterPair& pair) :
353     str(&str_),
354     first(0),
355     second(0)
356 {
357     assert(str->begin() <= pair.first);
358     assert(pair.first <= pair.second);
359     assert(pair.second <= str->end());
360     first = std::distance(str->begin(), pair.first);
361     second = std::distance(str->begin(), pair.second);
362 }
363 
Bind(const std::string & str_)364 void Font::Substring::Bind(const std::string& str_)
365 {
366     assert(std::distance(str_.begin(), str_.end()) >= second);
367     str = &str_;
368 }
369 
begin() const370 std::string::const_iterator Font::Substring::begin() const
371 { return std::next(str->begin(), first); }
372 
end() const373 std::string::const_iterator Font::Substring::end() const
374 { return std::next(str->begin(), second); }
375 
empty() const376 bool Font::Substring::empty() const
377 { return first == second; }
378 
size() const379 std::size_t Font::Substring::size() const
380 { return second - first; }
381 
operator std::string() const382 Font::Substring::operator std::string() const
383 { return std::string(begin(), end()); }
384 
operator ==(const std::string & rhs) const385 bool Font::Substring::operator==(const std::string& rhs) const
386 { return size() == rhs.size() && !std::memcmp(str->data() + first, rhs.data(), rhs.size()); }
387 
operator !=(const std::string & rhs) const388 bool Font::Substring::operator!=(const std::string& rhs) const
389 { return !operator==(rhs); }
390 
operator +=(const IterPair & rhs)391 Font::Substring& Font::Substring::operator+=(const IterPair& rhs)
392 {
393     assert(rhs.first <= rhs.second);
394     assert(std::distance(str->begin(), rhs.first) == second);
395     second = std::distance(str->begin(), rhs.second);
396     return *this;
397 }
398 
399 
400 ///////////////////////////////////////
401 // Free Functions
402 ///////////////////////////////////////
operator <<(std::ostream & os,const Font::Substring & substr)403 std::ostream& GG::operator<<(std::ostream& os, const Font::Substring& substr)
404 {
405     std::ostream_iterator<char> out_it(os);
406     std::copy(substr.begin(), substr.end(), out_it);
407     return os;
408 }
409 
CodePointIndexOf(std::size_t line,CPSize index,const std::vector<Font::LineData> & line_data)410 CPSize GG::CodePointIndexOf(std::size_t line, CPSize index,
411                             const std::vector<Font::LineData>& line_data)
412 {
413     CPSize retval(0);
414     if (line_data.size() <= line) {
415         auto it = line_data.rbegin();
416         auto end_it = line_data.rend();
417         while (it != end_it) {
418             if (!it->char_data.empty()) {
419                 retval = it->char_data.back().code_point_index + 1;
420                 break;
421             }
422             ++it;
423         }
424     } else if (index < line_data[line].char_data.size()) {
425         retval = line_data[line].char_data[Value(index)].code_point_index;
426     } else {
427         auto it = line_data.rbegin() + (line_data.size() - 1 - line);
428         auto end_it = line_data.rend();
429         while (it != end_it) {
430             if (!it->char_data.empty()) {
431                 retval = it->char_data.back().code_point_index + 1;
432                 break;
433             }
434             ++it;
435         }
436     }
437     return retval;
438 }
439 
StringIndexOf(std::size_t line,CPSize index,const std::vector<Font::LineData> & line_data)440 StrSize GG::StringIndexOf(std::size_t line, CPSize index,
441                           const std::vector<Font::LineData>& line_data)
442 {
443     StrSize retval(0);
444     if (line_data.size() <= line) {
445         auto it = line_data.rbegin();
446         auto end_it = line_data.rend();
447         while (it != end_it) {
448             if (!it->char_data.empty()) {
449                 retval = it->char_data.back().string_index + it->char_data.back().string_size;
450                 break;
451             }
452             ++it;
453         }
454     } else if (index < line_data[line].char_data.size()) {
455         retval = line_data[line].char_data[Value(index)].string_index;
456     } else {
457         auto it = line_data.rbegin() + (line_data.size() - 1 - line);
458         auto end_it = line_data.rend();
459         while (it != end_it) {
460             if (!it->char_data.empty()) {
461                 retval = it->char_data.back().string_index + it->char_data.back().string_size;
462                 break;
463             }
464             ++it;
465         }
466     }
467     return retval;
468 }
469 
LinePositionOf(CPSize index,const std::vector<Font::LineData> & line_data)470 std::pair<std::size_t, CPSize> GG::LinePositionOf(
471     CPSize index, const std::vector<Font::LineData>& line_data)
472 {
473     std::pair<std::size_t, CPSize> retval(std::numeric_limits<std::size_t>::max(),
474                                           INVALID_CP_SIZE);
475     for (std::size_t i = 0; i < line_data.size(); ++i) {
476         const auto& char_data = line_data[i].char_data;
477         if (!char_data.empty() &&
478             char_data.front().code_point_index <= index &&
479             index <= char_data.back().code_point_index)
480         {
481             retval.first = i;
482             retval.second = index - char_data.front().code_point_index;
483             break;
484         }
485     }
486     return retval;
487 }
488 
489 namespace {
490     namespace xpr = boost::xpressive;
491 
492     /** CompiledRegex maintains a compiled boost::xpressive regular
493         expression that includes a tag stack which can be cleared and
494         provided to callers without the overhead of recompiling the
495         regular expression.*/
496     class CompiledRegex {
497     public:
CompiledRegex(const std::unordered_set<std::string> & known_tags,bool strip_unpaired_tags)498         CompiledRegex(const std::unordered_set<std::string>& known_tags,
499                       bool strip_unpaired_tags) :
500             m_known_tags(&known_tags)
501         {
502             // Synonyms for s1 thru s5 sub matches
503             xpr::mark_tag tag_name_tag(1);
504             xpr::mark_tag open_bracket_tag(2);
505             xpr::mark_tag close_bracket_tag(3);
506             xpr::mark_tag whitespace_tag(4);
507             xpr::mark_tag text_tag(5);
508 
509 #if BOOST_VERSION >= 106000
510             using boost::placeholders::_1;
511 #endif
512 
513             // The comments before each regex are intended to clarify the mapping from xpressive
514             // notation to the more typical regex notation.  If you read xpressive or don't read
515             // regex then ignore them.
516 
517             // -+ 'non-greedy',   ~ 'not',   set[|] 'set',    _s 'space' = 'anything but space or <'
518             const xpr::sregex TAG_PARAM =
519                 -+~xpr::set[xpr::_s | '<'];
520 
521             //+_w one or more greed word chars,  () group no capture,  [] semantic operation
522             const xpr::sregex OPEN_TAG_NAME =
523                 (+xpr::_w)[xpr::check(boost::bind(&CompiledRegex::MatchesKnownTag, this, _1))];
524 
525             // (+_w) one or more greedy word check matches stack
526             const xpr::sregex CLOSE_TAG_NAME =
527                 (+xpr::_w)[xpr::check(boost::bind(&CompiledRegex::MatchesTopOfStack, this, _1))];
528 
529             // *blank  'zero or more greedy whitespace',   >> 'followed by',    _ln 'newline',
530             // (set = 'a', 'b') is '[ab]',    +blank 'one or more greedy blank'
531             const xpr::sregex WHITESPACE =
532                 (*xpr::blank >> (xpr::_ln | (xpr::set = '\n', '\r', '\f'))) | +xpr::blank;
533 
534             // < followed by not space or <   or one or more not space or <
535             const xpr::sregex TEXT =
536                 ('<' >> *~xpr::set[xpr::_s | '<']) | (+~xpr::set[xpr::_s | '<']);
537 
538             if (!strip_unpaired_tags) {
539                 m_EVERYTHING =
540                     ('<' >> (tag_name_tag = OPEN_TAG_NAME)           // < followed by TAG_NAME
541                      >> xpr::repeat<0, 9>(+xpr::blank >> TAG_PARAM)  // repeat 0 to 9 a single blank followed
542                                                                      // by TAG_PARAM
543                      >> (open_bracket_tag.proto_base() = '>'))       // s1. close tag and push operation
544                     [PushP(xpr::ref(m_text), xpr::ref(m_tag_stack), xpr::ref(m_ignore_tags), tag_name_tag)] |
545                     ("</" >> (tag_name_tag = CLOSE_TAG_NAME) >> (close_bracket_tag.proto_base() = '>')) |
546                     (whitespace_tag = WHITESPACE) |
547                     (text_tag = TEXT);
548             } else {
549                 // don't care about matching with tag stack when
550                 // matching close tags, or updating the stack when
551                 // matching open tags
552                 m_EVERYTHING =
553                     ('<' >> OPEN_TAG_NAME >> xpr::repeat<0, 9>(+xpr::blank >> TAG_PARAM) >> '>') |
554                     ("</" >> OPEN_TAG_NAME >> '>') |
555                     (whitespace_tag = WHITESPACE) |
556                     (text_tag = TEXT);
557             }
558         }
559 
BindRegexToText(const std::string & new_text,bool ignore_tags)560         xpr::sregex& BindRegexToText(const std::string& new_text, bool ignore_tags) {
561             if (!m_tag_stack.empty()) {
562                 std::stack<Font::Substring> empty_stack;
563                 std::swap(m_tag_stack, empty_stack);
564             }
565             m_text = &new_text;
566             m_ignore_tags = ignore_tags;
567             return m_EVERYTHING;
568         }
569 
570     private:
571 
MatchesKnownTag(const boost::xpressive::ssub_match & sub)572         bool MatchesKnownTag(const boost::xpressive::ssub_match& sub) {
573             return m_ignore_tags ? false : m_known_tags->count(sub.str()) != 0;
574         }
575 
MatchesTopOfStack(const boost::xpressive::ssub_match & sub)576         bool MatchesTopOfStack(const boost::xpressive::ssub_match& sub) {
577             bool retval = m_tag_stack.empty() ? false : m_tag_stack.top() == sub;
578             if (retval) {
579                 m_tag_stack.pop();
580                 if (m_tag_stack.empty() || m_tag_stack.top() != PRE_TAG)
581                     m_ignore_tags = false;
582             }
583             return retval;
584         }
585 
586         const std::string* m_text = nullptr;
587         const std::unordered_set<std::string>* m_known_tags = nullptr;
588         bool m_ignore_tags = false;
589 
590         // m_tag_stack is used to track XML opening/closing tags.
591         std::stack<Font::Substring> m_tag_stack;
592 
593         // The combined regular expression.
594         xpr::sregex m_EVERYTHING;
595     };
596 
597     /** TagHandler stores a set of all known tags and provides pre-compiled regexs for those tags.
598 
599      Known tags are tags that will be parsed into TextElement OPEN_TAG or CLOSE_TAG. */
600     class TagHandler {
601     public:
TagHandler()602         TagHandler() :
603             m_known_tags(),
604             m_regex_w_tags(m_known_tags, false),
605             m_regex_w_tags_skipping_unmatched(m_known_tags, true)
606         {}
607 
608         /** Add a tag to the set of known tags.*/
Insert(const std::string & tag)609         void Insert(const std::string& tag)
610         { m_known_tags.insert(tag); }
611 
612         /** Remove a tag from the set of known tags.*/
Erase(const std::string & tag)613         void Erase(const std::string& tag)
614         { m_known_tags.erase(tag); }
615 
616         /** Remove all tags from the set of known tags.*/
Clear()617         void Clear()
618         { m_known_tags.clear(); }
619 
IsKnown(const std::string & tag) const620         bool IsKnown(const std::string &tag) const
621         { return m_known_tags.count(tag); }
622 
623         // Return a regex bound to \p text using the currently known
624         // tags.  If required \p ignore_tags and/or \p strip_unpaired_tags.
Regex(const std::string & text,bool ignore_tags,bool strip_unpaired_tags=false)625         xpr::sregex& Regex(const std::string& text, bool ignore_tags, bool strip_unpaired_tags = false)
626         {
627             if (!strip_unpaired_tags)
628                 return m_regex_w_tags.BindRegexToText(text, ignore_tags);
629             else
630                 return m_regex_w_tags_skipping_unmatched.BindRegexToText(text, ignore_tags);
631         }
632 
633     private:
634         // set of tags known to the handler
635         std::unordered_set<std::string> m_known_tags;
636 
637         // Compiled regular expression including tag stack
638         CompiledRegex m_regex_w_tags;
639 
640         // Compiled regular expression using tags but skipping unmatched tags.
641         CompiledRegex m_regex_w_tags_skipping_unmatched;
642     };
643 
644 
645     // Global set of known tags. Wrapped in a function for deterministic static initialization order.
StaticTagHandler()646     TagHandler& StaticTagHandler()
647     {
648         static TagHandler tag_handler;
649         return tag_handler;
650     }
651 
652     // Registers the default action and known tags.
RegisterDefaultTags()653     int RegisterDefaultTags()
654     {
655         StaticTagHandler().Insert(ITALIC_TAG);
656         StaticTagHandler().Insert(SHADOW_TAG);
657         StaticTagHandler().Insert(UNDERLINE_TAG);
658         StaticTagHandler().Insert(SUPERSCRIPT_TAG);
659         StaticTagHandler().Insert(SUBSCRIPT_TAG);
660         StaticTagHandler().Insert(RGBA_TAG);
661         StaticTagHandler().Insert(ALIGN_LEFT_TAG);
662         StaticTagHandler().Insert(ALIGN_CENTER_TAG);
663         StaticTagHandler().Insert(ALIGN_RIGHT_TAG);
664         StaticTagHandler().Insert(PRE_TAG);
665 
666         // Must have return value to call at static initialization time.
667         return 0;
668     }
669 
670     // Register the default tags at static initialization time.
671     int register_tags_dummy = RegisterDefaultTags();
672 }
673 
674 ///////////////////////////////////////
675 // class GG::Font::TextElement
676 ///////////////////////////////////////
TextElement()677 Font::TextElement::TextElement() :
678     whitespace(false),
679     newline(false),
680     cached_width(-X1)
681 {}
682 
TextElement(bool ws,bool nl)683 Font::TextElement::TextElement(bool ws, bool nl) :
684     whitespace(ws),
685     newline(nl),
686     cached_width(-X1)
687 {}
688 
~TextElement()689 Font::TextElement::~TextElement()
690 {}
691 
Bind(const std::string & whole_text)692 void Font::TextElement::Bind(const std::string& whole_text)
693 { text.Bind(whole_text); }
694 
Type() const695 Font::TextElement::TextElementType Font::TextElement::Type() const
696 { return newline ? NEWLINE : (whitespace ? WHITESPACE : TEXT); }
697 
Width() const698 X Font::TextElement::Width() const
699 {
700     if (cached_width == -X1)
701         cached_width = std::accumulate(widths.begin(), widths.end(), X0);
702     return cached_width;
703 }
704 
StringSize() const705 StrSize Font::TextElement::StringSize() const
706 { return StrSize(text.size()); }
707 
CodePointSize() const708 CPSize Font::TextElement::CodePointSize() const
709 { return CPSize(widths.size()); }
710 
711 
operator ==(const TextElement & rhs) const712 bool Font::TextElement::operator==(const TextElement &rhs) const
713 {
714     return (text == rhs.text && widths == rhs.widths
715             && whitespace == rhs.whitespace && newline == rhs.newline);
716 }
717 
718 ///////////////////////////////////////
719 // class GG::Font::FormattingTag
720 ///////////////////////////////////////
FormattingTag()721 Font::FormattingTag::FormattingTag() :
722     TextElement(),
723     close_tag(false)
724 {}
725 
FormattingTag(bool close)726 Font::FormattingTag::FormattingTag(bool close) :
727     TextElement(false, false),
728     close_tag(close)
729 {}
730 
Bind(const std::string & whole_text)731 void Font::FormattingTag::Bind(const std::string& whole_text)
732 {
733     TextElement::Bind(whole_text);
734     tag_name.Bind(whole_text);
735     for (Substring& substring : params) {
736         substring.Bind(whole_text);
737     }
738 }
739 
Type() const740 Font::FormattingTag::TextElementType Font::FormattingTag::Type() const
741 { return close_tag ? CLOSE_TAG : OPEN_TAG; }
742 
operator ==(const TextElement & rhs) const743 bool Font::FormattingTag::operator==(const TextElement &rhs) const
744 {
745     Font::FormattingTag const* ft = dynamic_cast<Font::FormattingTag const*>(&rhs);
746     return (ft
747             && Font::TextElement::operator==(rhs)
748             && params == ft->params
749             && tag_name == ft->tag_name
750             && close_tag == ft->close_tag);
751 }
752 
753 ///////////////////////////////////////
754 // class GG::Font::TextAndElementsAssembler
755 ///////////////////////////////////////
756 class Font::TextAndElementsAssembler::Impl
757 {
758 public:
Impl(const Font & font)759     Impl(const Font& font) :
760         m_font(font),
761         m_text(),
762         m_text_elements(),
763         m_are_widths_calculated(false)
764     {}
765 
766     /** Return the constructed text.*/
Text() const767     const std::string& Text() const
768     { return m_text; }
769 
770     /** Return the constructed TextElements.*/
Elements()771     const std::vector<std::shared_ptr<TextElement>>& Elements()
772     {
773         if (!m_are_widths_calculated)
774             m_font.FillTemplatedText(m_text, m_text_elements, m_text_elements.begin());
775 
776         return m_text_elements;
777     }
778 
779     /** Add an open tag iff it exists as a recognized tag.*/
AddOpenTag(const std::string & tag,const std::vector<std::string> * params=nullptr)780     void AddOpenTag(const std::string& tag, const std::vector<std::string>* params = nullptr)
781     {
782         if (!StaticTagHandler().IsKnown(tag))
783             return;
784 
785         m_are_widths_calculated = false;
786 
787         // Create the opening part of an open tag, like this: "<tag"
788         auto element = std::make_shared<Font::FormattingTag>(false);
789         size_t tag_begin = m_text.size();
790         size_t tag_name_begin = m_text.append("<").size();
791         size_t tag_name_end = m_text.append(tag).size();
792         element->tag_name = Substring(m_text,
793                                       std::next(m_text.begin(), tag_name_begin),
794                                       std::next(m_text.begin(), tag_name_end));
795 
796         // If there are params add them, like this: "<tag param1 param2"
797         if (params) {
798             for (const std::string& param : *params) {
799                 m_text.append(" ");
800                 size_t param_begin = m_text.size();
801                 size_t param_end = m_text.append(param).size();
802 
803                 element->params.push_back(Substring(m_text,
804                                                     std::next(m_text.begin(), param_begin),
805                                                     std::next(m_text.begin(), param_end)));
806             }
807         }
808 
809         // Create the close part of an open tag to complete the tag, like this:"<tag param1 param2>"
810         size_t tag_end = m_text.append(">").size();
811         element->text = Substring(m_text,
812                                   std::next(m_text.begin(), tag_begin),
813                                   std::next(m_text.begin(), tag_end));
814 
815         m_text_elements.push_back(element);
816     }
817 
818     /** Add a close tag iff it exists as a recognized tag.*/
AddCloseTag(const std::string & tag)819     void AddCloseTag(const std::string& tag)
820     {
821         if (!StaticTagHandler().IsKnown(tag))
822             return;
823 
824         m_are_widths_calculated = false;
825 
826         // Create a close tag that looks like this: "</tag>"
827         auto element = std::make_shared<Font::FormattingTag>(true);
828         size_t tag_begin = m_text.size();
829         size_t tag_name_begin = m_text.append("</").size();
830         size_t tag_name_end = m_text.append(tag).size();
831         size_t tag_end = m_text.append(">").size();
832         element->text = Substring(m_text,
833                                   std::next(m_text.begin(), tag_begin),
834                                   std::next(m_text.begin(), tag_end));
835 
836         element->tag_name = Substring(m_text,
837                                       std::next(m_text.begin(), tag_name_begin),
838                                       std::next(m_text.begin(), tag_name_end));
839         m_text_elements.push_back(element);
840     }
841 
842     /** Add a text element.  Any whitespace in this text element will be non-breaking.*/
AddText(const std::string & text)843     void AddText(const std::string& text)
844     {
845         m_are_widths_calculated = false;
846 
847         auto element = std::make_shared<Font::TextElement>(false, false);
848         size_t begin = m_text.size();
849         size_t end = m_text.append(text).size();
850         element->text = Substring(m_text,
851                                   std::next(m_text.begin(), begin),
852                                   std::next(m_text.begin(), end));
853         m_text_elements.push_back(element);
854     }
855 
856     /** Add a white space element.*/
AddWhitespace(const std::string & whitespace)857     void AddWhitespace(const std::string& whitespace)
858     {
859         m_are_widths_calculated = false;
860 
861         auto element = std::make_shared<Font::TextElement>(true, false);
862         size_t begin = m_text.size();
863         size_t end = m_text.append(whitespace).size();
864         element->text = Substring(m_text,
865                                   std::next(m_text.begin(), begin),
866                                   std::next(m_text.begin(), end));
867         m_text_elements.push_back(element);
868     }
869 
870     /** Add a newline element.*/
AddNewline()871     void AddNewline()
872     {
873         m_are_widths_calculated = false;
874 
875         m_text_elements.push_back(std::make_shared<Font::TextElement>(false, true));
876     }
877 
878     /** Add open color tag.*/
AddOpenTag(const Clr & color)879     void AddOpenTag(const Clr& color)
880     {
881         std::vector<std::string> params = { std::to_string(color.r),
882                                             std::to_string(color.g),
883                                             std::to_string(color.b),
884                                             std::to_string(color.a) };
885 
886         AddOpenTag("rgba", &params);
887     }
888 
889 private:
890     const Font& m_font;
891     std::string m_text;
892     std::vector<std::shared_ptr<TextElement>> m_text_elements;
893     bool m_are_widths_calculated;
894 };
895 
896 
TextAndElementsAssembler(const Font & font)897 Font::TextAndElementsAssembler::TextAndElementsAssembler(const Font& font) :
898     m_impl(new Impl(font))
899 {}
900 
901 // Required because Impl is defined here
~TextAndElementsAssembler()902 Font::TextAndElementsAssembler::~TextAndElementsAssembler()
903 {}
904 
Text()905 const std::string& Font::TextAndElementsAssembler::Text()
906 { return m_impl->Text(); }
907 
Elements()908 const std::vector<std::shared_ptr<Font::TextElement>>& Font::TextAndElementsAssembler::Elements()
909 { return m_impl->Elements(); }
910 
AddOpenTag(const std::string & tag)911 Font::TextAndElementsAssembler& Font::TextAndElementsAssembler::AddOpenTag(const std::string& tag)
912 {
913     m_impl->AddOpenTag(tag);
914     return *this;
915 }
916 
AddOpenTag(const std::string & tag,const std::vector<std::string> & params)917 Font::TextAndElementsAssembler& Font::TextAndElementsAssembler::AddOpenTag(
918     const std::string& tag, const std::vector<std::string>& params)
919 {
920     m_impl->AddOpenTag(tag, &params);
921     return *this;
922 }
923 
AddCloseTag(const std::string & tag)924 Font::TextAndElementsAssembler& Font::TextAndElementsAssembler::AddCloseTag(const std::string& tag)
925 {
926     m_impl->AddCloseTag(tag);
927     return *this;
928 }
929 
AddText(const std::string & text)930 Font::TextAndElementsAssembler& Font::TextAndElementsAssembler::AddText(const std::string& text)
931 {
932     m_impl->AddText(text);
933     return *this;
934 }
935 
AddWhitespace(const std::string & whitespace)936 Font::TextAndElementsAssembler& Font::TextAndElementsAssembler::AddWhitespace(const std::string& whitespace)
937 {
938     m_impl->AddWhitespace(whitespace);
939     return *this;
940 }
941 
AddNewline()942 Font::TextAndElementsAssembler& Font::TextAndElementsAssembler::AddNewline()
943 {
944     m_impl->AddNewline();
945     return *this;
946 }
947 
AddOpenTag(const Clr & color)948 Font::TextAndElementsAssembler& Font::TextAndElementsAssembler::AddOpenTag(const Clr& color)
949 {
950     m_impl->AddOpenTag(color);
951     return *this;
952 }
953 
954 ///////////////////////////////////////
955 // class GG::Font::LineData
956 ///////////////////////////////////////
LineData()957 Font::LineData::LineData() :
958     justification(ALIGN_CENTER)
959 {}
960 
Width() const961 X Font::LineData::Width() const
962 { return char_data.empty() ? X0 : char_data.back().extent; }
963 
Empty() const964 bool Font::LineData::Empty() const
965 { return char_data.empty(); }
966 
967 ///////////////////////////////////////
968 // class GG::Font::RenderState
969 ///////////////////////////////////////
RenderState()970 Font::RenderState::RenderState() :
971     use_italics(0),
972     use_shadow(0),
973     draw_underline(0),
974     super_sub_shift(0)
975 {
976     // Initialize the color stack with the current color
977     GLfloat current[4];
978     glGetFloatv(GL_CURRENT_COLOR, current);
979     PushColor(current[0]*255, current[1]*255, current[2]*255, current[3]*255);
980 }
981 
RenderState(Clr color)982 Font::RenderState::RenderState (Clr color):
983     use_italics(0),
984     use_shadow(0),
985     draw_underline(0),
986     super_sub_shift(0)
987 {
988     PushColor(color.r, color.g, color.b, color.a);
989 }
990 
PushColor(GLubyte r,GLubyte g,GLubyte b,GLubyte a)991 void Font::RenderState::PushColor(GLubyte r, GLubyte g, GLubyte b, GLubyte a)
992 {
993     Clr color(r, g, b, a);
994     // The same color may end up being stored multiple times, but the cost of deduplication
995     // is greater than the cost of just letting it be so.
996     color_index_stack.push(used_colors.size());
997     used_colors.push_back(color);
998 }
999 
PopColor()1000 void Font::RenderState::PopColor()
1001 {
1002     // Never remove the initial color from the stack
1003     if (color_index_stack.size() > 1)
1004         color_index_stack.pop();
1005 }
1006 
CurrentIndex() const1007 int Font::RenderState::CurrentIndex() const
1008 { return color_index_stack.top(); }
1009 
CurrentColor() const1010 const Clr& Font::RenderState::CurrentColor() const
1011 { return used_colors[CurrentIndex()]; }
1012 
ColorsEmpty() const1013 bool Font::RenderState::ColorsEmpty() const
1014 { return color_index_stack.size() <= 1; }
1015 
1016 
1017 ///////////////////////////////////////
1018 // class GG::Font::RenderCache
1019 ///////////////////////////////////////
1020 
1021 // Must be here for scoped_ptr deleter to work
RenderCache()1022 Font::RenderCache::RenderCache() :
1023     vertices(new GL2DVertexBuffer()),
1024     coordinates(new GLTexCoordBuffer()),
1025     colors(new GLRGBAColorBuffer()),
1026     underline_vertices(new GL2DVertexBuffer()),
1027     underline_colors(new GLRGBAColorBuffer())
1028 {}
1029 
1030 // Must be here for scoped_ptr deleter to work
~RenderCache()1031 Font::RenderCache::~RenderCache()
1032 {}
1033 
1034 ///////////////////////////////////////
1035 // class GG::Font::LineData::CharData
1036 ///////////////////////////////////////
CharData()1037 Font::LineData::CharData::CharData() :
1038     extent(0),
1039     string_index(0)
1040 {}
1041 
CharData(X extent_,StrSize str_index,StrSize str_size,CPSize cp_index,const std::vector<std::shared_ptr<TextElement>> & tags_)1042 Font::LineData::CharData::CharData(X extent_, StrSize str_index, StrSize str_size, CPSize cp_index,
1043                                    const std::vector<std::shared_ptr<TextElement>>& tags_) :
1044     extent(extent_),
1045     string_index(str_index),
1046     string_size(str_size),
1047     code_point_index(cp_index),
1048     tags()
1049 {
1050     for (auto tag : tags_) {
1051         tags.push_back(std::dynamic_pointer_cast<FormattingTag>(tag));
1052     }
1053 }
1054 
1055 
1056 ///////////////////////////////////////
1057 // struct GG::Font::Glyph
1058 ///////////////////////////////////////
Glyph()1059 Font::Glyph::Glyph() :
1060     y_offset(0),
1061     left_bearing(0),
1062     advance(0),
1063     width(0)
1064 {}
1065 
Glyph(const std::shared_ptr<Texture> & texture,const Pt & ul,const Pt & lr,Y y_ofs,X lb,X adv)1066 Font::Glyph::Glyph(const std::shared_ptr<Texture>& texture, const Pt& ul, const Pt& lr, Y y_ofs, X lb, X adv) :
1067     sub_texture(texture, ul.x, ul.y, lr.x, lr.y),
1068     y_offset(y_ofs),
1069     left_bearing(lb),
1070     advance(adv),
1071     width(ul.x - lr.x)
1072 {}
1073 
1074 ///////////////////////////////////////
1075 // class GG::Font
1076 ///////////////////////////////////////
1077 
Font()1078 Font::Font() :
1079     m_pt_sz(0),
1080     m_ascent(0),
1081     m_descent(0),
1082     m_height(0),
1083     m_lineskip(0),
1084     m_underline_offset(0.0),
1085     m_underline_height(0.0),
1086     m_italics_offset(0.0),
1087     m_super_sub_offset(0.0),
1088     m_shadow_offset(0.0),
1089     m_space_width(0)
1090 {}
1091 
Font(const std::string & font_filename,unsigned int pts)1092 Font::Font(const std::string& font_filename, unsigned int pts) :
1093     m_font_filename(font_filename),
1094     m_pt_sz(pts),
1095     m_ascent(0),
1096     m_descent(0),
1097     m_height(0),
1098     m_lineskip(0),
1099     m_underline_offset(0.0),
1100     m_underline_height(0.0),
1101     m_italics_offset(0.0),
1102     m_shadow_offset(0.0),
1103     m_space_width(0)
1104 {
1105     if (!m_font_filename.empty()) {
1106         detail::FTFaceWrapper wrapper;
1107         FT_Error error = GetFace(wrapper.m_face);
1108         CheckFace(wrapper.m_face, error);
1109         Init(wrapper.m_face);
1110     }
1111 }
1112 
Font(const std::string & font_filename,unsigned int pts,const std::vector<unsigned char> & file_contents)1113 Font::Font(const std::string& font_filename, unsigned int pts,
1114            const std::vector<unsigned char>& file_contents) :
1115     m_font_filename(font_filename),
1116     m_pt_sz(pts),
1117     m_ascent(0),
1118     m_descent(0),
1119     m_height(0),
1120     m_lineskip(0),
1121     m_underline_offset(0.0),
1122     m_underline_height(0.0),
1123     m_italics_offset(0.0),
1124     m_shadow_offset(0.0),
1125     m_space_width(0)
1126 {
1127     assert(!file_contents.empty());
1128     detail::FTFaceWrapper wrapper;
1129     FT_Error error = GetFace(file_contents, wrapper.m_face);
1130     CheckFace(wrapper.m_face, error);
1131     Init(wrapper.m_face);
1132 }
1133 
~Font()1134 Font::~Font()
1135 {}
1136 
FontName() const1137 const std::string& Font::FontName() const
1138 { return m_font_filename; }
1139 
PointSize() const1140 unsigned int Font::PointSize() const
1141 { return m_pt_sz; }
1142 
UnicodeCharsets() const1143 const std::vector<UnicodeCharset>& Font::UnicodeCharsets() const
1144 { return m_charsets; }
1145 
Ascent() const1146 Y Font::Ascent() const
1147 { return m_ascent; }
1148 
Descent() const1149 Y Font::Descent() const
1150 { return m_descent; }
1151 
Height() const1152 Y Font::Height() const
1153 { return m_height; }
1154 
Lineskip() const1155 Y Font::Lineskip() const
1156 { return m_lineskip; }
1157 
SpaceWidth() const1158 X Font::SpaceWidth() const
1159 { return m_space_width; }
1160 
RenderText(const Pt & pt_,const std::string & text) const1161 X Font::RenderText(const Pt& pt_, const std::string& text) const
1162 {
1163     Pt pt = pt_;
1164     X orig_x = pt.x;
1165 
1166     double orig_color[4];
1167     glGetDoublev(GL_CURRENT_COLOR, orig_color);
1168     glBindTexture(GL_TEXTURE_2D, m_texture->OpenGLId());
1169 
1170     RenderCache cache;
1171     RenderState render_state;
1172 
1173     for (auto text_it = text.begin(); text_it != text.end();) {
1174         std::uint32_t c = utf8::next(text_it, text.end());
1175         auto it = m_glyphs.find(c);
1176         if (it == m_glyphs.end()) {
1177             pt.x += m_space_width; // move forward by the extent of the character when a whitespace or unprintable glyph is requested
1178         } else {
1179             pt.x += StoreGlyph(pt, it->second, &render_state, cache);
1180         }
1181     }
1182 
1183     cache.vertices->createServerBuffer();
1184     cache.coordinates->createServerBuffer();
1185     cache.colors->createServerBuffer();
1186     RenderCachedText(cache);
1187 
1188     return pt.x - orig_x;
1189 }
1190 
RenderText(const Pt & ul,const Pt & lr,const std::string & text,Flags<TextFormat> & format,const std::vector<LineData> & line_data,RenderState * render_state) const1191 void Font::RenderText(const Pt& ul, const Pt& lr, const std::string& text, Flags<TextFormat>& format,
1192                       const std::vector<LineData>& line_data, RenderState* render_state/* = 0*/) const
1193 {
1194     RenderState state;
1195     if (!render_state)
1196         render_state = &state;
1197 
1198     RenderText(ul, lr, text, format, line_data, *render_state,
1199                0, CP0, line_data.size(),
1200                line_data.empty() ? CP0 : CPSize(line_data.back().char_data.size()));
1201 }
1202 
RenderText(const Pt & ul,const Pt & lr,const std::string & text,Flags<TextFormat> & format,const std::vector<LineData> & line_data,RenderState & render_state,std::size_t begin_line,CPSize begin_char,std::size_t end_line,CPSize end_char) const1203 void Font::RenderText(const Pt& ul, const Pt& lr, const std::string& text, Flags<TextFormat>& format,
1204                       const std::vector<LineData>& line_data, RenderState& render_state,
1205                       std::size_t begin_line, CPSize begin_char,
1206                       std::size_t end_line, CPSize end_char) const
1207  {
1208     RenderCache cache;
1209     PreRenderText(ul, lr, text, format, line_data, render_state, begin_line, begin_char, end_line, end_char, cache);
1210     RenderCachedText(cache);
1211 }
1212 
PreRenderText(const Pt & ul,const Pt & lr,const std::string & text,Flags<TextFormat> & format,RenderCache & cache,const std::vector<LineData> & line_data,RenderState * render_state) const1213 void Font::PreRenderText(const Pt& ul, const Pt& lr, const std::string& text, Flags<TextFormat>& format, RenderCache& cache,
1214                          const std::vector<LineData>& line_data, RenderState* render_state/* = 0*/) const
1215  {
1216     RenderState state;
1217     if (!render_state)
1218         render_state = &state;
1219 
1220     PreRenderText(ul, lr, text, format, line_data, *render_state,
1221                   0, CP0, line_data.size(), line_data.empty() ? CP0 : CPSize(line_data.back().char_data.size()), cache);
1222 }
1223 
PreRenderText(const Pt & ul,const Pt & lr,const std::string & text,Flags<TextFormat> & format,const std::vector<LineData> & line_data,RenderState & render_state,std::size_t begin_line,CPSize begin_char,std::size_t end_line,CPSize end_char,RenderCache & cache) const1224 void Font::PreRenderText(const Pt& ul, const Pt& lr, const std::string& text, Flags<TextFormat>& format,
1225                          const std::vector<LineData>& line_data, RenderState& render_state,
1226                          std::size_t begin_line, CPSize begin_char,
1227                          std::size_t end_line, CPSize end_char,
1228                          RenderCache& cache) const
1229 {
1230     double orig_color[4];
1231     glGetDoublev(GL_CURRENT_COLOR, orig_color);
1232     //glBindTexture(GL_TEXTURE_2D, m_texture->OpenGLId());
1233 
1234     Y y_origin = ul.y; // default value for FORMAT_TOP
1235     if (format & FORMAT_BOTTOM)
1236         y_origin = lr.y - (static_cast<int>(end_line - begin_line - 1) * m_lineskip + m_height);
1237     else if (format & FORMAT_VCENTER)
1238         y_origin = ul.y + ((lr.y - ul.y) - (static_cast<int>(end_line - begin_line - 1) * m_lineskip + m_height)) / 2.0;
1239 
1240     for (std::size_t i = begin_line; i < end_line; ++i) {
1241         const LineData& line = line_data[i];
1242         X x_origin = ul.x; // default value for FORMAT_LEFT
1243         if (line.justification == ALIGN_RIGHT)
1244             x_origin = lr.x - line.Width();
1245         else if (line.justification == ALIGN_CENTER)
1246             x_origin = ul.x + ((lr.x - ul.x) - line.Width()) / 2.0;
1247         Y y = y_origin + static_cast<int>(i - begin_line) * m_lineskip;
1248         X x = x_origin;
1249 
1250         CPSize start = CP0;
1251         if (i == begin_line)
1252             start = std::max(CP0, std::min(begin_char, CPSize(line.char_data.size() - 1)));
1253         CPSize end = CPSize(line.char_data.size());
1254         if (i == end_line - 1)
1255             end = std::max(CP0, std::min(end_char, CPSize(line.char_data.size())));
1256 
1257         auto string_end_it = text.end();
1258         for (CPSize j = start; j < end; ++j) {
1259             const auto& char_data = line.char_data[Value(j)];
1260             for (auto tag : char_data.tags) {
1261                 HandleTag(tag, orig_color, render_state);
1262             }
1263             std::uint32_t c = utf8::peek_next(text.begin() + Value(char_data.string_index), string_end_it);
1264             assert((text[Value(char_data.string_index)] == '\n') == (c == WIDE_NEWLINE));
1265             if (c == WIDE_NEWLINE)
1266                 continue;
1267             auto it = m_glyphs.find(c);
1268             if (it == m_glyphs.end()) {
1269                 x = x_origin + char_data.extent; // move forward by the extent of the character when a whitespace or unprintable glyph is requested
1270             } else {
1271                 x += StoreGlyph(Pt(x, y), it->second, &render_state, cache);
1272             }
1273         }
1274     }
1275 
1276     cache.vertices->createServerBuffer();
1277     cache.coordinates->createServerBuffer();
1278     cache.colors->createServerBuffer();
1279 }
1280 
RenderCachedText(RenderCache & cache) const1281 void Font::RenderCachedText(RenderCache& cache) const
1282 {
1283     glBindTexture(GL_TEXTURE_2D, m_texture->OpenGLId());
1284 
1285     glPushClientAttrib(GL_CLIENT_ALL_ATTRIB_BITS);
1286     glEnableClientState(GL_VERTEX_ARRAY);
1287     glEnableClientState(GL_TEXTURE_COORD_ARRAY);
1288     glEnableClientState(GL_COLOR_ARRAY);
1289 
1290     cache.vertices->activate();
1291     cache.coordinates->activate();
1292     cache.colors->activate();
1293     glDrawArrays(GL_QUADS, 0,  cache.vertices->size());
1294 
1295     glBindTexture(GL_TEXTURE_2D, 0);
1296     glDisableClientState(GL_TEXTURE_COORD_ARRAY);
1297 
1298     cache.underline_vertices->activate();
1299     cache.underline_colors->activate();
1300     glDrawArrays(GL_QUADS, 0, cache.underline_vertices->size());
1301 
1302     glPopClientAttrib();
1303 }
1304 
ProcessTagsBefore(const std::vector<LineData> & line_data,RenderState & render_state,std::size_t begin_line,CPSize begin_char) const1305 void Font::ProcessTagsBefore(const std::vector<LineData>& line_data, RenderState& render_state,
1306                              std::size_t begin_line, CPSize begin_char) const
1307 {
1308     double orig_color[4];
1309     glGetDoublev(GL_CURRENT_COLOR, orig_color);
1310 
1311     if (line_data.empty())
1312         return;
1313 
1314     for (std::size_t i = 0; i <= begin_line; ++i) {
1315         const LineData& line = line_data[i];
1316         for (CPSize j = CP0;
1317              j < ((i == begin_line) ? begin_char : CPSize(line.char_data.size()));
1318              ++j)
1319         {
1320             for (auto& tag : line.char_data[Value(j)].tags) {
1321                 HandleTag(tag, orig_color, render_state);
1322             }
1323         }
1324     }
1325 }
1326 
StripTags(const std::string & text,bool strip_unpaired_tags)1327 std::string Font::StripTags(const std::string& text, bool strip_unpaired_tags)
1328 {
1329     using namespace boost::xpressive;
1330 
1331     sregex & regex = StaticTagHandler().Regex(text, false, strip_unpaired_tags);
1332 
1333     mark_tag tag_name_tag(1);
1334     mark_tag open_bracket_tag(2);
1335     mark_tag close_bracket_tag(3);
1336     mark_tag whitespace_tag(4);
1337     mark_tag text_tag(5);
1338 
1339     std::stringstream retval;
1340 
1341     // scan through matched markup and text, saving only the non-tag-text
1342     sregex_iterator it(text.begin(), text.end(), regex);
1343     sregex_iterator end_it;
1344     for (; it != end_it; ++it) {
1345         sub_match<std::string::const_iterator> const* text_match;
1346         sub_match<std::string::const_iterator> const* whitespace_match;
1347 
1348         if ((text_match = &(*it)[text_tag]) && (text_match->matched))
1349             retval << Substring(text, *text_match);
1350 
1351         else if ((whitespace_match = &(*it)[whitespace_tag]) && whitespace_match->matched)
1352             retval << Substring(text, *whitespace_match);
1353     }
1354 
1355     return retval.str();
1356 }
1357 
TextExtent(const std::vector<LineData> & line_data) const1358 Pt Font::TextExtent(const std::vector<LineData>& line_data) const
1359 {
1360     Pt retval;
1361     for (const LineData& line : line_data) {
1362         if (retval.x < line.Width())
1363             retval.x = line.Width();
1364     }
1365     bool is_empty = line_data.empty()
1366         || (line_data.size() == 1 && line_data.front().Empty());
1367     retval.y = is_empty ? Y0 : (static_cast<int>(line_data.size()) - 1) * m_lineskip + m_height;
1368     return retval;
1369 }
1370 
RegisterKnownTag(const std::string & tag)1371 void Font::RegisterKnownTag(const std::string& tag)
1372 { StaticTagHandler().Insert(tag); }
1373 
RemoveKnownTag(const std::string & tag)1374 void Font::RemoveKnownTag(const std::string& tag)
1375 { StaticTagHandler().Erase(tag); }
1376 
ClearKnownTags()1377 void Font::ClearKnownTags()
1378 {
1379     StaticTagHandler().Clear();
1380 
1381     // Always know the default tags.
1382     RegisterDefaultTags();
1383 }
1384 
ThrowBadGlyph(const std::string & format_str,std::uint32_t c)1385 void Font::ThrowBadGlyph(const std::string& format_str, std::uint32_t c)
1386 {
1387     boost::format format(isprint(c) ? "%c" : "U+%x");
1388     throw BadGlyph(boost::io::str(boost::format(format_str) % boost::io::str(format % c)));
1389 }
1390 
1391 namespace DebugOutput {
PrintParseResults(const std::vector<std::shared_ptr<Font::TextElement>> & text_elements)1392     void PrintParseResults(const std::vector<std::shared_ptr<Font::TextElement>>& text_elements) {
1393         std::cout << "results of parse:\n";
1394         for (auto& elem : text_elements) {
1395             if (auto tag_elem = std::dynamic_pointer_cast<Font::FormattingTag>(elem)) {
1396                 std::cout << "FormattingTag\n    text=\"" << tag_elem->text << "\" (@ "
1397                           << static_cast<const void*>(&*tag_elem->text.begin()) << ")\n    widths=";
1398                 for (const X& width : tag_elem->widths) {
1399                     std::cout << width << " ";
1400                 }
1401                 std::cout << "\n    whitespace=" << tag_elem->whitespace << "\n    newline=" << tag_elem->newline << "\n    params=\n";
1402                 for (const Font::Substring& param : tag_elem->params) {
1403                     std::cout << "        \"" << param << "\"\n";
1404                 }
1405                 std::cout << "    tag_name=\"" << tag_elem->tag_name << "\"\n    close_tag=" << tag_elem->close_tag << "\n";
1406             } else {
1407                 std::cout << "TextElement\n    text=\"" << elem->text << "\" (@ "
1408                           << static_cast<const void*>(&*elem->text.begin()) << ")\n    widths=";
1409                 for (const X& width : elem->widths) {
1410                     std::cout << width << " ";
1411                 }
1412                 std::cout << "\n    whitespace=" << elem->whitespace << "\n    newline=" << elem->newline << "\n";
1413             }
1414             std::cout << "    string_size=" << elem->StringSize() << "\n";
1415             std::cout << "\n";
1416         }
1417         std::cout << std::endl;
1418     }
1419 
PrintLineBreakdown(const std::string & text,const Flags<TextFormat> & format,const X box_width,const std::vector<Font::LineData> & line_data)1420     void PrintLineBreakdown(const std::string& text,
1421                             const Flags<TextFormat>& format,
1422                             const X box_width,
1423                             const std::vector<Font::LineData>& line_data)
1424     {
1425         std::cout << "Font::DetermineLines(text=\"" << text << "\" (@ "
1426                   << static_cast<const void*>(&*text.begin()) << ") format="
1427                   << format << " box_width=" << box_width << ")" << std::endl;
1428 
1429         std::cout << "Line breakdown:\n";
1430         for (std::size_t i = 0; i < line_data.size(); ++i) {
1431             std::cout << "Line " << i << ":\n    extents=";
1432             for (const auto& character : line_data[i].char_data) {
1433                 std::cout << character.extent << " ";
1434             }
1435             std::cout << "\n    string indices=";
1436             for (const auto& character : line_data[i].char_data) {
1437                 std::cout << character.string_index << " ";
1438             }
1439             std::cout << "\n    code point indices=";
1440             for (const auto& character : line_data[i].char_data) {
1441                 std::cout << character.code_point_index << " ";
1442             }
1443             std::cout << "\n    chars on line: \"";
1444             for (const auto& character : line_data[i].char_data) {
1445                 std::cout << text[Value(character.string_index)];
1446             }
1447             std::cout << "\"\n";
1448             for (std::size_t j = 0; j < line_data[i].char_data.size(); ++j) {
1449                 for (auto& tag_elem : line_data[i].char_data[j].tags) {
1450                     if (tag_elem) {
1451                         std::cout << "FormattingTag @" << j << "\n    text=\"" << tag_elem->text << "\"\n    widths=";
1452                         for (const X& width : tag_elem->widths) {
1453                             std::cout << width << " ";
1454                         }
1455                         std::cout << "\n    whitespace=" << tag_elem->whitespace
1456                                   << "\n    newline=" << tag_elem->newline << "\n    params=\n";
1457                         for (const auto& param : tag_elem->params) {
1458                             std::cout << "        \"" << param << "\"\n";
1459                         }
1460                         std::cout << "    tag_name=\"" << tag_elem->tag_name << "\"\n    close_tag="
1461                                   << tag_elem->close_tag << "\n";
1462                     }
1463                 }
1464             }
1465             std::cout << "    justification=" << line_data[i].justification << "\n" << std::endl;
1466         }
1467     }
1468 }
1469 
1470 std::vector<std::shared_ptr<Font::TextElement>>
ExpensiveParseFromTextToTextElements(const std::string & text,const Flags<TextFormat> & format) const1471     Font::ExpensiveParseFromTextToTextElements(const std::string& text,
1472                                                const Flags<TextFormat>& format) const
1473 {
1474     std::vector<std::shared_ptr<TextElement>> text_elements;
1475 
1476     using namespace boost::xpressive;
1477 
1478     if (text.empty())
1479         return text_elements;
1480 
1481     bool ignore_tags = format & FORMAT_IGNORETAGS;
1482 
1483     // Fetch and use the regular expression from the TagHandler which parses all the known XML tags.
1484     sregex& regex = StaticTagHandler().Regex(text, ignore_tags);
1485     sregex_iterator it(text.begin(), text.end(), regex);
1486 
1487     // These are the types found by the regular expression: XML open/close tags, text and
1488     // whitespace.  Each type will correspond to a type of TextElement.
1489     mark_tag tag_name_tag(1);
1490     mark_tag open_bracket_tag(2);
1491     mark_tag close_bracket_tag(3);
1492     mark_tag whitespace_tag(4);
1493     mark_tag text_tag(5);
1494 
1495     sregex_iterator end_it;
1496     while (it != end_it)
1497     {
1498         // Consolidate adjacent blocks of text.  If adjacent found substrings are all text, merge
1499         // them into a single Substring.
1500         bool need_increment = true;
1501         Substring combined_text;
1502         sub_match<std::string::const_iterator> const* text_match;
1503         while (it != end_it
1504                && (text_match = &(*it)[text_tag])
1505                && text_match->matched) {
1506             need_increment = false;
1507             if (combined_text.empty())
1508                 combined_text = Substring(text, *text_match);
1509             else
1510                 combined_text += *text_match;
1511             ++it;
1512         }
1513 
1514         // If the element is not a text element then it must be an open tag, a close tag or whitespace.
1515         if (combined_text.empty()) {
1516 
1517             // Open XML tag.
1518             if ((*it)[open_bracket_tag].matched) {
1519                 auto element = std::make_shared<Font::FormattingTag>(false);
1520                 element->text = Substring(text, (*it)[0]);
1521 
1522                 // Check open tags for submatches which are parameters.  For example a Color tag
1523                 // might have RGB parameters.
1524                 if (1 < (*it).nested_results().size()) {
1525                     element->params.reserve((*it).nested_results().size() - 1);
1526                     for (auto nested_it = ++(*it).nested_results().begin();
1527                          nested_it != (*it).nested_results().end(); ++nested_it)
1528                     {
1529                         element->params.push_back(Substring(text, (*nested_it)[0]));
1530                     }
1531                 }
1532                 element->tag_name = Substring(text, (*it)[tag_name_tag]);
1533                 text_elements.push_back(element);
1534 
1535             // Close XML tag
1536             } else if ((*it)[close_bracket_tag].matched) {
1537                 auto element = std::make_shared<Font::FormattingTag>(true);
1538                 element->text = Substring(text, (*it)[0]);
1539                 element->tag_name = Substring(text, (*it)[tag_name_tag]);
1540                 text_elements.push_back(element);
1541 
1542             // Whitespace element
1543             } else if ((*it)[whitespace_tag].matched) {
1544                 auto element = std::make_shared<Font::TextElement>(true, false);
1545                 element->text = Substring(text, (*it)[whitespace_tag]);
1546                 text_elements.push_back(element);
1547 
1548                 // If the last character of a whitespace element is a line ending then create a
1549                 // newline TextElement.
1550                 char last_char = *std::prev(element->text.end());
1551                 if (last_char == '\n' || last_char == '\f' || last_char == '\r') {
1552                     text_elements.push_back(std::make_shared<Font::TextElement>(false, true));
1553                 }
1554             }
1555 
1556         // Basic text element.
1557         } else {
1558             auto element = std::make_shared<Font::TextElement>(false, false);
1559             element->text = combined_text;
1560             text_elements.push_back(element);
1561         }
1562 
1563         if (need_increment)
1564             ++it;
1565     }
1566 
1567     // fill in the widths of code points in each TextElement
1568     FillTemplatedText(text, text_elements, text_elements.begin());
1569 
1570 #if DEBUG_DETERMINELINES
1571     DebugOutput::PrintParseResults(text_elements);
1572 #endif
1573     return text_elements;
1574 }
1575 
1576 
FillTemplatedText(const std::string & text,std::vector<std::shared_ptr<Font::TextElement>> & text_elements,std::vector<std::shared_ptr<Font::TextElement>>::iterator start) const1577 void Font::FillTemplatedText(
1578     const std::string& text,
1579     std::vector<std::shared_ptr<Font::TextElement>>& text_elements,
1580     std::vector<std::shared_ptr<Font::TextElement>>::iterator start) const
1581 {
1582     // For each TextElement in text_elements starting from start.
1583     auto& te_it = start;
1584     for (; te_it != text_elements.end(); ++te_it) {
1585         std::shared_ptr<TextElement> elem = (*te_it);
1586 
1587         // For each character in the TextElement.
1588         auto text_it = elem->text.begin();
1589         auto end_it = elem->text.end();
1590         while (text_it != end_it) {
1591 
1592             // Find and set the width of the character glyph.
1593             elem->widths.push_back(X0);
1594             std::uint32_t c = utf8::next(text_it, end_it);
1595             if (c != WIDE_NEWLINE) {
1596                 auto it = m_glyphs.find(c);
1597                 // use a space when an unrendered glyph is requested (the
1598                 // space chararacter is always renderable)
1599                 elem->widths.back() = (it != m_glyphs.end()) ? it->second.advance : m_space_width;
1600             }
1601         }
1602     }
1603 }
1604 
ChangeTemplatedText(std::string & text,std::vector<std::shared_ptr<Font::TextElement>> & text_elements,const std::string & new_text,size_t targ_offset) const1605 void Font::ChangeTemplatedText(
1606     std::string& text,
1607     std::vector<std::shared_ptr<Font::TextElement>>& text_elements,
1608     const std::string& new_text,
1609     size_t targ_offset) const
1610 {
1611     if (targ_offset >= text_elements.size())
1612         return;
1613 
1614     if (new_text.empty())
1615         return;
1616 
1617     int change_of_len = 0;
1618 
1619     // Find the target text element.
1620     size_t curr_offset = 0;
1621     std::vector<std::shared_ptr<Font::TextElement>>::iterator te_it = text_elements.begin();
1622     while (te_it != text_elements.end()) {
1623         if ((*te_it)->Type() == TextElement::TEXT) {
1624             // Change the target text element
1625             if (targ_offset == curr_offset) {
1626                 // Change text
1627                 size_t ii_sub_begin = (*te_it)->text.begin() - text.begin();
1628                 size_t sub_len = (*te_it)->text.end() - (*te_it)->text.begin();
1629                 text.erase(ii_sub_begin, sub_len);
1630                 text.insert(ii_sub_begin, new_text);
1631 
1632                 change_of_len = new_text.size() - sub_len;
1633                 (*te_it)->text = Substring(text,
1634                                            std::next(text.begin(), ii_sub_begin),
1635                                            std::next(text.begin(), ii_sub_begin + new_text.size()));
1636                 break;
1637             }
1638             ++curr_offset;
1639         }
1640         ++te_it;
1641     }
1642 
1643     if (te_it == text_elements.end())
1644         return;
1645 
1646     std::vector<std::shared_ptr<Font::TextElement>>::iterator start_of_reflow = te_it;
1647 
1648     if (change_of_len != 0) {
1649         ++te_it;
1650         // Adjust the offset of each subsequent text_element
1651         while (te_it != text_elements.end())
1652         {
1653             size_t ii_sub_begin = (*te_it)->text.begin() - text.begin();
1654             size_t ii_sub_end = (*te_it)->text.end() - text.begin();
1655             (*te_it)->text = Substring(text,
1656                                        std::next(text.begin(), ii_sub_begin + change_of_len),
1657                                        std::next(text.begin(), ii_sub_end + change_of_len));
1658 
1659             ++te_it;
1660         }
1661     }
1662 
1663     FillTemplatedText(text, text_elements, start_of_reflow);
1664 }
1665 
DetermineLines(const std::string & text,Flags<TextFormat> & format,X box_width,const std::vector<std::shared_ptr<TextElement>> & text_elements) const1666 std::vector<Font::LineData> Font::DetermineLines(
1667     const std::string& text, Flags<TextFormat>& format, X box_width,
1668     const std::vector<std::shared_ptr<TextElement>>& text_elements) const
1669 {
1670     // HACK - Workaround for #2166
1671     // On OSX, right clicking an unowned planet at game start may result in utf8::invalid_utf8 or utf8::not_enough_room
1672     if (!utf8::is_valid(text.begin(), text.end())) {
1673         std::cerr << "Invalid UTF8 in text: " << text;
1674         return std::vector<Font::LineData>{};
1675     }
1676 
1677     ValidateFormat(format);
1678 
1679     RenderState render_state;
1680     int tab_width = 8; // default tab width
1681     X tab_pixel_width = tab_width * m_space_width; // get the length of a tab stop
1682     bool expand_tabs = format & FORMAT_LEFT; // tab expansion only takes place when the lines are left-justified (otherwise, tabs are just spaces)
1683     Alignment orig_just = ALIGN_NONE;
1684     if (format & FORMAT_LEFT)
1685         orig_just = ALIGN_LEFT;
1686     if (format & FORMAT_CENTER)
1687         orig_just = ALIGN_CENTER;
1688     if (format & FORMAT_RIGHT)
1689         orig_just = ALIGN_RIGHT;
1690     bool last_line_of_curr_just = false; // is this the last line of the current justification? (for instance when a </right> tag is encountered)
1691 
1692     std::vector<Font::LineData> line_data;
1693     if (!text_elements.empty()) {
1694         line_data.push_back(LineData());
1695         line_data.back().justification = orig_just;
1696     }
1697 
1698     X x = X0;
1699     // the position within the original string of the current TextElement
1700     StrSize original_string_offset(0);
1701     // the index of the first code point of the current TextElement
1702     CPSize code_point_offset(0);
1703     std::vector<std::shared_ptr<TextElement>> pending_formatting_tags;
1704     for (const auto& elem : text_elements) {
1705         // if a newline is explicitly requested, start a new one
1706         if (elem->Type() == TextElement::NEWLINE) {
1707             line_data.push_back(LineData());
1708             SetJustification(last_line_of_curr_just,
1709                              line_data.back(),
1710                              orig_just,
1711                              line_data[line_data.size() - 2].justification);
1712             x = X0;
1713 
1714         } else if (elem->Type() == TextElement::WHITESPACE) {
1715             auto it = elem->text.begin();
1716             auto end_it = elem->text.end();
1717             while (it != end_it) {
1718                 StrSize char_index(std::distance(elem->text.begin(), it));
1719                 std::uint32_t c = utf8::next(it, end_it);
1720                 StrSize char_size = std::distance(elem->text.begin(), it) - char_index;
1721                 if (c != WIDE_CR && c != WIDE_FF) {
1722                     X advance_position = x + m_space_width;
1723                     if (c == WIDE_TAB && expand_tabs)
1724                         advance_position = (((x / tab_pixel_width) + 1) * tab_pixel_width);
1725                     else if (c == WIDE_NEWLINE)
1726                         advance_position = x;
1727                     X advance = advance_position - x;
1728                     // if we're using linewrap and this space won't fit on
1729                     // this line
1730                     if ((format & FORMAT_LINEWRAP) && box_width < advance_position) {
1731                         if (!x && box_width < advance) {
1732                             // if the space is larger than the line and alone
1733                             // on the line, let the space overrun this line
1734                             // and then start a new one
1735                             line_data.push_back(LineData());
1736                             x = X0; // reset the x-position to 0
1737                             SetJustification(last_line_of_curr_just,
1738                                              line_data.back(),
1739                                              orig_just,
1740                                              line_data[line_data.size() - 2].justification);
1741                         } else {
1742                             // otherwise start a new line and put the space there
1743                             line_data.push_back(LineData());
1744                             x = advance;
1745                             line_data.back().char_data.push_back(
1746                                 LineData::CharData(x,
1747                                                    original_string_offset + char_index,
1748                                                    char_size,
1749                                                    code_point_offset,
1750                                                    pending_formatting_tags));
1751                             pending_formatting_tags.clear();
1752                             SetJustification(last_line_of_curr_just,
1753                                              line_data.back(),
1754                                              orig_just,
1755                                              line_data[line_data.size() - 2].justification);
1756                         }
1757                     } else { // there's room for the space, or we're not using linewrap
1758                         x += advance;
1759                         line_data.back().char_data.push_back(
1760                             LineData::CharData(x,
1761                                                original_string_offset + char_index,
1762                                                char_size,
1763                                                code_point_offset,
1764                                                pending_formatting_tags));
1765                         pending_formatting_tags.clear();
1766                     }
1767                 }
1768                 ++code_point_offset;
1769             }
1770         } else if (elem->Type() == TextElement::TEXT) {
1771             if (format & FORMAT_WORDBREAK) {
1772                 // if the text "word" overruns this line, and isn't alone on
1773                 // this line, move it down to the next line
1774                 if (box_width < x + elem->Width() && x) {
1775                     line_data.push_back(LineData());
1776                     x = X0;
1777                     SetJustification(last_line_of_curr_just,
1778                                      line_data.back(),
1779                                      orig_just,
1780                                      line_data[line_data.size() - 2].justification);
1781                 }
1782                 auto it = elem->text.begin();
1783                 auto end_it = elem->text.end();
1784                 std::size_t j = 0;
1785                 while (it != end_it) {
1786                     StrSize char_index(std::distance(elem->text.begin(), it));
1787                     utf8::next(it, end_it);
1788                     StrSize char_size = std::distance(elem->text.begin(), it) - char_index;
1789                     x += elem->widths[j];
1790                     line_data.back().char_data.push_back(
1791                         LineData::CharData(x,
1792                                            original_string_offset + char_index,
1793                                            char_size,
1794                                            code_point_offset,
1795                                            pending_formatting_tags));
1796                     pending_formatting_tags.clear();
1797                     ++j;
1798                     ++code_point_offset;
1799                 }
1800             } else {
1801                 auto it = elem->text.begin();
1802                 auto end_it = elem->text.end();
1803                 std::size_t j = 0;
1804                 while (it != end_it) {
1805                     StrSize char_index(std::distance(elem->text.begin(), it));
1806                     utf8::next(it, end_it);
1807                     StrSize char_size = std::distance(elem->text.begin(), it) - char_index;
1808                     // if the char overruns this line, and isn't alone on this
1809                     // line, move it down to the next line
1810                     if ((format & FORMAT_LINEWRAP) && box_width < x + elem->widths[j] && x) {
1811                         line_data.push_back(LineData());
1812                         x = elem->widths[j];
1813                         line_data.back().char_data.push_back(
1814                             LineData::CharData(x,
1815                                                original_string_offset + char_index,
1816                                                char_size,
1817                                                code_point_offset,
1818                                                pending_formatting_tags));
1819                         pending_formatting_tags.clear();
1820                         SetJustification(last_line_of_curr_just,
1821                                          line_data.back(),
1822                                          orig_just,
1823                                          line_data[line_data.size() - 2].justification);
1824                     } else {
1825                         // there's room for this char on this line, or there's
1826                         // no wrapping in use
1827                         x += elem->widths[j];
1828                         line_data.back().char_data.push_back(
1829                             LineData::CharData(x,
1830                                                original_string_offset + char_index,
1831                                                char_size,
1832                                                code_point_offset,
1833                                                pending_formatting_tags));
1834                         pending_formatting_tags.clear();
1835                     }
1836                     ++j;
1837                     ++code_point_offset;
1838                 }
1839             }
1840         } else if (elem->Type() == TextElement::OPEN_TAG) {
1841             assert(std::dynamic_pointer_cast<FormattingTag>(elem));
1842             std::shared_ptr<FormattingTag> elem_as_tag =
1843                 std::static_pointer_cast<FormattingTag>(elem);
1844             if (elem_as_tag->tag_name == ALIGN_LEFT_TAG)
1845                 line_data.back().justification = ALIGN_LEFT;
1846             else if (elem_as_tag->tag_name == ALIGN_CENTER_TAG)
1847                 line_data.back().justification = ALIGN_CENTER;
1848             else if (elem_as_tag->tag_name == ALIGN_RIGHT_TAG)
1849                 line_data.back().justification = ALIGN_RIGHT;
1850             else if (elem_as_tag->tag_name != PRE_TAG)
1851                 pending_formatting_tags.push_back(elem);
1852             last_line_of_curr_just = false;
1853             code_point_offset += elem->CodePointSize();
1854         } else if (elem->Type() == TextElement::CLOSE_TAG) {
1855             assert(std::dynamic_pointer_cast<FormattingTag>(elem));
1856             std::shared_ptr<FormattingTag> elem_as_tag =
1857                 std::static_pointer_cast<FormattingTag>(elem);
1858             if ((elem_as_tag->tag_name == ALIGN_LEFT_TAG && line_data.back().justification == ALIGN_LEFT) ||
1859                 (elem_as_tag->tag_name == ALIGN_CENTER_TAG && line_data.back().justification == ALIGN_CENTER) ||
1860                 (elem_as_tag->tag_name == ALIGN_RIGHT_TAG && line_data.back().justification == ALIGN_RIGHT))
1861                 last_line_of_curr_just = true;
1862             else if (elem_as_tag->tag_name != PRE_TAG)
1863                 pending_formatting_tags.push_back(elem);
1864             code_point_offset += elem->CodePointSize();
1865         }
1866         original_string_offset += elem->StringSize();
1867     }
1868     // disregard the final pending formatting tag, if any, since this is the
1869     // end of the text, and so it cannot have any effect
1870 
1871 #if DEBUG_DETERMINELINES
1872     DebugOutput::PrintLineBreakdown(text, format, box_width, line_data);
1873 #endif
1874 
1875     return line_data;
1876 }
1877 
GetFace(FT_Face & face)1878 FT_Error Font::GetFace(FT_Face& face)
1879 { return FT_New_Face(g_library.m_library, m_font_filename.c_str(), 0, &face); }
1880 
GetFace(const std::vector<unsigned char> & file_contents,FT_Face & face)1881 FT_Error Font::GetFace(const std::vector<unsigned char>& file_contents, FT_Face& face)
1882 {
1883     return FT_New_Memory_Face(g_library.m_library, &file_contents[0],
1884                               file_contents.size(), 0, &face);
1885 }
1886 
CheckFace(FT_Face face,FT_Error error)1887 void Font::CheckFace(FT_Face face, FT_Error error)
1888 {
1889     if (error || !face)
1890         throw BadFile("Face object created from \"" + m_font_filename + "\" was invalid");
1891     if (!FT_IS_SCALABLE(face)) {
1892         throw UnscalableFont("Attempted to create font \"" + m_font_filename +
1893                              "\" with uscalable font face");
1894     }
1895 }
1896 
Init(FT_Face & face)1897 void Font::Init(FT_Face& face)
1898 {
1899     FT_Fixed scale;
1900 
1901     if (!m_pt_sz) {
1902         throw InvalidPointSize("Attempted to create font \"" + m_font_filename +
1903                                "\" with 0 point size");
1904     }
1905 
1906     // Set the character size and use default 72 DPI
1907     if (FT_Set_Char_Size(face, 0, m_pt_sz * 64, 0, 0)) // if error is returned
1908         throw BadPointSize("Could not set font size while attempting to create font \"" + m_font_filename + "\"");
1909 
1910     // Get the scalable font metrics for this font
1911     scale = face->size->metrics.y_scale;
1912     m_ascent = Y(static_cast<int>(face->size->metrics.ascender / 64.0)); // convert from fixed-point 26.6 format
1913     m_descent = Y(static_cast<int>(face->size->metrics.descender / 64.0)); // convert from fixed-point 26.6 format
1914     m_height = m_ascent - m_descent + 1;
1915     m_lineskip = Y(static_cast<int>(face->size->metrics.height / 64.0));
1916     // underline info
1917     m_underline_offset = std::floor(FT_MulFix(face->underline_position, scale) / 64.0);
1918     m_underline_height = std::ceil(FT_MulFix(face->underline_thickness, scale) / 64.0);
1919     if (m_underline_height < 1.0) {
1920         m_underline_height = 1.0;
1921     }
1922     // italics info
1923     m_italics_offset = Value(ITALICS_FACTOR * m_height / 2.0);
1924     // shadow info
1925     m_shadow_offset = 1.0;
1926     // super/subscript
1927     m_super_sub_offset = Value(m_height / 4.0);
1928 
1929     // we always need these whitespace, number, and punctuation characters
1930     std::vector<std::pair<std::uint32_t, std::uint32_t>> range_vec(
1931         PRINTABLE_ASCII_NONALPHA_RANGES.begin(),
1932         PRINTABLE_ASCII_NONALPHA_RANGES.end());
1933 
1934     // add ASCII letter characters or user-specified scripts to them, if the user specifies a specific set of
1935     // characters
1936     if (m_charsets.empty()) {
1937         range_vec.insert(range_vec.end(),
1938                          PRINTABLE_ASCII_ALPHA_RANGES.begin(),
1939                          PRINTABLE_ASCII_ALPHA_RANGES.end());
1940     } else {
1941         typedef std::pair<std::uint32_t, std::uint32_t> Pair;
1942         for (std::size_t i = 0; i < m_charsets.size(); ++i) {
1943             range_vec.push_back(Pair(m_charsets[i].m_first_char, m_charsets[i].m_last_char));
1944         }
1945     }
1946 
1947     //Get maximum texture size
1948     GLint GL_TEX_MAX_SIZE;
1949     glGetIntegerv(GL_MAX_TEXTURE_SIZE, &GL_TEX_MAX_SIZE);
1950     const std::size_t TEX_MAX_SIZE = GL_TEX_MAX_SIZE;
1951 
1952     std::map<std::uint32_t, TempGlyphData> temp_glyph_data;
1953 
1954     // Start with width and height of 16,
1955     // increase them as needed.
1956     // We will lay out the glyphs on the texture side by side
1957     // until the width would reach TEX_MAX_SIZE, then go to the next row.
1958     // QUESTION: Would a more square-like shape be better for the texture?
1959     Buffer2d<std::uint16_t> buffer(X(16), Y(16), 0);
1960 
1961     X x = X0;
1962     Y y = Y0;
1963     X max_x = X0;
1964     Y max_y = Y0;
1965     for (const auto& range : range_vec) {
1966         std::uint32_t low = range.first;
1967         std::uint32_t high = range.second;
1968 
1969         // copy glyph images
1970         for (std::uint32_t c = low; c < high; ++c) {
1971             if (!temp_glyph_data.count(c) && GenerateGlyph(face, c)) {
1972                 const FT_Bitmap& glyph_bitmap = face->glyph->bitmap;
1973                 if ((glyph_bitmap.width > TEX_MAX_SIZE) | (glyph_bitmap.rows > TEX_MAX_SIZE)) {
1974                     ThrowBadGlyph("GG::Font::Init : Glyph too large for buffer'%1%'", c); // catch broken fonts
1975                 }
1976 
1977                 if (Value(x) + glyph_bitmap.width >= TEX_MAX_SIZE) { // start a new row of glyph images
1978                     if (x > max_x) max_x = x;
1979                     x = X0;
1980                     y = max_y;
1981                 }
1982                 if (Value(y) + glyph_bitmap.rows >= TEX_MAX_SIZE) {
1983                     // We cannot make the texture any larger. The font does not fit.
1984                     ThrowBadGlyph("GG::Font::Init : Face too large for buffer. First glyph to no longer fit: '%1%'", c);
1985                 }
1986                 if (y + Y(glyph_bitmap.rows) > max_y) {
1987                     max_y = y + Y(glyph_bitmap.rows + 1); //Leave a one pixel gap between glyphs
1988                 }
1989 
1990                 std::uint8_t*  src_start = glyph_bitmap.buffer;
1991                 // Resize buffer to fit new data
1992                 buffer.at(x + X(glyph_bitmap.width), y + Y(glyph_bitmap.rows)) = 0;
1993 
1994                 for (unsigned int row = 0; row < glyph_bitmap.rows; ++row) {
1995                     std::uint8_t*  src = src_start + row * glyph_bitmap.pitch;
1996                     std::uint16_t* dst = &buffer.get(x, y + Y(row));
1997                     // Rows are always contiguous, so we can copy along a row using simple incrementation
1998                     for (unsigned int col = 0; col < glyph_bitmap.width; ++col) {
1999 #ifdef __BIG_ENDIAN__
2000                         *dst++ = *src++ | (255 << 8); // big-endian uses different byte ordering
2001 #else
2002                         *dst++ = (*src++ << 8) | 255; // alpha is the value from glyph_bitmap; luminance is always 100% white
2003 #endif
2004                     }
2005                 }
2006 
2007                 // record info on how to find and use this glyph later
2008                 temp_glyph_data[c] =
2009                     TempGlyphData(Pt(x, y), Pt(x + X(glyph_bitmap.width), y + Y(glyph_bitmap.rows)),
2010                                   Y(m_height - 1 + m_descent - face->glyph->bitmap_top),
2011                                   X(static_cast<int>((std::ceil(face->glyph->metrics.horiBearingX / 64.0)))),
2012                                   X(static_cast<int>((std::ceil(face->glyph->metrics.horiAdvance / 64.0)))));
2013 
2014                 // advance buffer write-position
2015                 x += X(glyph_bitmap.width + 1); //Leave a one pixel gap between glyphs
2016             }
2017         }
2018     }
2019 
2020     buffer.MakePowerOfTwo();
2021 
2022     // create opengl texture from buffer
2023     m_texture.reset(new Texture);
2024     m_texture->Init(buffer.BufferWidth(), buffer.BufferHeight(),
2025                     (unsigned char*)buffer.Buffer(), GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE, 2);
2026 
2027     // create Glyph objects from temp glyph data
2028     for (const auto& glyph_data : temp_glyph_data)
2029         m_glyphs[glyph_data.first] = Glyph(m_texture, glyph_data.second.ul, glyph_data.second.lr, glyph_data.second.y_offset, glyph_data.second.left_b, glyph_data.second.adv);
2030 
2031     // record the width of the space character
2032     auto glyph_it = m_glyphs.find(WIDE_SPACE);
2033     assert(glyph_it != m_glyphs.end());
2034     m_space_width = glyph_it->second.advance;
2035 }
2036 
GenerateGlyph(FT_Face face,std::uint32_t ch)2037 bool Font::GenerateGlyph(FT_Face face, std::uint32_t ch)
2038 {
2039     bool retval = true;
2040 
2041     // load the glyph
2042     if (!face)
2043         throw BadFace("GG::Font::GetGlyphBitmap : invalid font or font face");
2044 
2045     FT_UInt index = FT_Get_Char_Index(face, ch);
2046     if (index) {
2047         if (FT_Load_Glyph(face, index, FT_LOAD_DEFAULT)) {
2048             // loading of a glpyh failed so we replace it with
2049             // the 'Replacement Character' at codepoint 0xFFFD
2050             FT_UInt tmp_index = FT_Get_Char_Index(face, 0xFFFD);
2051             if (FT_Load_Glyph(face, tmp_index, FT_LOAD_DEFAULT))
2052                 ThrowBadGlyph("GG::Font::GetGlyphBitmap : Freetype could not load the glyph for character '%1%'", ch);
2053         }
2054 
2055         FT_GlyphSlot glyph = face->glyph;
2056 
2057         // render the glyph
2058         if (FT_Render_Glyph(glyph, ft_render_mode_normal))
2059             ThrowBadGlyph("GG::Font::GetGlyphBitmap : Freetype could not render the glyph for character '%1%'", ch);
2060     } else {
2061         retval = false;
2062     }
2063 
2064     return retval;
2065 }
2066 
ValidateFormat(Flags<TextFormat> & format) const2067 void Font::ValidateFormat(Flags<TextFormat>& format) const
2068 {
2069     // correct any disagreements in the format flags
2070     int dup_ct = 0;   // duplication count
2071     if (format & FORMAT_LEFT) ++dup_ct;
2072     if (format & FORMAT_RIGHT) ++dup_ct;
2073     if (format & FORMAT_CENTER) ++dup_ct;
2074     if (dup_ct != 1) {   // exactly one must be picked; when none or multiples are picked, use FORMAT_LEFT by default
2075         format &= ~(FORMAT_RIGHT | FORMAT_CENTER);
2076         format |= FORMAT_LEFT;
2077     }
2078     dup_ct = 0;
2079     if (format & FORMAT_TOP) ++dup_ct;
2080     if (format & FORMAT_BOTTOM) ++dup_ct;
2081     if (format & FORMAT_VCENTER) ++dup_ct;
2082     if (dup_ct != 1) {   // exactly one must be picked; when none or multiples are picked, use FORMAT_TOP by default
2083         format &= ~(FORMAT_BOTTOM | FORMAT_VCENTER);
2084         format |= FORMAT_TOP;
2085     }
2086     if ((format & FORMAT_WORDBREAK) && (format & FORMAT_LINEWRAP))   // only one of these can be picked; FORMAT_WORDBREAK overrides FORMAT_LINEWRAP
2087         format &= ~FORMAT_LINEWRAP;
2088 }
2089 
StoreGlyphImpl(Font::RenderCache & cache,Clr color,const Pt & pt,const Glyph & glyph,int x_top_offset,int y_shift) const2090 void Font::StoreGlyphImpl(Font::RenderCache& cache, Clr color, const Pt& pt,
2091                           const Glyph& glyph, int x_top_offset, int y_shift) const
2092 {
2093     cache.coordinates->store(glyph.sub_texture.TexCoords()[0], glyph.sub_texture.TexCoords()[1]);
2094     cache.vertices->store(pt.x + glyph.left_bearing + x_top_offset, pt.y + glyph.y_offset + y_shift);
2095     cache.colors->store(color);
2096 
2097     cache.coordinates->store(glyph.sub_texture.TexCoords()[2], glyph.sub_texture.TexCoords()[1]);
2098     cache.vertices->store(pt.x + glyph.sub_texture.Width() + glyph.left_bearing + x_top_offset,
2099                           pt.y + glyph.y_offset + y_shift);
2100     cache.colors->store(color);
2101 
2102     cache.coordinates->store(glyph.sub_texture.TexCoords()[2], glyph.sub_texture.TexCoords()[3]);
2103     cache.vertices->store(pt.x + glyph.sub_texture.Width() + glyph.left_bearing - x_top_offset,
2104                           pt.y + glyph.sub_texture.Height() + glyph.y_offset + y_shift);
2105     cache.colors->store(color);
2106 
2107     cache.coordinates->store(glyph.sub_texture.TexCoords()[0], glyph.sub_texture.TexCoords()[3]);
2108     cache.vertices->store(pt.x + glyph.left_bearing - x_top_offset,
2109                           pt.y + glyph.sub_texture.Height() + glyph.y_offset + y_shift);
2110     cache.colors->store(color);
2111 }
2112 
StoreUnderlineImpl(Font::RenderCache & cache,Clr color,const Pt & pt,const Glyph & glyph,Y descent,Y height,Y underline_height,Y underline_offset) const2113 void Font::StoreUnderlineImpl(Font::RenderCache& cache, Clr color, const Pt& pt, const Glyph& glyph,
2114                         Y descent, Y height, Y underline_height, Y underline_offset) const
2115 {
2116     X x1 = pt.x;
2117     Y y1(pt.y + height + descent - underline_offset);
2118     X x2 = x1 + glyph.advance;
2119     Y y2(y1 + underline_height);
2120 
2121     cache.underline_vertices->store(x1, y1);
2122     cache.underline_colors->store(color);
2123     cache.underline_vertices->store(x2, y1);
2124     cache.underline_colors->store(color);
2125     cache.underline_vertices->store(x2, y2);
2126     cache.underline_colors->store(color);
2127     cache.underline_vertices->store(x1, y2);
2128     cache.underline_colors->store(color);
2129 }
2130 
StoreGlyph(const Pt & pt,const Glyph & glyph,const Font::RenderState * render_state,Font::RenderCache & cache) const2131 X Font::StoreGlyph(const Pt& pt, const Glyph& glyph, const Font::RenderState* render_state,
2132                    Font::RenderCache& cache) const
2133 {
2134     int italic_top_offset = 0;
2135     int shadow_offset = 0;
2136     int super_sub_offset = 0;
2137 
2138     if (render_state && render_state->use_italics) {
2139         // Should we enable sub pixel italics offsets?
2140         italic_top_offset = static_cast<int>(m_italics_offset);
2141     }
2142     if (render_state && render_state->use_shadow) {
2143         shadow_offset = static_cast<int>(m_shadow_offset);
2144     }
2145     if (render_state) {
2146         super_sub_offset = -static_cast<int>(render_state->super_sub_shift * m_super_sub_offset);
2147     }
2148 
2149     // render shadows?
2150     if (shadow_offset > 0) {
2151         StoreGlyphImpl(cache, CLR_BLACK, pt + Pt(X1, Y0), glyph, italic_top_offset, super_sub_offset);
2152         StoreGlyphImpl(cache, CLR_BLACK, pt + Pt(X0, Y1), glyph, italic_top_offset, super_sub_offset);
2153         StoreGlyphImpl(cache, CLR_BLACK, pt + Pt(-X1, Y0), glyph, italic_top_offset, super_sub_offset);
2154         StoreGlyphImpl(cache, CLR_BLACK, pt + Pt(X0, -Y1), glyph, italic_top_offset, super_sub_offset);
2155         if (render_state && render_state->draw_underline) {
2156             StoreUnderlineImpl(cache, CLR_BLACK, pt + Pt(X0, Y1), glyph, m_descent,
2157                                m_height, Y(m_underline_height), Y(m_underline_offset));
2158             StoreUnderlineImpl(cache, CLR_BLACK, pt + Pt(X0, -Y1), glyph, m_descent,
2159                                m_height, Y(m_underline_height), Y(m_underline_offset));
2160         }
2161     }
2162 
2163     // render main text
2164     if (render_state) {
2165         StoreGlyphImpl(cache, render_state->CurrentColor(), pt, glyph, italic_top_offset, super_sub_offset);
2166         if (render_state->draw_underline) {
2167             StoreUnderlineImpl(cache, render_state->CurrentColor(), pt, glyph, m_descent,
2168                                m_height, Y(m_underline_height), Y(m_underline_offset));
2169         }
2170     }
2171 
2172     return glyph.advance;
2173 }
2174 
HandleTag(const std::shared_ptr<FormattingTag> & tag,double * orig_color,RenderState & render_state) const2175 void Font::HandleTag(const std::shared_ptr<FormattingTag>& tag, double* orig_color,
2176                      RenderState& render_state) const
2177 {
2178     if (tag->tag_name == ITALIC_TAG) {
2179         if (tag->close_tag) {
2180             if (render_state.use_italics) {
2181                 --render_state.use_italics;
2182             }
2183         } else {
2184             ++render_state.use_italics;
2185         }
2186     } else if (tag->tag_name == UNDERLINE_TAG) {
2187         if (tag->close_tag) {
2188             if (render_state.draw_underline) {
2189                 --render_state.draw_underline;
2190             }
2191         } else {
2192             ++render_state.draw_underline;
2193         }
2194     } else if (tag->tag_name == SHADOW_TAG) {
2195         if (tag->close_tag) {
2196             if (render_state.use_shadow) {
2197                 --render_state.use_shadow;
2198             }
2199         } else {
2200             ++render_state.use_shadow;
2201         }
2202     } else if (tag->tag_name == SUPERSCRIPT_TAG) {
2203         if (tag->close_tag) {
2204             --render_state.super_sub_shift;
2205         } else {
2206             ++render_state.super_sub_shift;
2207         }
2208     } else if (tag->tag_name == SUBSCRIPT_TAG) {
2209         if (tag->close_tag) {
2210             ++render_state.super_sub_shift;
2211         } else {
2212             --render_state.super_sub_shift;
2213         }
2214     } else if (tag->tag_name == RGBA_TAG) {
2215         if (tag->close_tag) {
2216             // Popping is ok also for an empty color stack.
2217             render_state.PopColor();
2218         } else {
2219             using boost::lexical_cast;
2220             bool well_formed_tag = true;
2221             if (4 == tag->params.size()) {
2222                 try {
2223                     int temp_color[4];
2224                     GLubyte color[4];
2225                     temp_color[0] = lexical_cast<int>(tag->params[0]);
2226                     temp_color[1] = lexical_cast<int>(tag->params[1]);
2227                     temp_color[2] = lexical_cast<int>(tag->params[2]);
2228                     temp_color[3] = lexical_cast<int>(tag->params[3]);
2229                     if (0 <= temp_color[0] && temp_color[0] <= 255 &&
2230                         0 <= temp_color[1] && temp_color[1] <= 255 &&
2231                         0 <= temp_color[2] && temp_color[2] <= 255 &&
2232                         0 <= temp_color[3] && temp_color[3] <= 255)
2233                     {
2234                         color[0] = temp_color[0];
2235                         color[1] = temp_color[1];
2236                         color[2] = temp_color[2];
2237                         color[3] = temp_color[3];
2238                         glColor4ubv(color);
2239                         render_state.PushColor(color[0], color[1], color[2], color[3]);
2240                     } else {
2241                         well_formed_tag = false;
2242                     }
2243                 } catch (const boost::bad_lexical_cast&) {
2244                     try {
2245                         double color[4];
2246                         color[0] = lexical_cast<double>(tag->params[0]);
2247                         color[1] = lexical_cast<double>(tag->params[1]);
2248                         color[2] = lexical_cast<double>(tag->params[2]);
2249                         color[3] = lexical_cast<double>(tag->params[3]);
2250                         if (0.0 <= color[0] && color[0] <= 1.0 &&
2251                             0.0 <= color[1] && color[1] <= 1.0 &&
2252                             0.0 <= color[2] && color[2] <= 1.0 &&
2253                             0.0 <= color[3] && color[3] <= 1.0)
2254                         {
2255                             glColor4dv(color);
2256                             render_state.PushColor(color[0], color[1], color[2], color[3]);
2257                         } else {
2258                             well_formed_tag = false;
2259                         }
2260                     } catch (const boost::bad_lexical_cast&) {
2261                         well_formed_tag = false;
2262                     }
2263                 }
2264             } else {
2265                 well_formed_tag = false;
2266             }
2267             if (!well_formed_tag) {
2268                 std::cerr << "GG::Font : Encountered malformed <rgba> formatting tag: "
2269                           << tag->text;
2270             }
2271         }
2272     }
2273 }
2274 
IsDefaultFont()2275 bool Font::IsDefaultFont()
2276 { return m_font_filename == StyleFactory::DefaultFontName(); }
2277 
GetDefaultFont(unsigned int pts)2278 std::shared_ptr<Font> Font::GetDefaultFont(unsigned int pts)
2279 { return GUI::GetGUI()->GetStyleFactory()->DefaultFont(pts); }
2280 
2281 
2282 ///////////////////////////////////////
2283 // class GG::FontManager
2284 ///////////////////////////////////////
2285 // FontKey
FontKey(const std::string & str,unsigned int pts)2286 FontManager::FontKey::FontKey(const std::string& str, unsigned int pts) :
2287     filename(str),
2288     points(pts)
2289 {}
2290 
operator <(const FontKey & rhs) const2291 bool FontManager::FontKey::operator<(const FontKey& rhs) const
2292 { return (filename < rhs.filename || (filename == rhs.filename && points < rhs.points)); }
2293 
2294 // FontManager
2295 // static(s)
2296 const std::shared_ptr<Font> FontManager::EMPTY_FONT{std::make_shared<Font>("", 0)};
2297 
FontManager()2298 FontManager::FontManager()
2299 {}
2300 
HasFont(const std::string & font_filename,unsigned int pts) const2301 bool FontManager::HasFont(const std::string& font_filename, unsigned int pts) const
2302 { return m_rendered_fonts.count(FontKey(font_filename, pts)); }
2303 
GetFont(const std::string & font_filename,unsigned int pts)2304 std::shared_ptr<Font> FontManager::GetFont(const std::string& font_filename, unsigned int pts)
2305 {
2306     std::vector<UnicodeCharset> v;
2307     std::vector<UnicodeCharset>::iterator it = v.end();
2308     return GetFont(font_filename, pts, it, it);
2309 }
2310 
GetFont(const std::string & font_filename,unsigned int pts,const std::vector<unsigned char> & file_contents)2311 std::shared_ptr<Font> FontManager::GetFont(const std::string& font_filename, unsigned int pts,
2312                                            const std::vector<unsigned char>& file_contents)
2313 {
2314     std::vector<UnicodeCharset> v;
2315     std::vector<UnicodeCharset>::iterator it = v.end();
2316     return GetFont(font_filename, pts, file_contents, it, it);
2317 }
2318 
FreeFont(const std::string & font_filename,unsigned int pts)2319 void FontManager::FreeFont(const std::string& font_filename, unsigned int pts)
2320 {
2321     FontKey key(font_filename, pts);
2322     std::map<FontKey, std::shared_ptr<Font>>::iterator it = m_rendered_fonts.find(key);
2323     if (it != m_rendered_fonts.end())
2324         m_rendered_fonts.erase(it);
2325 }
2326 
GetFontManager()2327 FontManager& GG::GetFontManager()
2328 {
2329     static FontManager manager;
2330     return manager;
2331 }
2332