Fix typo in log message.
[dcpomatic.git] / src / lib / font_config.cc
1 /*
2     Copyright (C) 2014-2021 Carl Hetherington <cth@carlh.net>
3
4     This file is part of DCP-o-matic.
5
6     DCP-o-matic 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
11     DCP-o-matic is distributed in the hope that it will be useful,
12     but WITHOUT ANY WARRANTY; without even the implied warranty of
13     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14     GNU General Public License for more details.
15
16     You should have received a copy of the GNU General Public License
17     along with DCP-o-matic.  If not, see <http://www.gnu.org/licenses/>.
18
19 */
20
21
22 #include "dcpomatic_assert.h"
23 #include "dcpomatic_log.h"
24 #include "font_config.h"
25 #include <fontconfig/fontconfig.h>
26 #include <boost/filesystem.hpp>
27 #include <boost/optional.hpp>
28
29
30 using std::string;
31 using boost::optional;
32
33
34 FontConfig* FontConfig::_instance;
35
36
37 FontConfig::FontConfig()
38 {
39         _config = FcInitLoadConfigAndFonts();
40         FcConfigSetCurrent(_config);
41 }
42
43
44 string
45 FontConfig::make_font_available(boost::filesystem::path font_file)
46 {
47         auto existing = _available_fonts.find(font_file);
48         if (existing != _available_fonts.end()) {
49                 return existing->second;
50         }
51
52         /* Make this font available to DCP-o-matic */
53         optional<string> font_name;
54         FcConfigAppFontAddFile (_config, reinterpret_cast<FcChar8 const *>(font_file.string().c_str()));
55         auto pattern = FcPatternBuild (
56                 0, FC_FILE, FcTypeString, font_file.string().c_str(), static_cast<char *>(0)
57                 );
58         auto object_set = FcObjectSetBuild (FC_FAMILY, FC_STYLE, FC_LANG, FC_FILE, static_cast<char *> (0));
59         auto font_set = FcFontList (_config, pattern, object_set);
60         if (font_set) {
61                 for (int i = 0; i < font_set->nfont; ++i) {
62                         FcPattern* font = font_set->fonts[i];
63                         FcChar8* file;
64                         FcChar8* family;
65                         FcChar8* style;
66                         if (
67                                 FcPatternGetString (font, FC_FILE, 0, &file) == FcResultMatch &&
68                                 FcPatternGetString (font, FC_FAMILY, 0, &family) == FcResultMatch &&
69                                 FcPatternGetString (font, FC_STYLE, 0, &style) == FcResultMatch
70                                 ) {
71                                 font_name = reinterpret_cast<char const *> (family);
72                         }
73                 }
74
75                 FcFontSetDestroy (font_set);
76         }
77
78         FcObjectSetDestroy (object_set);
79         FcPatternDestroy (pattern);
80
81         DCPOMATIC_ASSERT(font_name);
82
83         _available_fonts[font_file] = *font_name;
84
85         FcConfigBuildFonts(_config);
86         return *font_name;
87 }
88
89
90 optional<boost::filesystem::path>
91 FontConfig::system_font_with_name(string name)
92 {
93         optional<boost::filesystem::path> path;
94
95         LOG_GENERAL("Searching system for font %1", name);
96         auto pattern = FcNameParse(reinterpret_cast<FcChar8 const*>(name.c_str()));
97         auto object_set = FcObjectSetBuild(FC_FILE, nullptr);
98         auto font_set = FcFontList(_config, pattern, object_set);
99         if (font_set) {
100                 LOG_GENERAL("%1 candidate fonts found", font_set->nfont);
101                 for (int i = 0; i < font_set->nfont; ++i) {
102                         auto font = font_set->fonts[i];
103                         FcChar8* file;
104                         if (FcPatternGetString(font, FC_FILE, 0, &file) == FcResultMatch) {
105                                 path = boost::filesystem::path(reinterpret_cast<char*>(file));
106                                 LOG_GENERAL("Found %1", *path);
107                                 break;
108                         }
109                 }
110                 FcFontSetDestroy(font_set);
111         } else {
112                 LOG_GENERAL_NC("No candidate fonts found");
113         }
114
115         FcObjectSetDestroy(object_set);
116         FcPatternDestroy(pattern);
117
118         if (path) {
119                 LOG_GENERAL("Searched system for font %1, found %2", name, *path);
120         } else {
121                 LOG_GENERAL("Searched system for font %1; nothing found", name);
122         }
123
124         return path;
125 }
126
127
128 FontConfig *
129 FontConfig::instance()
130 {
131         if (!_instance) {
132                 _instance = new FontConfig();
133         }
134
135         return _instance;
136 }
137