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) {