check-point for linux fontmgr impl

Review URL: https://codereview.chromium.org/14305007

git-svn-id: http://skia.googlecode.com/svn/trunk@8808 2bbb7eff-a529-9590-31e7-b0007b416f81
diff --git a/include/ports/SkFontConfigInterface.h b/include/ports/SkFontConfigInterface.h
index 786c346..fb09242 100644
--- a/include/ports/SkFontConfigInterface.h
+++ b/include/ports/SkFontConfigInterface.h
@@ -49,11 +49,21 @@
                    fTTCIndex == other.fTTCIndex &&
                    fString == other.fString;
         }
+        bool operator!=(const FontIdentity& other) const {
+            return !(*this == other);
+        }
 
         uint32_t    fID;
         int32_t     fTTCIndex;
         SkString    fString;
         SkFontStyle fStyle;
+        
+        // If buffer is NULL, just return the number of bytes that would have
+        // been written. Will pad contents to a multiple of 4.
+        size_t writeToMemory(void* buffer = NULL) const;
+        
+        // Recreate from a flattened buffer, returning the number of bytes read.
+        size_t readFromMemory(const void* buffer, size_t length);
     };
 
     /**
diff --git a/include/ports/SkFontStyle.h b/include/ports/SkFontStyle.h
index ba4e4a0..9d9a912 100644
--- a/include/ports/SkFontStyle.h
+++ b/include/ports/SkFontStyle.h
@@ -50,6 +50,7 @@
 
     int weight() const { return fUnion.fR.fWeight; }
     int width() const { return fUnion.fR.fWidth; }
+    Slant slant() const { return (Slant)fUnion.fR.fSlant; }
 
     bool isItalic() const {
         return kItalic_Slant == fUnion.fR.fSlant;
diff --git a/src/ports/SkFontConfigInterface_direct.cpp b/src/ports/SkFontConfigInterface_direct.cpp
index d4bb3e6..8f0ea8a 100644
--- a/src/ports/SkFontConfigInterface_direct.cpp
+++ b/src/ports/SkFontConfigInterface_direct.cpp
@@ -13,9 +13,91 @@
 
 #include <fontconfig/fontconfig.h>
 
+#include "SkBuffer.h"
 #include "SkFontConfigInterface.h"
 #include "SkStream.h"
 
+size_t SkFontConfigInterface::FontIdentity::writeToMemory(void* addr) const {
+    size_t size = sizeof(fID) + sizeof(fTTCIndex);
+    size += sizeof(int32_t) + sizeof(int32_t) + sizeof(uint8_t); // weight, width, italic
+    size += sizeof(int32_t) + fString.size();    // store length+data
+    if (addr) {
+        SkWBuffer buffer(addr, size);
+
+        buffer.write32(fID);
+        buffer.write32(fTTCIndex);
+        buffer.write32(fString.size());
+        buffer.write32(fStyle.weight());
+        buffer.write32(fStyle.width());
+        buffer.write8(fStyle.slant());
+        buffer.write(fString.c_str(), fString.size());
+        buffer.padToAlign4();
+
+        SkASSERT(buffer.pos() == size);
+    }
+    return size;
+}
+
+size_t SkFontConfigInterface::FontIdentity::readFromMemory(const void* addr,
+                                                           size_t size) {
+    SkRBuffer buffer(addr, size);
+
+    fID = buffer.readU32();
+    fTTCIndex = buffer.readU32();
+    size_t strLen = buffer.readU32();
+    int weight = buffer.readU32();
+    int width = buffer.readU32();
+    SkFontStyle::Slant slant = (SkFontStyle::Slant)buffer.readU8();
+    fStyle = SkFontStyle(weight, width, slant);
+    fString.resize(strLen);
+    buffer.read(fString.writable_str(), strLen);
+    buffer.skipToAlign4();
+
+    return buffer.pos();    // the actual number of bytes read
+}
+
+#ifdef SK_DEBUG
+static void make_iden(SkFontConfigInterface::FontIdentity* iden) {
+    iden->fID = 10;
+    iden->fTTCIndex = 2;
+    iden->fString.set("Hello world");
+    iden->fStyle = SkFontStyle(300, 6, SkFontStyle::kItalic_Slant);
+}
+
+static void test_writeToMemory(const SkFontConfigInterface::FontIdentity& iden0,
+                               int initValue) {
+    SkFontConfigInterface::FontIdentity iden1;
+
+    size_t size0 = iden0.writeToMemory(NULL);
+
+    SkAutoMalloc storage(size0);
+    memset(storage.get(), initValue, size0);
+
+    size_t size1 = iden0.writeToMemory(storage.get());
+    SkASSERT(size0 == size1);
+
+    SkASSERT(iden0 != iden1);
+    size_t size2 = iden1.readFromMemory(storage.get(), size1);
+    SkASSERT(size2 == size1);
+    SkASSERT(iden0 == iden1);
+}
+
+static void fontconfiginterface_unittest() {
+    SkFontConfigInterface::FontIdentity iden0, iden1;
+
+    SkASSERT(iden0 == iden1);
+
+    make_iden(&iden0);
+    SkASSERT(iden0 != iden1);
+
+    make_iden(&iden1);
+    SkASSERT(iden0 == iden1);
+
+    test_writeToMemory(iden0, 0);
+    test_writeToMemory(iden0, 0);
+}
+#endif
+
 class SkFontConfigInterfaceDirect : public SkFontConfigInterface {
 public:
             SkFontConfigInterfaceDirect();
@@ -51,23 +133,6 @@
     return gDirect;
 }
 
-#if 0
-int SkFontConfigInterface::countFamilies() { return 0; }
-
-int SkFontConfigInterface::getFamilySet(int index, SkString* outFamilyName,
-                                        FontIdentity outIdentities[],
-                                        int maxCount) {
-    return 0;
-}
-
-int SkFontConfigInterface::matchFamilySet(const char familyName[],
-                                          SkString* outFamilyName,
-                                          FontIdentity outIdentities[],
-                                          int maxCount) {
-    return 0;
-}
-#endif
-
 ///////////////////////////////////////////////////////////////////////////////
 
 // Returns the string from the pattern, or NULL
@@ -354,7 +419,11 @@
 #define kMaxFontFamilyLength    2048
 
 SkFontConfigInterfaceDirect::SkFontConfigInterfaceDirect() {
+    SkAutoMutexAcquire ac(mutex_);
+
     FcInit();
+
+    SkDEBUGCODE(fontconfiginterface_unittest();)
 }
 
 SkFontConfigInterfaceDirect::~SkFontConfigInterfaceDirect() {
@@ -490,6 +559,8 @@
 }
 
 SkDataTable* SkFontConfigInterfaceDirect::getFamilyNames() {
+    SkAutoMutexAcquire ac(mutex_);
+
     FcPattern* pat = FcPatternCreate();
     FcObjectSet* os = FcObjectSetBuild (FC_FAMILY, (char *) 0);
     if (NULL == os) {
@@ -523,5 +594,130 @@
 bool SkFontConfigInterfaceDirect::matchFamilySet(const char inFamilyName[],
                                                  SkString* outFamilyName,
                                                  SkTArray<FontIdentity>* ids) {
+    SkAutoMutexAcquire ac(mutex_);
+
+#if 0
+    std::string familyStr(familyName ? familyName : "");
+    if (familyStr.length() > kMaxFontFamilyLength) {
+        return false;
+    }
+
+    SkAutoMutexAcquire ac(mutex_);
+
+    FcPattern* pattern = FcPatternCreate();
+
+    if (familyName) {
+        FcPatternAddString(pattern, FC_FAMILY, (FcChar8*)familyName);
+    }
+    FcPatternAddBool(pattern, FC_SCALABLE, FcTrue);
+
+    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().
+
+    const char* post_config_family = get_name(pattern, FC_FAMILY);
+
+    FcResult result;
+    FcFontSet* font_set = FcFontSort(0, pattern, 0, 0, &result);
+    if (!font_set) {
+        FcPatternDestroy(pattern);
+        return false;
+    }
+
+    FcPattern* match = MatchFont(font_set, post_config_family, familyStr);
+    if (!match) {
+        FcPatternDestroy(pattern);
+        FcFontSetDestroy(font_set);
+        return false;
+    }
+
+    FcPatternDestroy(pattern);
+
+    // From here out we just extract our results from 'match'
+
+    if (FcPatternGetString(match, FC_FAMILY, 0, &post_config_family) != FcResultMatch) {
+        FcFontSetDestroy(font_set);
+        return false;
+    }
+
+    FcChar8* c_filename;
+    if (FcPatternGetString(match, FC_FILE, 0, &c_filename) != FcResultMatch) {
+        FcFontSetDestroy(font_set);
+        return false;
+    }
+
+    int face_index;
+    if (FcPatternGetInteger(match, FC_INDEX, 0, &face_index) != FcResultMatch) {
+        FcFontSetDestroy(font_set);
+        return false;
+    }
+
+    FcFontSetDestroy(font_set);
+
+    if (outIdentity) {
+        outIdentity->fTTCIndex = face_index;
+        outIdentity->fString.set((const char*)c_filename);
+    }
+    if (outFamilyName) {
+        outFamilyName->set((const char*)post_config_family);
+    }
+    if (outStyle) {
+        *outStyle = GetFontStyle(match);
+    }
+    return true;
+
+////////////////////
+
+        int count;
+        FcPattern** match = MatchFont(font_set, post_config_family, &count);
+        if (!match) {
+            FcPatternDestroy(pattern);
+            FcFontSetDestroy(font_set);
+            return NULL;
+        }
+
+        FcPatternDestroy(pattern);
+
+        SkTDArray<FcPattern*> trimmedMatches;
+        for (int i = 0; i < count; ++i) {
+            const char* justName = find_just_name(get_name(match[i], FC_FILE));
+            if (!is_lower(*justName)) {
+                *trimmedMatches.append() = match[i];
+            }
+        }
+
+        SkFontStyleSet_FC* sset = SkNEW_ARGS(SkFontStyleSet_FC,
+                                             (trimmedMatches.begin(),
+                                              trimmedMatches.count()));
+#endif
     return false;
 }
+