1 /*
2 * Copyright 2009-2015 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
8 /* migrated from chrome/src/skia/ext/SkFontHost_fontconfig_direct.cpp */
9
10 #include "include/core/SkFontStyle.h"
11 #include "include/core/SkStream.h"
12 #include "include/core/SkString.h"
13 #include "include/core/SkTypeface.h"
14 #include "include/private/base/SkFixed.h"
15 #include "include/private/base/SkMutex.h"
16 #include "include/private/base/SkTArray.h"
17 #include "include/private/base/SkTDArray.h"
18 #include "include/private/base/SkTemplates.h"
19 #include "src/base/SkAutoMalloc.h"
20 #include "src/base/SkBuffer.h"
21 #include "src/ports/SkFontConfigInterface_direct.h"
22
23 #include <fontconfig/fontconfig.h>
24 #include <unistd.h>
25
26 namespace {
27
28 // FontConfig was thread antagonistic until 2.10.91 with known thread safety issues until 2.13.93.
29 // Before that, lock with a global mutex.
30 // See https://bug.skia.org/1497 and cl/339089311 for background.
f_c_mutex()31 static SkMutex& f_c_mutex() {
32 static SkMutex& mutex = *(new SkMutex);
33 return mutex;
34 }
35
36 struct FCLocker {
37 inline static constexpr int FontConfigThreadSafeVersion = 21393;
38
39 // Assume FcGetVersion() has always been thread safe.
FCLocker__anond6ffa5f20111::FCLocker40 FCLocker() {
41 if (FcGetVersion() < FontConfigThreadSafeVersion) {
42 f_c_mutex().acquire();
43 }
44 }
45
~FCLocker__anond6ffa5f20111::FCLocker46 ~FCLocker() {
47 AssertHeld();
48 if (FcGetVersion() < FontConfigThreadSafeVersion) {
49 f_c_mutex().release();
50 }
51 }
52
AssertHeld__anond6ffa5f20111::FCLocker53 static void AssertHeld() { SkDEBUGCODE(
54 if (FcGetVersion() < FontConfigThreadSafeVersion) {
55 f_c_mutex().assertHeld();
56 }
57 ) }
58 };
59
60 using UniqueFCConfig = std::unique_ptr<FcConfig, SkFunctionObject<FcConfigDestroy>>;
61
62 } // namespace
63
writeToMemory(void * addr) const64 size_t SkFontConfigInterface::FontIdentity::writeToMemory(void* addr) const {
65 size_t size = sizeof(fID) + sizeof(fTTCIndex);
66 size += sizeof(int32_t) + sizeof(int32_t) + sizeof(uint8_t); // weight, width, italic
67 size += sizeof(int32_t) + fString.size(); // store length+data
68 if (addr) {
69 SkWBuffer buffer(addr, size);
70
71 buffer.write32(fID);
72 buffer.write32(fTTCIndex);
73 buffer.write32(fString.size());
74 buffer.write32(fStyle.weight());
75 buffer.write32(fStyle.width());
76 buffer.write8(fStyle.slant());
77 buffer.write(fString.c_str(), fString.size());
78 buffer.padToAlign4();
79
80 SkASSERT(buffer.pos() == size);
81 }
82 return size;
83 }
84
readFromMemory(const void * addr,size_t size)85 size_t SkFontConfigInterface::FontIdentity::readFromMemory(const void* addr,
86 size_t size) {
87 SkRBuffer buffer(addr, size);
88
89 (void)buffer.readU32(&fID);
90 (void)buffer.readS32(&fTTCIndex);
91 uint32_t strLen, weight, width;
92 (void)buffer.readU32(&strLen);
93 (void)buffer.readU32(&weight);
94 (void)buffer.readU32(&width);
95 uint8_t u8;
96 (void)buffer.readU8(&u8);
97 SkFontStyle::Slant slant = (SkFontStyle::Slant)u8;
98 fStyle = SkFontStyle(weight, width, slant);
99 fString.resize(strLen);
100 (void)buffer.read(fString.data(), strLen);
101 buffer.skipToAlign4();
102
103 return buffer.pos(); // the actual number of bytes read
104 }
105
106 #ifdef SK_DEBUG
make_iden(SkFontConfigInterface::FontIdentity * iden)107 static void make_iden(SkFontConfigInterface::FontIdentity* iden) {
108 iden->fID = 10;
109 iden->fTTCIndex = 2;
110 iden->fString.set("Hello world");
111 iden->fStyle = SkFontStyle(300, 6, SkFontStyle::kItalic_Slant);
112 }
113
test_writeToMemory(const SkFontConfigInterface::FontIdentity & iden0,int initValue)114 static void test_writeToMemory(const SkFontConfigInterface::FontIdentity& iden0,
115 int initValue) {
116 SkFontConfigInterface::FontIdentity iden1;
117
118 size_t size0 = iden0.writeToMemory(nullptr);
119
120 SkAutoMalloc storage(size0);
121 memset(storage.get(), initValue, size0);
122
123 size_t size1 = iden0.writeToMemory(storage.get());
124 SkASSERT(size0 == size1);
125
126 SkASSERT(iden0 != iden1);
127 size_t size2 = iden1.readFromMemory(storage.get(), size1);
128 SkASSERT(size2 == size1);
129 SkASSERT(iden0 == iden1);
130 }
131
fontconfiginterface_unittest()132 static void fontconfiginterface_unittest() {
133 SkFontConfigInterface::FontIdentity iden0, iden1;
134
135 SkASSERT(iden0 == iden1);
136
137 make_iden(&iden0);
138 SkASSERT(iden0 != iden1);
139
140 make_iden(&iden1);
141 SkASSERT(iden0 == iden1);
142
143 test_writeToMemory(iden0, 0);
144 test_writeToMemory(iden0, 0);
145 }
146 #endif
147
148 ///////////////////////////////////////////////////////////////////////////////
149
150 // Returns the string from the pattern, or nullptr
get_string(FcPattern * pattern,const char field[],int index=0)151 static const char* get_string(FcPattern* pattern, const char field[], int index = 0) {
152 const char* name;
153 if (FcPatternGetString(pattern, field, index, (FcChar8**)&name) != FcResultMatch) {
154 name = nullptr;
155 }
156 return name;
157 }
158
159 ///////////////////////////////////////////////////////////////////////////////
160
161 namespace {
162
163 // Equivalence classes, used to match the Liberation and other fonts
164 // with their metric-compatible replacements. See the discussion in
165 // GetFontEquivClass().
166 enum FontEquivClass
167 {
168 OTHER,
169 SANS,
170 SERIF,
171 MONO,
172 SYMBOL,
173 PGOTHIC,
174 GOTHIC,
175 PMINCHO,
176 MINCHO,
177 SIMSUN,
178 NSIMSUN,
179 SIMHEI,
180 PMINGLIU,
181 MINGLIU,
182 PMINGLIUHK,
183 MINGLIUHK,
184 CAMBRIA,
185 CALIBRI,
186 };
187
188 // Match the font name against a whilelist of fonts, returning the equivalence
189 // class.
GetFontEquivClass(const char * fontname)190 FontEquivClass GetFontEquivClass(const char* fontname)
191 {
192 // It would be nice for fontconfig to tell us whether a given suggested
193 // replacement is a "strong" match (that is, an equivalent font) or
194 // a "weak" match (that is, fontconfig's next-best attempt at finding a
195 // substitute). However, I played around with the fontconfig API for
196 // a good few hours and could not make it reveal this information.
197 //
198 // So instead, we hardcode. Initially this function emulated
199 // /etc/fonts/conf.d/30-metric-aliases.conf
200 // from my Ubuntu system, but we're better off being very conservative.
201
202 // Arimo, Tinos and Cousine are a set of fonts metric-compatible with
203 // Arial, Times New Roman and Courier New with a character repertoire
204 // much larger than Liberation. Note that Cousine is metrically
205 // compatible with Courier New, but the former is sans-serif while
206 // the latter is serif.
207
208
209 struct FontEquivMap {
210 FontEquivClass clazz;
211 const char name[40];
212 };
213
214 static const FontEquivMap kFontEquivMap[] = {
215 { SANS, "Arial" },
216 { SANS, "Arimo" },
217 { SANS, "Liberation Sans" },
218
219 { SERIF, "Times New Roman" },
220 { SERIF, "Tinos" },
221 { SERIF, "Liberation Serif" },
222
223 { MONO, "Courier New" },
224 { MONO, "Cousine" },
225 { MONO, "Liberation Mono" },
226
227 { SYMBOL, "Symbol" },
228 { SYMBOL, "Symbol Neu" },
229
230 // MS Pゴシック
231 { PGOTHIC, "MS PGothic" },
232 { PGOTHIC, "\xef\xbc\xad\xef\xbc\xb3 \xef\xbc\xb0"
233 "\xe3\x82\xb4\xe3\x82\xb7\xe3\x83\x83\xe3\x82\xaf" },
234 { PGOTHIC, "Noto Sans CJK JP" },
235 { PGOTHIC, "IPAPGothic" },
236 { PGOTHIC, "MotoyaG04Gothic" },
237
238 // MS ゴシック
239 { GOTHIC, "MS Gothic" },
240 { GOTHIC, "\xef\xbc\xad\xef\xbc\xb3 "
241 "\xe3\x82\xb4\xe3\x82\xb7\xe3\x83\x83\xe3\x82\xaf" },
242 { GOTHIC, "Noto Sans Mono CJK JP" },
243 { GOTHIC, "IPAGothic" },
244 { GOTHIC, "MotoyaG04GothicMono" },
245
246 // MS P明朝
247 { PMINCHO, "MS PMincho" },
248 { PMINCHO, "\xef\xbc\xad\xef\xbc\xb3 \xef\xbc\xb0"
249 "\xe6\x98\x8e\xe6\x9c\x9d"},
250 { PMINCHO, "Noto Serif CJK JP" },
251 { PMINCHO, "IPAPMincho" },
252 { PMINCHO, "MotoyaG04Mincho" },
253
254 // MS 明朝
255 { MINCHO, "MS Mincho" },
256 { MINCHO, "\xef\xbc\xad\xef\xbc\xb3 \xe6\x98\x8e\xe6\x9c\x9d" },
257 { MINCHO, "Noto Serif CJK JP" },
258 { MINCHO, "IPAMincho" },
259 { MINCHO, "MotoyaG04MinchoMono" },
260
261 // 宋体
262 { SIMSUN, "Simsun" },
263 { SIMSUN, "\xe5\xae\x8b\xe4\xbd\x93" },
264 { SIMSUN, "Noto Serif CJK SC" },
265 { SIMSUN, "MSung GB18030" },
266 { SIMSUN, "Song ASC" },
267
268 // 新宋体
269 { NSIMSUN, "NSimsun" },
270 { NSIMSUN, "\xe6\x96\xb0\xe5\xae\x8b\xe4\xbd\x93" },
271 { NSIMSUN, "Noto Serif CJK SC" },
272 { NSIMSUN, "MSung GB18030" },
273 { NSIMSUN, "N Song ASC" },
274
275 // 黑体
276 { SIMHEI, "Simhei" },
277 { SIMHEI, "\xe9\xbb\x91\xe4\xbd\x93" },
278 { SIMHEI, "Noto Sans CJK SC" },
279 { SIMHEI, "MYingHeiGB18030" },
280 { SIMHEI, "MYingHeiB5HK" },
281
282 // 新細明體
283 { PMINGLIU, "PMingLiU"},
284 { PMINGLIU, "\xe6\x96\xb0\xe7\xb4\xb0\xe6\x98\x8e\xe9\xab\x94" },
285 { PMINGLIU, "Noto Serif CJK TC"},
286 { PMINGLIU, "MSung B5HK"},
287
288 // 細明體
289 { MINGLIU, "MingLiU"},
290 { MINGLIU, "\xe7\xb4\xb0\xe6\x98\x8e\xe9\xab\x94" },
291 { MINGLIU, "Noto Serif CJK TC"},
292 { MINGLIU, "MSung B5HK"},
293
294 // 新細明體
295 { PMINGLIUHK, "PMingLiU_HKSCS"},
296 { PMINGLIUHK, "\xe6\x96\xb0\xe7\xb4\xb0\xe6\x98\x8e\xe9\xab\x94_HKSCS" },
297 { PMINGLIUHK, "Noto Serif CJK TC"},
298 { PMINGLIUHK, "MSung B5HK"},
299
300 // 細明體
301 { MINGLIUHK, "MingLiU_HKSCS"},
302 { MINGLIUHK, "\xe7\xb4\xb0\xe6\x98\x8e\xe9\xab\x94_HKSCS" },
303 { MINGLIUHK, "Noto Serif CJK TC"},
304 { MINGLIUHK, "MSung B5HK"},
305
306 // Cambria
307 { CAMBRIA, "Cambria" },
308 { CAMBRIA, "Caladea" },
309
310 // Calibri
311 { CALIBRI, "Calibri" },
312 { CALIBRI, "Carlito" },
313 };
314
315 static const size_t kFontCount =
316 sizeof(kFontEquivMap)/sizeof(kFontEquivMap[0]);
317
318 // TODO(jungshik): If this loop turns out to be hot, turn
319 // the array to a static (hash)map to speed it up.
320 for (size_t i = 0; i < kFontCount; ++i) {
321 if (strcasecmp(kFontEquivMap[i].name, fontname) == 0)
322 return kFontEquivMap[i].clazz;
323 }
324 return OTHER;
325 }
326
327
328 // Return true if |font_a| and |font_b| are visually and at the metrics
329 // level interchangeable.
IsMetricCompatibleReplacement(const char * font_a,const char * font_b)330 bool IsMetricCompatibleReplacement(const char* font_a, const char* font_b)
331 {
332 FontEquivClass class_a = GetFontEquivClass(font_a);
333 FontEquivClass class_b = GetFontEquivClass(font_b);
334
335 return class_a != OTHER && class_a == class_b;
336 }
337
338 // Normally we only return exactly the font asked for. In last-resort
339 // cases, the request either doesn't specify a font or is one of the
340 // basic font names like "Sans", "Serif" or "Monospace". This function
341 // tells you whether a given request is for such a fallback.
IsFallbackFontAllowed(const SkString & family)342 bool IsFallbackFontAllowed(const SkString& family) {
343 const char* family_cstr = family.c_str();
344 return family.isEmpty() ||
345 strcasecmp(family_cstr, "sans") == 0 ||
346 strcasecmp(family_cstr, "serif") == 0 ||
347 strcasecmp(family_cstr, "monospace") == 0;
348 }
349
350 // Retrieves |is_bold|, |is_italic| and |font_family| properties from |font|.
get_int(FcPattern * pattern,const char object[],int missing)351 static int get_int(FcPattern* pattern, const char object[], int missing) {
352 int value;
353 if (FcPatternGetInteger(pattern, object, 0, &value) != FcResultMatch) {
354 return missing;
355 }
356 return value;
357 }
358
map_range(SkScalar value,SkScalar old_min,SkScalar old_max,SkScalar new_min,SkScalar new_max)359 static int map_range(SkScalar value,
360 SkScalar old_min, SkScalar old_max,
361 SkScalar new_min, SkScalar new_max)
362 {
363 SkASSERT(old_min < old_max);
364 SkASSERT(new_min <= new_max);
365 return new_min + ((value - old_min) * (new_max - new_min) / (old_max - old_min));
366 }
367
368 struct MapRanges {
369 SkScalar old_val;
370 SkScalar new_val;
371 };
372
map_ranges(SkScalar val,MapRanges const ranges[],int rangesCount)373 static SkScalar map_ranges(SkScalar val, MapRanges const ranges[], int rangesCount) {
374 // -Inf to [0]
375 if (val < ranges[0].old_val) {
376 return ranges[0].new_val;
377 }
378
379 // Linear from [i] to [i+1]
380 for (int i = 0; i < rangesCount - 1; ++i) {
381 if (val < ranges[i+1].old_val) {
382 return map_range(val, ranges[i].old_val, ranges[i+1].old_val,
383 ranges[i].new_val, ranges[i+1].new_val);
384 }
385 }
386
387 // From [n] to +Inf
388 // if (fcweight < Inf)
389 return ranges[rangesCount-1].new_val;
390 }
391
392 #ifndef FC_WEIGHT_DEMILIGHT
393 #define FC_WEIGHT_DEMILIGHT 65
394 #endif
395
skfontstyle_from_fcpattern(FcPattern * pattern)396 static SkFontStyle skfontstyle_from_fcpattern(FcPattern* pattern) {
397 typedef SkFontStyle SkFS;
398
399 static constexpr MapRanges weightRanges[] = {
400 { FC_WEIGHT_THIN, SkFS::kThin_Weight },
401 { FC_WEIGHT_EXTRALIGHT, SkFS::kExtraLight_Weight },
402 { FC_WEIGHT_LIGHT, SkFS::kLight_Weight },
403 { FC_WEIGHT_DEMILIGHT, 350 },
404 { FC_WEIGHT_BOOK, 380 },
405 { FC_WEIGHT_REGULAR, SkFS::kNormal_Weight },
406 { FC_WEIGHT_MEDIUM, SkFS::kMedium_Weight },
407 { FC_WEIGHT_DEMIBOLD, SkFS::kSemiBold_Weight },
408 { FC_WEIGHT_BOLD, SkFS::kBold_Weight },
409 { FC_WEIGHT_EXTRABOLD, SkFS::kExtraBold_Weight },
410 { FC_WEIGHT_BLACK, SkFS::kBlack_Weight },
411 { FC_WEIGHT_EXTRABLACK, SkFS::kExtraBlack_Weight },
412 };
413 SkScalar weight = map_ranges(get_int(pattern, FC_WEIGHT, FC_WEIGHT_REGULAR),
414 weightRanges, std::size(weightRanges));
415
416 static constexpr MapRanges widthRanges[] = {
417 { FC_WIDTH_ULTRACONDENSED, SkFS::kUltraCondensed_Width },
418 { FC_WIDTH_EXTRACONDENSED, SkFS::kExtraCondensed_Width },
419 { FC_WIDTH_CONDENSED, SkFS::kCondensed_Width },
420 { FC_WIDTH_SEMICONDENSED, SkFS::kSemiCondensed_Width },
421 { FC_WIDTH_NORMAL, SkFS::kNormal_Width },
422 { FC_WIDTH_SEMIEXPANDED, SkFS::kSemiExpanded_Width },
423 { FC_WIDTH_EXPANDED, SkFS::kExpanded_Width },
424 { FC_WIDTH_EXTRAEXPANDED, SkFS::kExtraExpanded_Width },
425 { FC_WIDTH_ULTRAEXPANDED, SkFS::kUltraExpanded_Width },
426 };
427 SkScalar width = map_ranges(get_int(pattern, FC_WIDTH, FC_WIDTH_NORMAL),
428 widthRanges, std::size(widthRanges));
429
430 SkFS::Slant slant = SkFS::kUpright_Slant;
431 switch (get_int(pattern, FC_SLANT, FC_SLANT_ROMAN)) {
432 case FC_SLANT_ROMAN: slant = SkFS::kUpright_Slant; break;
433 case FC_SLANT_ITALIC : slant = SkFS::kItalic_Slant ; break;
434 case FC_SLANT_OBLIQUE: slant = SkFS::kOblique_Slant; break;
435 default: SkASSERT(false); break;
436 }
437
438 return SkFontStyle(SkScalarRoundToInt(weight), SkScalarRoundToInt(width), slant);
439 }
440
fcpattern_from_skfontstyle(SkFontStyle style,FcPattern * pattern)441 static void fcpattern_from_skfontstyle(SkFontStyle style, FcPattern* pattern) {
442 typedef SkFontStyle SkFS;
443
444 static constexpr MapRanges weightRanges[] = {
445 { SkFS::kThin_Weight, FC_WEIGHT_THIN },
446 { SkFS::kExtraLight_Weight, FC_WEIGHT_EXTRALIGHT },
447 { SkFS::kLight_Weight, FC_WEIGHT_LIGHT },
448 { 350, FC_WEIGHT_DEMILIGHT },
449 { 380, FC_WEIGHT_BOOK },
450 { SkFS::kNormal_Weight, FC_WEIGHT_REGULAR },
451 { SkFS::kMedium_Weight, FC_WEIGHT_MEDIUM },
452 { SkFS::kSemiBold_Weight, FC_WEIGHT_DEMIBOLD },
453 { SkFS::kBold_Weight, FC_WEIGHT_BOLD },
454 { SkFS::kExtraBold_Weight, FC_WEIGHT_EXTRABOLD },
455 { SkFS::kBlack_Weight, FC_WEIGHT_BLACK },
456 { SkFS::kExtraBlack_Weight, FC_WEIGHT_EXTRABLACK },
457 };
458 int weight = map_ranges(style.weight(), weightRanges, std::size(weightRanges));
459
460 static constexpr MapRanges widthRanges[] = {
461 { SkFS::kUltraCondensed_Width, FC_WIDTH_ULTRACONDENSED },
462 { SkFS::kExtraCondensed_Width, FC_WIDTH_EXTRACONDENSED },
463 { SkFS::kCondensed_Width, FC_WIDTH_CONDENSED },
464 { SkFS::kSemiCondensed_Width, FC_WIDTH_SEMICONDENSED },
465 { SkFS::kNormal_Width, FC_WIDTH_NORMAL },
466 { SkFS::kSemiExpanded_Width, FC_WIDTH_SEMIEXPANDED },
467 { SkFS::kExpanded_Width, FC_WIDTH_EXPANDED },
468 { SkFS::kExtraExpanded_Width, FC_WIDTH_EXTRAEXPANDED },
469 { SkFS::kUltraExpanded_Width, FC_WIDTH_ULTRAEXPANDED },
470 };
471 int width = map_ranges(style.width(), widthRanges, std::size(widthRanges));
472
473 int slant = FC_SLANT_ROMAN;
474 switch (style.slant()) {
475 case SkFS::kUpright_Slant: slant = FC_SLANT_ROMAN ; break;
476 case SkFS::kItalic_Slant : slant = FC_SLANT_ITALIC ; break;
477 case SkFS::kOblique_Slant: slant = FC_SLANT_OBLIQUE; break;
478 default: SkASSERT(false); break;
479 }
480
481 FcPatternAddInteger(pattern, FC_WEIGHT, weight);
482 FcPatternAddInteger(pattern, FC_WIDTH , width);
483 FcPatternAddInteger(pattern, FC_SLANT , slant);
484 }
485
486 } // anonymous namespace
487
488 ///////////////////////////////////////////////////////////////////////////////
489
490 #define kMaxFontFamilyLength 2048
491 #ifdef SK_FONT_CONFIG_INTERFACE_ONLY_ALLOW_SFNT_FONTS
492 const char* kFontFormatTrueType = "TrueType";
493 const char* kFontFormatCFF = "CFF";
494 #endif
495
SkFontConfigInterfaceDirect(FcConfig * fc)496 SkFontConfigInterfaceDirect::SkFontConfigInterfaceDirect(FcConfig* fc) : fFC(fc)
497 {
498 SkDEBUGCODE(fontconfiginterface_unittest();)
499 }
500
~SkFontConfigInterfaceDirect()501 SkFontConfigInterfaceDirect::~SkFontConfigInterfaceDirect() {
502 if (fFC) {
503 FcConfigDestroy(fFC);
504 }
505 }
506
isAccessible(const char * filename)507 bool SkFontConfigInterfaceDirect::isAccessible(const char* filename) {
508 if (access(filename, R_OK) != 0) {
509 return false;
510 }
511 return true;
512 }
513
isValidPattern(FcPattern * pattern)514 bool SkFontConfigInterfaceDirect::isValidPattern(FcPattern* pattern) {
515 #ifdef SK_FONT_CONFIG_INTERFACE_ONLY_ALLOW_SFNT_FONTS
516 const char* font_format = get_string(pattern, FC_FONTFORMAT);
517 if (font_format
518 && 0 != strcmp(font_format, kFontFormatTrueType)
519 && 0 != strcmp(font_format, kFontFormatCFF))
520 {
521 return false;
522 }
523 #endif
524
525 // fontconfig can also return fonts which are unreadable
526 const char* c_filename = get_string(pattern, FC_FILE);
527 if (!c_filename) {
528 return false;
529 }
530
531 FcConfig* fc = fFC;
532 UniqueFCConfig fcStorage;
533 if (!fc) {
534 fcStorage.reset(FcConfigReference(nullptr));
535 fc = fcStorage.get();
536 }
537
538 const char* sysroot = (const char*)FcConfigGetSysRoot(fc);
539 SkString resolvedFilename;
540 if (sysroot) {
541 resolvedFilename = sysroot;
542 resolvedFilename += c_filename;
543 c_filename = resolvedFilename.c_str();
544 }
545 return this->isAccessible(c_filename);
546 }
547
548 // Find matching font from |font_set| for the given font family.
MatchFont(FcFontSet * font_set,const char * post_config_family,const SkString & family)549 FcPattern* SkFontConfigInterfaceDirect::MatchFont(FcFontSet* font_set,
550 const char* post_config_family,
551 const SkString& family) {
552 // Older versions of fontconfig have a bug where they cannot select
553 // only scalable fonts so we have to manually filter the results.
554 FcPattern* match = nullptr;
555 for (int i = 0; i < font_set->nfont; ++i) {
556 FcPattern* current = font_set->fonts[i];
557 if (this->isValidPattern(current)) {
558 match = current;
559 break;
560 }
561 }
562
563 if (match && !IsFallbackFontAllowed(family)) {
564 bool acceptable_substitute = false;
565 for (int id = 0; id < 255; ++id) {
566 const char* post_match_family = get_string(match, FC_FAMILY, id);
567 if (!post_match_family)
568 break;
569 acceptable_substitute =
570 (strcasecmp(post_config_family, post_match_family) == 0 ||
571 // Workaround for Issue 12530:
572 // requested family: "Bitstream Vera Sans"
573 // post_config_family: "Arial"
574 // post_match_family: "Bitstream Vera Sans"
575 // -> We should treat this case as a good match.
576 strcasecmp(family.c_str(), post_match_family) == 0) ||
577 IsMetricCompatibleReplacement(family.c_str(), post_match_family);
578 if (acceptable_substitute)
579 break;
580 }
581 if (!acceptable_substitute)
582 return nullptr;
583 }
584
585 return match;
586 }
587
matchFamilyName(const char familyName[],SkFontStyle style,FontIdentity * outIdentity,SkString * outFamilyName,SkFontStyle * outStyle)588 bool SkFontConfigInterfaceDirect::matchFamilyName(const char familyName[],
589 SkFontStyle style,
590 FontIdentity* outIdentity,
591 SkString* outFamilyName,
592 SkFontStyle* outStyle) {
593 SkString familyStr(familyName ? familyName : "");
594 if (familyStr.size() > kMaxFontFamilyLength) {
595 return false;
596 }
597
598 FcConfig* fc = fFC;
599 UniqueFCConfig fcStorage;
600 if (!fc) {
601 fcStorage.reset(FcConfigReference(nullptr));
602 fc = fcStorage.get();
603 }
604
605 FCLocker lock;
606 FcPattern* pattern = FcPatternCreate();
607
608 if (familyName) {
609 FcPatternAddString(pattern, FC_FAMILY, (FcChar8*)familyName);
610 }
611 fcpattern_from_skfontstyle(style, pattern);
612
613 FcPatternAddBool(pattern, FC_SCALABLE, FcTrue);
614
615 FcConfigSubstitute(fc, pattern, FcMatchPattern);
616 FcDefaultSubstitute(pattern);
617
618 // Font matching:
619 // CSS often specifies a fallback list of families:
620 // font-family: a, b, c, serif;
621 // However, fontconfig will always do its best to find *a* font when asked
622 // for something so we need a way to tell if the match which it has found is
623 // "good enough" for us. Otherwise, we can return nullptr which gets piped up
624 // and lets WebKit know to try the next CSS family name. However, fontconfig
625 // configs allow substitutions (mapping "Arial -> Helvetica" etc) and we
626 // wish to support that.
627 //
628 // Thus, if a specific family is requested we set @family_requested. Then we
629 // record two strings: the family name after config processing and the
630 // family name after resolving. If the two are equal, it's a good match.
631 //
632 // So consider the case where a user has mapped Arial to Helvetica in their
633 // config.
634 // requested family: "Arial"
635 // post_config_family: "Helvetica"
636 // post_match_family: "Helvetica"
637 // -> good match
638 //
639 // and for a missing font:
640 // requested family: "Monaco"
641 // post_config_family: "Monaco"
642 // post_match_family: "Times New Roman"
643 // -> BAD match
644 //
645 // However, we special-case fallback fonts; see IsFallbackFontAllowed().
646
647 const char* post_config_family = get_string(pattern, FC_FAMILY);
648 if (!post_config_family) {
649 // we can just continue with an empty name, e.g. default font
650 post_config_family = "";
651 }
652
653 FcResult result;
654 FcFontSet* font_set = FcFontSort(fc, pattern, 0, nullptr, &result);
655 if (!font_set) {
656 FcPatternDestroy(pattern);
657 return false;
658 }
659
660 FcPattern* match = this->MatchFont(font_set, post_config_family, familyStr);
661 if (!match) {
662 FcPatternDestroy(pattern);
663 FcFontSetDestroy(font_set);
664 return false;
665 }
666
667 FcPatternDestroy(pattern);
668
669 // From here out we just extract our results from 'match'
670
671 post_config_family = get_string(match, FC_FAMILY);
672 if (!post_config_family) {
673 FcFontSetDestroy(font_set);
674 return false;
675 }
676
677 const char* c_filename = get_string(match, FC_FILE);
678 if (!c_filename) {
679 FcFontSetDestroy(font_set);
680 return false;
681 }
682 const char* sysroot = (const char*)FcConfigGetSysRoot(fc);
683 SkString resolvedFilename;
684 if (sysroot) {
685 resolvedFilename = sysroot;
686 resolvedFilename += c_filename;
687 c_filename = resolvedFilename.c_str();
688 }
689
690 int face_index = get_int(match, FC_INDEX, 0);
691
692 FcFontSetDestroy(font_set);
693
694 if (outIdentity) {
695 outIdentity->fTTCIndex = face_index;
696 outIdentity->fString.set(c_filename);
697 }
698 if (outFamilyName) {
699 outFamilyName->set(post_config_family);
700 }
701 if (outStyle) {
702 *outStyle = skfontstyle_from_fcpattern(match);
703 }
704 return true;
705 }
706
openStream(const FontIdentity & identity)707 SkStreamAsset* SkFontConfigInterfaceDirect::openStream(const FontIdentity& identity) {
708 return SkStream::MakeFromFile(identity.fString.c_str()).release();
709 }
710