1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
|
// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "components/services/font/ppapi_fontconfig_matching.h"
#include <fcntl.h>
#include <fontconfig/fontconfig.h>
#include <stddef.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <string>
#include "base/posix/eintr_wrapper.h"
#include "base/strings/string_util.h"
#include "ppapi/c/private/pp_private_font_charset.h"
#include "ppapi/c/trusted/ppb_browser_font_trusted.h"
namespace {
// MSCharSetToFontconfig translates a Microsoft charset identifier to a
// fontconfig language set by appending to |langset|.
// Returns true if |langset| is Latin/Greek/Cyrillic.
bool MSCharSetToFontconfig(FcLangSet* langset, unsigned fdwCharSet) {
// We have need to translate raw fdwCharSet values into terms that
// fontconfig can understand. (See the description of fdwCharSet in the MSDN
// documentation for CreateFont:
// http://msdn.microsoft.com/en-us/library/dd183499(VS.85).aspx )
//
// Although the argument is /called/ 'charset', the actual values conflate
// character sets (which are sets of Unicode code points) and character
// encodings (which are algorithms for turning a series of bits into a
// series of code points.) Sometimes the values will name a language,
// sometimes they'll name an encoding. In the latter case I'm assuming that
// they mean the set of code points in the domain of that encoding.
//
// fontconfig deals with ISO 639-1 language codes:
// http://en.wikipedia.org/wiki/List_of_ISO_639-1_codes
//
// So, for each of the documented fdwCharSet values I've had to take a
// guess at the set of ISO 639-1 languages intended.
bool is_lgc = false;
switch (fdwCharSet) {
case PP_PRIVATEFONTCHARSET_ANSI:
// These values I don't really know what to do with, so I'm going to map
// them to English also.
case PP_PRIVATEFONTCHARSET_DEFAULT:
case PP_PRIVATEFONTCHARSET_MAC:
case PP_PRIVATEFONTCHARSET_OEM:
case PP_PRIVATEFONTCHARSET_SYMBOL:
is_lgc = true;
FcLangSetAdd(langset, reinterpret_cast<const FcChar8*>("en"));
break;
case PP_PRIVATEFONTCHARSET_BALTIC:
// The three baltic languages.
is_lgc = true;
FcLangSetAdd(langset, reinterpret_cast<const FcChar8*>("et"));
FcLangSetAdd(langset, reinterpret_cast<const FcChar8*>("lv"));
FcLangSetAdd(langset, reinterpret_cast<const FcChar8*>("lt"));
break;
case PP_PRIVATEFONTCHARSET_CHINESEBIG5:
FcLangSetAdd(langset, reinterpret_cast<const FcChar8*>("zh-tw"));
break;
case PP_PRIVATEFONTCHARSET_GB2312:
FcLangSetAdd(langset, reinterpret_cast<const FcChar8*>("zh-cn"));
break;
case PP_PRIVATEFONTCHARSET_EASTEUROPE:
// A scattering of eastern European languages.
is_lgc = true;
FcLangSetAdd(langset, reinterpret_cast<const FcChar8*>("pl"));
FcLangSetAdd(langset, reinterpret_cast<const FcChar8*>("cs"));
FcLangSetAdd(langset, reinterpret_cast<const FcChar8*>("sk"));
FcLangSetAdd(langset, reinterpret_cast<const FcChar8*>("hu"));
FcLangSetAdd(langset, reinterpret_cast<const FcChar8*>("hr"));
break;
case PP_PRIVATEFONTCHARSET_GREEK:
is_lgc = true;
FcLangSetAdd(langset, reinterpret_cast<const FcChar8*>("el"));
break;
case PP_PRIVATEFONTCHARSET_HANGUL:
case PP_PRIVATEFONTCHARSET_JOHAB:
// Korean
FcLangSetAdd(langset, reinterpret_cast<const FcChar8*>("ko"));
break;
case PP_PRIVATEFONTCHARSET_RUSSIAN:
is_lgc = true;
FcLangSetAdd(langset, reinterpret_cast<const FcChar8*>("ru"));
break;
case PP_PRIVATEFONTCHARSET_SHIFTJIS:
// Japanese
FcLangSetAdd(langset, reinterpret_cast<const FcChar8*>("ja"));
break;
case PP_PRIVATEFONTCHARSET_TURKISH:
is_lgc = true;
FcLangSetAdd(langset, reinterpret_cast<const FcChar8*>("tr"));
break;
case PP_PRIVATEFONTCHARSET_VIETNAMESE:
is_lgc = true;
FcLangSetAdd(langset, reinterpret_cast<const FcChar8*>("vi"));
break;
case PP_PRIVATEFONTCHARSET_ARABIC:
FcLangSetAdd(langset, reinterpret_cast<const FcChar8*>("ar"));
break;
case PP_PRIVATEFONTCHARSET_HEBREW:
FcLangSetAdd(langset, reinterpret_cast<const FcChar8*>("he"));
break;
case PP_PRIVATEFONTCHARSET_THAI:
FcLangSetAdd(langset, reinterpret_cast<const FcChar8*>("th"));
break;
// default:
// Don't add any languages in that case that we don't recognise the
// constant.
}
return is_lgc;
}
} // namespace
namespace font_service {
int MatchFontFaceWithFallback(const std::string& face,
bool is_bold,
bool is_italic,
uint32_t charset,
uint32_t fallback_family) {
FcLangSet* langset = FcLangSetCreate();
bool is_lgc = MSCharSetToFontconfig(langset, charset);
FcPattern* pattern = FcPatternCreate();
FcPatternAddString(pattern, FC_FAMILY,
reinterpret_cast<const FcChar8*>(face.c_str()));
// TODO(thestig) Check if we can access Chrome's per-script font preference
// here and select better default fonts for non-LGC case.
std::string generic_font_name;
if (is_lgc) {
switch (fallback_family) {
case PP_BROWSERFONT_TRUSTED_FAMILY_SERIF:
generic_font_name = "Times New Roman";
break;
case PP_BROWSERFONT_TRUSTED_FAMILY_SANSSERIF:
generic_font_name = "Arial";
break;
case PP_BROWSERFONT_TRUSTED_FAMILY_MONOSPACE:
generic_font_name = "Courier New";
break;
}
}
if (!generic_font_name.empty()) {
const FcChar8* fc_generic_font_name =
reinterpret_cast<const FcChar8*>(generic_font_name.c_str());
FcPatternAddString(pattern, FC_FAMILY, fc_generic_font_name);
}
if (is_bold)
FcPatternAddInteger(pattern, FC_WEIGHT, FC_WEIGHT_BOLD);
if (is_italic)
FcPatternAddInteger(pattern, FC_SLANT, FC_SLANT_ITALIC);
FcPatternAddLangSet(pattern, FC_LANG, langset);
FcPatternAddBool(pattern, FC_SCALABLE, FcTrue);
FcConfigSubstitute(nullptr, pattern, FcMatchPattern);
FcDefaultSubstitute(pattern);
FcResult result;
FcFontSet* font_set = FcFontSort(nullptr, pattern, 0, nullptr, &result);
int font_fd = -1;
int good_enough_index = -1;
bool good_enough_index_set = false;
const char* c_filename;
const char* c_sysroot =
reinterpret_cast<const char*>(FcConfigGetSysRoot(nullptr));
const std::string sysroot = c_sysroot ? c_sysroot : "";
if (font_set) {
for (int i = 0; i < font_set->nfont; ++i) {
FcPattern* current = font_set->fonts[i];
// Older versions of fontconfig have a bug where they cannot select
// only scalable fonts so we have to manually filter the results.
FcBool is_scalable;
if (FcPatternGetBool(current, FC_SCALABLE, 0, &is_scalable) !=
FcResultMatch ||
!is_scalable) {
continue;
}
if (FcPatternGetString(current, FC_FILE, 0,
reinterpret_cast<FcChar8**>(const_cast<char**>(
&c_filename))) != FcResultMatch) {
continue;
}
const std::string filename = sysroot + c_filename;
// We only want to return sfnt (TrueType) based fonts. We don't have a
// very good way of detecting this so we'll filter based on the
// filename.
bool is_sfnt = false;
static const char kSFNTExtensions[][5] = {".ttf", ".otc", ".TTF", ".ttc",
""};
for (size_t j = 0;; j++) {
if (kSFNTExtensions[j][0] == 0) {
// None of the extensions matched.
break;
}
if (base::EndsWith(filename, kSFNTExtensions[j],
base::CompareCase::SENSITIVE)) {
is_sfnt = true;
break;
}
}
if (!is_sfnt)
continue;
// This font is good enough to pass muster, but we might be able to do
// better with subsequent ones.
if (!good_enough_index_set) {
good_enough_index = i;
good_enough_index_set = true;
}
FcValue matrix;
bool have_matrix = FcPatternGet(current, FC_MATRIX, 0, &matrix) == 0;
if (is_italic && have_matrix) {
// we asked for an italic font, but fontconfig is giving us a
// non-italic font with a transformation matrix.
continue;
}
FcValue embolden;
const bool have_embolden =
FcPatternGet(current, FC_EMBOLDEN, 0, &embolden) == 0;
if (is_bold && have_embolden) {
// we asked for a bold font, but fontconfig gave us a non-bold font
// and asked us to apply fake bolding.
continue;
}
font_fd = HANDLE_EINTR(open(filename.c_str(), O_RDONLY));
if (font_fd >= 0)
break;
}
}
if (font_fd == -1 && good_enough_index_set) {
// We didn't find a font that we liked, so we fallback to something
// acceptable.
FcPattern* current = font_set->fonts[good_enough_index];
if (!FcPatternGetString(
current, FC_FILE, 0,
reinterpret_cast<FcChar8**>(const_cast<char**>(&c_filename)))) {
const std::string filename = sysroot + c_filename;
font_fd = HANDLE_EINTR(open(filename.c_str(), O_RDONLY));
}
}
if (font_set)
FcFontSetDestroy(font_set);
FcPatternDestroy(pattern);
return font_fd;
}
} // namespace font_service
|