The Battle for Wesnoth  1.19.1+dev
font_config.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2016 - 2024
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"
17 #include "font/error.hpp"
18 
19 #include "config.hpp"
20 #include "log.hpp"
21 #include "tstring.hpp"
22 
23 #include "filesystem.hpp"
24 
25 #include "serialization/parser.hpp"
27 
28 #include <sstream>
29 
30 
31 #include <fontconfig/fontconfig.h>
32 
33 static lg::log_domain log_font("font");
34 #define DBG_FT LOG_STREAM(debug, log_font)
35 #define LOG_FT LOG_STREAM(info, log_font)
36 #define WRN_FT LOG_STREAM(warn, log_font)
37 #define ERR_FT LOG_STREAM(err, log_font)
38 
39 namespace font {
40 
41 
42 bool check_font_file(std::string name) {
43  if(game_config::path.empty() == false) {
44  if(!filesystem::file_exists(game_config::path + "/fonts/" + name)) {
45  if(!filesystem::file_exists("fonts/" + name)) {
46  if(!filesystem::file_exists(name)) {
47  WRN_FT << "Failed opening font file '" << name << "': No such file or directory";
48  return false;
49  }
50  }
51  }
52  } else {
53  if(!filesystem::file_exists("fonts/" + name)) {
54  if(!filesystem::file_exists(name)) {
55  WRN_FT << "Failed opening font file '" << name << "': No such file or directory";
56  return false;
57  }
58  }
59  }
60  return true;
61 }
62 
63 namespace
64 {
65 
66 // Current font family for sanserif and monospace fonts in the game
67 
68 t_string family_order_sans;
69 t_string family_order_mono;
70 t_string family_order_light;
71 t_string family_order_script;
72 
73 } // end anon namespace
74 
75 /***
76  * Public interface
77  */
78 
80 {
81  config cfg;
82  try {
83  const auto cfg_path = filesystem::get_wml_location("hardwired/fonts.cfg");
84  if(!cfg_path) {
85  ERR_FT << "could not resolve path to fonts.cfg, file not found";
86  return false;
87  }
88 
89  filesystem::scoped_istream stream = preprocess_file(cfg_path.value());
90  read(cfg, *stream);
91  } catch(const config::error &e) {
92  ERR_FT << "could not read fonts.cfg:\n" << e.message;
93  return false;
94  }
95 
96  auto fonts_config = cfg.optional_child("fonts");
97  if (!fonts_config)
98  return false;
99 
100  family_order_sans = fonts_config["family_order"];
101  family_order_mono = fonts_config["family_order_monospace"];
102  family_order_light = fonts_config["family_order_light"];
103  family_order_script = fonts_config["family_order_script"];
104 
105  if(family_order_mono.empty()) {
106  ERR_FT << "No monospace font family order defined, falling back to sans serif order";
107  family_order_mono = family_order_sans;
108  }
109 
110  if(family_order_light.empty()) {
111  ERR_FT << "No light font family order defined, falling back to sans serif order";
112  family_order_light = family_order_sans;
113  }
114 
115  if(family_order_script.empty()) {
116  ERR_FT << "No script font family order defined, falling back to sans serif order";
117  family_order_script = family_order_sans;
118  }
119 
120  return true;
121 }
122 
124 {
125  switch(fclass) {
126  case FONT_MONOSPACE:
127  return family_order_mono;
128  case FONT_LIGHT:
129  return family_order_light;
130  case FONT_SCRIPT:
131  return family_order_script;
132  default:
133  return family_order_sans;
134  }
135 }
136 
137 /***
138  * Manager member functions
139  */
140 
142 {
143  std::string font_path = game_config::path + "/fonts";
144  if (!FcConfigAppFontAddDir(FcConfigGetCurrent(),
145  reinterpret_cast<const FcChar8 *>(font_path.c_str())))
146  {
147  ERR_FT << "Could not load the true type fonts";
148  throw font::error("font config lib failed to add the font path: '" + font_path + "'");
149  }
150 
151  std::string font_file = font_path + "/fonts.conf";
152  std::string font_file_contents = filesystem::read_file(font_file);
153 
154 // msys2 crosscompiling for windows for whatever reason makes the cache directory prefer using drives other than C:
155 // ie - D:\a\msys64\var\cache\fontconfig
156 // fontconfig also does not seem to provide a way to set the cachedir for a specific platform
157 // so load the fonts.conf file into memory and only for windows insert the cachedir configuration
158 #ifdef _WIN32
159  font_file_contents.insert(font_file_contents.find("</fontconfig>"), "<cachedir>"+filesystem::get_cache_dir()+"</cachedir>\n");
160 #endif
161 
162  if(!FcConfigParseAndLoadFromMemory(FcConfigGetCurrent(),
163  reinterpret_cast<const FcChar8*>(font_file_contents.c_str()),
164  FcFalse))
165  {
166  ERR_FT << "Could not load local font configuration";
167  throw font::error("font config lib failed to find font.conf: '" + font_file + "'");
168  }
169  else
170  {
171  LOG_FT << "Local font configuration loaded";
172  }
173 }
174 
176 {
177  FcConfigAppFontClear(FcConfigGetCurrent());
178 }
179 
180 } // end namespace font
A config object defines a single node in a WML file, with access to child nodes.
Definition: config.hpp:159
optional_config_impl< config > optional_child(config_key_type key, int n=0)
Euivalent to mandatory_child, but returns an empty optional if the nth child was not found.
Definition: config.cpp:385
Declarations for File-IO.
#define LOG_FT
Definition: font_config.cpp:35
static lg::log_domain log_font("font")
#define WRN_FT
Definition: font_config.cpp:36
#define ERR_FT
Definition: font_config.cpp:37
Standard logging facilities (interface).
std::string get_cache_dir()
Definition: filesystem.cpp:803
static bool file_exists(const bfs::path &fpath)
Definition: filesystem.cpp:324
std::string read_file(const std::string &fname)
Basic disk I/O - read file.
std::unique_ptr< std::istream > scoped_istream
Definition: filesystem.hpp:52
std::optional< 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, if either exists.
Collection of helper functions relating to Pango formatting.
family_class
Font classes for get_font_families().
@ FONT_MONOSPACE
@ FONT_LIGHT
@ FONT_SCRIPT
bool check_font_file(std::string name)
Test if a font file exists.
Definition: font_config.cpp:42
const t_string & get_font_families(family_class fclass)
Returns the currently defined fonts.
bool load_font_config()
Definition: font_config.cpp:79
std::string path
Definition: filesystem.cpp:89
filesystem::scoped_istream preprocess_file(const std::string &fname, preproc_map *defines)
Function to use the WML preprocessor on a file.
void read(config &cfg, std::istream &in, abstract_validator *validator)
Definition: parser.cpp:624
#define e