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 "SkBuffer.h"
11 #include "SkDataTable.h"
12 #include "SkFontConfigInterface_direct.h"
13 #include "SkFontStyle.h"
14 #include "SkMutex.h"
15 #include "SkStream.h"
16 #include "SkString.h"
17 #include "SkTArray.h"
18 #include "SkTDArray.h"
19 #include "SkTemplates.h"
20 #include "SkTypeface.h"
21 #include "SkTypes.h"
22
23 #include <fontconfig/fontconfig.h>
24 #include <unistd.h>
25
writeToMemory(void * addr) const26 size_t SkFontConfigInterface::FontIdentity::writeToMemory(void* addr) const {
27 size_t size = sizeof(fID) + sizeof(fTTCIndex);
28 size += sizeof(int32_t) + sizeof(int32_t) + sizeof(uint8_t); // weight, width, italic
29 size += sizeof(int32_t) + fString.size(); // store length+data
30 if (addr) {
31 SkWBuffer buffer(addr, size);
32
33 buffer.write32(fID);
34 buffer.write32(fTTCIndex);
35 buffer.write32(fString.size());
36 buffer.write32(fStyle.weight());
37 buffer.write32(fStyle.width());
38 buffer.write8(fStyle.slant());
39 buffer.write(fString.c_str(), fString.size());
40 buffer.padToAlign4();
41
42 SkASSERT(buffer.pos() == size);
43 }
44 return size;
45 }
46
readFromMemory(const void * addr,size_t size)47 size_t SkFontConfigInterface::FontIdentity::readFromMemory(const void* addr,
48 size_t size) {
49 SkRBuffer buffer(addr, size);
50
51 (void)buffer.readU32(&fID);
52 (void)buffer.readS32(&fTTCIndex);
53 uint32_t strLen, weight, width;
54 (void)buffer.readU32(&strLen);
55 (void)buffer.readU32(&weight);
56 (void)buffer.readU32(&width);
57 uint8_t u8;
58 (void)buffer.readU8(&u8);
59 SkFontStyle::Slant slant = (SkFontStyle::Slant)u8;
60 fStyle = SkFontStyle(weight, width, slant);
61 fString.resize(strLen);
62 (void)buffer.read(fString.writable_str(), strLen);
63 buffer.skipToAlign4();
64
65 return buffer.pos(); // the actual number of bytes read
66 }
67
68 #ifdef SK_DEBUG
make_iden(SkFontConfigInterface::FontIdentity * iden)69 static void make_iden(SkFontConfigInterface::FontIdentity* iden) {
70 iden->fID = 10;
71 iden->fTTCIndex = 2;
72 iden->fString.set("Hello world");
73 iden->fStyle = SkFontStyle(300, 6, SkFontStyle::kItalic_Slant);
74 }
75
test_writeToMemory(const SkFontConfigInterface::FontIdentity & iden0,int initValue)76 static void test_writeToMemory(const SkFontConfigInterface::FontIdentity& iden0,
77 int initValue) {
78 SkFontConfigInterface::FontIdentity iden1;
79
80 size_t size0 = iden0.writeToMemory(nullptr);
81
82 SkAutoMalloc storage(size0);
83 memset(storage.get(), initValue, size0);
84
85 size_t size1 = iden0.writeToMemory(storage.get());
86 SkASSERT(size0 == size1);
87
88 SkASSERT(iden0 != iden1);
89 size_t size2 = iden1.readFromMemory(storage.get(), size1);
90 SkASSERT(size2 == size1);
91 SkASSERT(iden0 == iden1);
92 }
93
fontconfiginterface_unittest()94 static void fontconfiginterface_unittest() {
95 SkFontConfigInterface::FontIdentity iden0, iden1;
96
97 SkASSERT(iden0 == iden1);
98
99 make_iden(&iden0);
100 SkASSERT(iden0 != iden1);
101
102 make_iden(&iden1);
103 SkASSERT(iden0 == iden1);
104
105 test_writeToMemory(iden0, 0);
106 test_writeToMemory(iden0, 0);
107 }
108 #endif
109
110 ///////////////////////////////////////////////////////////////////////////////
111
112 // Returns the string from the pattern, or nullptr
get_name(FcPattern * pattern,const char field[],int index=0)113 static const char* get_name(FcPattern* pattern, const char field[],
114 int index = 0) {
115 const char* name;
116 if (FcPatternGetString(pattern, field, index,
117 (FcChar8**)&name) != FcResultMatch) {
118 name = nullptr;
119 }
120 return name;
121 }
122
123 ///////////////////////////////////////////////////////////////////////////////
124
125 namespace {
126
127 // Equivalence classes, used to match the Liberation and other fonts
128 // with their metric-compatible replacements. See the discussion in
129 // GetFontEquivClass().
130 enum FontEquivClass
131 {
132 OTHER,
133 SANS,
134 SERIF,
135 MONO,
136 SYMBOL,
137 PGOTHIC,
138 GOTHIC,
139 PMINCHO,
140 MINCHO,
141 SIMSUN,
142 NSIMSUN,
143 SIMHEI,
144 PMINGLIU,
145 MINGLIU,
146 PMINGLIUHK,
147 MINGLIUHK,
148 CAMBRIA,
149 CALIBRI,
150 };
151
152 // Match the font name against a whilelist of fonts, returning the equivalence
153 // class.
GetFontEquivClass(const char * fontname)154 FontEquivClass GetFontEquivClass(const char* fontname)
155 {
156 // It would be nice for fontconfig to tell us whether a given suggested
157 // replacement is a "strong" match (that is, an equivalent font) or
158 // a "weak" match (that is, fontconfig's next-best attempt at finding a
159 // substitute). However, I played around with the fontconfig API for
160 // a good few hours and could not make it reveal this information.
161 //
162 // So instead, we hardcode. Initially this function emulated
163 // /etc/fonts/conf.d/30-metric-aliases.conf
164 // from my Ubuntu system, but we're better off being very conservative.
165
166 // Arimo, Tinos and Cousine are a set of fonts metric-compatible with
167 // Arial, Times New Roman and Courier New with a character repertoire
168 // much larger than Liberation. Note that Cousine is metrically
169 // compatible with Courier New, but the former is sans-serif while
170 // the latter is serif.
171
172
173 struct FontEquivMap {
174 FontEquivClass clazz;
175 const char name[40];
176 };
177
178 static const FontEquivMap kFontEquivMap[] = {
179 { SANS, "Arial" },
180 { SANS, "Arimo" },
181 { SANS, "Liberation Sans" },
182
183 { SERIF, "Times New Roman" },
184 { SERIF, "Tinos" },
185 { SERIF, "Liberation Serif" },
186
187 { MONO, "Courier New" },
188 { MONO, "Cousine" },
189 { MONO, "Liberation Mono" },
190
191 { SYMBOL, "Symbol" },
192 { SYMBOL, "Symbol Neu" },
193
194 // MS Pゴシック
195 { PGOTHIC, "MS PGothic" },
196 { PGOTHIC, "\xef\xbc\xad\xef\xbc\xb3 \xef\xbc\xb0"
197 "\xe3\x82\xb4\xe3\x82\xb7\xe3\x83\x83\xe3\x82\xaf" },
198 { PGOTHIC, "Noto Sans CJK JP" },
199 { PGOTHIC, "IPAPGothic" },
200 { PGOTHIC, "MotoyaG04Gothic" },
201
202 // MS ゴシック
203 { GOTHIC, "MS Gothic" },
204 { GOTHIC, "\xef\xbc\xad\xef\xbc\xb3 "
205 "\xe3\x82\xb4\xe3\x82\xb7\xe3\x83\x83\xe3\x82\xaf" },
206 { GOTHIC, "Noto Sans Mono CJK JP" },
207 { GOTHIC, "IPAGothic" },
208 { GOTHIC, "MotoyaG04GothicMono" },
209
210 // MS P明朝
211 { PMINCHO, "MS PMincho" },
212 { PMINCHO, "\xef\xbc\xad\xef\xbc\xb3 \xef\xbc\xb0"
213 "\xe6\x98\x8e\xe6\x9c\x9d"},
214 { PMINCHO, "IPAPMincho" },
215 { PMINCHO, "MotoyaG04Mincho" },
216
217 // MS 明朝
218 { MINCHO, "MS Mincho" },
219 { MINCHO, "\xef\xbc\xad\xef\xbc\xb3 \xe6\x98\x8e\xe6\x9c\x9d" },
220 { MINCHO, "IPAMincho" },
221 { MINCHO, "MotoyaG04MinchoMono" },
222
223 // 宋体
224 { SIMSUN, "Simsun" },
225 { SIMSUN, "\xe5\xae\x8b\xe4\xbd\x93" },
226 { SIMSUN, "MSung GB18030" },
227 { SIMSUN, "Song ASC" },
228
229 // 新宋体
230 { NSIMSUN, "NSimsun" },
231 { NSIMSUN, "\xe6\x96\xb0\xe5\xae\x8b\xe4\xbd\x93" },
232 { NSIMSUN, "MSung GB18030" },
233 { NSIMSUN, "N Song ASC" },
234
235 // 黑体
236 { SIMHEI, "Simhei" },
237 { SIMHEI, "\xe9\xbb\x91\xe4\xbd\x93" },
238 { SIMHEI, "Noto Sans CJK SC" },
239 { SIMHEI, "MYingHeiGB18030" },
240 { SIMHEI, "MYingHeiB5HK" },
241
242 // 新細明體
243 { PMINGLIU, "PMingLiU"},
244 { PMINGLIU, "\xe6\x96\xb0\xe7\xb4\xb0\xe6\x98\x8e\xe9\xab\x94" },
245 { PMINGLIU, "MSung B5HK"},
246
247 // 細明體
248 { MINGLIU, "MingLiU"},
249 { MINGLIU, "\xe7\xb4\xb0\xe6\x98\x8e\xe9\xab\x94" },
250 { MINGLIU, "MSung B5HK"},
251
252 // 新細明體
253 { PMINGLIUHK, "PMingLiU_HKSCS"},
254 { PMINGLIUHK, "\xe6\x96\xb0\xe7\xb4\xb0\xe6\x98\x8e\xe9\xab\x94_HKSCS" },
255 { PMINGLIUHK, "MSung B5HK"},
256
257 // 細明體
258 { MINGLIUHK, "MingLiU_HKSCS"},
259 { MINGLIUHK, "\xe7\xb4\xb0\xe6\x98\x8e\xe9\xab\x94_HKSCS" },
260 { MINGLIUHK, "MSung B5HK"},
261
262 // Cambria
263 { CAMBRIA, "Cambria" },
264 { CAMBRIA, "Caladea" },
265
266 // Calibri
267 { CALIBRI, "Calibri" },
268 { CALIBRI, "Carlito" },
269 };
270
271 static const size_t kFontCount =
272 sizeof(kFontEquivMap)/sizeof(kFontEquivMap[0]);
273
274 // TODO(jungshik): If this loop turns out to be hot, turn
275 // the array to a static (hash)map to speed it up.
276 for (size_t i = 0; i < kFontCount; ++i) {
277 if (strcasecmp(kFontEquivMap[i].name, fontname) == 0)
278 return kFontEquivMap[i].clazz;
279 }
280 return OTHER;
281 }
282
283
284 // Return true if |font_a| and |font_b| are visually and at the metrics
285 // level interchangeable.
IsMetricCompatibleReplacement(const char * font_a,const char * font_b)286 bool IsMetricCompatibleReplacement(const char* font_a, const char* font_b)
287 {
288 FontEquivClass class_a = GetFontEquivClass(font_a);
289 FontEquivClass class_b = GetFontEquivClass(font_b);
290
291 return class_a != OTHER && class_a == class_b;
292 }
293
294 // Normally we only return exactly the font asked for. In last-resort
295 // cases, the request either doesn't specify a font or is one of the
296 // basic font names like "Sans", "Serif" or "Monospace". This function
297 // tells you whether a given request is for such a fallback.
IsFallbackFontAllowed(const SkString & family)298 bool IsFallbackFontAllowed(const SkString& family) {
299 const char* family_cstr = family.c_str();
300 return family.isEmpty() ||
301 strcasecmp(family_cstr, "sans") == 0 ||
302 strcasecmp(family_cstr, "serif") == 0 ||
303 strcasecmp(family_cstr, "monospace") == 0;
304 }
305
306 // Retrieves |is_bold|, |is_italic| and |font_family| properties from |font|.
GetFontStyle(FcPattern * font)307 SkTypeface::Style GetFontStyle(FcPattern* font) {
308 int resulting_bold;
309 if (FcPatternGetInteger(font, FC_WEIGHT, 0, &resulting_bold))
310 resulting_bold = FC_WEIGHT_NORMAL;
311
312 int resulting_italic;
313 if (FcPatternGetInteger(font, FC_SLANT, 0, &resulting_italic))
314 resulting_italic = FC_SLANT_ROMAN;
315
316 // If we ask for an italic font, fontconfig might take a roman font and set
317 // the undocumented property FC_MATRIX to a skew matrix. It'll then say
318 // that the font is italic or oblique. So, if we see a matrix, we don't
319 // believe that it's italic.
320 FcValue matrix;
321 const bool have_matrix = FcPatternGet(font, FC_MATRIX, 0, &matrix) == 0;
322
323 // If we ask for an italic font, fontconfig might take a roman font and set
324 // FC_EMBOLDEN.
325 FcValue embolden;
326 const bool have_embolden = FcPatternGet(font, FC_EMBOLDEN, 0, &embolden) == 0;
327
328 int styleBits = 0;
329 if (resulting_bold > FC_WEIGHT_MEDIUM && !have_embolden) {
330 styleBits |= SkTypeface::kBold;
331 }
332 if (resulting_italic > FC_SLANT_ROMAN && !have_matrix) {
333 styleBits |= SkTypeface::kItalic;
334 }
335
336 return (SkTypeface::Style)styleBits;
337 }
338
339 } // anonymous namespace
340
341 ///////////////////////////////////////////////////////////////////////////////
342
343 #define kMaxFontFamilyLength 2048
344
SkFontConfigInterfaceDirect()345 SkFontConfigInterfaceDirect::SkFontConfigInterfaceDirect() {
346 SkAutoMutexAcquire ac(mutex_);
347
348 FcInit();
349
350 SkDEBUGCODE(fontconfiginterface_unittest();)
351 }
352
~SkFontConfigInterfaceDirect()353 SkFontConfigInterfaceDirect::~SkFontConfigInterfaceDirect() {
354 }
355
isAccessible(const char * filename)356 bool SkFontConfigInterfaceDirect::isAccessible(const char* filename) {
357 if (access(filename, R_OK) != 0) {
358 return false;
359 }
360 return true;
361 }
362
isValidPattern(FcPattern * pattern)363 bool SkFontConfigInterfaceDirect::isValidPattern(FcPattern* pattern) {
364 #ifdef SK_FONT_CONFIG_ONLY_ALLOW_SCALABLE_FONTS
365 FcBool is_scalable;
366 if (FcPatternGetBool(pattern, FC_SCALABLE, 0, &is_scalable) != FcResultMatch
367 || !is_scalable) {
368 return false;
369 }
370 #endif
371
372 // fontconfig can also return fonts which are unreadable
373 const char* c_filename = get_name(pattern, FC_FILE);
374 if (!c_filename) {
375 return false;
376 }
377 return this->isAccessible(c_filename);
378 }
379
380 // Find matching font from |font_set| for the given font family.
MatchFont(FcFontSet * font_set,const char * post_config_family,const SkString & family)381 FcPattern* SkFontConfigInterfaceDirect::MatchFont(FcFontSet* font_set,
382 const char* post_config_family,
383 const SkString& family) {
384 // Older versions of fontconfig have a bug where they cannot select
385 // only scalable fonts so we have to manually filter the results.
386 FcPattern* match = nullptr;
387 for (int i = 0; i < font_set->nfont; ++i) {
388 FcPattern* current = font_set->fonts[i];
389 if (this->isValidPattern(current)) {
390 match = current;
391 break;
392 }
393 }
394
395 if (match && !IsFallbackFontAllowed(family)) {
396 bool acceptable_substitute = false;
397 for (int id = 0; id < 255; ++id) {
398 const char* post_match_family = get_name(match, FC_FAMILY, id);
399 if (!post_match_family)
400 break;
401 acceptable_substitute =
402 (strcasecmp(post_config_family, post_match_family) == 0 ||
403 // Workaround for Issue 12530:
404 // requested family: "Bitstream Vera Sans"
405 // post_config_family: "Arial"
406 // post_match_family: "Bitstream Vera Sans"
407 // -> We should treat this case as a good match.
408 strcasecmp(family.c_str(), post_match_family) == 0) ||
409 IsMetricCompatibleReplacement(family.c_str(), post_match_family);
410 if (acceptable_substitute)
411 break;
412 }
413 if (!acceptable_substitute)
414 return nullptr;
415 }
416
417 return match;
418 }
419
matchFamilyName(const char familyName[],SkTypeface::Style style,FontIdentity * outIdentity,SkString * outFamilyName,SkTypeface::Style * outStyle)420 bool SkFontConfigInterfaceDirect::matchFamilyName(const char familyName[],
421 SkTypeface::Style style,
422 FontIdentity* outIdentity,
423 SkString* outFamilyName,
424 SkTypeface::Style* outStyle) {
425 SkString familyStr(familyName ? familyName : "");
426 if (familyStr.size() > kMaxFontFamilyLength) {
427 return false;
428 }
429
430 SkAutoMutexAcquire ac(mutex_);
431
432 FcPattern* pattern = FcPatternCreate();
433
434 if (familyName) {
435 FcPatternAddString(pattern, FC_FAMILY, (FcChar8*)familyName);
436 }
437 FcPatternAddInteger(pattern, FC_WEIGHT,
438 (style & SkTypeface::kBold) ? FC_WEIGHT_BOLD
439 : FC_WEIGHT_NORMAL);
440 FcPatternAddInteger(pattern, FC_SLANT,
441 (style & SkTypeface::kItalic) ? FC_SLANT_ITALIC
442 : FC_SLANT_ROMAN);
443 FcPatternAddBool(pattern, FC_SCALABLE, FcTrue);
444
445 FcConfigSubstitute(nullptr, pattern, FcMatchPattern);
446 FcDefaultSubstitute(pattern);
447
448 // Font matching:
449 // CSS often specifies a fallback list of families:
450 // font-family: a, b, c, serif;
451 // However, fontconfig will always do its best to find *a* font when asked
452 // for something so we need a way to tell if the match which it has found is
453 // "good enough" for us. Otherwise, we can return nullptr which gets piped up
454 // and lets WebKit know to try the next CSS family name. However, fontconfig
455 // configs allow substitutions (mapping "Arial -> Helvetica" etc) and we
456 // wish to support that.
457 //
458 // Thus, if a specific family is requested we set @family_requested. Then we
459 // record two strings: the family name after config processing and the
460 // family name after resolving. If the two are equal, it's a good match.
461 //
462 // So consider the case where a user has mapped Arial to Helvetica in their
463 // config.
464 // requested family: "Arial"
465 // post_config_family: "Helvetica"
466 // post_match_family: "Helvetica"
467 // -> good match
468 //
469 // and for a missing font:
470 // requested family: "Monaco"
471 // post_config_family: "Monaco"
472 // post_match_family: "Times New Roman"
473 // -> BAD match
474 //
475 // However, we special-case fallback fonts; see IsFallbackFontAllowed().
476
477 const char* post_config_family = get_name(pattern, FC_FAMILY);
478 if (!post_config_family) {
479 // we can just continue with an empty name, e.g. default font
480 post_config_family = "";
481 }
482
483 FcResult result;
484 FcFontSet* font_set = FcFontSort(0, pattern, 0, 0, &result);
485 if (!font_set) {
486 FcPatternDestroy(pattern);
487 return false;
488 }
489
490 FcPattern* match = this->MatchFont(font_set, post_config_family, familyStr);
491 if (!match) {
492 FcPatternDestroy(pattern);
493 FcFontSetDestroy(font_set);
494 return false;
495 }
496
497 FcPatternDestroy(pattern);
498
499 // From here out we just extract our results from 'match'
500
501 post_config_family = get_name(match, FC_FAMILY);
502 if (!post_config_family) {
503 FcFontSetDestroy(font_set);
504 return false;
505 }
506
507 const char* c_filename = get_name(match, FC_FILE);
508 if (!c_filename) {
509 FcFontSetDestroy(font_set);
510 return false;
511 }
512
513 int face_index;
514 if (FcPatternGetInteger(match, FC_INDEX, 0, &face_index) != FcResultMatch) {
515 FcFontSetDestroy(font_set);
516 return false;
517 }
518
519 FcFontSetDestroy(font_set);
520
521 if (outIdentity) {
522 outIdentity->fTTCIndex = face_index;
523 outIdentity->fString.set(c_filename);
524 }
525 if (outFamilyName) {
526 outFamilyName->set(post_config_family);
527 }
528 if (outStyle) {
529 *outStyle = GetFontStyle(match);
530 }
531 return true;
532 }
533
openStream(const FontIdentity & identity)534 SkStreamAsset* SkFontConfigInterfaceDirect::openStream(const FontIdentity& identity) {
535 return SkStream::NewFromFile(identity.fString.c_str());
536 }
537
538 ///////////////////////////////////////////////////////////////////////////////
539
find_name(const SkTDArray<const char * > & list,const char * str)540 static bool find_name(const SkTDArray<const char*>& list, const char* str) {
541 int count = list.count();
542 for (int i = 0; i < count; ++i) {
543 if (!strcmp(list[i], str)) {
544 return true;
545 }
546 }
547 return false;
548 }
549
getFamilyNames()550 SkDataTable* SkFontConfigInterfaceDirect::getFamilyNames() {
551 SkAutoMutexAcquire ac(mutex_);
552
553 FcPattern* pat = FcPatternCreate();
554 SkAutoTCallVProc<FcPattern, FcPatternDestroy> autoDestroyPat(pat);
555 if (nullptr == pat) {
556 return nullptr;
557 }
558
559 FcObjectSet* os = FcObjectSetBuild(FC_FAMILY, (char *)0);
560 SkAutoTCallVProc<FcObjectSet, FcObjectSetDestroy> autoDestroyOs(os);
561 if (nullptr == os) {
562 return nullptr;
563 }
564
565 FcFontSet* fs = FcFontList(nullptr, pat, os);
566 SkAutoTCallVProc<FcFontSet, FcFontSetDestroy> autoDestroyFs(fs);
567 if (nullptr == fs) {
568 return nullptr;
569 }
570
571 SkTDArray<const char*> names;
572 SkTDArray<size_t> sizes;
573 for (int i = 0; i < fs->nfont; ++i) {
574 FcPattern* match = fs->fonts[i];
575 const char* famName = get_name(match, FC_FAMILY);
576 if (famName && !find_name(names, famName)) {
577 *names.append() = famName;
578 *sizes.append() = strlen(famName) + 1;
579 }
580 }
581
582 return SkDataTable::NewCopyArrays((const void*const*)names.begin(),
583 sizes.begin(), names.count());
584 }
585