The Battle for Wesnoth  1.17.0-dev
font_config.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2016 - 2021
3  by Chris Beck<render787@gmail.com>
4  Part of the Battle for Wesnoth Project https://www.wesnoth.org/
5 
6  This program is free software; you can redistribute it and/or modify
7  it under the terms of the GNU General Public License as published by
8  the Free Software Foundation; either version 2 of the License, or
9  (at your option) any later version.
10  This program is distributed in the hope that it will be useful,
11  but WITHOUT ANY WARRANTY.
12 
13  See the COPYING file for more details.
14 */
15 
16 #include "font/font_config.hpp"
18 #include "font/error.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 
63 bool check_font_file(std::string name) {
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 
87 #ifdef CAIRO_HAS_WIN32_FONT
88 bool is_valid_font_file(const std::string& file)
89 {
90  static const std::array<std::string, 3> font_exts { ".ttf", ".ttc", ".otf" };
91 
92  for(const std::string& ext : font_exts) {
93  if(filesystem::ends_with(file, ext)) {
94  return true;
95  }
96  }
97 
98  return false;
99 }
100 #endif
101 
102 // Current font family for sanserif and monospace fonts in the game
103 
104 t_string family_order_sans;
105 t_string family_order_mono;
106 t_string family_order_light;
107 t_string family_order_script;
108 
109 } // end anon namespace
110 
111 /***
112  * Public interface
113  */
114 
116 {
117  config cfg;
118  try {
119  const std::string& cfg_path = filesystem::get_wml_location("hardwired/fonts.cfg");
120  if(cfg_path.empty()) {
121  ERR_FT << "could not resolve path to fonts.cfg, file not found\n";
122  return false;
123  }
124 
125  filesystem::scoped_istream stream = preprocess_file(cfg_path);
126  read(cfg, *stream);
127  } catch(const config::error &e) {
128  ERR_FT << "could not read fonts.cfg:\n"
129  << e.message << '\n';
130  return false;
131  }
132 
133  const config &fonts_config = cfg.child("fonts");
134  if (!fonts_config)
135  return false;
136 
137  family_order_sans = fonts_config["family_order"];
138  family_order_mono = fonts_config["family_order_monospace"];
139  family_order_light = fonts_config["family_order_light"];
140  family_order_script = fonts_config["family_order_script"];
141 
142  if(family_order_mono.empty()) {
143  ERR_FT << "No monospace font family order defined, falling back to sans serif order\n";
144  family_order_mono = family_order_sans;
145  }
146 
147  if(family_order_light.empty()) {
148  ERR_FT << "No light font family order defined, falling back to sans serif order\n";
149  family_order_light = family_order_sans;
150  }
151 
152  if(family_order_script.empty()) {
153  ERR_FT << "No script font family order defined, falling back to sans serif order\n";
154  family_order_script = family_order_sans;
155  }
156 
157  return true;
158 }
159 
161 {
162  switch(fclass) {
163  case FONT_MONOSPACE:
164  return family_order_mono;
165  case FONT_LIGHT:
166  return family_order_light;
167  case FONT_SCRIPT:
168  return family_order_script;
169  default:
170  return family_order_sans;
171  }
172 }
173 
174 /***
175  * Manager member functions
176  */
177 
179 {
180 #ifdef CAIRO_HAS_FT_FONT
181  std::string font_path = game_config::path + "/fonts";
182  if (!FcConfigAppFontAddDir(FcConfigGetCurrent(),
183  reinterpret_cast<const FcChar8 *>(font_path.c_str())))
184  {
185  ERR_FT << "Could not load the true type fonts" << std::endl;
186  throw font::error("font config lib failed to add the font path: '" + font_path + "'");
187  }
188 
189  std::string font_file = font_path + "/fonts.conf";
190  if(!FcConfigParseAndLoad(FcConfigGetCurrent(),
191  reinterpret_cast<const FcChar8*>(font_file.c_str()),
192  FcFalse))
193  {
194  ERR_FT << "Could not load local font configuration\n";
195  throw font::error("font config lib failed to find font.conf: '" + font_file + "'");
196  }
197  else
198  {
199  LOG_FT << "Local font configuration loaded\n";
200  }
201 #endif
202 
203 #ifdef CAIRO_HAS_WIN32_FONT
204  for(const std::string& path : filesystem::get_binary_paths("fonts")) {
205  std::vector<std::string> files;
208  }
209  for(const std::string& file : files) {
210  if(is_valid_font_file(file))
211  {
212  const std::wstring wfile = unicode_cast<std::wstring>(file);
213  AddFontResourceExW(wfile.c_str(), FR_PRIVATE, nullptr);
214  }
215  }
216  }
217 #endif
218 }
219 
221 {
222 #ifdef CAIRO_HAS_FT_FONT
223  FcConfigAppFontClear(FcConfigGetCurrent());
224 #endif
225 
226 #ifdef CAIRO_HAS_WIN32_FONT
227  for(const std::string& path : filesystem::get_binary_paths("fonts")) {
228  std::vector<std::string> files;
231  for(const std::string& file : files) {
232  if(is_valid_font_file(file))
233  {
234  const std::wstring wfile = unicode_cast<std::wstring>(file);
235  RemoveFontResourceExW(wfile.c_str(), FR_PRIVATE, nullptr);
236  }
237  }
238  }
239 #endif
240 }
241 
242 
243 } // 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:402
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
Collection of helper functions relating to Pango formatting.
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)
#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).
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:627
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)
Get a list of all files and/or directories in a given directory.
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:39
std::string path
Definition: game_config.cpp:39
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:58
Standard logging facilities (interface).
std::string message
Definition: exceptions.hpp:30
#define e
A config object defines a single node in a WML file, with access to child nodes.
Definition: config.hpp:61
filesystem::scoped_istream preprocess_file(const std::string &fname, preproc_map *defines)
Function to use the WML preprocessor on a file.