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__anon05c285f80111::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__anon05c285f80111::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__anon05c285f80111::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 using UniqueFCConfig = std::unique_ptr<FcConfig, SkFunctionWrapper<decltype(FcConfigDestroy), FcConfigDestroy>>;
78
79 } // namespace
80
writeToMemory(void * addr) const81 size_t SkFontConfigInterface::FontIdentity::writeToMemory(void* addr) const {
82 size_t size = sizeof(fID) + sizeof(fTTCIndex);
83 size += sizeof(int32_t) + sizeof(int32_t) + sizeof(uint8_t); // weight, width, italic
84 size += sizeof(int32_t) + fString.size(); // store length+data
85 if (addr) {
86 SkWBuffer buffer(addr, size);
87
88 buffer.write32(fID);
89 buffer.write32(fTTCIndex);
90 buffer.write32(fString.size());
91 buffer.write32(fStyle.weight());
92 buffer.write32(fStyle.width());
93 buffer.write8(fStyle.slant());
94 buffer.write(fString.c_str(), fString.size());
95 buffer.padToAlign4();
96
97 SkASSERT(buffer.pos() == size);
98 }
99 return size;
100 }
101
readFromMemory(const void * addr,size_t size)102 size_t SkFontConfigInterface::FontIdentity::readFromMemory(const void* addr,
103 size_t size) {
104 SkRBuffer buffer(addr, size);
105
106 (void)buffer.readU32(&fID);
107 (void)buffer.readS32(&fTTCIndex);
108 uint32_t strLen, weight, width;
109 (void)buffer.readU32(&strLen);
110 (void)buffer.readU32(&weight);
111 (void)buffer.readU32(&width);
112 uint8_t u8;
113 (void)buffer.readU8(&u8);
114 SkFontStyle::Slant slant = (SkFontStyle::Slant)u8;
115 fStyle = SkFontStyle(weight, width, slant);
116 fString.resize(strLen);
117 (void)buffer.read(fString.writable_str(), strLen);
118 buffer.skipToAlign4();
119
120 return buffer.pos(); // the actual number of bytes read
121 }
122
123 #ifdef SK_DEBUG
make_iden(SkFontConfigInterface::FontIdentity * iden)124 static void make_iden(SkFontConfigInterface::FontIdentity* iden) {
125 iden->fID = 10;
126 iden->fTTCIndex = 2;
127 iden->fString.set("Hello world");
128 iden->fStyle = SkFontStyle(300, 6, SkFontStyle::kItalic_Slant);
129 }
130
test_writeToMemory(const SkFontConfigInterface::FontIdentity & iden0,int initValue)131 static void test_writeToMemory(const SkFontConfigInterface::FontIdentity& iden0,
132 int initValue) {
133 SkFontConfigInterface::FontIdentity iden1;
134
135 size_t size0 = iden0.writeToMemory(nullptr);
136
137 SkAutoMalloc storage(size0);
138 memset(storage.get(), initValue, size0);
139
140 size_t size1 = iden0.writeToMemory(storage.get());
141 SkASSERT(size0 == size1);
142
143 SkASSERT(iden0 != iden1);
144 size_t size2 = iden1.readFromMemory(storage.get(), size1);
145 SkASSERT(size2 == size1);
146 SkASSERT(iden0 == iden1);
147 }
148
fontconfiginterface_unittest()149 static void fontconfiginterface_unittest() {
150 SkFontConfigInterface::FontIdentity iden0, iden1;
151
152 SkASSERT(iden0 == iden1);
153
154 make_iden(&iden0);
155 SkASSERT(iden0 != iden1);
156
157 make_iden(&iden1);
158 SkASSERT(iden0 == iden1);
159
160 test_writeToMemory(iden0, 0);
161 test_writeToMemory(iden0, 0);
162 }
163 #endif
164
165 ///////////////////////////////////////////////////////////////////////////////
166
167 // Returns the string from the pattern, or nullptr
get_string(FcPattern * pattern,const char field[],int index=0)168 static const char* get_string(FcPattern* pattern, const char field[], int index = 0) {
169 const char* name;
170 if (FcPatternGetString(pattern, field, index, (FcChar8**)&name) != FcResultMatch) {
171 name = nullptr;
172 }
173 return name;
174 }
175
176 ///////////////////////////////////////////////////////////////////////////////
177
178 namespace {
179
180 // Equivalence classes, used to match the Liberation and other fonts
181 // with their metric-compatible replacements. See the discussion in
182 // GetFontEquivClass().
183 enum FontEquivClass
184 {
185 OTHER,
186 SANS,
187 SERIF,
188 MONO,
189 SYMBOL,
190 PGOTHIC,
191 GOTHIC,
192 PMINCHO,
193 MINCHO,
194 SIMSUN,
195 NSIMSUN,
196 SIMHEI,
197 PMINGLIU,
198 MINGLIU,
199 PMINGLIUHK,
200 MINGLIUHK,
201 CAMBRIA,
202 CALIBRI,
203 };
204
205 // Match the font name against a whilelist of fonts, returning the equivalence
206 // class.
GetFontEquivClass(const char * fontname)207 FontEquivClass GetFontEquivClass(const char* fontname)
208 {
209 // It would be nice for fontconfig to tell us whether a given suggested
210 // replacement is a "strong" match (that is, an equivalent font) or
211 // a "weak" match (that is, fontconfig's next-best attempt at finding a
212 // substitute). However, I played around with the fontconfig API for
213 // a good few hours and could not make it reveal this information.
214 //
215 // So instead, we hardcode. Initially this function emulated
216 // /etc/fonts/conf.d/30-metric-aliases.conf
217 // from my Ubuntu system, but we're better off being very conservative.
218
219 // Arimo, Tinos and Cousine are a set of fonts metric-compatible with
220 // Arial, Times New Roman and Courier New with a character repertoire
221 // much larger than Liberation. Note that Cousine is metrically
222 // compatible with Courier New, but the former is sans-serif while
223 // the latter is serif.
224
225
226 struct FontEquivMap {
227 FontEquivClass clazz;
228 const char name[40];
229 };
230
231 static const FontEquivMap kFontEquivMap[] = {
232 { SANS, "Arial" },
233 { SANS, "Arimo" },
234 { SANS, "Liberation Sans" },
235
236 { SERIF, "Times New Roman" },
237 { SERIF, "Tinos" },
238 { SERIF, "Liberation Serif" },
239
240 { MONO, "Courier New" },
241 { MONO, "Cousine" },
242 { MONO, "Liberation Mono" },
243
244 { SYMBOL, "Symbol" },
245 { SYMBOL, "Symbol Neu" },
246
247 // MS Pゴシック
248 { PGOTHIC, "MS PGothic" },
249 { PGOTHIC, "\xef\xbc\xad\xef\xbc\xb3 \xef\xbc\xb0"
250 "\xe3\x82\xb4\xe3\x82\xb7\xe3\x83\x83\xe3\x82\xaf" },
251 { PGOTHIC, "Noto Sans CJK JP" },
252 { PGOTHIC, "IPAPGothic" },
253 { PGOTHIC, "MotoyaG04Gothic" },
254
255 // MS ゴシック
256 { GOTHIC, "MS Gothic" },
257 { GOTHIC, "\xef\xbc\xad\xef\xbc\xb3 "
258 "\xe3\x82\xb4\xe3\x82\xb7\xe3\x83\x83\xe3\x82\xaf" },
259 { GOTHIC, "Noto Sans Mono CJK JP" },
260 { GOTHIC, "IPAGothic" },
261 { GOTHIC, "MotoyaG04GothicMono" },
262
263 // MS P明朝
264 { PMINCHO, "MS PMincho" },
265 { PMINCHO, "\xef\xbc\xad\xef\xbc\xb3 \xef\xbc\xb0"
266 "\xe6\x98\x8e\xe6\x9c\x9d"},
267 { PMINCHO, "Noto Serif CJK JP" },
268 { PMINCHO, "IPAPMincho" },
269 { PMINCHO, "MotoyaG04Mincho" },
270
271 // MS 明朝
272 { MINCHO, "MS Mincho" },
273 { MINCHO, "\xef\xbc\xad\xef\xbc\xb3 \xe6\x98\x8e\xe6\x9c\x9d" },
274 { MINCHO, "Noto Serif CJK JP" },
275 { MINCHO, "IPAMincho" },
276 { MINCHO, "MotoyaG04MinchoMono" },
277
278 // 宋体
279 { SIMSUN, "Simsun" },
280 { SIMSUN, "\xe5\xae\x8b\xe4\xbd\x93" },
281 { SIMSUN, "Noto Serif CJK SC" },
282 { SIMSUN, "MSung GB18030" },
283 { SIMSUN, "Song ASC" },
284
285 // 新宋体
286 { NSIMSUN, "NSimsun" },
287 { NSIMSUN, "\xe6\x96\xb0\xe5\xae\x8b\xe4\xbd\x93" },
288 { NSIMSUN, "Noto Serif CJK SC" },
289 { NSIMSUN, "MSung GB18030" },
290 { NSIMSUN, "N Song ASC" },
291
292 // 黑体
293 { SIMHEI, "Simhei" },
294 { SIMHEI, "\xe9\xbb\x91\xe4\xbd\x93" },
295 { SIMHEI, "Noto Sans CJK SC" },
296 { SIMHEI, "MYingHeiGB18030" },
297 { SIMHEI, "MYingHeiB5HK" },
298
299 // 新細明體
300 { PMINGLIU, "PMingLiU"},
301 { PMINGLIU, "\xe6\x96\xb0\xe7\xb4\xb0\xe6\x98\x8e\xe9\xab\x94" },
302 { PMINGLIU, "Noto Serif CJK TC"},
303 { PMINGLIU, "MSung B5HK"},
304
305 // 細明體
306 { MINGLIU, "MingLiU"},
307 { MINGLIU, "\xe7\xb4\xb0\xe6\x98\x8e\xe9\xab\x94" },
308 { MINGLIU, "Noto Serif CJK TC"},
309 { MINGLIU, "MSung B5HK"},
310
311 // 新細明體
312 { PMINGLIUHK, "PMingLiU_HKSCS"},
313 { PMINGLIUHK, "\xe6\x96\xb0\xe7\xb4\xb0\xe6\x98\x8e\xe9\xab\x94_HKSCS" },
314 { PMINGLIUHK, "Noto Serif CJK TC"},
315 { PMINGLIUHK, "MSung B5HK"},
316
317 // 細明體
318 { MINGLIUHK, "MingLiU_HKSCS"},
319 { MINGLIUHK, "\xe7\xb4\xb0\xe6\x98\x8e\xe9\xab\x94_HKSCS" },
320 { MINGLIUHK, "Noto Serif CJK TC"},
321 { MINGLIUHK, "MSung B5HK"},
322
323 // Cambria
324 { CAMBRIA, "Cambria" },
325 { CAMBRIA, "Caladea" },
326
327 // Calibri
328 { CALIBRI, "Calibri" },
329 { CALIBRI, "Carlito" },
330 };
331
332 static const size_t kFontCount =
333 sizeof(kFontEquivMap)/sizeof(kFontEquivMap[0]);
334
335 // TODO(jungshik): If this loop turns out to be hot, turn
336 // the array to a static (hash)map to speed it up.
337 for (size_t i = 0; i < kFontCount; ++i) {
338 if (strcasecmp(kFontEquivMap[i].name, fontname) == 0)
339 return kFontEquivMap[i].clazz;
340 }
341 return OTHER;
342 }
343
344
345 // Return true if |font_a| and |font_b| are visually and at the metrics
346 // level interchangeable.
IsMetricCompatibleReplacement(const char * font_a,const char * font_b)347 bool IsMetricCompatibleReplacement(const char* font_a, const char* font_b)
348 {
349 FontEquivClass class_a = GetFontEquivClass(font_a);
350 FontEquivClass class_b = GetFontEquivClass(font_b);
351
352 return class_a != OTHER && class_a == class_b;
353 }
354
355 // Normally we only return exactly the font asked for. In last-resort
356 // cases, the request either doesn't specify a font or is one of the
357 // basic font names like "Sans", "Serif" or "Monospace". This function
358 // tells you whether a given request is for such a fallback.
IsFallbackFontAllowed(const SkString & family)359 bool IsFallbackFontAllowed(const SkString& family) {
360 const char* family_cstr = family.c_str();
361 return family.isEmpty() ||
362 strcasecmp(family_cstr, "sans") == 0 ||
363 strcasecmp(family_cstr, "serif") == 0 ||
364 strcasecmp(family_cstr, "monospace") == 0;
365 }
366
367 // Retrieves |is_bold|, |is_italic| and |font_family| properties from |font|.
get_int(FcPattern * pattern,const char object[],int missing)368 static int get_int(FcPattern* pattern, const char object[], int missing) {
369 int value;
370 if (FcPatternGetInteger(pattern, object, 0, &value) != FcResultMatch) {
371 return missing;
372 }
373 return value;
374 }
375
map_range(SkScalar value,SkScalar old_min,SkScalar old_max,SkScalar new_min,SkScalar new_max)376 static int map_range(SkScalar value,
377 SkScalar old_min, SkScalar old_max,
378 SkScalar new_min, SkScalar new_max)
379 {
380 SkASSERT(old_min < old_max);
381 SkASSERT(new_min <= new_max);
382 return new_min + ((value - old_min) * (new_max - new_min) / (old_max - old_min));
383 }
384
385 struct MapRanges {
386 SkScalar old_val;
387 SkScalar new_val;
388 };
389
map_ranges(SkScalar val,MapRanges const ranges[],int rangesCount)390 static SkScalar map_ranges(SkScalar val, MapRanges const ranges[], int rangesCount) {
391 // -Inf to [0]
392 if (val < ranges[0].old_val) {
393 return ranges[0].new_val;
394 }
395
396 // Linear from [i] to [i+1]
397 for (int i = 0; i < rangesCount - 1; ++i) {
398 if (val < ranges[i+1].old_val) {
399 return map_range(val, ranges[i].old_val, ranges[i+1].old_val,
400 ranges[i].new_val, ranges[i+1].new_val);
401 }
402 }
403
404 // From [n] to +Inf
405 // if (fcweight < Inf)
406 return ranges[rangesCount-1].new_val;
407 }
408
409 #ifndef FC_WEIGHT_DEMILIGHT
410 #define FC_WEIGHT_DEMILIGHT 65
411 #endif
412
skfontstyle_from_fcpattern(FcPattern * pattern)413 static SkFontStyle skfontstyle_from_fcpattern(FcPattern* pattern) {
414 typedef SkFontStyle SkFS;
415
416 static constexpr MapRanges weightRanges[] = {
417 { FC_WEIGHT_THIN, SkFS::kThin_Weight },
418 { FC_WEIGHT_EXTRALIGHT, SkFS::kExtraLight_Weight },
419 { FC_WEIGHT_LIGHT, SkFS::kLight_Weight },
420 { FC_WEIGHT_DEMILIGHT, 350 },
421 { FC_WEIGHT_BOOK, 380 },
422 { FC_WEIGHT_REGULAR, SkFS::kNormal_Weight },
423 { FC_WEIGHT_MEDIUM, SkFS::kMedium_Weight },
424 { FC_WEIGHT_DEMIBOLD, SkFS::kSemiBold_Weight },
425 { FC_WEIGHT_BOLD, SkFS::kBold_Weight },
426 { FC_WEIGHT_EXTRABOLD, SkFS::kExtraBold_Weight },
427 { FC_WEIGHT_BLACK, SkFS::kBlack_Weight },
428 { FC_WEIGHT_EXTRABLACK, SkFS::kExtraBlack_Weight },
429 };
430 SkScalar weight = map_ranges(get_int(pattern, FC_WEIGHT, FC_WEIGHT_REGULAR),
431 weightRanges, SK_ARRAY_COUNT(weightRanges));
432
433 static constexpr MapRanges widthRanges[] = {
434 { FC_WIDTH_ULTRACONDENSED, SkFS::kUltraCondensed_Width },
435 { FC_WIDTH_EXTRACONDENSED, SkFS::kExtraCondensed_Width },
436 { FC_WIDTH_CONDENSED, SkFS::kCondensed_Width },
437 { FC_WIDTH_SEMICONDENSED, SkFS::kSemiCondensed_Width },
438 { FC_WIDTH_NORMAL, SkFS::kNormal_Width },
439 { FC_WIDTH_SEMIEXPANDED, SkFS::kSemiExpanded_Width },
440 { FC_WIDTH_EXPANDED, SkFS::kExpanded_Width },
441 { FC_WIDTH_EXTRAEXPANDED, SkFS::kExtraExpanded_Width },
442 { FC_WIDTH_ULTRAEXPANDED, SkFS::kUltraExpanded_Width },
443 };
444 SkScalar width = map_ranges(get_int(pattern, FC_WIDTH, FC_WIDTH_NORMAL),
445 widthRanges, SK_ARRAY_COUNT(widthRanges));
446
447 SkFS::Slant slant = SkFS::kUpright_Slant;
448 switch (get_int(pattern, FC_SLANT, FC_SLANT_ROMAN)) {
449 case FC_SLANT_ROMAN: slant = SkFS::kUpright_Slant; break;
450 case FC_SLANT_ITALIC : slant = SkFS::kItalic_Slant ; break;
451 case FC_SLANT_OBLIQUE: slant = SkFS::kOblique_Slant; break;
452 default: SkASSERT(false); break;
453 }
454
455 return SkFontStyle(SkScalarRoundToInt(weight), SkScalarRoundToInt(width), slant);
456 }
457
fcpattern_from_skfontstyle(SkFontStyle style,FcPattern * pattern)458 static void fcpattern_from_skfontstyle(SkFontStyle style, FcPattern* pattern) {
459 typedef SkFontStyle SkFS;
460
461 static constexpr MapRanges weightRanges[] = {
462 { SkFS::kThin_Weight, FC_WEIGHT_THIN },
463 { SkFS::kExtraLight_Weight, FC_WEIGHT_EXTRALIGHT },
464 { SkFS::kLight_Weight, FC_WEIGHT_LIGHT },
465 { 350, FC_WEIGHT_DEMILIGHT },
466 { 380, FC_WEIGHT_BOOK },
467 { SkFS::kNormal_Weight, FC_WEIGHT_REGULAR },
468 { SkFS::kMedium_Weight, FC_WEIGHT_MEDIUM },
469 { SkFS::kSemiBold_Weight, FC_WEIGHT_DEMIBOLD },
470 { SkFS::kBold_Weight, FC_WEIGHT_BOLD },
471 { SkFS::kExtraBold_Weight, FC_WEIGHT_EXTRABOLD },
472 { SkFS::kBlack_Weight, FC_WEIGHT_BLACK },
473 { SkFS::kExtraBlack_Weight, FC_WEIGHT_EXTRABLACK },
474 };
475 int weight = map_ranges(style.weight(), weightRanges, SK_ARRAY_COUNT(weightRanges));
476
477 static constexpr MapRanges widthRanges[] = {
478 { SkFS::kUltraCondensed_Width, FC_WIDTH_ULTRACONDENSED },
479 { SkFS::kExtraCondensed_Width, FC_WIDTH_EXTRACONDENSED },
480 { SkFS::kCondensed_Width, FC_WIDTH_CONDENSED },
481 { SkFS::kSemiCondensed_Width, FC_WIDTH_SEMICONDENSED },
482 { SkFS::kNormal_Width, FC_WIDTH_NORMAL },
483 { SkFS::kSemiExpanded_Width, FC_WIDTH_SEMIEXPANDED },
484 { SkFS::kExpanded_Width, FC_WIDTH_EXPANDED },
485 { SkFS::kExtraExpanded_Width, FC_WIDTH_EXTRAEXPANDED },
486 { SkFS::kUltraExpanded_Width, FC_WIDTH_ULTRAEXPANDED },
487 };
488 int width = map_ranges(style.width(), widthRanges, SK_ARRAY_COUNT(widthRanges));
489
490 int slant = FC_SLANT_ROMAN;
491 switch (style.slant()) {
492 case SkFS::kUpright_Slant: slant = FC_SLANT_ROMAN ; break;
493 case SkFS::kItalic_Slant : slant = FC_SLANT_ITALIC ; break;
494 case SkFS::kOblique_Slant: slant = FC_SLANT_OBLIQUE; break;
495 default: SkASSERT(false); break;
496 }
497
498 FcPatternAddInteger(pattern, FC_WEIGHT, weight);
499 FcPatternAddInteger(pattern, FC_WIDTH , width);
500 FcPatternAddInteger(pattern, FC_SLANT , slant);
501 }
502
503 } // anonymous namespace
504
505 ///////////////////////////////////////////////////////////////////////////////
506
507 #define kMaxFontFamilyLength 2048
508 #ifdef SK_FONT_CONFIG_INTERFACE_ONLY_ALLOW_SFNT_FONTS
509 const char* kFontFormatTrueType = "TrueType";
510 const char* kFontFormatCFF = "CFF";
511 #endif
512
SkFontConfigInterfaceDirect()513 SkFontConfigInterfaceDirect::SkFontConfigInterfaceDirect() {
514 SkDEBUGCODE(fontconfiginterface_unittest();)
515 }
516
~SkFontConfigInterfaceDirect()517 SkFontConfigInterfaceDirect::~SkFontConfigInterfaceDirect() {
518 }
519
isAccessible(const char * filename)520 bool SkFontConfigInterfaceDirect::isAccessible(const char* filename) {
521 if (access(filename, R_OK) != 0) {
522 return false;
523 }
524 return true;
525 }
526
isValidPattern(FcPattern * pattern)527 bool SkFontConfigInterfaceDirect::isValidPattern(FcPattern* pattern) {
528 #ifdef SK_FONT_CONFIG_INTERFACE_ONLY_ALLOW_SFNT_FONTS
529 const char* font_format = get_string(pattern, FC_FONTFORMAT);
530 if (font_format
531 && strcmp(font_format, kFontFormatTrueType) != 0
532 && strcmp(font_format, kFontFormatCFF) != 0)
533 {
534 return false;
535 }
536 #endif
537
538 // fontconfig can also return fonts which are unreadable
539 const char* c_filename = get_string(pattern, FC_FILE);
540 if (!c_filename) {
541 return false;
542 }
543 UniqueFCConfig fcConfig(FcConfigReference(nullptr));
544 const char* sysroot = (const char*)FcConfigGetSysRoot(fcConfig.get());
545 SkString resolvedFilename;
546 if (sysroot) {
547 resolvedFilename = sysroot;
548 resolvedFilename += c_filename;
549 c_filename = resolvedFilename.c_str();
550 }
551 return this->isAccessible(c_filename);
552 }
553
554 // Find matching font from |font_set| for the given font family.
MatchFont(FcFontSet * font_set,const char * post_config_family,const SkString & family)555 FcPattern* SkFontConfigInterfaceDirect::MatchFont(FcFontSet* font_set,
556 const char* post_config_family,
557 const SkString& family) {
558 // Older versions of fontconfig have a bug where they cannot select
559 // only scalable fonts so we have to manually filter the results.
560 FcPattern* match = nullptr;
561 for (int i = 0; i < font_set->nfont; ++i) {
562 FcPattern* current = font_set->fonts[i];
563 if (this->isValidPattern(current)) {
564 match = current;
565 break;
566 }
567 }
568
569 if (match && !IsFallbackFontAllowed(family)) {
570 bool acceptable_substitute = false;
571 for (int id = 0; id < 255; ++id) {
572 const char* post_match_family = get_string(match, FC_FAMILY, id);
573 if (!post_match_family)
574 break;
575 acceptable_substitute =
576 (strcasecmp(post_config_family, post_match_family) == 0 ||
577 // Workaround for Issue 12530:
578 // requested family: "Bitstream Vera Sans"
579 // post_config_family: "Arial"
580 // post_match_family: "Bitstream Vera Sans"
581 // -> We should treat this case as a good match.
582 strcasecmp(family.c_str(), post_match_family) == 0) ||
583 IsMetricCompatibleReplacement(family.c_str(), post_match_family);
584 if (acceptable_substitute)
585 break;
586 }
587 if (!acceptable_substitute)
588 return nullptr;
589 }
590
591 return match;
592 }
593
matchFamilyName(const char familyName[],SkFontStyle style,FontIdentity * outIdentity,SkString * outFamilyName,SkFontStyle * outStyle)594 bool SkFontConfigInterfaceDirect::matchFamilyName(const char familyName[],
595 SkFontStyle style,
596 FontIdentity* outIdentity,
597 SkString* outFamilyName,
598 SkFontStyle* outStyle) {
599 SkString familyStr(familyName ? familyName : "");
600 if (familyStr.size() > kMaxFontFamilyLength) {
601 return false;
602 }
603
604 FCLocker lock;
605 UniqueFCConfig fcConfig(FcConfigReference(nullptr));
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(fcConfig.get(), 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(fcConfig.get(), 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(fcConfig.get());
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