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