Home | History | Annotate | Download | only in ports
      1 /*
      2  * Copyright 2008 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 #include <map>
      9 #include <string>
     10 
     11 #include <fontconfig/fontconfig.h>
     12 
     13 #include "SkFontHost.h"
     14 #include "SkStream.h"
     15 
     16 /** An extern from SkFontHost_FreeType. */
     17 SkTypeface::Style find_name_and_style(SkStream* stream, SkString* name);
     18 
     19 /** This lock must be held while modifying global_fc_* globals. */
     20 SK_DECLARE_STATIC_MUTEX(global_fc_map_lock);
     21 
     22 /** Map from file names to file ids. */
     23 static std::map<std::string, unsigned> global_fc_map;
     24 /** Map from file ids to file names. */
     25 static std::map<unsigned, std::string> global_fc_map_inverted;
     26 /** The next file id. */
     27 static unsigned global_fc_map_next_id = 0;
     28 
     29 /**
     30  * Check to see if the filename has already been assigned a fileid and, if so, use it.
     31  * Otherwise, assign one. Return the resulting fileid.
     32  */
     33 static unsigned FileIdFromFilename(const char* filename) {
     34     SkAutoMutexAcquire ac(global_fc_map_lock);
     35 
     36     std::map<std::string, unsigned>::const_iterator i = global_fc_map.find(filename);
     37     if (i == global_fc_map.end()) {
     38         const unsigned fileid = global_fc_map_next_id++;
     39         global_fc_map[filename] = fileid;
     40         global_fc_map_inverted[fileid] = filename;
     41         return fileid;
     42     } else {
     43         return i->second;
     44     }
     45 }
     46 
     47 static unsigned FileIdFromUniqueId(unsigned uniqueid) {
     48     return uniqueid >> 8;
     49 }
     50 
     51 static SkTypeface::Style StyleFromUniqueId(unsigned uniqueid) {
     52     return static_cast<SkTypeface::Style>(uniqueid & 0xff);
     53 }
     54 
     55 static unsigned UniqueIdFromFileIdAndStyle(unsigned fileid, SkTypeface::Style style) {
     56     SkASSERT((style & 0xff) == style);
     57     return (fileid << 8) | static_cast<int>(style);
     58 }
     59 
     60 class FontConfigTypeface : public SkTypeface {
     61 public:
     62     FontConfigTypeface(Style style, uint32_t id) : SkTypeface(style, id) { }
     63 };
     64 
     65 /**
     66  * Find a matching font where @type (one of FC_*) is equal to @value. For a
     67  * list of types, see http://fontconfig.org/fontconfig-devel/x19.html#AEN27.
     68  * The variable arguments are a list of triples, just like the first three
     69  * arguments, and must be NULL terminated.
     70  *
     71  * For example,
     72  *   FontMatchString(FC_FILE, FcTypeString, "/usr/share/fonts/myfont.ttf", NULL);
     73  */
     74 static FcPattern* FontMatch(const char* type, FcType vtype, const void* value, ...) {
     75     va_list ap;
     76     va_start(ap, value);
     77 
     78     FcPattern* pattern = FcPatternCreate();
     79 
     80     for (;;) {
     81         FcValue fcvalue;
     82         fcvalue.type = vtype;
     83         switch (vtype) {
     84             case FcTypeString:
     85                 fcvalue.u.s = (FcChar8*) value;
     86                 break;
     87             case FcTypeInteger:
     88                 fcvalue.u.i = (int)(intptr_t)value;
     89                 break;
     90             default:
     91                 SkDEBUGFAIL("FontMatch unhandled type");
     92         }
     93         FcPatternAdd(pattern, type, fcvalue, FcFalse);
     94 
     95         type = va_arg(ap, const char *);
     96         if (!type)
     97             break;
     98         // FcType is promoted to int when passed through ...
     99         vtype = static_cast<FcType>(va_arg(ap, int));
    100         value = va_arg(ap, const void *);
    101     };
    102     va_end(ap);
    103 
    104     FcConfigSubstitute(NULL, pattern, FcMatchPattern);
    105     FcDefaultSubstitute(pattern);
    106 
    107     FcResult result;
    108     FcPattern* match = FcFontMatch(NULL, pattern, &result);
    109     FcPatternDestroy(pattern);
    110 
    111     return match;
    112 }
    113 
    114 // static
    115 SkTypeface* SkFontHost::CreateTypeface(const SkTypeface* familyFace,
    116                                        const char familyName[],
    117                                        SkTypeface::Style style)
    118 {
    119     const char* resolved_family_name = NULL;
    120     FcPattern* face_match = NULL;
    121 
    122     {
    123         SkAutoMutexAcquire ac(global_fc_map_lock);
    124         if (FcTrue != FcInit()) {
    125             SkASSERT(false && "Could not initialize fontconfig.");
    126         }
    127     }
    128 
    129     if (familyFace) {
    130         // Here we use the inverted global id map to find the filename from the
    131         // SkTypeface object. Given the filename we can ask fontconfig for the
    132         // familyname of the font.
    133         SkAutoMutexAcquire ac(global_fc_map_lock);
    134 
    135         const unsigned fileid = FileIdFromUniqueId(familyFace->uniqueID());
    136         std::map<unsigned, std::string>::const_iterator i = global_fc_map_inverted.find(fileid);
    137         if (i == global_fc_map_inverted.end()) {
    138             return NULL;
    139         }
    140 
    141         face_match = FontMatch(FC_FILE, FcTypeString, i->second.c_str(), NULL);
    142         if (!face_match) {
    143             return NULL;
    144         }
    145 
    146         FcChar8* family;
    147         if (FcPatternGetString(face_match, FC_FAMILY, 0, &family)) {
    148             FcPatternDestroy(face_match);
    149             return NULL;
    150         }
    151         // At this point, @family is pointing into the @face_match object so we
    152         // cannot release it yet.
    153 
    154         resolved_family_name = reinterpret_cast<char*>(family);
    155     } else if (familyName) {
    156         resolved_family_name = familyName;
    157     }
    158 
    159     const int bold = (style & SkTypeface::kBold) ? FC_WEIGHT_BOLD : FC_WEIGHT_NORMAL;
    160     const int italic = (style & SkTypeface::kItalic) ? FC_SLANT_ITALIC : FC_SLANT_ROMAN;
    161 
    162     FcPattern* match;
    163     if (resolved_family_name) {
    164         match = FontMatch(FC_FAMILY, FcTypeString, resolved_family_name,
    165                           FC_WEIGHT, FcTypeInteger, bold,
    166                           FC_SLANT, FcTypeInteger, italic,
    167                           NULL);
    168     } else {
    169         match = FontMatch(FC_WEIGHT, FcTypeInteger, reinterpret_cast<void*>(bold),
    170                           FC_SLANT, FcTypeInteger, italic,
    171                           NULL);
    172     }
    173 
    174     if (face_match)
    175         FcPatternDestroy(face_match);
    176 
    177     if (!match)
    178         return NULL;
    179 
    180     FcChar8* filename;
    181     if (FcPatternGetString(match, FC_FILE, 0, &filename) != FcResultMatch) {
    182         FcPatternDestroy(match);
    183         return NULL;
    184     }
    185     // Now @filename is pointing into @match
    186 
    187     const unsigned fileid = FileIdFromFilename(reinterpret_cast<char*>(filename));
    188     const unsigned id = UniqueIdFromFileIdAndStyle(fileid, style);
    189     SkTypeface* typeface = SkNEW_ARGS(FontConfigTypeface, (style, id));
    190     FcPatternDestroy(match);
    191 
    192     return typeface;
    193 }
    194 
    195 // static
    196 SkTypeface* SkFontHost::CreateTypefaceFromStream(SkStream* stream) {
    197     SkDEBUGFAIL("SkFontHost::CreateTypefaceFromStream unimplemented");
    198     return NULL;
    199 }
    200 
    201 // static
    202 SkTypeface* SkFontHost::CreateTypefaceFromFile(const char path[]) {
    203     SkDEBUGFAIL("SkFontHost::CreateTypefaceFromFile unimplemented");
    204     return NULL;
    205 }
    206 
    207 // static
    208 SkStream* SkFontHost::OpenStream(uint32_t id) {
    209     SkAutoMutexAcquire ac(global_fc_map_lock);
    210     const unsigned fileid = FileIdFromUniqueId(id);
    211 
    212     std::map<unsigned, std::string>::const_iterator i = global_fc_map_inverted.find(fileid);
    213     if (i == global_fc_map_inverted.end()) {
    214         return NULL;
    215     }
    216 
    217     return SkNEW_ARGS(SkFILEStream, (i->second.c_str()));
    218 }
    219 
    220 size_t SkFontHost::GetFileName(SkFontID fontID, char path[], size_t length, int32_t* index) {
    221     SkAutoMutexAcquire ac(global_fc_map_lock);
    222     const unsigned fileid = FileIdFromUniqueId(fontID);
    223 
    224     std::map<unsigned, std::string>::const_iterator i = global_fc_map_inverted.find(fileid);
    225     if (i == global_fc_map_inverted.end()) {
    226         return 0;
    227     }
    228 
    229     const std::string& str = i->second;
    230     if (path) {
    231         memcpy(path, str.c_str(), SkMin32(str.size(), length));
    232     }
    233     if (index) {    // TODO: check if we're in a TTC
    234         *index = 0;
    235     }
    236     return str.size();
    237 }
    238 
    239 void SkFontHost::Serialize(const SkTypeface*, SkWStream*) {
    240     SkDEBUGFAIL("SkFontHost::Serialize unimplemented");
    241 }
    242 
    243 SkTypeface* SkFontHost::Deserialize(SkStream* stream) {
    244     SkDEBUGFAIL("SkFontHost::Deserialize unimplemented");
    245     return NULL;
    246 }
    247 
    248 SkFontID SkFontHost::NextLogicalFont(SkFontID currFontID, SkFontID origFontID) {
    249     // We don't handle font fallback, WebKit does.
    250     return 0;
    251 }
    252