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