Home | History | Annotate | Download | only in ports
      1 
      2 /*
      3  * Copyright 2008 Google Inc.
      4  *
      5  * Use of this source code is governed by a BSD-style license that can be
      6  * found in the LICENSE file.
      7  */
      8 
      9 
     10 // -----------------------------------------------------------------------------
     11 // This file provides implementations of the font resolution members of
     12 // SkFontHost by using the fontconfig[1] library. Fontconfig is usually found
     13 // on Linux systems and handles configuration, parsing and caching issues
     14 // involved with enumerating and matching fonts.
     15 //
     16 // [1] http://fontconfig.org
     17 // -----------------------------------------------------------------------------
     18 
     19 #include <map>
     20 #include <string>
     21 
     22 #include <fontconfig/fontconfig.h>
     23 
     24 #include "SkFontHost.h"
     25 #include "SkStream.h"
     26 
     27 // This is an extern from SkFontHost_FreeType
     28 SkTypeface::Style find_name_and_style(SkStream* stream, SkString* name);
     29 
     30 // -----------------------------------------------------------------------------
     31 // The rest of Skia requires that fonts be identified by a unique unsigned id
     32 // and that we be able to load them given the id. What we actually get from
     33 // fontconfig is the filename of the font so we keep a locked map from
     34 // filenames to fileid numbers and back.
     35 //
     36 // Note that there's also a unique id in the SkTypeface. This is unique over
     37 // both filename and style. Thus we encode that id as (fileid << 8) | style.
     38 // Although truetype fonts can support multiple faces in a single file, at the
     39 // moment Skia doesn't.
     40 // -----------------------------------------------------------------------------
     41 SK_DECLARE_STATIC_MUTEX(global_fc_map_lock);
     42 static std::map<std::string, unsigned> global_fc_map;
     43 static std::map<unsigned, std::string> global_fc_map_inverted;
     44 static std::map<uint32_t, SkTypeface *> global_fc_typefaces;
     45 static unsigned global_fc_map_next_id = 0;
     46 
     47 static unsigned UniqueIdToFileId(unsigned uniqueid)
     48 {
     49     return uniqueid >> 8;
     50 }
     51 
     52 static SkTypeface::Style UniqueIdToStyle(unsigned uniqueid)
     53 {
     54     return static_cast<SkTypeface::Style>(uniqueid & 0xff);
     55 }
     56 
     57 static unsigned FileIdAndStyleToUniqueId(unsigned fileid,
     58                                          SkTypeface::Style style)
     59 {
     60     SkASSERT((style & 0xff) == style);
     61     return (fileid << 8) | static_cast<int>(style);
     62 }
     63 
     64 // -----------------------------------------------------------------------------
     65 // Normally we only return exactly the font asked for. In last-resort cases,
     66 // the request is for one of the basic font names "Sans", "Serif" or
     67 // "Monospace". This function tells you whether a given request is for such a
     68 // fallback.
     69 // -----------------------------------------------------------------------------
     70 static bool IsFallbackFontAllowed(const char* request)
     71 {
     72     return strcmp(request, "Sans") == 0 ||
     73            strcmp(request, "Serif") == 0 ||
     74            strcmp(request, "Monospace") == 0;
     75 }
     76 
     77 class FontConfigTypeface : public SkTypeface {
     78 public:
     79     FontConfigTypeface(Style style, uint32_t id)
     80         : SkTypeface(style, id)
     81     { }
     82 };
     83 
     84 // -----------------------------------------------------------------------------
     85 // Find a matching font where @type (one of FC_*) is equal to @value. For a
     86 // list of types, see http://fontconfig.org/fontconfig-devel/x19.html#AEN27.
     87 // The variable arguments are a list of triples, just like the first three
     88 // arguments, and must be NULL terminated.
     89 //
     90 // For example,
     91 //   FontMatchString(FC_FILE, FcTypeString, "/usr/share/fonts/myfont.ttf",
     92 //                   NULL);
     93 // -----------------------------------------------------------------------------
     94 static FcPattern* FontMatch(const char* type, FcType vtype, const void* value,
     95                             ...)
     96 {
     97     va_list ap;
     98     va_start(ap, value);
     99 
    100     FcPattern* pattern = FcPatternCreate();
    101     const char* family_requested = NULL;
    102 
    103     for (;;) {
    104         FcValue fcvalue;
    105         fcvalue.type = vtype;
    106         switch (vtype) {
    107             case FcTypeString:
    108                 fcvalue.u.s = (FcChar8*) value;
    109                 break;
    110             case FcTypeInteger:
    111                 fcvalue.u.i = (int)(intptr_t)value;
    112                 break;
    113             default:
    114                 SkDEBUGFAIL("FontMatch unhandled type");
    115         }
    116         FcPatternAdd(pattern, type, fcvalue, 0);
    117 
    118         if (vtype == FcTypeString && strcmp(type, FC_FAMILY) == 0)
    119             family_requested = (const char*) value;
    120 
    121         type = va_arg(ap, const char *);
    122         if (!type)
    123             break;
    124         // FcType is promoted to int when passed through ...
    125         vtype = static_cast<FcType>(va_arg(ap, int));
    126         value = va_arg(ap, const void *);
    127     };
    128     va_end(ap);
    129 
    130     FcConfigSubstitute(0, pattern, FcMatchPattern);
    131     FcDefaultSubstitute(pattern);
    132 
    133     // Font matching:
    134     // CSS often specifies a fallback list of families:
    135     //    font-family: a, b, c, serif;
    136     // However, fontconfig will always do its best to find *a* font when asked
    137     // for something so we need a way to tell if the match which it has found is
    138     // "good enough" for us. Otherwise, we can return NULL which gets piped up
    139     // and lets WebKit know to try the next CSS family name. However, fontconfig
    140     // configs allow substitutions (mapping "Arial -> Helvetica" etc) and we
    141     // wish to support that.
    142     //
    143     // Thus, if a specific family is requested we set @family_requested. Then we
    144     // record two strings: the family name after config processing and the
    145     // family name after resolving. If the two are equal, it's a good match.
    146     //
    147     // So consider the case where a user has mapped Arial to Helvetica in their
    148     // config.
    149     //    requested family: "Arial"
    150     //    post_config_family: "Helvetica"
    151     //    post_match_family: "Helvetica"
    152     //      -> good match
    153     //
    154     // and for a missing font:
    155     //    requested family: "Monaco"
    156     //    post_config_family: "Monaco"
    157     //    post_match_family: "Times New Roman"
    158     //      -> BAD match
    159     //
    160     // However, we special-case fallback fonts; see IsFallbackFontAllowed().
    161     FcChar8* post_config_family;
    162     FcPatternGetString(pattern, FC_FAMILY, 0, &post_config_family);
    163 
    164     FcResult result;
    165     FcPattern* match = FcFontMatch(0, pattern, &result);
    166     if (!match) {
    167         FcPatternDestroy(pattern);
    168         return NULL;
    169     }
    170 
    171     FcChar8* post_match_family;
    172     FcPatternGetString(match, FC_FAMILY, 0, &post_match_family);
    173     const bool family_names_match =
    174         !family_requested ?
    175         true :
    176         strcasecmp((char *)post_config_family, (char *)post_match_family) == 0;
    177 
    178     FcPatternDestroy(pattern);
    179 
    180     if (!family_names_match && !IsFallbackFontAllowed(family_requested)) {
    181         FcPatternDestroy(match);
    182         return NULL;
    183     }
    184 
    185     return match;
    186 }
    187 
    188 // -----------------------------------------------------------------------------
    189 // Check to see if the filename has already been assigned a fileid and, if so,
    190 // use it. Otherwise, assign one. Return the resulting fileid.
    191 // -----------------------------------------------------------------------------
    192 static unsigned FileIdFromFilename(const char* filename)
    193 {
    194     SkAutoMutexAcquire ac(global_fc_map_lock);
    195 
    196     std::map<std::string, unsigned>::const_iterator i =
    197         global_fc_map.find(filename);
    198     if (i == global_fc_map.end()) {
    199         const unsigned fileid = global_fc_map_next_id++;
    200         global_fc_map[filename] = fileid;
    201         global_fc_map_inverted[fileid] = filename;
    202         return fileid;
    203     } else {
    204         return i->second;
    205     }
    206 }
    207 
    208 // static
    209 SkTypeface* SkFontHost::CreateTypeface(const SkTypeface* familyFace,
    210                                        const char familyName[],
    211                                        const void* data, size_t bytelength,
    212                                        SkTypeface::Style style)
    213 {
    214     const char* resolved_family_name = NULL;
    215     FcPattern* face_match = NULL;
    216 
    217     {
    218         SkAutoMutexAcquire ac(global_fc_map_lock);
    219         FcInit();
    220     }
    221 
    222     if (familyFace) {
    223         // Here we use the inverted global id map to find the filename from the
    224         // SkTypeface object. Given the filename we can ask fontconfig for the
    225         // familyname of the font.
    226         SkAutoMutexAcquire ac(global_fc_map_lock);
    227 
    228         const unsigned fileid = UniqueIdToFileId(familyFace->uniqueID());
    229         std::map<unsigned, std::string>::const_iterator i =
    230             global_fc_map_inverted.find(fileid);
    231         if (i == global_fc_map_inverted.end())
    232             return NULL;
    233 
    234         FcInit();
    235         face_match = FontMatch(FC_FILE, FcTypeString, i->second.c_str(),
    236                                NULL);
    237 
    238         if (!face_match)
    239             return NULL;
    240         FcChar8* family;
    241         if (FcPatternGetString(face_match, FC_FAMILY, 0, &family)) {
    242             FcPatternDestroy(face_match);
    243             return NULL;
    244         }
    245         // At this point, @family is pointing into the @face_match object so we
    246         // cannot release it yet.
    247 
    248         resolved_family_name = reinterpret_cast<char*>(family);
    249     } else if (familyName) {
    250         resolved_family_name = familyName;
    251     } else {
    252         return NULL;
    253     }
    254 
    255     // At this point, we have a resolved_family_name from somewhere
    256     SkASSERT(resolved_family_name);
    257 
    258     const int bold = style & SkTypeface::kBold ?
    259                      FC_WEIGHT_BOLD : FC_WEIGHT_NORMAL;
    260     const int italic = style & SkTypeface::kItalic ?
    261                        FC_SLANT_ITALIC : FC_SLANT_ROMAN;
    262     FcPattern* match = FontMatch(FC_FAMILY, FcTypeString, resolved_family_name,
    263                                  FC_WEIGHT, FcTypeInteger, bold,
    264                                  FC_SLANT, FcTypeInteger, italic,
    265                                  NULL);
    266     if (face_match)
    267         FcPatternDestroy(face_match);
    268 
    269     if (!match)
    270         return NULL;
    271 
    272     FcChar8* filename;
    273     if (FcPatternGetString(match, FC_FILE, 0, &filename) != FcResultMatch) {
    274         FcPatternDestroy(match);
    275         return NULL;
    276     }
    277     // Now @filename is pointing into @match
    278 
    279     const unsigned fileid = FileIdFromFilename(reinterpret_cast<char*>(filename));
    280     const unsigned id = FileIdAndStyleToUniqueId(fileid, style);
    281     SkTypeface* typeface = SkNEW_ARGS(FontConfigTypeface, (style, id));
    282     FcPatternDestroy(match);
    283 
    284     {
    285         SkAutoMutexAcquire ac(global_fc_map_lock);
    286         global_fc_typefaces[id] = typeface;
    287     }
    288 
    289     return typeface;
    290 }
    291 
    292 // static
    293 SkTypeface* SkFontHost::CreateTypefaceFromStream(SkStream* stream)
    294 {
    295     SkDEBUGFAIL("SkFontHost::CreateTypefaceFromStream unimplemented");
    296     return NULL;
    297 }
    298 
    299 // static
    300 SkTypeface* SkFontHost::CreateTypefaceFromFile(const char path[])
    301 {
    302     SkDEBUGFAIL("SkFontHost::CreateTypefaceFromFile unimplemented");
    303     return NULL;
    304 }
    305 
    306 // static
    307 SkStream* SkFontHost::OpenStream(uint32_t id)
    308 {
    309     SkAutoMutexAcquire ac(global_fc_map_lock);
    310     const unsigned fileid = UniqueIdToFileId(id);
    311 
    312     std::map<unsigned, std::string>::const_iterator i =
    313         global_fc_map_inverted.find(fileid);
    314     if (i == global_fc_map_inverted.end())
    315         return NULL;
    316 
    317     return SkNEW_ARGS(SkFILEStream, (i->second.c_str()));
    318 }
    319 
    320 size_t SkFontHost::GetFileName(SkFontID fontID, char path[], size_t length,
    321                                int32_t* index) {
    322     SkAutoMutexAcquire ac(global_fc_map_lock);
    323     const unsigned fileid = UniqueIdToFileId(fontID);
    324 
    325     std::map<unsigned, std::string>::const_iterator i =
    326     global_fc_map_inverted.find(fileid);
    327     if (i == global_fc_map_inverted.end()) {
    328         return 0;
    329     }
    330 
    331     const std::string& str = i->second;
    332     if (path) {
    333         memcpy(path, str.c_str(), SkMin32(str.size(), length));
    334     }
    335     if (index) {    // TODO: check if we're in a TTC
    336         *index = 0;
    337     }
    338     return str.size();
    339 }
    340 
    341 void SkFontHost::Serialize(const SkTypeface*, SkWStream*) {
    342     SkDEBUGFAIL("SkFontHost::Serialize unimplemented");
    343 }
    344 
    345 SkTypeface* SkFontHost::Deserialize(SkStream* stream) {
    346     SkDEBUGFAIL("SkFontHost::Deserialize unimplemented");
    347     return NULL;
    348 }
    349 
    350 SkFontID SkFontHost::NextLogicalFont(SkFontID currFontID, SkFontID origFontID) {
    351     // We don't handle font fallback, WebKit does.
    352     return 0;
    353 }
    354 
    355