The Battle for Wesnoth  1.15.12+dev
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 https://www.wesnoth.org/
4 
5  This program is free software; you can redistribute it and/or modify
6  it under the terms of the GNU General Public License as published by
7  the Free Software Foundation; either version 2 of the License, or
8  (at your option) any later version.
9  This program is distributed in the hope that it will be useful,
10  but WITHOUT ANY WARRANTY.
11 
12  See the COPYING file for more details.
13 */
14 
15 #include "font/font_config.hpp"
17 #include "font/error.hpp"
18 
19 #include "config.hpp"
20 #include "log.hpp"
21 #include "tstring.hpp"
22 
23 #include "filesystem.hpp"
24 #include "game_config.hpp"
25 
26 #include "serialization/parser.hpp"
30 #include "preferences/general.hpp"
31 
32 #include <list>
33 #include <set>
34 #include <stack>
35 #include <sstream>
36 #include <vector>
37 
38 #include <cairo-features.h>
39 
40 #ifdef CAIRO_HAS_WIN32_FONT
41 #include <windows.h>
42 #undef CAIRO_HAS_FT_FONT
43 #endif
44 
45 #ifdef CAIRO_HAS_FT_FONT
46 #include <fontconfig/fontconfig.h>
47 #endif
48 
49 #if !defined(CAIRO_HAS_FT_FONT) && !defined(CAIRO_HAS_WIN32_FONT)
50 #error unable to find font loading tools.
51 #endif
52 
53 static lg::log_domain log_font("font");
54 #define DBG_FT LOG_STREAM(debug, log_font)
55 #define LOG_FT LOG_STREAM(info, log_font)
56 #define WRN_FT LOG_STREAM(warn, log_font)
57 #define ERR_FT LOG_STREAM(err, log_font)
58 
59 namespace font {
60 
61 
62 bool check_font_file(std::string name) {
63  if(game_config::path.empty() == false) {
64  if(!filesystem::file_exists(game_config::path + "/fonts/" + name)) {
65  if(!filesystem::file_exists("fonts/" + name)) {
66  if(!filesystem::file_exists(name)) {
67  WRN_FT << "Failed opening font file '" << name << "': No such file or directory" << std::endl;
68  return false;
69  }
70  }
71  }
72  } else {
73  if(!filesystem::file_exists("fonts/" + name)) {
74  if(!filesystem::file_exists(name)) {
75  WRN_FT << "Failed opening font file '" << name << "': No such file or directory" << std::endl;
76  return false;
77  }
78  }
79  }
80  return true;
81 }
82 
83 namespace
84 {
85 bool add_font_to_fontlist(const config &fonts_config,
86  std::vector<font::subset_descriptor>& fontlist, const std::string& name)
87 {
88  const config &font = fonts_config.find_child("font", "name", name);
89  if (!font) {
90  return false;
91  }
92  //DBG_FT << "Adding a font record: " << font.debug() << std::endl;
93 
94  fontlist.push_back(font::subset_descriptor(font));
95 
96  return true;
97 }
98 
99 #ifdef CAIRO_HAS_WIN32_FONT
100 bool is_valid_font_file(const std::string& file)
101 {
102  static const std::array<std::string, 3> font_exts { ".ttf", ".ttc", ".otf" };
103 
104  for(const std::string& ext : font_exts) {
105  if(filesystem::ends_with(file, ext)) {
106  return true;
107  }
108  }
109 
110  return false;
111 }
112 #endif
113 
114 // Current font family for sanserif and monospace fonts in the game
115 
116 t_string family_order_sans;
117 t_string family_order_mono;
118 t_string family_order_light;
119 t_string family_order_script;
120 
121 } // end anon namespace
122 
123 /***
124  * Public interface
125  */
126 
128 {
129  config cfg;
130  try {
131  const std::string& cfg_path = filesystem::get_wml_location("hardwired/fonts.cfg");
132  if(cfg_path.empty()) {
133  ERR_FT << "could not resolve path to fonts.cfg, file not found\n";
134  return false;
135  }
136 
137  filesystem::scoped_istream stream = preprocess_file(cfg_path);
138  read(cfg, *stream);
139  } catch(const config::error &e) {
140  ERR_FT << "could not read fonts.cfg:\n"
141  << e.message << '\n';
142  return false;
143  }
144 
145  const config &fonts_config = cfg.child("fonts");
146  if (!fonts_config)
147  return false;
148 
149  std::set<std::string> known_fonts;
150  for (const config &font : fonts_config.child_range("font")) {
151  known_fonts.insert(font["name"]);
152  if (font.has_attribute("bold_name")) {
153  known_fonts.insert(font["bold_name"]);
154  }
155  if (font.has_attribute("italic_name")) {
156  known_fonts.insert(font["italic_name"]);
157  }
158  }
159 
160  family_order_sans = fonts_config["family_order"];
161  family_order_mono = fonts_config["family_order_monospace"];
162  family_order_light = fonts_config["family_order_light"];
163  family_order_script = fonts_config["family_order_script"];
164 
165  if(family_order_mono.empty()) {
166  ERR_FT << "No monospace font family order defined, falling back to sans serif order\n";
167  family_order_mono = family_order_sans;
168  }
169 
170  if(family_order_light.empty()) {
171  ERR_FT << "No light font family order defined, falling back to sans serif order\n";
172  family_order_light = family_order_sans;
173  }
174 
175  if(family_order_script.empty()) {
176  ERR_FT << "No script font family order defined, falling back to sans serif order\n";
177  family_order_script = family_order_sans;
178  }
179 
180  std::vector<font::subset_descriptor> fontlist;
181 
182  for(auto font : utils::split(fonts_config["order"])) {
183  add_font_to_fontlist(fonts_config, fontlist, font);
184  known_fonts.erase(font);
185  }
186 
187  for(auto kfont : known_fonts) {
188  add_font_to_fontlist(fonts_config, fontlist, kfont);
189  }
190 
191  if(fontlist.empty())
192  return false;
193 
194  return true;
195 }
196 
198 {
199  switch(fclass) {
200  case FONT_MONOSPACE:
201  return family_order_mono;
202  case FONT_LIGHT:
203  return family_order_light;
204  case FONT_SCRIPT:
205  return family_order_script;
206  default:
207  return family_order_sans;
208  }
209 }
210 
211 /***
212  * Manager member functions
213  */
214 
216 {
217 #ifdef CAIRO_HAS_FT_FONT
218  std::string font_path = game_config::path + "/fonts";
219  if (!FcConfigAppFontAddDir(FcConfigGetCurrent(),
220  reinterpret_cast<const FcChar8 *>(font_path.c_str())))
221  {
222  ERR_FT << "Could not load the true type fonts" << std::endl;
223  throw font::error("font config lib failed to add the font path: '" + font_path + "'");
224  }
225 
226  std::string font_file = font_path + "/fonts.conf";
227  if(!FcConfigParseAndLoad(FcConfigGetCurrent(),
228  reinterpret_cast<const FcChar8*>(font_file.c_str()),
229  FcFalse))
230  {
231  ERR_FT << "Could not load local font configuration\n";
232  throw font::error("font config lib failed to find font.conf: '" + font_file + "'");
233  }
234  else
235  {
236  LOG_FT << "Local font configuration loaded\n";
237  }
238 #endif
239 
240 #ifdef CAIRO_HAS_WIN32_FONT
241  for(const std::string& path : filesystem::get_binary_paths("fonts")) {
242  std::vector<std::string> files;
245  }
246  for(const std::string& file : files) {
247  if(is_valid_font_file(file))
248  {
249  const std::wstring wfile = unicode_cast<std::wstring>(file);
250  AddFontResourceExW(wfile.c_str(), FR_PRIVATE, nullptr);
251  }
252  }
253  }
254 #endif
255 }
256 
258 {
259 #ifdef CAIRO_HAS_FT_FONT
260  FcConfigAppFontClear(FcConfigGetCurrent());
261 #endif
262 
263 #ifdef CAIRO_HAS_WIN32_FONT
264  for(const std::string& path : filesystem::get_binary_paths("fonts")) {
265  std::vector<std::string> files;
268  for(const std::string& file : files) {
269  if(is_valid_font_file(file))
270  {
271  const std::wstring wfile = unicode_cast<std::wstring>(file);
272  RemoveFontResourceExW(wfile.c_str(), FR_PRIVATE, nullptr);
273  }
274  }
275  }
276 #endif
277 }
278 
279 
280 } // 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:414
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:62
Collection of helper functions relating to Pango formatting.
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:860
static l_noret error(LoadState *S, const char *why)
Definition: lundump.cpp:40
static bool file_exists(const bfs::path &fpath)
Definition: filesystem.cpp:263
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:356
#define WRN_FT
Definition: font_config.cpp:56
#define LOG_FT
Definition: font_config.cpp:55
Definitions for the interface to Wesnoth Markup Language (WML).
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:626
void get_files_in_dir(const std::string &dir, std::vector< std::string > *files, std::vector< std::string > *dirs, name_mode mode, filter_mode filter, reorder_mode reorder, file_tree_checksum *checksum)
Populates &#39;files&#39; with all the files and &#39;dirs&#39; with all the directories in dir.
Definition: filesystem.cpp:349
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:38
static lg::log_domain log_font("font")
bool load_font_config()
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.
std::string get_wml_location(const std::string &filename, const std::string &current_dir)
Returns a complete path to the actual WML file or directory or an empty string if the file isn&#39;t pres...
Declarations for File-IO.
#define ERR_FT
Definition: font_config.cpp:57
std::vector< std::string > split(const config_attribute_value &val)
Standard logging facilities (interface).
std::string message
Definition: exceptions.hpp:29
#define e
A config object defines a single node in a WML file, with access to child nodes.
Definition: config.hpp:59
filesystem::scoped_istream preprocess_file(const std::string &fname, preproc_map *defines)
Function to use the WML preprocessor on a file.