Home | History | Annotate | Download | only in ports
      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 
     26 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 
     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
     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 
     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 
     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
    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.
    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         //  
    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         //  
    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         //  
    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         //  
    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.
    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.
    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|.
    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 
    345 SkFontConfigInterfaceDirect::SkFontConfigInterfaceDirect() {
    346     SkAutoMutexAcquire ac(mutex_);
    347 
    348     FcInit();
    349 
    350     SkDEBUGCODE(fontconfiginterface_unittest();)
    351 }
    352 
    353 SkFontConfigInterfaceDirect::~SkFontConfigInterfaceDirect() {
    354 }
    355 
    356 bool SkFontConfigInterfaceDirect::isAccessible(const char* filename) {
    357     if (access(filename, R_OK) != 0) {
    358         return false;
    359     }
    360     return true;
    361 }
    362 
    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.
    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 
    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 
    534 SkStreamAsset* SkFontConfigInterfaceDirect::openStream(const FontIdentity& identity) {
    535     return SkStream::NewFromFile(identity.fString.c_str());
    536 }
    537 
    538 ///////////////////////////////////////////////////////////////////////////////
    539 
    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 
    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