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