The Battle for Wesnoth  1.13.10+dev
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages
sdl_ttf.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2016 - 2017 by Chris Beck<render787@gmail.com>
3  Part of the Battle for Wesnoth Project http://www.wesnoth.org/
4 
5  This program is free software; you can redistribute it and/or modify
6  it under the terms of the GNU General Public License as published by
7  the Free Software Foundation; either version 2 of the License, or
8  (at your option) any later version.
9  This program is distributed in the hope that it will be useful,
10  but WITHOUT ANY WARRANTY.
11 
12  See the COPYING file for more details.
13 */
14 
15 #include "font/sdl_ttf.hpp"
16 
17 #include "font/error.hpp"
18 #include "font/font_config.hpp"
19 #include "font/font_id.hpp"
20 #include "font/text_cache.hpp"
21 #include "font/text_surface.hpp"
22 
23 #include "filesystem.hpp"
24 #include "game_config.hpp"
25 #include "log.hpp"
26 #include "font/marked-up_text.hpp"
27 #include "preferences/general.hpp"
28 #include "tooltips.hpp"
29 
30 #include "sdl/rect.hpp"
31 #include "sdl/surface.hpp"
33 
34 #include <SDL_ttf.h>
35 
36 #include <map>
37 #include <string>
38 #include <vector>
39 
40 static lg::log_domain log_font("font");
41 #define DBG_FT LOG_STREAM(debug, log_font)
42 #define LOG_FT LOG_STREAM(info, log_font)
43 #define WRN_FT LOG_STREAM(warn, log_font)
44 #define ERR_FT LOG_STREAM(err, log_font)
45 
46 namespace font {
47 
48 /***
49  * Caches used to speed up font rendering
50  */
51 
52 // Record stored in the font table.
53 // If the record for font_id (FOO, Bold + Underline) is a record (BAR, Bold),
54 // it means that BAR is a Bold-styled version of FOO which we shipped with the
55 // game, and now SDL_TTF should be used to style BAR as underline for the final results.
56 struct ttf_record
57 {
58  TTF_Font* font;
59  int style;
60 };
61 
62 static std::map<font_id, ttf_record> font_table;
63 static std::vector<std::string> font_names;
64 static std::vector<std::string> bold_names;
65 static std::vector<std::string> italic_names;
66 
68 {
70  : cbmap()
71  {
72  }
73 
74  typedef std::pair<int, subset_id> block_t;
75  typedef std::map<int, block_t> cbmap_t;
76  cbmap_t cbmap;
77  /** Associates not-associated parts of a range with a new font. */
78  void insert(int first, int last, subset_id id)
79  {
80  if (first > last) return;
81  cbmap_t::iterator i = cbmap.lower_bound(first);
82  // At this point, either first <= i->first or i is past the end.
83  if (i != cbmap.begin()) {
84  cbmap_t::iterator j = i;
85  --j;
86  if (first <= j->second.first /* prev.last */) {
87  insert(j->second.first + 1, last, id);
88  return;
89  }
90  }
91  if (i != cbmap.end()) {
92  if (/* next.first */ i->first <= last) {
93  insert(first, i->first - 1, id);
94  return;
95  }
96  }
97  cbmap.emplace(first, block_t(last, id));
98  }
99  /**
100  * Compresses map by merging consecutive ranges with the same font, even
101  * if there is some unassociated ranges in-between.
102  */
103  void compress()
104  {
105  LOG_FT << "Font map size before compression: " << cbmap.size() << " ranges\n";
106  cbmap_t::iterator i = cbmap.begin(), e = cbmap.end();
107  while (i != e) {
108  cbmap_t::iterator j = i;
109  ++j;
110  if (j == e || i->second.second != j->second.second) {
111  i = j;
112  continue;
113  }
114  i->second.first = j->second.first;
115  cbmap.erase(j);
116  }
117  LOG_FT << "Font map size after compression: " << cbmap.size() << " ranges\n";
118  }
120  {
121  cbmap_t::iterator i = cbmap.upper_bound(ch);
122  // At this point, either ch < i->first or i is past the end.
123  if (i != cbmap.begin()) {
124  --i;
125  if (ch <= i->second.first /* prev.last */)
126  return i->second.second;
127  }
128  return -1;
129  }
130 };
131 
133 
134 //cache sizes of small text
135 typedef std::map<std::string,SDL_Rect> line_size_cache_map;
136 
137 //map of styles -> sizes -> cache
138 static std::map<int,std::map<int,line_size_cache_map> > line_size_cache;
139 
140 typedef std::map<std::pair<std::string, int>, TTF_Font*> open_font_cache;
141 open_font_cache open_fonts;
142 
143 static TTF_Font* open_font_impl(const std::string & , int);
144 
145 // A wrapper which caches the results of open_font_impl.
146 // Note that clear_fonts() is responsible to clean up all of these font pointers,
147 // so to avoid memory leaks fonts should only be opened from this function.
148 static TTF_Font* open_font(const std::string& fname, int size)
149 {
150  const std::pair<std::string, int> key = std::make_pair(fname, size);
151  const open_font_cache::iterator it = open_fonts.find(key);
152  if (it != open_fonts.end()) {
153  return it->second;
154  }
155 
156  TTF_Font* result = open_font_impl(fname, size);
157  open_fonts.emplace(key, result);
158  return result;
159 }
160 
161 static TTF_Font* open_font_impl(const std::string & fname, int size) {
163  if(!game_config::path.empty()) {
164  name = game_config::path + "/fonts/" + fname;
165  if(!filesystem::file_exists(name)) {
166  name = "fonts/" + fname;
167  if(!filesystem::file_exists(name)) {
168  name = fname;
169  if(!filesystem::file_exists(name)) {
170  ERR_FT << "Failed opening font: '" << name << "': No such file or directory" << std::endl;
171  return nullptr;
172  }
173  }
174  }
175 
176  } else {
177  name = "fonts/" + fname;
178  if(!filesystem::file_exists(name)) {
179  if(!filesystem::file_exists(fname)) {
180  ERR_FT << "Failed opening font: '" << name << "': No such file or directory" << std::endl;
181  return nullptr;
182  }
183  name = fname;
184  }
185  }
186 
188  TTF_Font* font = TTF_OpenFontRW(rwops.release(), true, size); // SDL takes ownership of rwops
189  if(font == nullptr) {
190  ERR_FT << "Failed opening font: '" << fname << "'\n";
191  ERR_FT << "TTF_OpenFont: " << TTF_GetError() << std::endl;
192  return nullptr;
193  }
194 
195  DBG_FT << "Opened a font: " << fname << std::endl;
196 
197  return font;
198 }
199 
200 // Gets an appropriately configured TTF Font, for this font size and style.
201 // Loads fonts if necessary. For styled fonts, we search for a ``shipped''
202 // version of the font which is prestyled. If this fails we find the closest
203 // thing which we did ship, and store a record of this, which allows to
204 // rapidly correct the remaining styling using SDL_TTF.
205 //
206 // Uses the font table for caching.
208 {
209  const auto it = font_table.find(id);
210  if(it != font_table.end()) {
211  if (it->second.font != nullptr) {
212  // If we found a valid record, use SDL_TTF to add in the difference
213  // between its intrinsic style and the desired style.
214  TTF_SetFontStyle(it->second.font, it->second.style ^ id.style);
215  }
216  return it->second.font;
217  }
218 
219  // There's no record, so we need to try to find a solution for this font
220  // and make a record of it. If the indices are out of bounds don't bother though.
221  if(id.subset < 0 || size_t(id.subset) >= font_names.size()) {
222  return nullptr;
223  }
224 
225  // Favor to use the shipped Italic font over bold if both are present and are needed.
226  if ((id.style & TTF_STYLE_ITALIC) && italic_names[id.subset].size()) {
227  if (TTF_Font* font = open_font(italic_names[id.subset], id.size)) {
228  ttf_record rec {font, TTF_STYLE_ITALIC};
229  font_table.emplace(id, rec);
230  return sdl_ttf::get_font(id);
231  }
232  }
233 
234  // Now see if the shipped Bold font is useful and available.
235  if ((id.style & TTF_STYLE_BOLD) && bold_names[id.subset].size()) {
236  if (TTF_Font* font = open_font(bold_names[id.subset], id.size)) {
237  ttf_record rec {font, TTF_STYLE_BOLD};
238  font_table.emplace(id, rec);
239  return sdl_ttf::get_font(id);
240  }
241  }
242 
243  // Try just to use the basic version of the font then.
244  if (font_names[id.subset].size()) {
245  if(TTF_Font* font = open_font(font_names[id.subset], id.size)) {
246  ttf_record rec {font, TTF_STYLE_NORMAL};
247  font_table.emplace(id, rec);
248  return sdl_ttf::get_font(id);
249  }
250  }
251 
252  // Failed to find a font.
253  ttf_record rec {nullptr, TTF_STYLE_NORMAL};
254  font_table.emplace(id, rec);
255  return nullptr;
256 }
257 
258 
259 /***
260  * Interface to SDL_TTF
261  */
262 
263 static surface render_text(const std::string& text, int fontsize, const color_t& color, int style, bool use_markup)
264 {
265  // we keep blank lines and spaces (may be wanted for indentation)
266  const std::vector<std::string> lines = utils::split(text, '\n', 0);
267  std::vector<std::vector<surface> > surfaces;
268  surfaces.reserve(lines.size());
269  size_t width = 0, height = 0;
270 
271  for(std::vector< std::string >::const_iterator ln = lines.begin(), ln_end = lines.end(); ln != ln_end; ++ln) {
272 
273  int sz = fontsize;
274  int text_style = style;
275 
276  std::string::const_iterator after_markup = use_markup ?
277  parse_markup(ln->begin(), ln->end(), &sz, nullptr, &text_style) : ln->begin();
278  text_surface txt_surf(sz, color, text_style);
279 
280  if (after_markup == ln->end() && (ln+1 != ln_end || lines.begin()+1 == ln_end)) {
281  // we replace empty line by a space (to have a line height)
282  // except for the last line if we have several
283  txt_surf.set_text(" ");
284  } else if (after_markup == ln->begin()) {
285  // simple case, no markup to skip
286  txt_surf.set_text(*ln);
287  } else {
288  const std::string line(after_markup,ln->end());
289  txt_surf.set_text(line);
290  }
291 
292  const text_surface& cached_surf = text_cache::find(txt_surf);
293  const std::vector<surface>&res = cached_surf.get_surfaces();
294 
295  if (!res.empty()) {
296  surfaces.push_back(res);
297  width = std::max<size_t>(cached_surf.width(), width);
298  height += cached_surf.height();
299  }
300  }
301 
302  if (surfaces.empty()) {
303  return surface();
304  } else if (surfaces.size() == 1 && surfaces.front().size() == 1) {
305  surface surf = surfaces.front().front();
306  return surf;
307  } else {
308  surface res(create_compatible_surface(surfaces.front().front(),width,height));
309  if (res.null())
310  return res;
311 
312  size_t ypos = 0;
313  for(std::vector< std::vector<surface> >::iterator i = surfaces.begin(),
314  i_end = surfaces.end(); i != i_end; ++i) {
315  size_t xpos = 0;
316  height = 0;
317 
318  for(std::vector<surface>::iterator j = i->begin(),
319  j_end = i->end(); j != j_end; ++j) {
320  SDL_Rect dstrect = sdl::create_rect(xpos, ypos, 0, 0);
321  blit_surface(*j, nullptr, res, &dstrect);
322  xpos += (*j)->w;
323  height = std::max<size_t>((*j)->h, height);
324  }
325  ypos += height;
326  }
327 
328  return res;
329  }
330 }
331 
332 
333 surface get_rendered_text(const std::string& str, int size, const color_t& color, int style)
334 {
335  // TODO maybe later also to parse markup here, but a lot to check
336  return render_text(str, size, color, style, false);
337 }
338 
339 SDL_Rect draw_text_line(surface& gui_surface, const SDL_Rect& area, int size,
340  const color_t& color, const std::string& text,
341  int x, int y, bool use_tooltips, int style)
342 {
343  size = preferences::font_scaled(size);
344  if (gui_surface.null()) {
345  text_surface const &u = text_cache::find(text_surface(text, size, color, style));
346  return sdl::create_rect(0, 0, u.width(), u.height());
347  }
348 
349  if(area.w == 0) { // no place to draw
350  return {0, 0, 0, 0};
351  }
352 
353  const std::string etext = make_text_ellipsis(text, size, area.w);
354 
355  // for the main current use, we already parsed markup
356  surface surface(render_text(etext,size,color,style,false));
357  if(surface == nullptr) {
358  return {0, 0, 0, 0};
359  }
360 
361  SDL_Rect dest;
362  if(x!=-1) {
363  dest.x = x;
364 #ifdef HAVE_FRIBIDI
365  // Oron -- Conditional, until all draw_text_line calls have fixed area parameter
366  if(getenv("NO_RTL") == nullptr) {
367  bool is_rtl = text_cache::find(text_surface(text, size, color, style)).is_rtl();
368  if(is_rtl)
369  dest.x = area.x + area.w - surface->w - (x - area.x);
370  }
371 #endif
372  } else
373  dest.x = (area.w/2)-(surface->w/2);
374  if(y!=-1)
375  dest.y = y;
376  else
377  dest.y = (area.h/2)-(surface->h/2);
378  dest.w = surface->w;
379  dest.h = surface->h;
380 
381  if(line_width(text, size) > area.w) {
382  tooltips::add_tooltip(dest,text);
383  }
384 
385  if(dest.x + dest.w > area.x + area.w) {
386  dest.w = area.x + area.w - dest.x;
387  }
388 
389  if(dest.y + dest.h > area.y + area.h) {
390  dest.h = area.y + area.h - dest.y;
391  }
392 
393  if(gui_surface != nullptr) {
394  SDL_Rect src = dest;
395  src.x = 0;
396  src.y = 0;
397  sdl_blit(surface,&src,gui_surface,&dest);
398  }
399 
400  if(use_tooltips) {
401  tooltips::add_tooltip(dest,text);
402  }
403 
404  return dest;
405 }
406 
408 {
409  // Only returns the maximal size of the first font
410  TTF_Font* const font = sdl_ttf::get_font(font_id(0, size));
411  if(font == nullptr)
412  return 0;
413  return TTF_FontHeight(font);
414 }
415 
416 int line_width(const std::string& line, int font_size, int style)
417 {
418  return line_size(line,font_size,style).w;
419 }
420 
421 SDL_Rect line_size(const std::string& line, int font_size, int style)
422 {
423  line_size_cache_map& cache = line_size_cache[style][font_size];
424 
425  const line_size_cache_map::const_iterator i = cache.find(line);
426  if(i != cache.end()) {
427  return i->second;
428  }
429 
430  SDL_Rect res;
431 
432  const color_t col { 0, 0, 0, 0 };
433  text_surface s(line, font_size, col, style);
434 
435  res.w = s.width();
436  res.h = s.height();
437  res.x = res.y = 0;
438 
439  cache.emplace(line,res);
440  return res;
441 }
442 
444  int max_width, int style)
445 {
446  if (line_width(text, font_size, style) <= max_width)
447  return text;
448  if(line_width(ellipsis, font_size, style) > max_width)
449  return "";
450 
451  std::string current_substring;
452 
453  utf8::iterator itor(text);
454 
455  for(; itor != utf8::iterator::end(text); ++itor) {
456  std::string tmp = current_substring;
457  tmp.append(itor.substr().first, itor.substr().second);
458 
459  if (line_width(tmp + ellipsis, font_size, style) > max_width) {
460  return current_substring + ellipsis;
461  }
462 
463  current_substring.append(itor.substr().first, itor.substr().second);
464  }
465 
466  return text; // Should not happen
467 }
468 
469 void cache_mode(CACHE mode)
470 {
471  if(mode == CACHE_LOBBY) {
472  text_cache::resize(1000);
473  } else {
474  text_cache::resize(50);
475  }
476 }
477 
478 /***
479  * Initialize and destruction
480  */
481 
483  const int res = TTF_Init();
484  if(res == -1) {
485  ERR_FT << "Could not initialize SDL_TTF" << std::endl;
486  throw font::error("SDL_TTF could not initialize, TTF_INIT returned: " + std::to_string(res));
487  } else {
488  LOG_FT << "Initialized true type fonts\n";
489  }
490 }
491 
492 static void clear_fonts() {
493  for(const auto & i : open_fonts) {
494  TTF_CloseFont(i.second);
495  }
496 
497  open_fonts.clear();
498 
499  font_table.clear();
500 
501  font_names.clear();
502  bold_names.clear();
503  italic_names.clear();
504 
505  char_blocks.cbmap.clear();
506  line_size_cache.clear();
507 }
508 
510  clear_fonts();
511  TTF_Quit();
512 }
513 
514 //sets the font list to be used.
515 void sdl_ttf::set_font_list(const std::vector<subset_descriptor>& fontlist)
516 {
517  clear_fonts();
518 
519  for(const auto & f : fontlist) {
520  if (!check_font_file(f.name)) continue;
521  // Insert fonts only if the font file exists
522  const subset_id subset = font_names.size();
523  font_names.push_back(f.name);
524 
525  if (f.bold_name && check_font_file(*f.bold_name)) {
526  bold_names.push_back(*f.bold_name);
527  } else {
528  bold_names.emplace_back();
529  }
530 
531  if (f.italic_name && check_font_file(*f.italic_name)) {
532  italic_names.push_back(*f.italic_name);
533  } else {
534  italic_names.emplace_back();
535  }
536 
537  for (const subset_descriptor::range &cp_range : f.present_codepoints) {
538  char_blocks.insert(cp_range.first, cp_range.second, subset);
539  }
540  }
541  char_blocks.compress();
542 
543  assert(font_names.size() == bold_names.size());
544  assert(font_names.size() == italic_names.size());
545 
546  DBG_FT << "Set the font list. The styled font families are:\n";
547 
548  for (size_t i = 0; i < font_names.size(); ++i) {
549  DBG_FT << "[" << i << "]:\t\tbase:\t'" << font_names[i] << "'\tbold:\t'" << bold_names[i] << "'\titalic:\t'" << italic_names[i] << "'\n";
550  }
551 }
552 
553 //Splits the UTF-8 text into text_chunks using the same font.
554 std::vector<text_chunk> sdl_ttf::split_text(const std::string& utf8_text) {
555  text_chunk current_chunk(0);
556  std::vector<text_chunk> chunks;
557 
558  if (utf8_text.empty())
559  return chunks;
560 
561  try {
562  utf8::iterator ch(utf8_text);
563  int sub = char_blocks.get_id(*ch);
564  if (sub >= 0) current_chunk.subset = sub;
565  for(utf8::iterator end = utf8::iterator::end(utf8_text); ch != end; ++ch)
566  {
567  sub = char_blocks.get_id(*ch);
568  if (sub >= 0 && sub != current_chunk.subset) {
569  chunks.push_back(current_chunk);
570  current_chunk.text.clear();
571  current_chunk.subset = sub;
572  }
573  current_chunk.text.append(ch.substr().first, ch.substr().second);
574  }
575  if (!current_chunk.text.empty()) {
576  chunks.push_back(current_chunk);
577  }
578  }
580  WRN_FT << "Invalid UTF-8 string: \"" << utf8_text << "\"" << std::endl;
581  }
582  return chunks;
583 }
584 
585 } // end namespace font
static std::map< font_id, ttf_record > font_table
Definition: sdl_ttf.cpp:62
TTF_Font * font
Definition: sdl_ttf.cpp:58
std::vector< char_t > string
subset_id get_id(int ch)
Definition: sdl_ttf.cpp:119
bool check_font_file(std::string name)
Test if a font file exists.
Definition: font_config.cpp:63
Note: Specific to sdl_ttf.
static text_surface & find(text_surface const &t)
Definition: text_cache.cpp:37
bool null() const
Definition: surface.hpp:66
std::string::const_iterator parse_markup(std::string::const_iterator i1, std::string::const_iterator i2, int *font_size, color_t *color, int *style)
Parses the markup-tags at the front of a string.
static l_noret error(LoadState *S, const char *why)
Definition: lundump.cpp:39
#define ERR_FT
Definition: sdl_ttf.cpp:44
static surface render_text(const std::string &text, int fontsize, const color_t &color, int style, bool use_markup)
Definition: sdl_ttf.cpp:263
int subset_id
Definition: font_id.hpp:31
void insert(int first, int last, subset_id id)
Associates not-associated parts of a range with a new font.
Definition: sdl_ttf.cpp:78
rwops_ptr make_read_RWops(const std::string &path)
static TTF_Font * open_font_impl(const std::string &, int)
Definition: sdl_ttf.cpp:161
SDL_Rect line_size(const std::string &line, int font_size, int style)
Determine the size of a line of text given a certain font size.
Definition: sdl_ttf.cpp:421
std::vector< surface > const & get_surfaces() const
CACHE
Definition: sdl_ttf.hpp:59
size_t height() const
std::string text
Definition: font_id.hpp:68
std::vector< std::string > split(const std::string &val, const char c, const int flags)
Splits a (comma-)separated string into a vector of pieces.
static std::map< int, std::map< int, line_size_cache_map > > line_size_cache
Definition: sdl_ttf.cpp:138
void blit_surface(const surface &surf, const SDL_Rect *srcrect, surface &dst, const SDL_Rect *dstrect)
Replacement for sdl_blit.
Definition: utils.cpp:2082
static std::vector< text_chunk > split_text(const std::string &utf8_text)
Definition: sdl_ttf.cpp:554
const std::string ellipsis
Definition: constants.cpp:35
static lg::log_domain log_font("font")
std::map< int, block_t > cbmap_t
Definition: sdl_ttf.cpp:75
std::map< std::string, SDL_Rect > line_size_cache_map
Definition: sdl_ttf.cpp:135
int font_scaled(int size)
Definition: general.cpp:449
void compress()
Compresses map by merging consecutive ranges with the same font, even if there is some unassociated r...
Definition: sdl_ttf.cpp:103
std::string path
Definition: game_config.cpp:56
subset_id subset
Definition: font_id.hpp:67
int get_max_height(int size)
Definition: sdl_ttf.cpp:407
open_font_cache open_fonts
Definition: sdl_ttf.cpp:141
static std::vector< std::string > italic_names
Definition: sdl_ttf.cpp:65
void set_text(const std::string &str)
std::pair< int, subset_id > block_t
Definition: sdl_ttf.cpp:74
size_t size(const utf8::string &str)
Length in characters of a UTF-8 string.
Definition: unicode.cpp:86
std::pair< int, int > range
Thrown by operations encountering invalid UTF-8 data.
#define WRN_FT
Definition: sdl_ttf.cpp:43
void cache_mode(CACHE mode)
Definition: sdl_ttf.cpp:469
static void resize(unsigned int size)
Definition: text_cache.cpp:26
static void clear_fonts()
Definition: sdl_ttf.cpp:492
static map_location::DIRECTION s
static void set_font_list(const std::vector< subset_descriptor > &fontlist)
Definition: sdl_ttf.cpp:515
#define LOG_FT
Definition: sdl_ttf.cpp:42
size_t width() const
static tcache cache
Definition: minimap.cpp:137
#define DBG_FT
Definition: sdl_ttf.cpp:41
surface get_rendered_text(const std::string &str, int size, const color_t &color, int style)
Definition: sdl_ttf.cpp:333
#define i
Declarations for File-IO.
std::unique_ptr< SDL_RWops, void(*)(SDL_RWops *)> rwops_ptr
Definition: filesystem.hpp:40
static iterator_base end(const string_type &str)
std::string make_text_ellipsis(const std::string &text, int font_size, int max_width, int style)
If the text exceeds the specified max width, end it with an ellipsis (...)
Definition: sdl_ttf.cpp:443
SDL_Rect create_rect(const int x, const int y, const int w, const int h)
Creates an SDL_Rect with the given dimensions.
Definition: rect.hpp:42
Contains the SDL_Rect helper code.
SDL_Rect draw_text_line(surface &gui_surface, const SDL_Rect &area, int size, const color_t &color, const std::string &text, int x, int y, bool use_tooltips, int style)
Definition: sdl_ttf.cpp:339
#define f
const std::pair< typename string_type::const_iterator, typename string_type::const_iterator > & substr() const
Standard logging facilities (interface).
surface create_compatible_surface(const surface &surf, int width, int height)
Definition: utils.cpp:2063
static const char * name(const std::vector< SDL_Joystick * > &joysticks, const size_t index)
Definition: joystick.cpp:48
#define e
void sdl_blit(const surface &src, SDL_Rect *src_rect, surface &dst, SDL_Rect *dst_rect)
Definition: utils.hpp:33
const int font_size
int add_tooltip(const SDL_Rect &rect, const std::string &message, const std::string &action, bool use_markup, const surface &foreground)
Definition: tooltips.cpp:180
bool file_exists(const std::string &name)
Returns true if a file or directory with such name already exists.
static TTF_Font * open_font(const std::string &fname, int size)
Definition: sdl_ttf.cpp:148
int line_width(const std::string &line, int font_size, int style)
Determine the width of a line of text given a certain font size.
Definition: sdl_ttf.cpp:416
std::string::const_iterator iterator
Definition: tokenizer.hpp:24
static std::vector< std::string > font_names
Definition: sdl_ttf.cpp:63
static TTF_Font * get_font(font_id)
Definition: sdl_ttf.cpp:207
static char_block_map char_blocks
Definition: sdl_ttf.cpp:132
static std::vector< std::string > bold_names
Definition: sdl_ttf.cpp:64
std::map< std::pair< std::string, int >, TTF_Font * > open_font_cache
Definition: sdl_ttf.cpp:140