Parses sample code provided by Android project. Attempts to keep FontFamily data...
authortomhudson <tomhudson@chromium.org>
Tue, 5 Aug 2014 20:35:00 +0000 (13:35 -0700)
committerCommit bot <commit-bot@chromium.org>
Tue, 5 Aug 2014 20:35:02 +0000 (13:35 -0700)
R=bungeman@google.com, djsollen@google.com, tomhudson@google.com

Author: tomhudson@chromium.org

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

src/ports/SkFontConfigParser_android.cpp
src/ports/SkFontConfigParser_android.h
tests/FontConfigParser.cpp

index fbbb6a6..d1169b9 100644 (file)
@@ -77,15 +77,190 @@ template <typename T> static bool parseNonNegativeInteger(const char* s, T* valu
 
 namespace lmpParser {
 
-void startElementHandler(void* data, const char* tag,
-                                   const char** attributes) {
-    //SkDebugf("lmp started %s", tag);
+void familyElementHandler(FontFamily* family, const char** attributes) {
+    // A non-fallback <family> tag must have a canonical name attribute.
+    // A (fallback) <family> tag may have lang and variant attributes.
+    for (int i = 0; attributes[i] != NULL; i += 2) {
+        const char* name = attributes[i];
+        const char* value = attributes[i+1];
+        int nameLen = strlen(name);
+        int valueLen = strlen(value);
+        if (nameLen == 4 && !strncmp("name", name, nameLen)) {
+            family->fNames.push_back().set(value);
+        } else if (nameLen == 4 && !strncmp("lang", name, nameLen)) {
+            family->fLanguage = SkLanguage (value);
+        } else if (nameLen == 7 && !strncmp("variant", name, nameLen)) {
+            // Value should be either elegant or compact.
+            if (valueLen == 7 && !strncmp("elegant", value, valueLen)) {
+                family->fVariant = SkPaintOptionsAndroid::kElegant_Variant;
+            } else if (valueLen == 7 && !strncmp("compact", value, valueLen)) {
+                family->fVariant = SkPaintOptionsAndroid::kCompact_Variant;
+            }
+        }
+    }
 }
 
-void endElementHandler(void* data, const char* tag) {
+void fontFileNameHandler(void *data, const char *s, int len) {
+    FamilyData *familyData = (FamilyData*) data;
+    familyData->currentFontInfo->fFileName.set(s, len);
+}
 
-    //SkDebugf("lmp ended %s", tag);
+void familyElementEndHandler(FontFamily* family) {
+    for (int i = 0; i < family->fFontFiles.count(); i++) {
+        family->fFontFiles[i].fPaintOptions.setLanguage(family->fLanguage);
+        family->fFontFiles[i].fPaintOptions.setFontVariant(family->fVariant);
+    }
+}
+
+void fontElementHandler(XML_Parser* parser, FontFileInfo* file, const char** attributes) {
+    // A <font> should have weight (integer) and style (normal, italic) attributes.
+    // NOTE: we ignore the style.
+    // The element should contain a filename.
+    for (int i = 0; attributes[i] != NULL; i += 2) {
+        const char* name = attributes[i];
+        const char* value = attributes[i+1];
+        int nameLen = strlen(name);
+        if (nameLen == 6 && !strncmp("weight", name, nameLen)) {
+            parseNonNegativeInteger(value, &file->fWeight);
+        }
+    }
+    XML_SetCharacterDataHandler(*parser, fontFileNameHandler);
+}
+
+FontFamily* findFamily(FamilyData* familyData, const char* familyName) {
+    unsigned int nameLen = strlen(familyName);
+    for (int i = 0; i < familyData->families.count(); i++) {
+        FontFamily* candidate = familyData->families[i];
+        for (int j = 0; j < candidate->fNames.count(); j++) {
+            if (!strncmp(candidate->fNames[j].c_str(), familyName, nameLen) &&
+                nameLen == strlen(candidate->fNames[j].c_str())) {
+                return candidate;
+            }
+        }
+    }
 
+    return NULL;
+}
+
+void aliasElementHandler(FamilyData* familyData, const char** attributes) {
+    // An <alias> must have name and to attributes.
+    //   It may have weight (integer).
+    // If it *does not* have a weight, it is a variant name for a <family>.
+    // If it *does* have a weight, it names the <font>(s) of a specific weight
+    //   from a <family>.
+
+    SkString aliasName;
+    SkString to;
+    int weight = 0;
+    for (int i = 0; attributes[i] != NULL; i += 2) {
+        const char* name = attributes[i];
+        const char* value = attributes[i+1];
+        int nameLen = strlen(name);
+        if (nameLen == 4 && !strncmp("name", name, nameLen)) {
+            aliasName.set(value);
+        } else if (nameLen == 2 && !strncmp("to", name, nameLen)) {
+            to.set(value);
+        } else if (nameLen == 6 && !strncmp("weight", name, nameLen)) {
+            parseNonNegativeInteger(value, &weight);
+        }
+    }
+
+    // Assumes that the named family is already declared
+    FontFamily* targetFamily = findFamily(familyData, to.c_str());
+    if (!targetFamily) {
+        SkDebugf("---- Font alias target %s (NOT FOUND)", to.c_str());
+        return;
+    }
+
+    if (weight) {
+        FontFamily* family = new FontFamily();
+        family->fNames.push_back().set(aliasName);
+
+        for (int i = 0; i < targetFamily->fFontFiles.count(); i++) {
+            if (targetFamily->fFontFiles[i].fWeight == weight) {
+                family->fFontFiles.push_back(targetFamily->fFontFiles[i]);
+            }
+        }
+        *familyData->families.append() = family;
+    } else {
+        targetFamily->fNames.push_back().set(aliasName);
+    }
+}
+
+bool findWeight400(FontFamily* family) {
+    for (int i = 0; i < family->fFontFiles.count(); i++) {
+        if (family->fFontFiles[i].fWeight == 400) {
+            return true;
+        }
+    }
+    return false;
+}
+
+bool desiredWeight(int weight) {
+    return (weight == 400 || weight == 700);
+}
+
+int countDesiredWeight(FontFamily* family) {
+    int count = 0;
+    for (int i = 0; i < family->fFontFiles.count(); i++) {
+        if (desiredWeight(family->fFontFiles[i].fWeight)) {
+            count++;
+        }
+    }
+    return count;
+}
+
+// To meet Skia's expectations, any family that contains weight=400
+// fonts should *only* contain {400,700}
+void purgeUndesiredWeights(FontFamily* family) {
+    int count = countDesiredWeight(family);
+    for (int i = 1, j = 0; i < family->fFontFiles.count(); i++) {
+        if (desiredWeight(family->fFontFiles[j].fWeight)) {
+            j++;
+        }
+        if ((i != j) && desiredWeight(family->fFontFiles[i].fWeight)) {
+            family->fFontFiles[j] = family->fFontFiles[i];
+        }
+    }
+    family->fFontFiles.resize_back(count);
+}
+
+void familysetElementEndHandler(FamilyData* familyData) {
+    for (int i = 0; i < familyData->families.count(); i++) {
+        if (findWeight400(familyData->families[i])) {
+            purgeUndesiredWeights(familyData->families[i]);
+        }
+    }
+}
+
+void startElementHandler(void* data, const char* tag,
+                         const char** attributes) {
+    FamilyData* familyData = (FamilyData*) data;
+    int len = strlen(tag);
+    if (len == 6 && !strncmp(tag, "family", len)) {
+        familyData->currentFamily = new FontFamily();
+        familyElementHandler(familyData->currentFamily, attributes);
+    } else if (len == 4 && !strncmp(tag, "font", len)) {
+        FontFileInfo* file = &familyData->currentFamily->fFontFiles.push_back();
+        familyData->currentFontInfo = file;
+        fontElementHandler(familyData->parser, file, attributes);
+    } else if (len == 5 && !strncmp(tag, "alias", len)) {
+        aliasElementHandler(familyData, attributes);
+    }
+}
+
+void endElementHandler(void* data, const char* tag) {
+    FamilyData *familyData = (FamilyData*) data;
+    int len = strlen(tag);
+    if (len == 9 && strncmp(tag, "familyset", len) == 0) {
+        familysetElementEndHandler(familyData);
+    } else if (len == 6 && strncmp(tag, "family", len) == 0) {
+        familyElementEndHandler(familyData->currentFamily);
+        *familyData->families.append() = familyData->currentFamily;
+        familyData->currentFamily = NULL;
+    } else if (len == 4 && !strncmp(tag, "font", len)) {
+        XML_SetCharacterDataHandler(*familyData->parser, NULL);
+    }
 }
 
 } // lmpParser
index 8119fd7..513422d 100644 (file)
 #include "SkTDArray.h"
 
 struct FontFileInfo {
-    FontFileInfo() : fIndex(0) { }
+    FontFileInfo() : fIndex(0), fWeight(0) { }
 
     SkString              fFileName;
     int                   fIndex;
     SkPaintOptionsAndroid fPaintOptions;
+    int                   fWeight;
 };
 
 /**
@@ -31,12 +32,17 @@ struct FontFileInfo {
  * they are read from the configuration files.
  */
 struct FontFamily {
-    FontFamily() : fIsFallbackFont(false), order(-1) {}
+    FontFamily()
+        : fVariant(SkPaintOptionsAndroid::kDefault_Variant)
+        , order(-1)
+        , fIsFallbackFont(false) { }
 
-    SkTArray<SkString> fNames;
-    SkTArray<FontFileInfo> fFontFiles;
-    bool fIsFallbackFont;
-    int order; // only used internally by SkFontConfigParser
+    SkTArray<SkString>                 fNames;
+    SkTArray<FontFileInfo>             fFontFiles;
+    SkLanguage                         fLanguage;
+    SkPaintOptionsAndroid::FontVariant fVariant;
+    int                                order; // only used internally by SkFontConfigParser
+    bool                               fIsFallbackFont;
 };
 
 namespace SkFontConfigParser {
index 78d01d9..73e9d2b 100644 (file)
@@ -24,9 +24,26 @@ void DumpLoadedFonts(SkTDArray<FontFamily*> fontFamilies) {
 #if SK_DEBUG_FONTS
     for (int i = 0; i < fontFamilies.count(); ++i) {
         SkDebugf("Family %d:\n", i);
+        switch(fontFamilies[i]->fVariant) {
+            case SkPaintOptionsAndroid::kElegant_Variant: SkDebugf("  elegant"); break;
+            case SkPaintOptionsAndroid::kCompact_Variant: SkDebugf("  compact"); break;
+            default: break;
+        }
+        if (!fontFamilies[i]->fLanguage.getTag().isEmpty()) {
+            SkDebugf("  language: %s", fontFamilies[i]->fLanguage.getTag().c_str());
+        }
         for (int j = 0; j < fontFamilies[i]->fNames.count(); ++j) {
             SkDebugf("  name %s\n", fontFamilies[i]->fNames[j].c_str());
         }
+        for (int j = 0; j < fontFamilies[i]->fFontFiles.count(); ++j) {
+            const FontFileInfo& ffi = fontFamilies[i]->fFontFiles[j];
+            SkDebugf("  file (%d %s %d) %s\n",
+                     ffi.fWeight,
+                     ffi.fPaintOptions.getLanguage().getTag().isEmpty() ? "" :
+                         ffi.fPaintOptions.getLanguage().getTag().c_str(),
+                     ffi.fPaintOptions.getFontVariant(),
+                     ffi.fFileName.c_str());
+        }
     }
 #endif // SK_DEBUG_FONTS
 }
@@ -43,27 +60,26 @@ DEF_TEST(FontConfigParserAndroid, reporter) {
     DumpLoadedFonts(preV17FontFamilies);
     ValidateLoadedFonts(preV17FontFamilies, reporter);
 
+
     SkTDArray<FontFamily*> v17FontFamilies;
     SkFontConfigParser::GetTestFontFamilies(v17FontFamilies,
         GetResourcePath("android_fonts/v17/system_fonts.xml").c_str(),
         GetResourcePath("android_fonts/v17/fallback_fonts.xml").c_str());
 
-
     REPORTER_ASSERT(reporter, v17FontFamilies.count() == 41);
 
     DumpLoadedFonts(v17FontFamilies);
     ValidateLoadedFonts(v17FontFamilies, reporter);
 
+
     SkTDArray<FontFamily*> v22FontFamilies;
     SkFontConfigParser::GetTestFontFamilies(v22FontFamilies,
         GetResourcePath("android_fonts/v22/fonts.xml").c_str(),
         NULL);
 
-    //REPORTER_ASSERT(reporter, v22FontFamilies.count() > 0);
-    if (v22FontFamilies.count() > 0) {
-        REPORTER_ASSERT(reporter, v22FontFamilies[0]->fNames.count() > 0);
-    }
+    REPORTER_ASSERT(reporter, v22FontFamilies.count() == 53);
 
-    //ValidateLoadedFonts(v22FontFamilies, reporter);
+    DumpLoadedFonts(v22FontFamilies);
+    ValidateLoadedFonts(v22FontFamilies, reporter);
 }