The Battle for Wesnoth  1.19.27+dev
font_config.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2016 - 2025
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 #ifdef _WIN32
33 #include <pango/pangocairo.h>
34 #endif
35 
36 static lg::log_domain log_font("font");
37 #define DBG_FT LOG_STREAM(debug, log_font)
38 #define LOG_FT LOG_STREAM(info, log_font)
39 #define WRN_FT LOG_STREAM(warn, log_font)
40 #define ERR_FT LOG_STREAM(err, log_font)
41 
42 namespace font
43 {
44 namespace
45 {
46 
47 /** Records the game's families for sans serif, script, and monospace fonts */
48 struct font_families
49 {
50  font_families() = default;
51 
52  explicit font_families(const config& cfg)
53  : sans(cfg["family_order"].t_str())
54  , mono(cfg["family_order_monospace"].t_str())
55  , script(cfg["family_order_script"].t_str())
56  {
57  if(mono.empty()) {
58  ERR_FT << "No monospace font family defined, falling back to sans serif";
59  mono = sans;
60  }
61 
62  if(script.empty()) {
63  ERR_FT << "No script font family defined, falling back to sans serif";
64  script = sans;
65  }
66  }
67 
71 };
72 
73 font_families families;
74 
75 } //namespace
76 
77 /***
78  * Public interface
79  */
80 
82 try {
83  auto stream = preprocess_file(filesystem::get_wml_location("hardwired/fonts.cfg").value());
84  families = font_families{ io::read(*stream).mandatory_child("fonts") };
85  return true;
86 
87 } catch(const utils::bad_optional_access&) {
88  ERR_FT << "could not resolve path to fonts.cfg, file not found";
89  return false;
90 
91 } catch(const config::error& e) {
92  ERR_FT << "could not read fonts.cfg:\n" << e.message;
93  return false;
94 }
95 
97 {
98  switch(fclass) {
100  return families.mono;
102  return families.script;
103  default:
104  return families.sans;
105  }
106 }
107 
108 /***
109  * Manager member functions
110  */
111 
113 {
114  std::string font_path = game_config::path + "/fonts";
115  if (!FcConfigAppFontAddDir(FcConfigGetCurrent(),
116  reinterpret_cast<const FcChar8 *>(font_path.c_str())))
117  {
118  ERR_FT << "Could not load the true type fonts";
119  throw font::error("font config lib failed to add the font path: '" + font_path + "'");
120  }
121 
122  std::string font_file = font_path + "/fonts.conf";
123  std::string font_file_contents = filesystem::read_file(font_file);
124 
125 // msys2 crosscompiling for windows for whatever reason makes the cache directory prefer using drives other than C:
126 // ie - D:\a\msys64\var\cache\fontconfig
127 // fontconfig also does not seem to provide a way to set the cachedir for a specific platform
128 // so load the fonts.conf file into memory and only for windows insert the cachedir configuration
129 #ifdef _WIN32
130  font_file_contents.insert(font_file_contents.find("</fontconfig>"), "<cachedir>"+filesystem::get_cache_dir()+"</cachedir>\n");
131 #endif
132 
133  if(!FcConfigParseAndLoadFromMemory(FcConfigGetCurrent(),
134  reinterpret_cast<const FcChar8*>(font_file_contents.c_str()),
135  FcFalse))
136  {
137  ERR_FT << "Could not load local font configuration";
138  throw font::error("font config lib failed to find font.conf: '" + font_file + "'");
139  }
140  else
141  {
142  LOG_FT << "Local font configuration loaded";
143  }
144 
145 #ifdef _WIN32
146  // Pango's default Windows back-end is the native win32/GDI one, which never
147  // consults fontconfig and thus never sees the custom fonts registered above.
148  // Setting PANGOCAIRO_BACKEND=fontconfig in main() is meant to prevent this,
149  // but on some Windows builds it has not reliably taken effect (since the
150  // SDL3 migration) before Pango's default font map is first created, so force
151  // it explicitly here instead of relying on that environment variable being
152  // seen in time.
153  PangoFontMap* fc_font_map = pango_cairo_font_map_new_for_font_type(CAIRO_FONT_TYPE_FT);
154  if(fc_font_map) {
155  pango_cairo_font_map_set_default(PANGO_CAIRO_FONT_MAP(fc_font_map));
156  g_object_unref(fc_font_map);
157  } else {
158  ERR_FT << "Could not create a fontconfig-backed Pango font map";
159  }
160 #endif
161 }
162 
164 {
165  FcConfigAppFontClear(FcConfigGetCurrent());
166 }
167 
168 } // end namespace font
A config object defines a single node in a WML file, with access to child nodes.
Definition: config.hpp:157
config & mandatory_child(std::string_view key, int n=0)
Returns the nth child with the given key, or throws an error if there is none.
Definition: config.cpp:362
bool empty() const
Definition: tstring.hpp:200
Definitions for the interface to Wesnoth Markup Language (WML).
const config * cfg
Declarations for File-IO.
t_string mono
Definition: font_config.cpp:69
#define LOG_FT
Definition: font_config.cpp:38
t_string script
Definition: font_config.cpp:70
static lg::log_domain log_font("font")
#define ERR_FT
Definition: font_config.cpp:40
t_string sans
Definition: font_config.cpp:68
Standard logging facilities (interface).
std::string get_cache_dir()
Definition: filesystem.cpp:880
utils::optional< std::string > get_wml_location(const std::string &path, const utils::optional< std::string > &current_dir)
Returns a translated path to the actual file or directory, if it exists.
std::string read_file(const std::string &fname)
Basic disk I/O - read file.
Graphical text output.
const t_string & get_font_families(family_class fclass)
Returns the currently defined fonts.
Definition: font_config.cpp:96
bool load_font_config()
Definition: font_config.cpp:81
std::string path
Definition: filesystem.cpp:106
config read(std::istream &in, abstract_validator *validator)
Definition: parser.cpp:610
filesystem::scoped_istream preprocess_file(const std::string &fname, preproc_map &defines)
Function to use the WML preprocessor on a file.
std::unique_ptr< MIX_Audio, decltype(&MIX_DestroyAudio)> value
Definition: sound.cpp:138
#define e