The Battle for Wesnoth  1.13.11+dev
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages
font_config.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2016 - 2018 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/font_config.hpp"
17 #include "font/error.hpp"
18 #include "font/sdl_ttf.hpp"
19 
20 #include "config.hpp"
21 #include "log.hpp"
22 #include "tstring.hpp"
23 
24 #include "filesystem.hpp"
25 #include "game_config.hpp"
26 
27 #include "serialization/parser.hpp"
31 #include "preferences/general.hpp"
32 
33 #include <list>
34 #include <set>
35 #include <stack>
36 #include <sstream>
37 #include <vector>
38 
39 #include <cairo-features.h>
40 
41 #ifdef CAIRO_HAS_WIN32_FONT
42 #include <windows.h>
43 #undef CAIRO_HAS_FT_FONT
44 #endif
45 
46 #ifdef CAIRO_HAS_FT_FONT
47 #include <fontconfig/fontconfig.h>
48 #endif
49 
50 #if !defined(CAIRO_HAS_FT_FONT) && !defined(CAIRO_HAS_WIN32_FONT)
51 #error unable to find font loading tools.
52 #endif
53 
54 static lg::log_domain log_font("font");
55 #define DBG_FT LOG_STREAM(debug, log_font)
56 #define LOG_FT LOG_STREAM(info, log_font)
57 #define WRN_FT LOG_STREAM(warn, log_font)
58 #define ERR_FT LOG_STREAM(err, log_font)
59 
60 namespace font {
61 
62 
64  if(game_config::path.empty() == false) {
65  if(!filesystem::file_exists(game_config::path + "/fonts/" + name)) {
66  if(!filesystem::file_exists("fonts/" + name)) {
67  if(!filesystem::file_exists(name)) {
68  WRN_FT << "Failed opening font file '" << name << "': No such file or directory" << std::endl;
69  return false;
70  }
71  }
72  }
73  } else {
74  if(!filesystem::file_exists("fonts/" + name)) {
75  if(!filesystem::file_exists(name)) {
76  WRN_FT << "Failed opening font file '" << name << "': No such file or directory" << std::endl;
77  return false;
78  }
79  }
80  }
81  return true;
82 }
83 
84 namespace
85 {
86 bool add_font_to_fontlist(const config &fonts_config,
87  std::vector<font::subset_descriptor>& fontlist, const std::string& name)
88 {
89  const config &font = fonts_config.find_child("font", "name", name);
90  if (!font) {
91  return false;
92  }
93  //DBG_FT << "Adding a font record: " << font.debug() << std::endl;
94 
95  fontlist.push_back(font::subset_descriptor(font));
96 
97  return true;
98 }
99 
100 #ifdef CAIRO_HAS_WIN32_FONT
101 bool is_valid_font_file(const std::string& file)
102 {
103  static const std::array<std::string, 3> font_exts { ".ttf", ".ttc", ".otf" };
104 
105  for(const std::string& ext : font_exts) {
106  if(filesystem::ends_with(file, ext)) {
107  return true;
108  }
109  }
110 
111  return false;
112 }
113 #endif
114 
115 // Current font family for sanserif and monospace fonts in the game
116 
117 t_string family_order_sans;
118 t_string family_order_mono;
119 t_string family_order_light;
120 t_string family_order_script;
121 
122 } // end anon namespace
123 
124 /***
125  * Public interface
126  */
127 
129 {
130  //read font config separately, so we do not have to re-read the whole
131  //config when changing languages
132  config cfg;
133  try {
134  const std::string& cfg_path = filesystem::get_wml_location("hardwired/fonts.cfg");
135  if(cfg_path.empty()) {
136  ERR_FT << "could not resolve path to fonts.cfg, file not found\n";
137  return false;
138  }
139 
140  filesystem::scoped_istream stream = preprocess_file(cfg_path);
141  read(cfg, *stream);
142  } catch(config::error &e) {
143  ERR_FT << "could not read fonts.cfg:\n"
144  << e.message << '\n';
145  return false;
146  }
147 
148  const config &fonts_config = cfg.child("fonts");
149  if (!fonts_config)
150  return false;
151 
152  std::set<std::string> known_fonts;
153  for (const config &font : fonts_config.child_range("font")) {
154  known_fonts.insert(font["name"]);
155  if (font.has_attribute("bold_name")) {
156  known_fonts.insert(font["bold_name"]);
157  }
158  if (font.has_attribute("italic_name")) {
159  known_fonts.insert(font["italic_name"]);
160  }
161  }
162 
163  family_order_sans = fonts_config["family_order"];
164  family_order_mono = fonts_config["family_order_monospace"];
165  family_order_light = fonts_config["family_order_light"];
166  family_order_script = fonts_config["family_order_script"];
167 
168  if(family_order_mono.empty()) {
169  ERR_FT << "No monospace font family order defined, falling back to sans serif order\n";
170  family_order_mono = family_order_sans;
171  }
172 
173  if(family_order_light.empty()) {
174  ERR_FT << "No light font family order defined, falling back to sans serif order\n";
175  family_order_light = family_order_sans;
176  }
177 
178  if(family_order_script.empty()) {
179  ERR_FT << "No script font family order defined, falling back to sans serif order\n";
180  family_order_script = family_order_sans;
181  }
182 
183  std::vector<font::subset_descriptor> fontlist;
184 
185  for(auto font : utils::split(fonts_config["order"])) {
186  add_font_to_fontlist(fonts_config, fontlist, font);
187  known_fonts.erase(font);
188  }
189 
190  for(auto kfont : known_fonts) {
191  add_font_to_fontlist(fonts_config, fontlist, kfont);
192  }
193 
194  if(fontlist.empty())
195  return false;
196 
197  sdl_ttf::set_font_list(fontlist);
198  return true;
199 }
200 
202 {
203  switch(fclass) {
204  case FONT_MONOSPACE:
205  return family_order_mono;
206  case FONT_LIGHT:
207  return family_order_light;
208  case FONT_SCRIPT:
209  return family_order_script;
210  default:
211  return family_order_sans;
212  }
213 }
214 
215 /***
216  * Manager member functions
217  */
218 
220 {
221 #ifdef CAIRO_HAS_FT_FONT
222  std::string font_path = game_config::path + "/fonts";
223  if (!FcConfigAppFontAddDir(FcConfigGetCurrent(),
224  reinterpret_cast<const FcChar8 *>(font_path.c_str())))
225  {
226  ERR_FT << "Could not load the true type fonts" << std::endl;
227  throw font::error("font config lib failed to add the font path: '" + font_path + "'");
228  }
229 
230  std::string font_file = font_path + "/fonts.conf";
231  if(!FcConfigParseAndLoad(FcConfigGetCurrent(),
232  reinterpret_cast<const FcChar8*>(font_file.c_str()),
233  FcFalse))
234  {
235  ERR_FT << "Could not load local font configuration\n";
236  throw font::error("font config lib failed to find font.conf: '" + font_file + "'");
237  }
238  else
239  {
240  LOG_FT << "Local font configuration loaded\n";
241  }
242 #endif
243 
244 #ifdef CAIRO_HAS_WIN32_FONT
245  for(const std::string& path : filesystem::get_binary_paths("fonts")) {
246  std::vector<std::string> files;
249  }
250  for(const std::string& file : files) {
251  if(is_valid_font_file(file))
252  {
253  const std::wstring wfile = unicode_cast<std::wstring>(file);
254  AddFontResourceExW(wfile.c_str(), FR_PRIVATE, nullptr);
255  }
256  }
257  }
258 #endif
259 }
260 
262 {
263 #ifdef CAIRO_HAS_FT_FONT
264  FcConfigAppFontClear(FcConfigGetCurrent());
265 #endif
266 
267 #ifdef CAIRO_HAS_WIN32_FONT
268  for(const std::string& path : filesystem::get_binary_paths("fonts")) {
269  std::vector<std::string> files;
272  for(const std::string& file : files) {
273  if(is_valid_font_file(file))
274  {
275  const std::wstring wfile = unicode_cast<std::wstring>(file);
276  RemoveFontResourceExW(wfile.c_str(), FR_PRIVATE, nullptr);
277  }
278  }
279  }
280 #endif
281 }
282 
283 
284 } // end namespace font
config & child(config_key_type key, int n=0)
Returns the nth child with the given key, or a reference to an invalid config if there is none...
Definition: config.cpp:419
std::vector< char_t > string
family_class
Font classes for get_font_families().
bool check_font_file(std::string name)
Test if a font file exists.
Definition: font_config.cpp:63
Note: Specific to sdl_ttf.
config & find_child(config_key_type key, const std::string &name, const std::string &value)
Returns the first child of tag key with a name attribute containing value.
Definition: config.cpp:782
static l_noret error(LoadState *S, const char *why)
Definition: lundump.cpp:39
bool ends_with(const std::string &str, const std::string &suffix)
ucs4_convert_impl::enableif< TD, typename TS::value_type >::type unicode_cast(const TS &source)
child_itors child_range(config_key_type key)
Definition: config.cpp:362
#define WRN_FT
Definition: font_config.cpp:57
#define LOG_FT
Definition: font_config.cpp:56
Definitions for the interface to Wesnoth Markup Language (WML).
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.
const t_string & get_font_families(family_class fclass)
Returns the currently defined fonts.
void read(config &cfg, std::istream &in, abstract_validator *validator)
Definition: parser.cpp:612
bool is_directory(const std::string &fname)
Returns true if the given file is a directory.
std::unique_ptr< std::istream > scoped_istream
Definition: filesystem.hpp:37
std::string path
Definition: game_config.cpp:56
static lg::log_domain log_font("font")
bool load_font_config()
bool has_attribute(config_key_type key) const
Definition: config.cpp:213
const std::vector< std::string > & get_binary_paths(const std::string &type)
Returns a vector with all possible paths to a given type of binary, e.g.
void get_files_in_dir(const std::string &dir, std::vector< std::string > *files, std::vector< std::string > *dirs=nullptr, file_name_option mode=FILE_NAME_ONLY, file_filter_option filter=NO_FILTER, file_reorder_option reorder=DONT_REORDER, file_tree_checksum *checksum=nullptr)
Populates 'files' with all the files and 'dirs' with all the directories in dir.
std::string get_wml_location(const std::string &filename, const std::string &current_dir=std::string())
Returns a complete path to the actual WML file or directory or an empty string if the file isn't pres...
static void set_font_list(const std::vector< subset_descriptor > &fontlist)
Definition: sdl_ttf.cpp:515
Declarations for File-IO.
#define ERR_FT
Definition: font_config.cpp:58
Standard logging facilities (interface).
std::string message
Definition: exceptions.hpp:31
static const char * name(const std::vector< SDL_Joystick * > &joysticks, const size_t index)
Definition: joystick.cpp:48
#define e
A config object defines a single node in a WML file, with access to child nodes.
Definition: config.hpp:93
filesystem::scoped_istream preprocess_file(const std::string &fname, preproc_map *defines)
bool file_exists(const std::string &name)
Returns true if a file or directory with such name already exists.