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
26size_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
47size_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
69static 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
76static 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
94static 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
113static 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
125namespace {
126
127// Equivalence classes, used to match the Liberation and other fonts
128// with their metric-compatible replacements.  See the discussion in
129// GetFontEquivClass().
130enum 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.
154FontEquivClass 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.
286bool 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.
298bool 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|.
307SkTypeface::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
345SkFontConfigInterfaceDirect::SkFontConfigInterfaceDirect() {
346    SkAutoMutexAcquire ac(mutex_);
347
348    FcInit();
349
350    SkDEBUGCODE(fontconfiginterface_unittest();)
351}
352
353SkFontConfigInterfaceDirect::~SkFontConfigInterfaceDirect() {
354}
355
356bool SkFontConfigInterfaceDirect::isAccessible(const char* filename) {
357    if (access(filename, R_OK) != 0) {
358        return false;
359    }
360    return true;
361}
362
363bool 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.
381FcPattern* 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
420bool 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
534SkStreamAsset* SkFontConfigInterfaceDirect::openStream(const FontIdentity& identity) {
535    return SkStream::NewFromFile(identity.fString.c_str());
536}
537
538///////////////////////////////////////////////////////////////////////////////
539
540static 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
550SkDataTable* 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