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