struct TagHandler { /** Called at the start tag. *Calledimmediatelyaftertheparenttagretunsthishandlerfromacallto'tag'. *Allowssettingupforhandlingthetagcontentandprocessingattributes. *Ifnullptr,willnotbecalled.
*/ void (*start)(FamilyData* data, constchar* tag, constchar** attributes);
/** Called at the end tag. *Allowspost-processingofanyaccumulatedinformation. *Thiswillbethelastcallmadeinrelationtothecurrenttag. *Ifnullptr,willnotbecalled.
*/ void (*end)(FamilyData* data, constchar* tag);
/** Called when a nested tag is encountered. *Thisisresponsiblefordetermininghowtohandlethetag. *Ifthetagisnotrecognized,returnnullptrtoskipthetag. *Ifnullptr,allnestedtagswillbeskipped.
*/ const TagHandler* (*tag)(FamilyData* data, constchar* tag, constchar** attributes);
/** The character handler for this tag. *Thisisonlyactiveforcharacterdatacontaineddirectlyinthistag(notsub-tags). *ThefirstparameterwillbecastabletoaFamilyData*. *Ifnullptr,anycharacterdatainthistagwillbeignored.
*/
XML_CharacterDataHandler chars;
};
XML_Parser fParser; // The expat parser doing the work, owned by caller
std::vector<std::unique_ptr<FontFamily>>& fFamilies; // Collection of 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.
SkString fCurrentFamilyList; // Name from family-list tag
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);
if (!self->fCurrentFamilyList.isEmpty()) {
family->fNames.push_back().set(self->fCurrentFamilyList);
family->fIsFallbackFont = false;
}
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)) {
SkAutoAsciiToLC tolc(value);
to.set(tolc.lc());
} 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) {
std::unique_ptr<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.push_back(std::move(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);
}
};
staticconst TagHandler topLevelHandler = { /*start*/nullptr, /*end*/nullptr, /*tag*/[](FamilyData* self, const char* tag, const char** attributes) -> const TagHandler* {
size_t len = strlen(tag); if (MEMEQ("familyset", tag, len)) { // 'version' (non-negative integer) [default 0] for (size_t i = 0; ATTS_NON_NULL(attributes, i); i += 2) { constchar* name = attributes[i];
size_t nameLen = strlen(name); if (MEMEQ("version", name, nameLen)) { constchar* value = attributes[i+1]; if (parse_non_negative_integer(value, &self->fVersion)) { if (self->fVersion >= 21) { return &lmpParser::familySetHandler;
}
}
}
} return &jbParser::familySetHandler;
} elseif (MEMEQ("fonts-modification", tag, len)) { // Modern OEM customization files (e.g. /product/etc/fonts_customization.xml) // use the <fonts-modification> root tag instead of <familyset>. // // Skia natively supports parsing the standard children of this file: // - `<family name="...">`: Instantiated perfectly as new global named families. // - `<font>` and `<axis>`: Parsed and bound correctly to the new named families. // - `<alias>`: Mapped properly to existing named families. // // What is NOT supported: // 1. `customizationType="append-to-existing"` // If an OEM writes `<family customizationType="append-to-existing" name="sans-serif">` // Minikin would find the existing `sans-serif` family from `fonts.xml` and merge // these new fonts directly into it. Skia's parser ignores `customizationType` // entirely. By purely looking at `name="sans-serif"`, Skia will simply instantiate // a second, completely independent `FontFamily` named "sans-serif". This can lead // to shadowing instead of merging. (OEMs primarily use `new-named-family` which // Skia maps perfectly). // // 2. `supportedAxes="wght,ital"` // If an OEM writes `<font supportedAxes="wght">`, Minikin restricts the exposed // variable font axes of the TTF to only weight. Skia completely ignores the // `supportedAxes` attribute, meaning all valid axes contained within the font file // remain fully exposed and animatable.
// Force the modern parser.
self->fVersion = 21; return &lmpParser::familySetHandler;
} return nullptr;
}, /*chars*/nullptr,
};
// 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(std::vector<std::unique_ptr<FontFamily>>& fontFamilies, const SkString& basePath)
{
size_t 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;
}
/** *InsomeversionsofAndroidpriortoAndroid4.2(JellyBeanMR1atAPI *Level17)thefallbackfontsforcertainlocaleswereencodedintheirown *XMLfileswithasuffixthatidentifiedthelocale.Wesearchtheprovided *directoryforthosefiles,addalloftheirentriestothefallbackchain,and *includethelocaleaspartofeachentry.
*/ staticvoid append_fallback_font_families_for_locale(
std::vector<std::unique_ptr<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 (std::unique_ptr<FontFamily>& family : vendorFonts) { int order = family->fOrder; if (order < 0) { if (currentOrder < 0) { // Default case - just add it to the end of the fallback list
fallbackFonts.push_back(std::move(family));
} else { // no order specified on this font, but we're incrementing the order // based on an earlier order insertion request
fallbackFonts.insert(fallbackFonts.begin() + currentOrder, std::move(family));
++currentOrder;
}
} 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(fallbackFonts.begin() + order, std::move(family));
currentOrder = order + 1;
}
}
}
// Version 21 of the system font configuration does not need any fallback configuration files.
SkString basePath(getenv("ANDROID_ROOT"));
basePath.append(kFontFilePrefix);
if (append_system_font_families(fontFamilies, basePath) >= 21) { // Product fonts (fonts_customization.xml) leverage <fonts-modification version="21"> // Therefore, we only attempt to parse them on modern devices (Android Lollipop+). // Pre-21 systems fall through to the legacy fallback fonts handling below.
SkFontMgr_Android_Parser::GetCustomFontFamilies(
fontFamilies, basePath, PRODUCT_CUSTOMIZATION_FILE, nullptr, nullptr); return;
}
// Append all the fallback fonts to system fonts
std::vector<std::unique_ptr<FontFamily>> fallbackFonts;
append_system_fallback_font_families(fallbackFonts, basePath);
mixin_vendor_fallback_font_families(fallbackFonts, basePath);
fontFamilies.insert(fontFamilies.end(),
std::make_move_iterator(fallbackFonts.begin()),
std::make_move_iterator(fallbackFonts.end()));
}
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.