Merge "Read font info from system config files"
diff --git a/Android.mk b/Android.mk
index 3f65320..8a8ed78 100644
--- a/Android.mk
+++ b/Android.mk
@@ -89,6 +89,7 @@
 	src/images/SkCreateRLEPixelRef.cpp \
 	src/images/SkImageDecoder_Factory.cpp \
 	src/images/SkImageEncoder_Factory.cpp \
+	src/ports/FontHostConfiguration_android.cpp \
 	src/ports/SkFontHost_android.cpp \
 	src/ports/SkFontHost_gamma.cpp \
 	src/ports/SkFontHost_FreeType.cpp \
@@ -221,7 +222,8 @@
 	libemoji \
 	libjpeg \
 	libutils \
-	libz
+	libz \
+	libexpat
 
 LOCAL_STATIC_LIBRARIES := \
 	libft2 \
@@ -243,7 +245,8 @@
 	external/giflib \
 	external/jpeg \
 	external/webp/include \
-	frameworks/opt/emoji
+	frameworks/opt/emoji \
+	external/expat/lib
 
 ifeq ($(NO_FALLBACK_FONT),true)
 	LOCAL_CFLAGS += -DNO_FALLBACK_FONT
diff --git a/src/ports/FontHostConfiguration_android.cpp b/src/ports/FontHostConfiguration_android.cpp
new file mode 100644
index 0000000..78f1de4
--- /dev/null
+++ b/src/ports/FontHostConfiguration_android.cpp
@@ -0,0 +1,192 @@
+/* libs/graphics/ports/FontHostConfiguration_android.cpp
+**
+** Copyright 2011, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include "FontHostConfiguration_android.h"
+#include <expat.h>
+#include "SkTDArray.h"
+
+#define SYSTEM_FONTS_FILE "/system/etc/system_fonts.xml"
+#define FALLBACK_FONTS_FILE "/system/etc/fallback_fonts.xml"
+#define VENDOR_FONTS_FILE "/vendor/etc/fallback_fonts.xml"
+
+
+// These defines are used to determine the kind of tag that we're currently populating with data.
+// We only care about the sibling tags nameset and fileset for now.
+#define NO_TAG 0
+#define NAMESET_TAG 1
+#define FILESET_TAG 2
+
+/**
+ * The FamilyData structure is passed around by the parser so that each handler can read these
+ * variables that are relevant to the current parsing.
+ */
+struct FamilyData {
+    FamilyData(XML_Parser *parserRef, SkTDArray<FontFamily*> &familiesRef) :
+            parser(parserRef), families(familiesRef), currentTag(NO_TAG) {};
+
+    XML_Parser *parser;                // The expat parser doing the work
+    SkTDArray<FontFamily*> &families;  // The array that each family is put into as it is parsed
+    FontFamily *currentFamily;         // The current family being created
+    int currentTag;                    // A flag to indicate whether we're in nameset/fileset tags
+};
+
+/**
+ * Handler for arbitrary text. This is used to parse the text inside each name or file tag. The
+ * resulting strings are put into the fNames or fFileNames arrays.
+ */
+void textHandler(void *data, const char *s, int len) {
+    FamilyData *familyData = (FamilyData*) data;
+    // Make sure we're in the right state to store this name information
+    if (familyData->currentFamily &&
+            (familyData->currentTag == NAMESET_TAG || familyData->currentTag == FILESET_TAG)) {
+        // Malloc new buffer to store the string
+        char *buff;
+        buff = (char*) malloc((len + 1) * sizeof(char));
+        strncpy(buff, s, len);
+        buff[len] = '\0';
+        switch (familyData->currentTag) {
+        case NAMESET_TAG:
+            *(familyData->currentFamily->fNames.append()) = buff;
+            break;
+        case FILESET_TAG:
+            *(familyData->currentFamily->fFileNames.append()) = buff;
+            break;
+        default:
+            // Noop - don't care about any text that's not in the Fonts or Names list
+            break;
+        }
+    }
+}
+
+/**
+ * Handler for the start of a tag. The only tags we expect are family, nameset, fileset, name,
+ * and file.
+ */
+void startElementHandler(void *data, const char *tag, const char **atts) {
+    FamilyData *familyData = (FamilyData*) data;
+    int len = strlen(tag);
+    if (strncmp(tag, "family", len)== 0) {
+        familyData->currentFamily = new FontFamily();
+        familyData->currentFamily->order = -1;
+        // The Family tag has an optional "order" attribute with an integer value >= 0
+        // If this attribute does not exist, the default value is -1
+        for (int i = 0; atts[i] != NULL; i += 2) {
+            const char* attribute = atts[i];
+            const char* valueString = atts[i+1];
+            int value;
+            int len = sscanf(valueString, "%d", &value);
+            if (len > 0) {
+                familyData->currentFamily->order = value;
+            }
+        }
+    } else if (len == 7 && strncmp(tag, "nameset", len)== 0) {
+        familyData->currentTag = NAMESET_TAG;
+    } else if (len == 7 && strncmp(tag, "fileset", len) == 0) {
+        familyData->currentTag = FILESET_TAG;
+    } else if ((strncmp(tag, "name", len) == 0 && familyData->currentTag == NAMESET_TAG) ||
+            (strncmp(tag, "file", len) == 0 && familyData->currentTag == FILESET_TAG)) {
+        // If it's a Name, parse the text inside
+        XML_SetCharacterDataHandler(*familyData->parser, textHandler);
+    }
+}
+
+/**
+ * Handler for the end of tags. We only care about family, nameset, fileset, name, and file.
+ */
+void endElementHandler(void *data, const char *tag) {
+    FamilyData *familyData = (FamilyData*) data;
+    int len = strlen(tag);
+    if (strncmp(tag, "family", len)== 0) {
+        // Done parsing a Family - store the created currentFamily in the families array
+        *familyData->families.append() = familyData->currentFamily;
+        familyData->currentFamily = NULL;
+    } else if (len == 7 && strncmp(tag, "nameset", len)== 0) {
+        familyData->currentTag = NO_TAG;
+    } else if (len == 7 && strncmp(tag, "fileset", len)== 0) {
+        familyData->currentTag = NO_TAG;
+    } else if ((strncmp(tag, "name", len) == 0 && familyData->currentTag == NAMESET_TAG) ||
+            (strncmp(tag, "file", len) == 0 && familyData->currentTag == FILESET_TAG)) {
+        // Disable the arbitrary text handler installed to load Name data
+        XML_SetCharacterDataHandler(*familyData->parser, NULL);
+    }
+}
+
+/**
+ * This function parses the given filename and stores the results in the given families array.
+ */
+void parseConfigFile(const char *filename, SkTDArray<FontFamily*> &families) {
+    XML_Parser parser = XML_ParserCreate(NULL);
+    FamilyData *familyData = new FamilyData(&parser, families);
+    XML_SetUserData(parser, familyData);
+    XML_SetElementHandler(parser, startElementHandler, endElementHandler);
+    FILE *file = fopen(filename, "r");
+    if (file == NULL) {
+        // Some of the files we attempt to parse (in particular, /vendor/etc/fallback_fonts.xml)
+        // are optional - failure here is okay because one of these optional files may not exist.
+        return;
+    }
+    char buffer[512];
+    bool done = false;
+    while (!done) {
+        fgets(buffer, sizeof(buffer), file);
+        int len = strlen(buffer);
+        if (feof(file) != 0) {
+            done = true;
+        }
+        XML_Parse(parser, buffer, len, done);
+    }
+}
+
+/**
+ * Loads data on font families from various expected configuration files. The resulting data
+ * is returned in the given fontFamilies array.
+ */
+void getFontFamilies(SkTDArray<FontFamily*> &fontFamilies) {
+
+    SkTDArray<FontFamily*> fallbackFonts;
+    SkTDArray<FontFamily*> vendorFonts;
+    parseConfigFile(SYSTEM_FONTS_FILE, fontFamilies);
+    parseConfigFile(FALLBACK_FONTS_FILE, fallbackFonts);
+    parseConfigFile(VENDOR_FONTS_FILE, vendorFonts);
+
+    // This loop inserts the vendor fallback fonts in the correct order in the overall
+    // fallbacks list.
+    int currentOrder = -1;
+    for (int i = 0; i < vendorFonts.count(); ++i) {
+        FontFamily* family = vendorFonts[i];
+        int order = family->order;
+        if (order < 0) {
+            if (currentOrder < 0) {
+                // Default case - just add it to the end of the fallback list
+                *fallbackFonts.append() = family;
+            } else {
+                // no order specified on this font, but we're incrementing the order
+                // based on an earlier order insertion request
+                *fallbackFonts.insert(currentOrder++) = family;
+            }
+        } else {
+            // Add the font into the fallback list in the specified order. Set currentOrder
+            // for correct placement of other fonts in the vendor list.
+            *fallbackFonts.insert(order) = family;
+            currentOrder = order + 1;
+        }
+    }
+    // Append all fallback fonts to system fonts
+    for (int i = 0; i < fallbackFonts.count(); ++i) {
+        *fontFamilies.append() = fallbackFonts[i];
+    }
+}
diff --git a/src/ports/FontHostConfiguration_android.h b/src/ports/FontHostConfiguration_android.h
new file mode 100644
index 0000000..57a0126
--- /dev/null
+++ b/src/ports/FontHostConfiguration_android.h
@@ -0,0 +1,41 @@
+/* libs/graphics/ports/FontHostConfiguration_android.h
+**
+** Copyright 2011, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+#ifndef FONTHOSTCONFIGURATION_ANDROID_H_
+#define FONTHOSTCONFIGURATION_ANDROID_H_
+
+#include "SkTDArray.h"
+
+/**
+ * The FontFamily data structure is created during parsing and handed back to Skia to fold
+ * into its representation of font families. fNames is the list of font names that alias to a
+ * font family. fFileNames is the list of font filenames for the family. Order is the priority
+ * order for the font. This is used internally to determine the order in which to place fallback
+ * fonts as they are read from the configuration files.
+ */
+struct FontFamily {
+    SkTDArray<const char*>  fNames;
+    SkTDArray<const char*>  fFileNames;
+    int order;
+};
+
+/**
+ * Parses all system font configuration files and returns the results in an array of FontFamily
+ * structures.
+ */
+void getFontFamilies(SkTDArray<FontFamily*> &fontFamilies);
+
+#endif /* FONTHOSTCONFIGURATION_ANDROID_H_ */
diff --git a/src/ports/SkFontHost_android.cpp b/src/ports/SkFontHost_android.cpp
index 7d17544..76d9961 100644
--- a/src/ports/SkFontHost_android.cpp
+++ b/src/ports/SkFontHost_android.cpp
@@ -23,6 +23,7 @@
 #include "SkStream.h"
 #include "SkThread.h"
 #include "SkTSearch.h"
+#include "FontHostConfiguration_android.h"
 #include <stdio.h>
 
 #define FONT_CACHE_MEMORY_BUDGET    (768 * 1024)
@@ -395,64 +396,80 @@
     const char* const*  fNames;     // null-terminated list
 };
 
-static const char* gSansNames[] = {
-    "sans-serif", "arial", "helvetica", "tahoma", "verdana", NULL
-};
-
-static const char* gSerifNames[] = {
-    "serif", "times", "times new roman", "palatino", "georgia", "baskerville",
-    "goudy", "fantasy", "cursive", "ITC Stone Serif", NULL
-};
-
-static const char* gMonoNames[] = {
-    "monospace", "courier", "courier new", "monaco", NULL
-};
-
 // deliberately empty, but we use the address to identify fallback fonts
 static const char* gFBNames[] = { NULL };
 
-/*  Fonts must be grouped by family, with the first font in a family having the
-    list of names (even if that list is empty), and the following members having
-    null for the list. The names list must be NULL-terminated
-*/
-static const FontInitRec gSystemFonts[] = {
-    { "DroidSans.ttf",              gSansNames  },
-    { "DroidSans-Bold.ttf",         NULL        },
-    { "DroidSerif-Regular.ttf",     gSerifNames },
-    { "DroidSerif-Bold.ttf",        NULL        },
-    { "DroidSerif-Italic.ttf",      NULL        },
-    { "DroidSerif-BoldItalic.ttf",  NULL        },
-    { "DroidSansMono.ttf",          gMonoNames  },
-    /*  These are optional, and can be ignored if not found in the file system.
-        These are appended to gFallbackFonts[] as they are seen, so we list
-        them in the order we want them to be accessed by NextLogicalFont().
-     */
-    { "DroidSansArabic.ttf",        gFBNames    },
-    { "DroidSansHebrew-Regular.ttf",gFBNames    },
-    { "DroidSansHebrew-Bold.ttf",   NULL        },
-    { "DroidSansThai.ttf",          gFBNames    },
-    { "MTLmr3m.ttf",                gFBNames    }, // Motoya Japanese Font
-    { "MTLc3m.ttf",                 gFBNames    }, // Motoya Japanese Font
-    { "DroidSansJapanese.ttf",      gFBNames    },
-    { "DroidSansEthiopic-Regular.ttf",gFBNames  },
-    { "DroidSansEthiopic-Bold.ttf", NULL        },
-    { "DroidSansFallback.ttf",      gFBNames    }
-};
 
-#define DEFAULT_NAMES   gSansNames
+/*  Fonts are grouped by family, with the first font in a family having the
+    list of names (even if that list is empty), and the following members having
+    null for the list. The names list must be NULL-terminated.
+*/
+static FontInitRec *gSystemFonts;
+static size_t gNumSystemFonts = 0;
+
+#define SYSTEM_FONTS_FILE "/system/etc/system_fonts.cfg"
 
 // these globals are assigned (once) by load_system_fonts()
 static FamilyRec* gDefaultFamily;
 static SkTypeface* gDefaultNormal;
+static char** gDefaultNames = NULL;
+static uint32_t *gFallbackFonts;
 
-/*  This is sized conservatively, assuming that it will never be a size issue.
-    It will be initialized in load_system_fonts(), and will be filled with the
-    fontIDs that can be used for fallback consideration, in sorted order (sorted
-    meaning element[0] should be used first, then element[1], etc. When we hit
-    a fontID==0 in the array, the list is done, hence our allocation size is
-    +1 the total number of possible system fonts. Also see NextLogicalFont().
- */
-static uint32_t gFallbackFonts[SK_ARRAY_COUNT(gSystemFonts)+1];
+/*  Load info from a configuration file that populates the system/fallback font structures
+*/
+static void load_font_info() {
+//    load_font_info_xml("/system/etc/system_fonts.xml");
+    SkTDArray<FontFamily*> fontFamilies;
+    getFontFamilies(fontFamilies);
+
+    SkTDArray<FontInitRec> fontInfo;
+    bool firstInFamily = false;
+    for (int i = 0; i < fontFamilies.count(); ++i) {
+        FontFamily *family = fontFamilies[i];
+        firstInFamily = true;
+        for (int j = 0; j < family->fFileNames.count(); ++j) {
+            FontInitRec fontInfoRecord;
+            fontInfoRecord.fFileName = family->fFileNames[j];
+            if (j == 0) {
+                if (family->fNames.count() == 0) {
+                    // Fallback font
+                    fontInfoRecord.fNames = (char **)gFBNames;
+                } else {
+                    SkTDArray<const char*> names = family->fNames;
+                    const char **nameList = (const char**)
+                            malloc((names.count() + 1) * sizeof(char*));
+                    if (nameList == NULL) {
+                        // shouldn't get here
+                        break;
+                    }
+                    if (gDefaultNames == NULL) {
+                        gDefaultNames = (char**) nameList;
+                    }
+                    for (int i = 0; i < names.count(); ++i) {
+                        nameList[i] = names[i];
+                    }
+                    nameList[names.count()] = NULL;
+                    fontInfoRecord.fNames = nameList;
+                }
+            } else {
+                fontInfoRecord.fNames = NULL;
+            }
+            *fontInfo.append() = fontInfoRecord;
+        }
+    }
+    gNumSystemFonts = fontInfo.count();
+    gSystemFonts = (FontInitRec*) malloc(gNumSystemFonts * sizeof(FontInitRec));
+    gFallbackFonts = (uint32_t*) malloc((gNumSystemFonts + 1) * sizeof(uint32_t));
+    if (gSystemFonts == NULL) {
+        // shouldn't get here
+        gNumSystemFonts = 0;
+    }
+    for (size_t i = 0; i < gNumSystemFonts; ++i) {
+        gSystemFonts[i].fFileName = fontInfo[i].fFileName;
+        gSystemFonts[i].fNames = fontInfo[i].fNames;
+    }
+    fontFamilies.deleteAll();
+}
 
 /*  Called once (ensured by the sentinel check at the beginning of our body).
     Initializes all the globals, and register the system fonts.
@@ -463,11 +480,13 @@
         return;
     }
 
+    load_font_info();
+
     const FontInitRec* rec = gSystemFonts;
     SkTypeface* firstInFamily = NULL;
     int fallbackCount = 0;
 
-    for (size_t i = 0; i < SK_ARRAY_COUNT(gSystemFonts); i++) {
+    for (size_t i = 0; i < gNumSystemFonts; i++) {
         // if we're the first in a new family, clear firstInFamily
         if (rec[i].fNames != NULL) {
             firstInFamily = NULL;
@@ -505,7 +524,7 @@
             const char* const* names = rec[i].fNames;
 
             // record the default family if this is it
-            if (names == DEFAULT_NAMES) {
+            if (names == gDefaultNames) {
                 gDefaultFamily = family;
             }
             // add the names to map to this family
@@ -553,7 +572,7 @@
         stream->read(str.writable_str(), len);
 
         const FontInitRec* rec = gSystemFonts;
-        for (size_t i = 0; i < SK_ARRAY_COUNT(gSystemFonts); i++) {
+        for (size_t i = 0; i < gNumSystemFonts; i++) {
             if (strcmp(rec[i].fFileName, str.c_str()) == 0) {
                 // backup until we hit the fNames
                 for (int j = i; j >= 0; --j) {