The Battle for Wesnoth  1.15.0-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 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"
16 #include "font/error.hpp"
17 
18 #include "config.hpp"
19 #include "log.hpp"
20 #include "tstring.hpp"
21 
22 #include "filesystem.hpp"
23 #include "game_config.hpp"
24 
25 #include "serialization/parser.hpp"
29 #include "preferences/general.hpp"
30 
31 #include <list>
32 #include <set>
33 #include <stack>
34 #include <sstream>
35 #include <vector>
36 
37 #include <cairo-features.h>
38 
39 #ifdef CAIRO_HAS_WIN32_FONT
40 #include <windows.h>
41 #undef CAIRO_HAS_FT_FONT
42 #endif
43 
44 #ifdef CAIRO_HAS_FT_FONT
45 #include <fontconfig/fontconfig.h>
46 #endif
47 
48 #if !defined(CAIRO_HAS_FT_FONT) && !defined(CAIRO_HAS_WIN32_FONT)
49 #error unable to find font loading tools.
50 #endif
51 
52 static lg::log_domain log_font("font");
53 #define DBG_FT LOG_STREAM(debug, log_font)
54 #define LOG_FT LOG_STREAM(info, log_font)
55 #define WRN_FT LOG_STREAM(warn, log_font)
56 #define ERR_FT LOG_STREAM(err, log_font)
57 
58 namespace font {
59 
60 
61 bool check_font_file(std::string name) {
62  if(game_config::path.empty() == false) {
63  if(!filesystem::file_exists(game_config::path + "/fonts/" + name)) {
64  if(!filesystem::file_exists("fonts/" + name)) {
65  if(!filesystem::file_exists(name)) {
66  WRN_FT << "Failed opening font file '" << name << "': No such file or directory" << std::endl;
67  return false;
68  }
69  }
70  }
71  } else {
72  if(!filesystem::file_exists("fonts/" + name)) {
73  if(!filesystem::file_exists(name)) {
74  WRN_FT << "Failed opening font file '" << name << "': No such file or directory" << std::endl;
75  return false;
76  }
77  }
78  }
79  return true;
80 }
81 
82 namespace
83 {
84 #ifdef CAIRO_HAS_WIN32_FONT
85 bool is_valid_font_file(const std::string& file)
86 {
87  static const std::array<std::string, 3> font_exts { ".ttf", ".ttc", ".otf" };
88 
89  for(const std::string& ext : font_exts) {
90  if(filesystem::ends_with(file, ext)) {
91  return true;
92  }
93  }
94 
95  return false;
96 }
97 #endif
98 
99 // Current font family for sanserif and monospace fonts in the game
100 
101 t_string family_order_sans;
102 t_string family_order_mono;
103 t_string family_order_light;
104 t_string family_order_script;
105 
106 } // end anon namespace
107 
108 /***
109  * Public interface
110  */
111 
113 {
114  //read font config separately, so we do not have to re-read the whole
115  //config when changing languages
116  config cfg;
117  try {
118  const std::string& cfg_path = filesystem::get_wml_location("hardwired/fonts.cfg");
119  if(cfg_path.empty()) {
120  ERR_FT << "could not resolve path to fonts.cfg, file not found\n";
121  return false;
122  }
123 
124  filesystem::scoped_istream stream = preprocess_file(cfg_path);
125  read(cfg, *stream);
126  } catch(const config::error &e) {
127  ERR_FT << "could not read fonts.cfg:\n"
128  << e.message << '\n';
129  return false;
130  }
131 
132  const config &fonts_config = cfg.child("fonts");
133  if (!fonts_config)
134  return false;
135 
136  family_order_sans = fonts_config["family_order"];
137  family_order_mono = fonts_config["family_order_monospace"];
138  family_order_light = fonts_config["family_order_light"];
139  family_order_script = fonts_config["family_order_script"];
140 
141  if(family_order_mono.empty()) {
142  ERR_FT << "No monospace font family order defined, falling back to sans serif order\n";
143  family_order_mono = family_order_sans;
144  }
145 
146  if(family_order_light.empty()) {
147  ERR_FT << "No light font family order defined, falling back to sans serif order\n";
148  family_order_light = family_order_sans;
149  }
150 
151  if(family_order_script.empty()) {
152  ERR_FT << "No script font family order defined, falling back to sans serif order\n";
153  family_order_script = family_order_sans;
154  }
155 
156  return true;
157 }
158 
160 {
161  switch(fclass) {
162  case FONT_MONOSPACE:
163  return family_order_mono;
164  case FONT_LIGHT:
165  return family_order_light;
166  case FONT_SCRIPT:
167  return family_order_script;
168  default:
169  return family_order_sans;
170  }
171 }
172 
173 /***
174  * Manager member functions
175  */
176 
178 {
179 #ifdef CAIRO_HAS_FT_FONT
180  std::string font_path = game_config::path + "/fonts";
181  if (!FcConfigAppFontAddDir(FcConfigGetCurrent(),
182  reinterpret_cast<const FcChar8 *>(font_path.c_str())))
183  {
184  ERR_FT << "Could not load the true type fonts" << std::endl;
185  throw font::error("font config lib failed to add the font path: '" + font_path + "'");
186  }
187 
188  std::string font_file = font_path + "/fonts.conf";
189  if(!FcConfigParseAndLoad(FcConfigGetCurrent(),
190  reinterpret_cast<const FcChar8*>(font_file.c_str()),
191  FcFalse))
192  {
193  ERR_FT << "Could not load local font configuration\n";
194  throw font::error("font config lib failed to find font.conf: '" + font_file + "'");
195  }
196  else
197  {
198  LOG_FT << "Local font configuration loaded\n";
199  }
200 #endif
201 
202 #ifdef CAIRO_HAS_WIN32_FONT
203  for(const std::string& path : filesystem::get_binary_paths("fonts")) {
204  std::vector<std::string> files;
207  }
208  for(const std::string& file : files) {
209  if(is_valid_font_file(file))
210  {
211  const std::wstring wfile = unicode_cast<std::wstring>(file);
212  AddFontResourceExW(wfile.c_str(), FR_PRIVATE, nullptr);
213  }
214  }
215  }
216 #endif
217 }
218 
220 {
221 #ifdef CAIRO_HAS_FT_FONT
222  FcConfigAppFontClear(FcConfigGetCurrent());
223 #endif
224 
225 #ifdef CAIRO_HAS_WIN32_FONT
226  for(const std::string& path : filesystem::get_binary_paths("fonts")) {
227  std::vector<std::string> files;
230  for(const std::string& file : files) {
231  if(is_valid_font_file(file))
232  {
233  const std::wstring wfile = unicode_cast<std::wstring>(file);
234  RemoveFontResourceExW(wfile.c_str(), FR_PRIVATE, nullptr);
235  }
236  }
237  }
238 #endif
239 }
240 
241 
242 } // 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:423
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:61
Collection of helper functions relating to Pango formatting.
static bool file_exists(const fs::path &fpath)
Definition: filesystem.cpp:300
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)
#define WRN_FT
Definition: font_config.cpp:55
#define LOG_FT
Definition: font_config.cpp:54
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:612
bool is_directory(const std::string &fname)
Returns true if the given file is a directory.
Definition: filesystem.cpp:984
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.
void get_files_in_dir(const std::string &dir, std::vector< std::string > *files, std::vector< std::string > *dirs, file_name_option mode, file_filter_option filter, file_reorder_option 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:386
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:56
Standard logging facilities (interface).
std::string message
Definition: exceptions.hpp:31
#define e
A config object defines a single node in a WML file, with access to child nodes.
Definition: config.hpp:68
filesystem::scoped_istream preprocess_file(const std::string &fname, preproc_map *defines)
std::string path
File path.