Minimal changes to SkFontHost_fontconfig to not crash in normal use.
authorbungeman@google.com <bungeman@google.com@2bbb7eff-a529-9590-31e7-b0007b416f81>
Tue, 6 Nov 2012 16:55:24 +0000 (16:55 +0000)
committerbungeman@google.com <bungeman@google.com@2bbb7eff-a529-9590-31e7-b0007b416f81>
Tue, 6 Nov 2012 16:55:24 +0000 (16:55 +0000)
git-svn-id: http://skia.googlecode.com/svn/trunk@6312 2bbb7eff-a529-9590-31e7-b0007b416f81

src/ports/SkFontHost_fontconfig.cpp

index e028a01..0f79a30 100644 (file)
@@ -1,4 +1,3 @@
-
 /*
  * Copyright 2008 Google Inc.
  *
@@ -6,16 +5,6 @@
  * found in the LICENSE file.
  */
 
-
-// -----------------------------------------------------------------------------
-// This file provides implementations of the font resolution members of
-// SkFontHost by using the fontconfig[1] library. Fontconfig is usually found
-// on Linux systems and handles configuration, parsing and caching issues
-// involved with enumerating and matching fonts.
-//
-// [1] http://fontconfig.org
-// -----------------------------------------------------------------------------
-
 #include <map>
 #include <string>
 
 #include "SkFontHost.h"
 #include "SkStream.h"
 
-// This is an extern from SkFontHost_FreeType
+/** An extern from SkFontHost_FreeType. */
 SkTypeface::Style find_name_and_style(SkStream* stream, SkString* name);
 
-// -----------------------------------------------------------------------------
-// The rest of Skia requires that fonts be identified by a unique unsigned id
-// and that we be able to load them given the id. What we actually get from
-// fontconfig is the filename of the font so we keep a locked map from
-// filenames to fileid numbers and back.
-//
-// Note that there's also a unique id in the SkTypeface. This is unique over
-// both filename and style. Thus we encode that id as (fileid << 8) | style.
-// Although truetype fonts can support multiple faces in a single file, at the
-// moment Skia doesn't.
-// -----------------------------------------------------------------------------
+/** This lock must be held while modifying global_fc_* globals. */
 SK_DECLARE_STATIC_MUTEX(global_fc_map_lock);
+
+/** Map from file names to file ids. */
 static std::map<std::string, unsigned> global_fc_map;
+/** Map from file ids to file names. */
 static std::map<unsigned, std::string> global_fc_map_inverted;
-static std::map<uint32_t, SkTypeface *> global_fc_typefaces;
+/** The next file id. */
 static unsigned global_fc_map_next_id = 0;
 
-static unsigned UniqueIdToFileId(unsigned uniqueid)
-{
+/**
+ * Check to see if the filename has already been assigned a fileid and, if so, use it.
+ * Otherwise, assign one. Return the resulting fileid.
+ */
+static unsigned FileIdFromFilename(const char* filename) {
+    SkAutoMutexAcquire ac(global_fc_map_lock);
+
+    std::map<std::string, unsigned>::const_iterator i = global_fc_map.find(filename);
+    if (i == global_fc_map.end()) {
+        const unsigned fileid = global_fc_map_next_id++;
+        global_fc_map[filename] = fileid;
+        global_fc_map_inverted[fileid] = filename;
+        return fileid;
+    } else {
+        return i->second;
+    }
+}
+
+static unsigned FileIdFromUniqueId(unsigned uniqueid) {
     return uniqueid >> 8;
 }
 
-static SkTypeface::Style UniqueIdToStyle(unsigned uniqueid)
-{
+static SkTypeface::Style StyleFromUniqueId(unsigned uniqueid) {
     return static_cast<SkTypeface::Style>(uniqueid & 0xff);
 }
 
-static unsigned FileIdAndStyleToUniqueId(unsigned fileid,
-                                         SkTypeface::Style style)
-{
+static unsigned UniqueIdFromFileIdAndStyle(unsigned fileid, SkTypeface::Style style) {
     SkASSERT((style & 0xff) == style);
     return (fileid << 8) | static_cast<int>(style);
 }
 
-// -----------------------------------------------------------------------------
-// Normally we only return exactly the font asked for. In last-resort cases,
-// the request is for one of the basic font names "Sans", "Serif" or
-// "Monospace". This function tells you whether a given request is for such a
-// fallback.
-// -----------------------------------------------------------------------------
-static bool IsFallbackFontAllowed(const char* request)
-{
-    return strcmp(request, "Sans") == 0 ||
-           strcmp(request, "Serif") == 0 ||
-           strcmp(request, "Monospace") == 0;
-}
-
 class FontConfigTypeface : public SkTypeface {
 public:
-    FontConfigTypeface(Style style, uint32_t id)
-        : SkTypeface(style, id)
-    { }
+    FontConfigTypeface(Style style, uint32_t id) : SkTypeface(style, id) { }
 };
 
-// -----------------------------------------------------------------------------
-// Find a matching font where @type (one of FC_*) is equal to @value. For a
-// list of types, see http://fontconfig.org/fontconfig-devel/x19.html#AEN27.
-// The variable arguments are a list of triples, just like the first three
-// arguments, and must be NULL terminated.
-//
-// For example,
-//   FontMatchString(FC_FILE, FcTypeString, "/usr/share/fonts/myfont.ttf",
-//                   NULL);
-// -----------------------------------------------------------------------------
-static FcPattern* FontMatch(const char* type, FcType vtype, const void* value,
-                            ...)
-{
+/**
+ * Find a matching font where @type (one of FC_*) is equal to @value. For a
+ * list of types, see http://fontconfig.org/fontconfig-devel/x19.html#AEN27.
+ * The variable arguments are a list of triples, just like the first three
+ * arguments, and must be NULL terminated.
+ *
+ * For example,
+ *   FontMatchString(FC_FILE, FcTypeString, "/usr/share/fonts/myfont.ttf", NULL);
+ */
+static FcPattern* FontMatch(const char* type, FcType vtype, const void* value, ...) {
     va_list ap;
     va_start(ap, value);
 
     FcPattern* pattern = FcPatternCreate();
-    const char* family_requested = NULL;
 
     for (;;) {
         FcValue fcvalue;
@@ -113,10 +90,7 @@ static FcPattern* FontMatch(const char* type, FcType vtype, const void* value,
             default:
                 SkDEBUGFAIL("FontMatch unhandled type");
         }
-        FcPatternAdd(pattern, type, fcvalue, 0);
-
-        if (vtype == FcTypeString && strcmp(type, FC_FAMILY) == 0)
-            family_requested = (const char*) value;
+        FcPatternAdd(pattern, type, fcvalue, FcFalse);
 
         type = va_arg(ap, const char *);
         if (!type)
@@ -127,84 +101,16 @@ static FcPattern* FontMatch(const char* type, FcType vtype, const void* value,
     };
     va_end(ap);
 
-    FcConfigSubstitute(0, pattern, FcMatchPattern);
+    FcConfigSubstitute(NULL, pattern, FcMatchPattern);
     FcDefaultSubstitute(pattern);
 
-    // Font matching:
-    // CSS often specifies a fallback list of families:
-    //    font-family: a, b, c, serif;
-    // However, fontconfig will always do its best to find *a* font when asked
-    // for something so we need a way to tell if the match which it has found is
-    // "good enough" for us. Otherwise, we can return NULL which gets piped up
-    // and lets WebKit know to try the next CSS family name. However, fontconfig
-    // configs allow substitutions (mapping "Arial -> Helvetica" etc) and we
-    // wish to support that.
-    //
-    // Thus, if a specific family is requested we set @family_requested. Then we
-    // record two strings: the family name after config processing and the
-    // family name after resolving. If the two are equal, it's a good match.
-    //
-    // So consider the case where a user has mapped Arial to Helvetica in their
-    // config.
-    //    requested family: "Arial"
-    //    post_config_family: "Helvetica"
-    //    post_match_family: "Helvetica"
-    //      -> good match
-    //
-    // and for a missing font:
-    //    requested family: "Monaco"
-    //    post_config_family: "Monaco"
-    //    post_match_family: "Times New Roman"
-    //      -> BAD match
-    //
-    // However, we special-case fallback fonts; see IsFallbackFontAllowed().
-    FcChar8* post_config_family;
-    FcPatternGetString(pattern, FC_FAMILY, 0, &post_config_family);
-
     FcResult result;
-    FcPattern* match = FcFontMatch(0, pattern, &result);
-    if (!match) {
-        FcPatternDestroy(pattern);
-        return NULL;
-    }
-
-    FcChar8* post_match_family;
-    FcPatternGetString(match, FC_FAMILY, 0, &post_match_family);
-    const bool family_names_match =
-        !family_requested ?
-        true :
-        strcasecmp((char *)post_config_family, (char *)post_match_family) == 0;
-
+    FcPattern* match = FcFontMatch(NULL, pattern, &result);
     FcPatternDestroy(pattern);
 
-    if (!family_names_match && !IsFallbackFontAllowed(family_requested)) {
-        FcPatternDestroy(match);
-        return NULL;
-    }
-
     return match;
 }
 
-// -----------------------------------------------------------------------------
-// Check to see if the filename has already been assigned a fileid and, if so,
-// use it. Otherwise, assign one. Return the resulting fileid.
-// -----------------------------------------------------------------------------
-static unsigned FileIdFromFilename(const char* filename)
-{
-    SkAutoMutexAcquire ac(global_fc_map_lock);
-
-    std::map<std::string, unsigned>::const_iterator i =
-        global_fc_map.find(filename);
-    if (i == global_fc_map.end()) {
-        const unsigned fileid = global_fc_map_next_id++;
-        global_fc_map[filename] = fileid;
-        global_fc_map_inverted[fileid] = filename;
-        return fileid;
-    } else {
-        return i->second;
-    }
-}
-
 // static
 SkTypeface* SkFontHost::CreateTypeface(const SkTypeface* familyFace,
                                        const char familyName[],
@@ -215,7 +121,9 @@ SkTypeface* SkFontHost::CreateTypeface(const SkTypeface* familyFace,
 
     {
         SkAutoMutexAcquire ac(global_fc_map_lock);
-        FcInit();
+        if (FcTrue != FcInit()) {
+            SkASSERT(false && "Could not initialize fontconfig.");
+        }
     }
 
     if (familyFace) {
@@ -224,18 +132,17 @@ SkTypeface* SkFontHost::CreateTypeface(const SkTypeface* familyFace,
         // familyname of the font.
         SkAutoMutexAcquire ac(global_fc_map_lock);
 
-        const unsigned fileid = UniqueIdToFileId(familyFace->uniqueID());
-        std::map<unsigned, std::string>::const_iterator i =
-            global_fc_map_inverted.find(fileid);
-        if (i == global_fc_map_inverted.end())
+        const unsigned fileid = FileIdFromUniqueId(familyFace->uniqueID());
+        std::map<unsigned, std::string>::const_iterator i = global_fc_map_inverted.find(fileid);
+        if (i == global_fc_map_inverted.end()) {
             return NULL;
+        }
 
-        FcInit();
-        face_match = FontMatch(FC_FILE, FcTypeString, i->second.c_str(),
-                               NULL);
-
-        if (!face_match)
+        face_match = FontMatch(FC_FILE, FcTypeString, i->second.c_str(), NULL);
+        if (!face_match) {
             return NULL;
+        }
+
         FcChar8* family;
         if (FcPatternGetString(face_match, FC_FAMILY, 0, &family)) {
             FcPatternDestroy(face_match);
@@ -247,21 +154,23 @@ SkTypeface* SkFontHost::CreateTypeface(const SkTypeface* familyFace,
         resolved_family_name = reinterpret_cast<char*>(family);
     } else if (familyName) {
         resolved_family_name = familyName;
+    }
+
+    const int bold = (style & SkTypeface::kBold) ? FC_WEIGHT_BOLD : FC_WEIGHT_NORMAL;
+    const int italic = (style & SkTypeface::kItalic) ? FC_SLANT_ITALIC : FC_SLANT_ROMAN;
+
+    FcPattern* match;
+    if (resolved_family_name) {
+        match = FontMatch(FC_FAMILY, FcTypeString, resolved_family_name,
+                          FC_WEIGHT, FcTypeInteger, bold,
+                          FC_SLANT, FcTypeInteger, italic,
+                          NULL);
     } else {
-        return NULL;
+        match = FontMatch(FC_WEIGHT, FcTypeInteger, reinterpret_cast<void*>(bold),
+                          FC_SLANT, FcTypeInteger, italic,
+                          NULL);
     }
 
-    // At this point, we have a resolved_family_name from somewhere
-    SkASSERT(resolved_family_name);
-
-    const int bold = style & SkTypeface::kBold ?
-                     FC_WEIGHT_BOLD : FC_WEIGHT_NORMAL;
-    const int italic = style & SkTypeface::kItalic ?
-                       FC_SLANT_ITALIC : FC_SLANT_ROMAN;
-    FcPattern* match = FontMatch(FC_FAMILY, FcTypeString, resolved_family_name,
-                                 FC_WEIGHT, FcTypeInteger, bold,
-                                 FC_SLANT, FcTypeInteger, italic,
-                                 NULL);
     if (face_match)
         FcPatternDestroy(face_match);
 
@@ -276,53 +185,43 @@ SkTypeface* SkFontHost::CreateTypeface(const SkTypeface* familyFace,
     // Now @filename is pointing into @match
 
     const unsigned fileid = FileIdFromFilename(reinterpret_cast<char*>(filename));
-    const unsigned id = FileIdAndStyleToUniqueId(fileid, style);
+    const unsigned id = UniqueIdFromFileIdAndStyle(fileid, style);
     SkTypeface* typeface = SkNEW_ARGS(FontConfigTypeface, (style, id));
     FcPatternDestroy(match);
 
-    {
-        SkAutoMutexAcquire ac(global_fc_map_lock);
-        global_fc_typefaces[id] = typeface;
-    }
-
     return typeface;
 }
 
 // static
-SkTypeface* SkFontHost::CreateTypefaceFromStream(SkStream* stream)
-{
+SkTypeface* SkFontHost::CreateTypefaceFromStream(SkStream* stream) {
     SkDEBUGFAIL("SkFontHost::CreateTypefaceFromStream unimplemented");
     return NULL;
 }
 
 // static
-SkTypeface* SkFontHost::CreateTypefaceFromFile(const char path[])
-{
+SkTypeface* SkFontHost::CreateTypefaceFromFile(const char path[]) {
     SkDEBUGFAIL("SkFontHost::CreateTypefaceFromFile unimplemented");
     return NULL;
 }
 
 // static
-SkStream* SkFontHost::OpenStream(uint32_t id)
-{
+SkStream* SkFontHost::OpenStream(uint32_t id) {
     SkAutoMutexAcquire ac(global_fc_map_lock);
-    const unsigned fileid = UniqueIdToFileId(id);
+    const unsigned fileid = FileIdFromUniqueId(id);
 
-    std::map<unsigned, std::string>::const_iterator i =
-        global_fc_map_inverted.find(fileid);
-    if (i == global_fc_map_inverted.end())
+    std::map<unsigned, std::string>::const_iterator i = global_fc_map_inverted.find(fileid);
+    if (i == global_fc_map_inverted.end()) {
         return NULL;
+    }
 
     return SkNEW_ARGS(SkFILEStream, (i->second.c_str()));
 }
 
-size_t SkFontHost::GetFileName(SkFontID fontID, char path[], size_t length,
-                               int32_t* index) {
+size_t SkFontHost::GetFileName(SkFontID fontID, char path[], size_t length, int32_t* index) {
     SkAutoMutexAcquire ac(global_fc_map_lock);
-    const unsigned fileid = UniqueIdToFileId(fontID);
+    const unsigned fileid = FileIdFromUniqueId(fontID);
 
-    std::map<unsigned, std::string>::const_iterator i =
-    global_fc_map_inverted.find(fileid);
+    std::map<unsigned, std::string>::const_iterator i = global_fc_map_inverted.find(fileid);
     if (i == global_fc_map_inverted.end()) {
         return 0;
     }