/* * 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.
*/
// Despite the name and location, this is portable code.
/** * This file contains TWO 'familyset' handlers: * One for JB and earlier which works with * /system/etc/system_fonts.xml * /system/etc/fallback_fonts.xml * /vendor/etc/fallback_fonts.xml * /system/etc/fallback_fonts-XX.xml * /vendor/etc/fallback_fonts-XX.xml * and the other for LMP and later which works with * /system/etc/fonts.xml * * If the 'familyset' 'version' attribute is 21 or higher the LMP parser is used, otherwise the JB. * * API 15 4.0.4_r2.1 system_fonts.xml, vendor_fonts.xml, fallback_fonts.xml (system+vendor) no lang. * API 16 4.1.1_r1 fallback_fonts-xx-XX.xml are added. Use the xx list in order. * API 17 4.2.2_r1.1 fallback_fonts-xx.xml are removed and 'lang' is added. * API 21 5.0.0_r1.0.1 fonts.xml replaces the other files. * API 29 10.0.0_r1 /product/etc/fonts_customization.xml with base /product/fonts is added. * The NDK interface is added and reading the files directly is discouraged.
*/
struct FamilyData;
struct TagHandler { /** Called at the start tag. * Called immediately after the parent tag retuns this handler from a call to 'tag'. * Allows setting up for handling the tag content and processing attributes. * If nullptr, will not be called.
*/ void (*start)(FamilyData* data, constchar* tag, constchar** attributes);
/** Called at the end tag. * Allows post-processing of any accumulated information. * This will be the last call made in relation to the current tag. * If nullptr, will not be called.
*/ void (*end)(FamilyData* data, constchar* tag);
/** Called when a nested tag is encountered. * This is responsible for determining how to handle the tag. * If the tag is not recognized, return nullptr to skip the tag. * If nullptr, all nested tags will be skipped.
*/ const TagHandler* (*tag)(FamilyData* data, constchar* tag, constchar** attributes);
/** The character handler for this tag. * This is only active for character data contained directly in this tag (not sub-tags). * The first parameter will be castable to a FamilyData*. * If nullptr, any character data in this tag will be ignored.
*/
XML_CharacterDataHandler chars;
};
XML_Parser fParser; // The expat parser doing the work, owned by caller
SkTDArray<FontFamily*>& fFamilies; // The array to append families, owned by caller
std::unique_ptr<FontFamily> fCurrentFamily; // The family being created, owned by this
FontFileInfo* fCurrentFontInfo; // The info being created, owned by fCurrentFamily int fVersion; // The version of the file parsed. const SkString& fBasePath; // The current base path. constbool fIsFallback; // The file being parsed is a fallback file constchar* fFilename; // The name of the file currently being parsed.
int fDepth; // The current element depth of the parse. int fSkip; // The depth to stop skipping, 0 if not skipping.
SkTDArray<const TagHandler*> fHandler; // The stack of current tag handlers.
};
staticbool is_whitespace(char c) { return c == ' ' || c == '\n'|| c == '\r' || c == '\t';
}
staticvoid trim_string(SkString* s) { char* str = s->data(); constchar* start = str; // start is inclusive constchar* end = start + s->size(); // end is exclusive while (is_whitespace(*start)) { ++start; } if (start != end) {
--end; // make end inclusive while (is_whitespace(*end)) { --end; }
++end; // make end exclusive
}
size_t len = end - start;
memmove(str, start, len);
s->resize(len);
}
staticvoid parse_space_separated_languages(constchar* value, size_t valueLen,
TArray<SkLanguage, true>& languages)
{
size_t i = 0; while (true) { for (; i < valueLen && is_whitespace(value[i]); ++i) { } if (i == valueLen) { break; }
size_t j; for (j = i + 1; j < valueLen && !is_whitespace(value[j]); ++j) { }
languages.emplace_back(value + i, j - i);
i = j; if (i == valueLen) { break; }
}
}
staticconst TagHandler fontHandler = { /*start*/[](FamilyData* self, const char* tag, const char** attributes) { // 'weight' (non-negative integer) [default 0] // 'style' ("normal", "italic") [default "auto"] // 'index' (non-negative integer) [default 0] // The character data should be a filename.
FontFileInfo& file = self->fCurrentFamily->fFonts.push_back();
self->fCurrentFontInfo = &file;
SkString fallbackFor; for (size_t i = 0; ATTS_NON_NULL(attributes, i); i += 2) { constchar* name = attributes[i]; constchar* value = attributes[i+1];
size_t nameLen = strlen(name); if (MEMEQ("weight", name, nameLen)) { if (!parse_non_negative_integer(value, &file.fWeight)) {
SK_FONTCONFIGPARSER_WARNING("'%s' is an invalid weight", value);
}
} elseif (MEMEQ("style", name, nameLen)) {
size_t valueLen = strlen(value); if (MEMEQ("normal", value, valueLen)) {
file.fStyle = FontFileInfo::Style::kNormal;
} elseif (MEMEQ("italic", value, valueLen)) {
file.fStyle = FontFileInfo::Style::kItalic;
}
} elseif (MEMEQ("index", name, nameLen)) { if (!parse_non_negative_integer(value, &file.fIndex)) {
SK_FONTCONFIGPARSER_WARNING("'%s' is an invalid index", value);
}
} elseif (MEMEQ("fallbackFor", name, nameLen)) { /** fallbackFor specifies a family fallback and should have been on family. */
fallbackFor = value;
}
} if (!fallbackFor.isEmpty()) {
std::unique_ptr<FontFamily>* fallbackFamily =
self->fCurrentFamily->fallbackFamilies.find(fallbackFor); if (!fallbackFamily) {
std::unique_ptr<FontFamily> newFallbackFamily( new FontFamily(self->fCurrentFamily->fBasePath, true));
fallbackFamily = self->fCurrentFamily->fallbackFamilies.set(
fallbackFor, std::move(newFallbackFamily));
(*fallbackFamily)->fLanguages = self->fCurrentFamily->fLanguages;
(*fallbackFamily)->fVariant = self->fCurrentFamily->fVariant;
(*fallbackFamily)->fOrder = self->fCurrentFamily->fOrder;
(*fallbackFamily)->fFallbackFor = fallbackFor;
}
self->fCurrentFontInfo = &(*fallbackFamily)->fFonts.emplace_back(file);
self->fCurrentFamily->fFonts.pop_back();
}
}, /*end*/[](FamilyData* self, const char* tag) {
trim_string(&self->fCurrentFontInfo->fFileName);
}, /*tag*/[](FamilyData* self, const char* tag, const char** attributes) -> const TagHandler* {
size_t len = strlen(tag); if (MEMEQ("axis", tag, len)) { return &axisHandler;
} return nullptr;
}, /*chars*/[](void* data, const char* s, int len) {
FamilyData* self = static_cast<FamilyData*>(data);
self->fCurrentFontInfo->fFileName.append(s, len);
}
};
staticconst TagHandler familyHandler = { /*start*/[](FamilyData* self, const char* tag, const char** attributes) { // 'name' (string) [optional] // 'lang' (space separated string) [default ""] // 'variant' ("elegant", "compact") [default "default"] // If there is no name, this is a fallback only font.
FontFamily* family = new FontFamily(self->fBasePath, true);
self->fCurrentFamily.reset(family); for (size_t i = 0; ATTS_NON_NULL(attributes, i); i += 2) { constchar* name = attributes[i]; constchar* value = attributes[i+1];
size_t nameLen = strlen(name);
size_t valueLen = strlen(value); if (MEMEQ("name", name, nameLen)) {
SkAutoAsciiToLC tolc(value);
family->fNames.push_back().set(tolc.lc());
family->fIsFallbackFont = false;
} elseif (MEMEQ("lang", name, nameLen)) {
parse_space_separated_languages(value, valueLen, family->fLanguages);
} elseif (MEMEQ("variant", name, nameLen)) { if (MEMEQ("elegant", value, valueLen)) {
family->fVariant = kElegant_FontVariant;
} elseif (MEMEQ("compact", value, valueLen)) {
family->fVariant = kCompact_FontVariant;
}
}
}
}, /*end*/[](FamilyData* self, const char* tag) {
*self->fFamilies.append() = self->fCurrentFamily.release();
}, /*tag*/[](FamilyData* self, const char* tag, const char** attributes) -> const TagHandler* {
size_t len = strlen(tag); if (MEMEQ("font", tag, len)) { return &fontHandler;
} return nullptr;
}, /*chars*/nullptr,
};
static FontFamily* find_family(FamilyData* self, const SkString& familyName) { for (int i = 0; i < self->fFamilies.size(); i++) {
FontFamily* candidate = self->fFamilies[i]; for (int j = 0; j < candidate->fNames.size(); j++) { if (candidate->fNames[j] == familyName) { return candidate;
}
}
} return nullptr;
}
staticconst TagHandler aliasHandler = { /*start*/[](FamilyData* self, const char* tag, const char** attributes) { // 'name' (string) introduces a new family name. // 'to' (string) specifies which (previous) family to alias // 'weight' (non-negative integer) [optional] // If it *does not* have a weight, 'name' is an alias for the entire 'to' family. // If it *does* have a weight, 'name' is a new family consisting of // the font(s) with 'weight' from the 'to' family.
SkString aliasName;
SkString to; int weight = 0; for (size_t i = 0; ATTS_NON_NULL(attributes, i); i += 2) { constchar* name = attributes[i]; constchar* value = attributes[i+1];
size_t nameLen = strlen(name); if (MEMEQ("name", name, nameLen)) {
SkAutoAsciiToLC tolc(value);
aliasName.set(tolc.lc());
} elseif (MEMEQ("to", name, nameLen)) {
to.set(value);
} elseif (MEMEQ("weight", name, nameLen)) { if (!parse_non_negative_integer(value, &weight)) {
SK_FONTCONFIGPARSER_WARNING("'%s' is an invalid weight", value);
}
}
}
// Assumes that the named family is already declared
FontFamily* targetFamily = find_family(self, to); if (!targetFamily) {
SK_FONTCONFIGPARSER_WARNING("'%s' alias target not found", to.c_str()); return;
}
if (weight) {
FontFamily* family = new FontFamily(targetFamily->fBasePath, self->fIsFallback);
family->fNames.push_back().set(aliasName);
for (int i = 0; i < targetFamily->fFonts.size(); i++) { if (targetFamily->fFonts[i].fWeight == weight) {
family->fFonts.push_back(targetFamily->fFonts[i]);
}
}
*self->fFamilies.append() = family;
} else {
targetFamily->fNames.push_back().set(aliasName);
}
}, /*end*/nullptr, /*tag*/nullptr, /*chars*/nullptr,
};
staticconst TagHandler nameHandler = { /*start*/[](FamilyData* self, const char* tag, const char** attributes) { // The character data should be a name for the font.
self->fCurrentFamily->fNames.push_back();
}, /*end*/nullptr, /*tag*/nullptr, /*chars*/[](void* data, const char* s, int len) {
FamilyData* self = static_cast<FamilyData*>(data);
SkAutoAsciiToLC tolc(s, len);
self->fCurrentFamily->fNames.back().append(tolc.lc(), len);
}
};
/** * 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.
*/ staticint parse_config_file(constchar* 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 (!file.isValid()) {
SkDebugf(SK_FONTMGR_ANDROID_PARSER_PREFIX "'%s' could not be opened\n", filename); return -1;
}
SkAutoTCallVProc<std::remove_pointer_t<XML_Parser>, XML_ParserFree> parser(
XML_ParserCreate_MM(nullptr, &sk_XML_alloc, nullptr)); if (!parser) {
SkDebugf(SK_FONTMGR_ANDROID_PARSER_PREFIX "could not create XML parser\n"); return -1;
}
// 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, start_element_handler, end_element_handler);
// One would assume it would be faster to have a buffer on the stack and call XML_Parse. // But XML_Parse will call XML_GetBuffer anyway and memmove the passed buffer into it. // (Unless XML_CONTEXT_BYTES is undefined, but all users define it.) // In debug, buffer a small odd number of bytes to detect slicing in XML_CharacterDataHandler. staticconstint bufferSize = 512 SkDEBUGCODE( - 507); bool done = false; while (!done) { void* buffer = XML_GetBuffer(parser, bufferSize); if (!buffer) {
SkDebugf(SK_FONTMGR_ANDROID_PARSER_PREFIX "could not buffer enough to continue\n"); return -1;
}
size_t len = file.read(buffer, bufferSize);
done = file.isAtEnd();
XML_Status status = XML_ParseBuffer(parser, len, done); if (XML_STATUS_ERROR == status) {
XML_Error error = XML_GetErrorCode(parser); int line = XML_GetCurrentLineNumber(parser); int column = XML_GetCurrentColumnNumber(parser); const XML_LChar* errorString = XML_ErrorString(error);
SkDebugf(SK_FONTMGR_ANDROID_PARSER_PREFIX "%s:%d:%d error %d: %s.\n",
filename, line, column, error, errorString); return -1;
}
} return self.fVersion;
}
/** Returns the version of the system font file actually found, negative if none. */ staticint append_system_font_families(SkTDArray<FontFamily*>& fontFamilies, const SkString& basePath)
{ int initialCount = fontFamilies.size(); int version = parse_config_file(LMP_SYSTEM_FONTS_FILE, fontFamilies, basePath, false); if (version < 0 || fontFamilies.size() == initialCount) {
version = parse_config_file(OLD_SYSTEM_FONTS_FILE, fontFamilies, basePath, false);
} return version;
}
/** * In some versions of Android prior to Android 4.2 (JellyBean MR1 at API * Level 17) the fallback fonts for certain locales were encoded in their own * XML files with a suffix that identified the locale. We search the provided * directory for those files,add all of their entries to the fallback chain, and * include the locale as part of each entry.
*/ staticvoid append_fallback_font_families_for_locale(SkTDArray<FontFamily*>& fallbackFonts, constchar* dir, const SkString& basePath)
{
SkOSFile::Iter iter(dir, nullptr);
SkString fileName; while (iter.next(&fileName, false)) { // The size of the prefix and suffix. staticconst size_t fixedLen = sizeof(LOCALE_FALLBACK_FONTS_PREFIX) - 1
+ sizeof(LOCALE_FALLBACK_FONTS_SUFFIX) - 1;
// The size of the prefix, suffix, and a minimum valid language code staticconst size_t minSize = fixedLen + 2;
// 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.size(); ++i) {
FontFamily* family = vendorFonts[i]; int order = family->fOrder; 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;
}
}
}
void SkFontMgr_Android_Parser::GetSystemFontFamilies(SkTDArray<FontFamily*>& fontFamilies) { // Version 21 of the system font configuration does not need any fallback configuration files.
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, basePath);
mixin_vendor_fallback_font_families(fallbackFonts, basePath);
fontFamilies.append(fallbackFonts.size(), fallbackFonts.begin());
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.