| Index: src/ports/SkFontConfigParser_android.cpp
|
| diff --git a/src/ports/SkFontConfigParser_android.cpp b/src/ports/SkFontConfigParser_android.cpp
|
| index 90450a99408ab008b4a0c8c7a0081f4dd1bb81ae..78d997dccfd9987ff055d5953656f354cdec9e6f 100644
|
| --- a/src/ports/SkFontConfigParser_android.cpp
|
| +++ b/src/ports/SkFontConfigParser_android.cpp
|
| @@ -6,6 +6,8 @@
|
| */
|
|
|
| #include "SkFontConfigParser_android.h"
|
| +#include "SkFontMgr_android.h"
|
| +#include "SkStream.h"
|
| #include "SkTDArray.h"
|
| #include "SkTSearch.h"
|
| #include "SkTypeface.h"
|
| @@ -15,6 +17,7 @@
|
| #include <stdio.h>
|
|
|
| #include <limits>
|
| +#include <stdlib.h>
|
|
|
| // From Android version LMP onwards, all font files collapse into
|
| // /system/etc/fonts.xml. Instead of trying to detect which version
|
| @@ -30,6 +33,10 @@
|
| #define LOCALE_FALLBACK_FONTS_PREFIX "fallback_fonts-"
|
| #define LOCALE_FALLBACK_FONTS_SUFFIX ".xml"
|
|
|
| +#ifndef SK_FONT_FILE_PREFIX
|
| +# define SK_FONT_FILE_PREFIX "/fonts/"
|
| +#endif
|
| +
|
| /**
|
| * This file contains TWO parsers: one for JB and earlier (system_fonts.xml /
|
| * fallback_fonts.xml), one for LMP and later (fonts.xml).
|
| @@ -37,36 +44,47 @@
|
| * version 21 or higher we switch to the LMP parser.
|
| */
|
|
|
| -// 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
|
| +/** Used to track which tag is currently populating with data.
|
| + * Only nameset and fileset are of interest for now.
|
| + */
|
| +enum CurrentTag {
|
| + kNo_CurrentTag,
|
| + kNameSet_CurrentTag,
|
| + kFileSet_CurrentTag
|
| +};
|
|
|
| /**
|
| * 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 parser, SkTDArray<FontFamily*>& families)
|
| + FamilyData(XML_Parser parser, SkTDArray<FontFamily*>& families,
|
| + const SkString& basePath, bool isFallback)
|
| : fParser(parser)
|
| , fFamilies(families)
|
| , fCurrentFamily(NULL)
|
| , fCurrentFontInfo(NULL)
|
| - , fCurrentTag(NO_TAG)
|
| + , fCurrentTag(kNo_CurrentTag)
|
| , fVersion(0)
|
| - { }
|
| + , fBasePath(basePath)
|
| + , fIsFallback(isFallback)
|
| + { };
|
|
|
| XML_Parser fParser; // The expat parser doing the work, owned by caller
|
| SkTDArray<FontFamily*>& fFamilies; // The array to append families, owned by caller
|
| SkAutoTDelete<FontFamily> fCurrentFamily; // The family being created, owned by this
|
| - FontFileInfo* fCurrentFontInfo; // The fontInfo being created, owned by currentFamily
|
| - int fCurrentTag; // Flag to indicate when we're in nameset/fileset tags
|
| + FontFileInfo* fCurrentFontInfo; // The fontInfo being created, owned by fCurrentFamily
|
| + CurrentTag fCurrentTag; // The kind of tag currently being parsed.
|
| int fVersion; // The version of the file parsed.
|
| + const SkString& fBasePath; // The current base path.
|
| + const bool fIsFallback; // Indicates the file being parsed is a fallback file
|
| };
|
|
|
| -/** http://www.w3.org/TR/html-markup/datatypes.html#common.data.integer.non-negative-def */
|
| +/** Parses a null terminated string into an integer type, checking for overflow.
|
| + * http://www.w3.org/TR/html-markup/datatypes.html#common.data.integer.non-negative-def
|
| + *
|
| + * If the string cannot be parsed into 'value', returns false and does not change 'value'.
|
| + */
|
| template <typename T> static bool parse_non_negative_integer(const char* s, T* value) {
|
| SK_COMPILE_ASSERT(std::numeric_limits<T>::is_integer, T_must_be_integer);
|
| const T nMax = std::numeric_limits<T>::max() / 10;
|
| @@ -88,6 +106,13 @@ template <typename T> static bool parse_non_negative_integer(const char* s, T* v
|
| return true;
|
| }
|
|
|
| +static bool memeq(const char* s1, const char* s2, size_t n1, size_t n2) {
|
| + return n1 == n2 && 0 == memcmp(s1, s2, n1);
|
| +}
|
| +#define MEMEQ(c, s, n) memeq(c, s, sizeof(c) - 1, n)
|
| +
|
| +#define ATTS_NON_NULL(a, i) (a[i] != NULL && a[i+1] != NULL)
|
| +
|
| namespace lmpParser {
|
|
|
| void familyElementHandler(FontFamily* family, const char** attributes) {
|
| @@ -95,69 +120,63 @@ void familyElementHandler(FontFamily* family, const char** attributes) {
|
| // A fallback <family> tag has no name, and may have lang and variant
|
| // attributes.
|
| family->fIsFallbackFont = true;
|
| - for (size_t i = 0; attributes[i] != NULL &&
|
| - attributes[i+1] != NULL; i += 2) {
|
| + for (size_t i = 0; ATTS_NON_NULL(attributes, i); i += 2) {
|
| const char* name = attributes[i];
|
| const char* value = attributes[i+1];
|
| size_t nameLen = strlen(name);
|
| size_t valueLen = strlen(value);
|
| - if (nameLen == 4 && !strncmp("name", name, nameLen)) {
|
| + if (MEMEQ("name", name, nameLen)) {
|
| SkAutoAsciiToLC tolc(value);
|
| family->fNames.push_back().set(tolc.lc());
|
| family->fIsFallbackFont = false;
|
| - } else if (nameLen == 4 && !strncmp("lang", name, nameLen)) {
|
| - family->fLanguage = SkLanguage (value);
|
| - } else if (nameLen == 7 && !strncmp("variant", name, nameLen)) {
|
| + } else if (MEMEQ("lang", name, nameLen)) {
|
| + family->fLanguage = SkLanguage(value, valueLen);
|
| + } else if (MEMEQ("variant", name, nameLen)) {
|
| // Value should be either elegant or compact.
|
| - if (valueLen == 7 && !strncmp("elegant", value, valueLen)) {
|
| + if (MEMEQ("elegant", value, valueLen)) {
|
| family->fVariant = kElegant_FontVariant;
|
| - } else if (valueLen == 7 && !strncmp("compact", value, valueLen)) {
|
| + } else if (MEMEQ("compact", value, valueLen)) {
|
| family->fVariant = kCompact_FontVariant;
|
| }
|
| }
|
| }
|
| }
|
|
|
| -void fontFileNameHandler(void* data, const char* s, int len) {
|
| - FamilyData* familyData = (FamilyData*) data;
|
| - familyData->fCurrentFontInfo->fFileName.set(s, len);
|
| +void XMLCALL fontFileNameHandler(void* data, const char* s, int len) {
|
| + FamilyData* self = static_cast<FamilyData*>(data);
|
| + self->fCurrentFontInfo->fFileName.append(s, len);
|
| }
|
|
|
| -void fontElementHandler(XML_Parser parser, FontFileInfo* file, const char** attributes) {
|
| +void fontElementHandler(FamilyData* self, 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 (size_t i = 0; attributes[i] != NULL &&
|
| - attributes[i+1] != NULL; i += 2) {
|
| + for (size_t i = 0; ATTS_NON_NULL(attributes, i); i += 2) {
|
| const char* name = attributes[i];
|
| const char* value = attributes[i+1];
|
| size_t nameLen = strlen(name);
|
| - if (nameLen == 6 && !strncmp("weight", name, nameLen)) {
|
| + if (MEMEQ("weight", name, nameLen)) {
|
| if (!parse_non_negative_integer(value, &file->fWeight)) {
|
| SkDebugf("---- Font weight %s (INVALID)", value);
|
| - file->fWeight = 0;
|
| }
|
| }
|
| }
|
| - XML_SetCharacterDataHandler(parser, fontFileNameHandler);
|
| + XML_SetCharacterDataHandler(self->fParser, fontFileNameHandler);
|
| }
|
|
|
| -FontFamily* findFamily(FamilyData* familyData, const char* familyName) {
|
| - size_t nameLen = strlen(familyName);
|
| - for (int i = 0; i < familyData->fFamilies.count(); i++) {
|
| - FontFamily* candidate = familyData->fFamilies[i];
|
| +FontFamily* findFamily(FamilyData* self, const SkString& familyName) {
|
| + for (int i = 0; i < self->fFamilies.count(); i++) {
|
| + FontFamily* candidate = self->fFamilies[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())) {
|
| + if (candidate->fNames[j] == familyName) {
|
| return candidate;
|
| }
|
| }
|
| }
|
| -
|
| return NULL;
|
| }
|
|
|
| -void aliasElementHandler(FamilyData* familyData, const char** attributes) {
|
| +void aliasElementHandler(FamilyData* self, 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>.
|
| @@ -167,30 +186,31 @@ void aliasElementHandler(FamilyData* familyData, const char** attributes) {
|
| SkString aliasName;
|
| SkString to;
|
| int weight = 0;
|
| - for (size_t i = 0; attributes[i] != NULL &&
|
| - attributes[i+1] != NULL; i += 2) {
|
| + for (size_t i = 0; ATTS_NON_NULL(attributes, i); i += 2) {
|
| const char* name = attributes[i];
|
| const char* value = attributes[i+1];
|
| size_t nameLen = strlen(name);
|
| - if (nameLen == 4 && !strncmp("name", name, nameLen)) {
|
| + if (MEMEQ("name", name, nameLen)) {
|
| SkAutoAsciiToLC tolc(value);
|
| aliasName.set(tolc.lc());
|
| - } else if (nameLen == 2 && !strncmp("to", name, nameLen)) {
|
| + } else if (MEMEQ("to", name, nameLen)) {
|
| to.set(value);
|
| - } else if (nameLen == 6 && !strncmp("weight", name, nameLen)) {
|
| - parse_non_negative_integer(value, &weight);
|
| + } else if (MEMEQ("weight", name, nameLen)) {
|
| + if (!parse_non_negative_integer(value, &weight)) {
|
| + SkDebugf("---- Font weight %s (INVALID)", value);
|
| + }
|
| }
|
| }
|
|
|
| // Assumes that the named family is already declared
|
| - FontFamily* targetFamily = findFamily(familyData, to.c_str());
|
| + FontFamily* targetFamily = findFamily(self, to);
|
| if (!targetFamily) {
|
| SkDebugf("---- Font alias target %s (NOT FOUND)", to.c_str());
|
| return;
|
| }
|
|
|
| if (weight) {
|
| - FontFamily* family = new FontFamily();
|
| + FontFamily* family = new FontFamily(targetFamily->fBasePath, self->fIsFallback);
|
| family->fNames.push_back().set(aliasName);
|
|
|
| for (int i = 0; i < targetFamily->fFonts.count(); i++) {
|
| @@ -198,34 +218,34 @@ void aliasElementHandler(FamilyData* familyData, const char** attributes) {
|
| family->fFonts.push_back(targetFamily->fFonts[i]);
|
| }
|
| }
|
| - *familyData->fFamilies.append() = family;
|
| + *self->fFamilies.append() = family;
|
| } else {
|
| targetFamily->fNames.push_back().set(aliasName);
|
| }
|
| }
|
|
|
| -void startElementHandler(void* data, const char* tag, const char** attributes) {
|
| - FamilyData* familyData = (FamilyData*) data;
|
| +void XMLCALL startElementHandler(void* data, const char* tag, const char** attributes) {
|
| + FamilyData* self = static_cast<FamilyData*>(data);
|
| size_t len = strlen(tag);
|
| - if (len == 6 && !strncmp(tag, "family", len)) {
|
| - familyData->fCurrentFamily.reset(new FontFamily());
|
| - familyElementHandler(familyData->fCurrentFamily, attributes);
|
| - } else if (len == 4 && !strncmp(tag, "font", len)) {
|
| - FontFileInfo* file = &familyData->fCurrentFamily->fFonts.push_back();
|
| - familyData->fCurrentFontInfo = file;
|
| - fontElementHandler(familyData->fParser, file, attributes);
|
| - } else if (len == 5 && !strncmp(tag, "alias", len)) {
|
| - aliasElementHandler(familyData, attributes);
|
| + if (MEMEQ("family", tag, len)) {
|
| + self->fCurrentFamily.reset(new FontFamily(self->fBasePath, self->fIsFallback));
|
| + familyElementHandler(self->fCurrentFamily, attributes);
|
| + } else if (MEMEQ("font", tag, len)) {
|
| + FontFileInfo* file = &self->fCurrentFamily->fFonts.push_back();
|
| + self->fCurrentFontInfo = file;
|
| + fontElementHandler(self, file, attributes);
|
| + } else if (MEMEQ("alias", tag, len)) {
|
| + aliasElementHandler(self, attributes);
|
| }
|
| }
|
|
|
| -void endElementHandler(void* data, const char* tag) {
|
| - FamilyData* familyData = (FamilyData*) data;
|
| +void XMLCALL endElementHandler(void* data, const char* tag) {
|
| + FamilyData* self = static_cast<FamilyData*>(data);
|
| size_t len = strlen(tag);
|
| - if (len == 6 && strncmp(tag, "family", len) == 0) {
|
| - *familyData->fFamilies.append() = familyData->fCurrentFamily.detach();
|
| - } else if (len == 4 && !strncmp(tag, "font", len)) {
|
| - XML_SetCharacterDataHandler(familyData->fParser, NULL);
|
| + if (MEMEQ("family", tag, len)) {
|
| + *self->fFamilies.append() = self->fCurrentFamily.detach();
|
| + } else if (MEMEQ("font", tag, len)) {
|
| + XML_SetCharacterDataHandler(self->fParser, NULL);
|
| }
|
| }
|
|
|
| @@ -237,20 +257,19 @@ namespace jbParser {
|
| * 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 text_handler(void* data, const char* s, int len) {
|
| - FamilyData* familyData = (FamilyData*) data;
|
| +static void XMLCALL text_handler(void* data, const char* s, int len) {
|
| + FamilyData* self = static_cast<FamilyData*>(data);
|
| // Make sure we're in the right state to store this name information
|
| - if (familyData->fCurrentFamily.get() &&
|
| - (familyData->fCurrentTag == NAMESET_TAG || familyData->fCurrentTag == FILESET_TAG)) {
|
| - switch (familyData->fCurrentTag) {
|
| - case NAMESET_TAG: {
|
| + if (self->fCurrentFamily.get()) {
|
| + switch (self->fCurrentTag) {
|
| + case kNameSet_CurrentTag: {
|
| SkAutoAsciiToLC tolc(s, len);
|
| - familyData->fCurrentFamily->fNames.push_back().set(tolc.lc(), len);
|
| + self->fCurrentFamily->fNames.back().append(tolc.lc(), len);
|
| break;
|
| }
|
| - case FILESET_TAG:
|
| - if (familyData->fCurrentFontInfo) {
|
| - familyData->fCurrentFontInfo->fFileName.set(s, len);
|
| + case kFileSet_CurrentTag:
|
| + if (self->fCurrentFontInfo) {
|
| + self->fCurrentFontInfo->fFileName.append(s, len);
|
| }
|
| break;
|
| default:
|
| @@ -264,98 +283,87 @@ static void text_handler(void* data, const char* s, int len) {
|
| * Handler for font files. This processes the attributes for language and
|
| * variants then lets textHandler handle the actual file name
|
| */
|
| -static void font_file_element_handler(FamilyData* familyData, const char** attributes) {
|
| - FontFileInfo& newFileInfo = familyData->fCurrentFamily->fFonts.push_back();
|
| +static void font_file_element_handler(FamilyData* self, const char** attributes) {
|
| + FontFamily& currentFamily = *self->fCurrentFamily.get();
|
| + FontFileInfo& newFileInfo = currentFamily.fFonts.push_back();
|
| if (attributes) {
|
| - size_t currentAttributeIndex = 0;
|
| - while (attributes[currentAttributeIndex] &&
|
| - attributes[currentAttributeIndex + 1]) {
|
| - const char* attributeName = attributes[currentAttributeIndex];
|
| - const char* attributeValue = attributes[currentAttributeIndex+1];
|
| + for (size_t i = 0; ATTS_NON_NULL(attributes, i); i += 2) {
|
| + const char* attributeName = attributes[i];
|
| + const char* attributeValue = attributes[i+1];
|
| size_t nameLength = strlen(attributeName);
|
| size_t valueLength = strlen(attributeValue);
|
| - if (nameLength == 7 && strncmp(attributeName, "variant", nameLength) == 0) {
|
| - const FontVariant prevVariant = familyData->fCurrentFamily->fVariant;
|
| - if (valueLength == 7 && strncmp(attributeValue, "elegant", valueLength) == 0) {
|
| - familyData->fCurrentFamily->fVariant = kElegant_FontVariant;
|
| - } else if (valueLength == 7 &&
|
| - strncmp(attributeValue, "compact", valueLength) == 0) {
|
| - familyData->fCurrentFamily->fVariant = kCompact_FontVariant;
|
| + if (MEMEQ("variant", attributeName, nameLength)) {
|
| + const FontVariant prevVariant = currentFamily.fVariant;
|
| + if (MEMEQ("elegant", attributeValue, valueLength)) {
|
| + currentFamily.fVariant = kElegant_FontVariant;
|
| + } else if (MEMEQ("compact", attributeValue, valueLength)) {
|
| + currentFamily.fVariant = kCompact_FontVariant;
|
| }
|
| - if (familyData->fCurrentFamily->fFonts.count() > 1 &&
|
| - familyData->fCurrentFamily->fVariant != prevVariant) {
|
| + if (currentFamily.fFonts.count() > 1 && currentFamily.fVariant != prevVariant) {
|
| SkDebugf("Every font file within a family must have identical variants");
|
| - sk_throw();
|
| }
|
|
|
| - } else if (nameLength == 4 && strncmp(attributeName, "lang", nameLength) == 0) {
|
| - SkLanguage prevLang = familyData->fCurrentFamily->fLanguage;
|
| - familyData->fCurrentFamily->fLanguage = SkLanguage(attributeValue);
|
| - if (familyData->fCurrentFamily->fFonts.count() > 1 &&
|
| - familyData->fCurrentFamily->fLanguage != prevLang) {
|
| + } else if (MEMEQ("lang", attributeName, nameLength)) {
|
| + SkLanguage prevLang = currentFamily.fLanguage;
|
| + currentFamily.fLanguage = SkLanguage(attributeValue, valueLength);
|
| + if (currentFamily.fFonts.count() > 1 && currentFamily.fLanguage != prevLang) {
|
| SkDebugf("Every font file within a family must have identical languages");
|
| - sk_throw();
|
| }
|
| - } else if (nameLength == 5 && strncmp(attributeName, "index", nameLength) == 0) {
|
| - int value;
|
| - if (parse_non_negative_integer(attributeValue, &value)) {
|
| - newFileInfo.fIndex = value;
|
| - } else {
|
| +
|
| + } else if (MEMEQ("index", attributeName, nameLength)) {
|
| + if (!parse_non_negative_integer(attributeValue, &newFileInfo.fIndex)) {
|
| SkDebugf("---- SystemFonts index=%s (INVALID)", attributeValue);
|
| }
|
| }
|
| - //each element is a pair of attributeName/attributeValue string pairs
|
| - currentAttributeIndex += 2;
|
| }
|
| }
|
| - familyData->fCurrentFontInfo = &newFileInfo;
|
| - XML_SetCharacterDataHandler(familyData->fParser, text_handler);
|
| + self->fCurrentFontInfo = &newFileInfo;
|
| + XML_SetCharacterDataHandler(self->fParser, text_handler);
|
| }
|
|
|
| /**
|
| * Handler for the start of a tag. The only tags we expect are familyset, family,
|
| * nameset, fileset, name, and file.
|
| */
|
| -static void start_element_handler(void* data, const char* tag, const char** atts) {
|
| - FamilyData* familyData = (FamilyData*) data;
|
| +static void XMLCALL start_element_handler(void* data, const char* tag, const char** attributes) {
|
| + FamilyData* self = static_cast<FamilyData*>(data);
|
| size_t len = strlen(tag);
|
| - if (len == 9 && strncmp(tag, "familyset", len) == 0) {
|
| + if (MEMEQ("familyset", tag, len)) {
|
| // The familyset tag has an optional "version" attribute with an integer value >= 0
|
| - for (size_t i = 0; atts[i] != NULL &&
|
| - atts[i+1] != NULL; i += 2) {
|
| - size_t nameLen = strlen(atts[i]);
|
| - if (nameLen == 7 && strncmp(atts[i], "version", nameLen)) continue;
|
| - const char* valueString = atts[i+1];
|
| + for (size_t i = 0; ATTS_NON_NULL(attributes, i); i += 2) {
|
| + size_t nameLen = strlen(attributes[i]);
|
| + if (!MEMEQ("version", attributes[i], nameLen)) continue;
|
| + const char* valueString = attributes[i+1];
|
| int version;
|
| if (parse_non_negative_integer(valueString, &version) && (version >= 21)) {
|
| - XML_SetElementHandler(familyData->fParser,
|
| + XML_SetElementHandler(self->fParser,
|
| lmpParser::startElementHandler,
|
| lmpParser::endElementHandler);
|
| - familyData->fVersion = version;
|
| + self->fVersion = version;
|
| }
|
| }
|
| - } else if (len == 6 && strncmp(tag, "family", len) == 0) {
|
| - familyData->fCurrentFamily.reset(new FontFamily());
|
| + } else if (MEMEQ("family", tag, len)) {
|
| + self->fCurrentFamily.reset(new FontFamily(self->fBasePath, self->fIsFallback));
|
| // 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 (size_t i = 0; atts[i] != NULL &&
|
| - atts[i+1] != NULL; i += 2) {
|
| - const char* valueString = atts[i+1];
|
| + for (size_t i = 0; ATTS_NON_NULL(attributes, i); i += 2) {
|
| + const char* valueString = attributes[i+1];
|
| int value;
|
| if (parse_non_negative_integer(valueString, &value)) {
|
| - familyData->fCurrentFamily->fOrder = value;
|
| + self->fCurrentFamily->fOrder = value;
|
| }
|
| }
|
| - } else if (len == 7 && strncmp(tag, "nameset", len) == 0) {
|
| - familyData->fCurrentTag = NAMESET_TAG;
|
| - } else if (len == 7 && strncmp(tag, "fileset", len) == 0) {
|
| - familyData->fCurrentTag = FILESET_TAG;
|
| - } else if (len == 4 && strncmp(tag, "name", len) == 0 && familyData->fCurrentTag == NAMESET_TAG) {
|
| + } else if (MEMEQ("nameset", tag, len)) {
|
| + self->fCurrentTag = kNameSet_CurrentTag;
|
| + } else if (MEMEQ("fileset", tag, len)) {
|
| + self->fCurrentTag = kFileSet_CurrentTag;
|
| + } else if (MEMEQ("name", tag, len) && self->fCurrentTag == kNameSet_CurrentTag) {
|
| // If it's a Name, parse the text inside
|
| - XML_SetCharacterDataHandler(familyData->fParser, text_handler);
|
| - } else if (len == 4 && strncmp(tag, "file", len) == 0 && familyData->fCurrentTag == FILESET_TAG) {
|
| + self->fCurrentFamily->fNames.push_back();
|
| + XML_SetCharacterDataHandler(self->fParser, text_handler);
|
| + } else if (MEMEQ("file", tag, len) && self->fCurrentTag == kFileSet_CurrentTag) {
|
| // If it's a file, parse the attributes, then parse the text inside
|
| - font_file_element_handler(familyData, atts);
|
| + font_file_element_handler(self, attributes);
|
| }
|
| }
|
|
|
| @@ -363,70 +371,102 @@ static void start_element_handler(void* data, const char* tag, const char** atts
|
| * Handler for the end of tags. We only care about family, nameset, fileset,
|
| * name, and file.
|
| */
|
| -static void end_element_handler(void* data, const char* tag) {
|
| - FamilyData* familyData = (FamilyData*) data;
|
| +static void XMLCALL end_element_handler(void* data, const char* tag) {
|
| + FamilyData* self = static_cast<FamilyData*>(data);
|
| size_t len = strlen(tag);
|
| - if (len == 6 && strncmp(tag, "family", len)== 0) {
|
| + if (MEMEQ("family", tag, len)) {
|
| // Done parsing a Family - store the created currentFamily in the families array
|
| - *familyData->fFamilies.append() = familyData->fCurrentFamily.detach();
|
| - } else if (len == 7 && strncmp(tag, "nameset", len) == 0) {
|
| - familyData->fCurrentTag = NO_TAG;
|
| - } else if (len == 7 && strncmp(tag, "fileset", len) == 0) {
|
| - familyData->fCurrentTag = NO_TAG;
|
| - } else if ((len == 4 &&
|
| - strncmp(tag, "name", len) == 0 &&
|
| - familyData->fCurrentTag == NAMESET_TAG) ||
|
| - (len == 4 &&
|
| - strncmp(tag, "file", len) == 0 &&
|
| - familyData->fCurrentTag == FILESET_TAG)) {
|
| + *self->fFamilies.append() = self->fCurrentFamily.detach();
|
| + } else if (MEMEQ("nameset", tag, len)) {
|
| + self->fCurrentTag = kNo_CurrentTag;
|
| + } else if (MEMEQ("fileset", tag, len)) {
|
| + self->fCurrentTag = kNo_CurrentTag;
|
| + } else if ((MEMEQ("name", tag, len) && self->fCurrentTag == kNameSet_CurrentTag) ||
|
| + (MEMEQ("file", tag, len) && self->fCurrentTag == kFileSet_CurrentTag)) {
|
| // Disable the arbitrary text handler installed to load Name data
|
| - XML_SetCharacterDataHandler(familyData->fParser, NULL);
|
| + XML_SetCharacterDataHandler(self->fParser, NULL);
|
| }
|
| }
|
|
|
| } // namespace jbParser
|
|
|
| +static void XMLCALL xml_entity_decl_handler(void *data,
|
| + const XML_Char *entityName,
|
| + int is_parameter_entity,
|
| + const XML_Char *value,
|
| + int value_length,
|
| + const XML_Char *base,
|
| + const XML_Char *systemId,
|
| + const XML_Char *publicId,
|
| + const XML_Char *notationName)
|
| +{
|
| + FamilyData* self = static_cast<FamilyData*>(data);
|
| + SkDebugf("Entity declaration %s found, stopping processing.", entityName);
|
| + XML_StopParser(self->fParser, XML_FALSE);
|
| +}
|
| +
|
| +template<typename T> struct remove_ptr {typedef T type;};
|
| +template<typename T> struct remove_ptr<T*> {typedef T type;};
|
| +
|
| /**
|
| * This function parses the given filename and stores the results in the given
|
| * families array. Returns the version of the file, negative if the file does not exist.
|
| */
|
| -static int parse_config_file(const char* filename, SkTDArray<FontFamily*>& families) {
|
| -
|
| - FILE* file = fopen(filename, "r");
|
| +static int parse_config_file(const char* filename, SkTDArray<FontFamily*>& families,
|
| + const SkString& basePath, bool isFallback)
|
| +{
|
| + SkFILEStream file(filename);
|
|
|
| // 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 (NULL == file) {
|
| + if (!file.isValid()) {
|
| + SkDebugf("File %s could not be opened.\n", filename);
|
| return -1;
|
| }
|
|
|
| - XML_Parser parser = XML_ParserCreate(NULL);
|
| - FamilyData familyData(parser, families);
|
| - XML_SetUserData(parser, &familyData);
|
| + SkAutoTCallVProc<remove_ptr<XML_Parser>::type, XML_ParserFree> parser(XML_ParserCreate(NULL));
|
| + if (!parser) {
|
| + SkDebugf("Could not create XML parser.\n");
|
| + return -1;
|
| + }
|
| +
|
| + FamilyData self(parser, families, basePath, isFallback);
|
| + XML_SetUserData(parser, &self);
|
| +
|
| + // Disable entity processing, to inhibit internal entity expansion. See expat CVE-2013-0340
|
| + XML_SetEntityDeclHandler(parser, xml_entity_decl_handler);
|
| +
|
| // Start parsing oldschool; switch these in flight if we detect a newer version of the file.
|
| XML_SetElementHandler(parser, jbParser::start_element_handler, jbParser::end_element_handler);
|
|
|
| char buffer[512];
|
| bool done = false;
|
| while (!done) {
|
| - fgets(buffer, sizeof(buffer), file);
|
| - size_t len = strlen(buffer);
|
| - if (feof(file) != 0) {
|
| - done = true;
|
| + size_t len = file.read(buffer, SK_ARRAY_COUNT(buffer));
|
| + done = file.isAtEnd();
|
| + XML_Status status = XML_Parse(parser, buffer, len, done);
|
| + if (XML_STATUS_ERROR == status) {
|
| + XML_Error error = XML_GetErrorCode(parser);
|
| + int line = XML_GetCurrentLineNumber(parser);
|
| + int column = XML_GetCurrentColumnNumber(parser);
|
| + int index = XML_GetCurrentByteIndex(parser);
|
| + const XML_LChar* errorString = XML_ErrorString(error);
|
| + SkDebugf("Line: %d Column: %d (Offset: %d) Error %d: %s.\n",
|
| + line, column, index, error, errorString);
|
| + return -1;
|
| }
|
| - XML_Parse(parser, buffer, len, done);
|
| }
|
| - XML_ParserFree(parser);
|
| - fclose(file);
|
| - return familyData.fVersion;
|
| + return self.fVersion;
|
| }
|
|
|
| /** Returns the version of the system font file actually found, negative if none. */
|
| -static int append_system_font_families(SkTDArray<FontFamily*>& fontFamilies) {
|
| +static int append_system_font_families(SkTDArray<FontFamily*>& fontFamilies,
|
| + const SkString& basePath)
|
| +{
|
| int initialCount = fontFamilies.count();
|
| - int version = parse_config_file(LMP_SYSTEM_FONTS_FILE, fontFamilies);
|
| + int version = parse_config_file(LMP_SYSTEM_FONTS_FILE, fontFamilies, basePath, false);
|
| if (version < 0 || fontFamilies.count() == initialCount) {
|
| - version = parse_config_file(OLD_SYSTEM_FONTS_FILE, fontFamilies);
|
| + version = parse_config_file(OLD_SYSTEM_FONTS_FILE, fontFamilies, basePath, false);
|
| }
|
| return version;
|
| }
|
| @@ -439,7 +479,8 @@ static int append_system_font_families(SkTDArray<FontFamily*>& fontFamilies) {
|
| * include the locale as part of each entry.
|
| */
|
| static void append_fallback_font_families_for_locale(SkTDArray<FontFamily*>& fallbackFonts,
|
| - const char* dir)
|
| + const char* dir,
|
| + const SkString& basePath)
|
| {
|
| #if defined(SK_BUILD_FOR_ANDROID_FRAMEWORK)
|
| // The framework is beyond Android 4.2 and can therefore skip this function
|
| @@ -470,7 +511,7 @@ static void append_fallback_font_families_for_locale(SkTDArray<FontFamily*>& fal
|
| absoluteFilename.printf("%s/%s", dir, fileName.c_str());
|
|
|
| SkTDArray<FontFamily*> langSpecificFonts;
|
| - parse_config_file(absoluteFilename.c_str(), langSpecificFonts);
|
| + parse_config_file(absoluteFilename.c_str(), langSpecificFonts, basePath, true);
|
|
|
| for (int i = 0; i < langSpecificFonts.count(); ++i) {
|
| FontFamily* family = langSpecificFonts[i];
|
| @@ -487,15 +528,23 @@ static void append_fallback_font_families_for_locale(SkTDArray<FontFamily*>& fal
|
| }
|
| }
|
|
|
| -static void append_system_fallback_font_families(SkTDArray<FontFamily*>& fallbackFonts) {
|
| - parse_config_file(FALLBACK_FONTS_FILE, fallbackFonts);
|
| - append_fallback_font_families_for_locale(fallbackFonts, LOCALE_FALLBACK_FONTS_SYSTEM_DIR);
|
| +static void append_system_fallback_font_families(SkTDArray<FontFamily*>& fallbackFonts,
|
| + const SkString& basePath)
|
| +{
|
| + parse_config_file(FALLBACK_FONTS_FILE, fallbackFonts, basePath, true);
|
| + append_fallback_font_families_for_locale(fallbackFonts,
|
| + LOCALE_FALLBACK_FONTS_SYSTEM_DIR,
|
| + basePath);
|
| }
|
|
|
| -static void mixin_vendor_fallback_font_families(SkTDArray<FontFamily*>& fallbackFonts) {
|
| +static void mixin_vendor_fallback_font_families(SkTDArray<FontFamily*>& fallbackFonts,
|
| + const SkString& basePath)
|
| +{
|
| SkTDArray<FontFamily*> vendorFonts;
|
| - parse_config_file(VENDOR_FONTS_FILE, vendorFonts);
|
| - append_fallback_font_families_for_locale(vendorFonts, LOCALE_FALLBACK_FONTS_VENDOR_DIR);
|
| + parse_config_file(VENDOR_FONTS_FILE, vendorFonts, basePath, true);
|
| + append_fallback_font_families_for_locale(vendorFonts,
|
| + LOCALE_FALLBACK_FONTS_VENDOR_DIR,
|
| + basePath);
|
|
|
| // This loop inserts the vendor fallback fonts in the correct order in the
|
| // overall fallbacks list.
|
| @@ -521,40 +570,32 @@ static void mixin_vendor_fallback_font_families(SkTDArray<FontFamily*>& fallback
|
| }
|
| }
|
|
|
| -/**
|
| - * 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) {
|
| +void SkFontConfigParser::GetSystemFontFamilies(SkTDArray<FontFamily*>& fontFamilies) {
|
| // Version 21 of the system font configuration does not need any fallback configuration files.
|
| - if (append_system_font_families(fontFamilies) >= 21) {
|
| + SkString basePath(getenv("ANDROID_ROOT"));
|
| + basePath.append(SK_FONT_FILE_PREFIX, sizeof(SK_FONT_FILE_PREFIX) - 1);
|
| +
|
| + if (append_system_font_families(fontFamilies, basePath) >= 21) {
|
| return;
|
| }
|
|
|
| // Append all the fallback fonts to system fonts
|
| SkTDArray<FontFamily*> fallbackFonts;
|
| - append_system_fallback_font_families(fallbackFonts);
|
| - mixin_vendor_fallback_font_families(fallbackFonts);
|
| - for (int i = 0; i < fallbackFonts.count(); ++i) {
|
| - fallbackFonts[i]->fIsFallbackFont = true;
|
| - *fontFamilies.append() = fallbackFonts[i];
|
| - }
|
| + append_system_fallback_font_families(fallbackFonts, basePath);
|
| + mixin_vendor_fallback_font_families(fallbackFonts, basePath);
|
| + fontFamilies.append(fallbackFonts.count(), fallbackFonts.begin());
|
| }
|
|
|
| -void SkFontConfigParser::GetTestFontFamilies(SkTDArray<FontFamily*>& fontFamilies,
|
| - const char* testMainConfigFile,
|
| - const char* testFallbackConfigFile) {
|
| - parse_config_file(testMainConfigFile, fontFamilies);
|
| -
|
| - SkTDArray<FontFamily*> fallbackFonts;
|
| - if (testFallbackConfigFile) {
|
| - parse_config_file(testFallbackConfigFile, fallbackFonts);
|
| +void SkFontConfigParser::GetCustomFontFamilies(SkTDArray<FontFamily*>& fontFamilies,
|
| + const SkString& basePath,
|
| + const char* fontsXml,
|
| + const char* fallbackFontsXml)
|
| +{
|
| + if (fontsXml) {
|
| + parse_config_file(fontsXml, fontFamilies, basePath, false);
|
| }
|
| -
|
| - // Append all fallback fonts to system fonts
|
| - for (int i = 0; i < fallbackFonts.count(); ++i) {
|
| - fallbackFonts[i]->fIsFallbackFont = true;
|
| - *fontFamilies.append() = fallbackFonts[i];
|
| + if (fallbackFontsXml) {
|
| + parse_config_file(fallbackFontsXml, fontFamilies, basePath, true);
|
| }
|
| }
|
|
|
|
|