aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/ports/SkFontConfigParser_android.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/ports/SkFontConfigParser_android.cpp')
-rw-r--r--src/ports/SkFontConfigParser_android.cpp276
1 files changed, 276 insertions, 0 deletions
diff --git a/src/ports/SkFontConfigParser_android.cpp b/src/ports/SkFontConfigParser_android.cpp
new file mode 100644
index 0000000000..9214d1bf61
--- /dev/null
+++ b/src/ports/SkFontConfigParser_android.cpp
@@ -0,0 +1,276 @@
+/*
+ * Copyright 2011 The Android Open Source Project
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "SkFontConfigParser_android.h"
+#include "SkTDArray.h"
+#include "SkTypeface.h"
+
+#include <expat.h>
+#include <sys/system_properties.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),
+ currentFamily(NULL),
+ currentFontInfo(NULL),
+ 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
+ FontFileInfo *currentFontInfo; // The current fontInfo 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 FontFileInfo arrays.
+ */
+static 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:
+ if (familyData->currentFontInfo) {
+ familyData->currentFontInfo->fFileName = buff;
+ }
+ break;
+ default:
+ // Noop - don't care about any text that's not in the Fonts or Names list
+ break;
+ }
+ }
+}
+
+/**
+ * Handler for font files. This processes the attributes for language and
+ * variants then lets textHandler handle the actual file name
+ */
+static void fontFileElementHandler(FamilyData *familyData, const char **attributes) {
+ FontFileInfo* newFileInfo = new FontFileInfo();
+ if (attributes) {
+ int currentAttributeIndex = 0;
+ while (attributes[currentAttributeIndex]) {
+ const char* attributeName = attributes[currentAttributeIndex];
+ const char* attributeValue = attributes[currentAttributeIndex+1];
+ int nameLength = strlen(attributeName);
+ int valueLength = strlen(attributeValue);
+ if (strncmp(attributeName, "variant", nameLength) == 0) {
+ if (strncmp(attributeValue, "elegant", valueLength) == 0) {
+ newFileInfo->fPaintOptions.setFontVariant(SkPaintOptionsAndroid::kElegant_Variant);
+ } else if (strncmp(attributeValue, "compact", valueLength) == 0) {
+ newFileInfo->fPaintOptions.setFontVariant(SkPaintOptionsAndroid::kCompact_Variant);
+ }
+ } else if (strncmp(attributeName, "lang", nameLength) == 0) {
+ newFileInfo->fPaintOptions.setLanguage(attributeValue);
+ }
+ //each element is a pair of attributeName/attributeValue string pairs
+ currentAttributeIndex += 2;
+ }
+ }
+ *(familyData->currentFamily->fFontFiles.append()) = newFileInfo;
+ familyData->currentFontInfo = newFileInfo;
+ XML_SetCharacterDataHandler(*familyData->parser, textHandler);
+}
+
+/**
+ * Handler for the start of a tag. The only tags we expect are family, nameset,
+ * fileset, name, and file.
+ */
+static 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* 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) {
+ // If it's a Name, parse the text inside
+ XML_SetCharacterDataHandler(*familyData->parser, textHandler);
+ } else if (strncmp(tag, "file", len) == 0 && familyData->currentTag == FILESET_TAG) {
+ // If it's a file, parse the attributes, then parse the text inside
+ fontFileElementHandler(familyData, atts);
+ }
+}
+
+/**
+ * Handler for the end of tags. We only care about family, nameset, fileset,
+ * name, and file.
+ */
+static 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.
+ */
+static 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");
+ // 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.
+ if (file == NULL) {
+ 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);
+ }
+ fclose(file);
+}
+
+static void getSystemFontFamilies(SkTDArray<FontFamily*> &fontFamilies) {
+ parseConfigFile(SYSTEM_FONTS_FILE, fontFamilies);
+}
+
+static void getFallbackFontFamilies(SkTDArray<FontFamily*> &fallbackFonts) {
+ SkTDArray<FontFamily*> vendorFonts;
+ 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;
+ }
+ }
+}
+
+/**
+ * Loads data on font families from various expected configuration files. The
+ * resulting data is returned in the given fontFamilies array.
+ */
+void SkFontConfigParser::GetFontFamilies(SkTDArray<FontFamily*> &fontFamilies) {
+
+ getSystemFontFamilies(fontFamilies);
+
+ // Append all the fallback fonts to system fonts
+ SkTDArray<FontFamily*> fallbackFonts;
+ getFallbackFontFamilies(fallbackFonts);
+ for (int i = 0; i < fallbackFonts.count(); ++i) {
+ fallbackFonts[i]->fIsFallbackFont = true;
+ *fontFamilies.append() = fallbackFonts[i];
+ }
+}
+
+void SkFontConfigParser::GetTestFontFamilies(SkTDArray<FontFamily*> &fontFamilies,
+ const char* testMainConfigFile,
+ const char* testFallbackConfigFile) {
+ parseConfigFile(testMainConfigFile, fontFamilies);
+
+ SkTDArray<FontFamily*> fallbackFonts;
+ parseConfigFile(testFallbackConfigFile, fallbackFonts);
+
+ // Append all fallback fonts to system fonts
+ for (int i = 0; i < fallbackFonts.count(); ++i) {
+ fallbackFonts[i]->fIsFallbackFont = true;
+ *fontFamilies.append() = fallbackFonts[i];
+ }
+}
+
+/**
+ * Read the persistent locale.
+ */
+void SkFontConfigParser::GetLocale(AndroidLocale &locale)
+{
+ char propLang[PROP_VALUE_MAX], propRegn[PROP_VALUE_MAX];
+ __system_property_get("persist.sys.language", propLang);
+ __system_property_get("persist.sys.country", propRegn);
+
+ if (*propLang == 0 && *propRegn == 0) {
+ /* Set to ro properties, default is en_US */
+ __system_property_get("ro.product.locale.language", propLang);
+ __system_property_get("ro.product.locale.region", propRegn);
+ if (*propLang == 0 && *propRegn == 0) {
+ strcpy(propLang, "en");
+ strcpy(propRegn, "US");
+ }
+ }
+ strncpy(locale.language, propLang, 2);
+ locale.language[2] = '\0';
+ strncpy(locale.region, propRegn, 2);
+ locale.region[2] = '\0';
+}