2 * Copyright 2011 The Android Open Source Project
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
8 #include "SkFontConfigParser_android.h"
10 #include "SkTSearch.h"
11 #include "SkTypeface.h"
21 // From Android version LMP onwards, all font files collapse into
22 // /system/etc/fonts.xml. Instead of trying to detect which version
23 // we're on, try to open fonts.xml; if that fails, fall back to the
25 #define LMP_SYSTEM_FONTS_FILE "/system/etc/fonts.xml"
26 #define OLD_SYSTEM_FONTS_FILE "/system/etc/system_fonts.xml"
27 #define FALLBACK_FONTS_FILE "/system/etc/fallback_fonts.xml"
28 #define VENDOR_FONTS_FILE "/vendor/etc/fallback_fonts.xml"
30 #define LOCALE_FALLBACK_FONTS_SYSTEM_DIR "/system/etc"
31 #define LOCALE_FALLBACK_FONTS_VENDOR_DIR "/vendor/etc"
32 #define LOCALE_FALLBACK_FONTS_PREFIX "fallback_fonts-"
33 #define LOCALE_FALLBACK_FONTS_SUFFIX ".xml"
36 * This file contains TWO parsers: one for JB and earlier (system_fonts.xml /
37 * fallback_fonts.xml), one for LMP and later (fonts.xml).
38 * We start with the JB parser, and if we detect a <familyset> tag with
39 * version 21 or higher we switch to the LMP parser.
42 // These defines are used to determine the kind of tag that we're currently
43 // populating with data. We only care about the sibling tags nameset and fileset
50 * The FamilyData structure is passed around by the parser so that each handler
51 * can read these variables that are relevant to the current parsing.
54 FamilyData(XML_Parser* parserRef, SkTDArray<FontFamily*> &familiesRef) :
56 families(familiesRef),
58 currentFontInfo(NULL),
59 currentTag(NO_TAG) {};
61 XML_Parser* parser; // The expat parser doing the work
62 SkTDArray<FontFamily*> &families; // The array that each family is put into as it is parsed
63 FontFamily* currentFamily; // The current family being created
64 FontFileInfo* currentFontInfo; // The current fontInfo being created
65 int currentTag; // A flag to indicate whether we're in nameset/fileset tags
68 /** http://www.w3.org/TR/html-markup/datatypes.html#common.data.integer.non-negative-def */
69 template <typename T> static bool parseNonNegativeInteger(const char* s, T* value) {
70 SK_COMPILE_ASSERT(std::numeric_limits<T>::is_integer, T_must_be_integer);
71 const T nMax = std::numeric_limits<T>::max() / 10;
72 const T dMax = std::numeric_limits<T>::max() - (nMax * 10);
76 if (*s < '0' || '9' < *s) {
81 if (n > nMax || (n == nMax && d > dMax)) {
92 void familyElementHandler(FontFamily* family, const char** attributes) {
93 // A non-fallback <family> tag must have a canonical name attribute.
94 // A fallback <family> tag has no name, and may have lang and variant
96 family->fIsFallbackFont = true;
97 for (size_t i = 0; attributes[i] != NULL &&
98 attributes[i+1] != NULL; i += 2) {
99 const char* name = attributes[i];
100 const char* value = attributes[i+1];
101 size_t nameLen = strlen(name);
102 size_t valueLen = strlen(value);
103 if (nameLen == 4 && !strncmp("name", name, nameLen)) {
104 SkAutoAsciiToLC tolc(value);
105 family->fNames.push_back().set(tolc.lc());
106 family->fIsFallbackFont = false;
107 } else if (nameLen == 4 && !strncmp("lang", name, nameLen)) {
108 family->fLanguage = SkLanguage (value);
109 } else if (nameLen == 7 && !strncmp("variant", name, nameLen)) {
110 // Value should be either elegant or compact.
111 if (valueLen == 7 && !strncmp("elegant", value, valueLen)) {
112 family->fVariant = kElegant_FontVariant;
113 } else if (valueLen == 7 && !strncmp("compact", value, valueLen)) {
114 family->fVariant = kCompact_FontVariant;
120 void fontFileNameHandler(void* data, const char* s, int len) {
121 FamilyData* familyData = (FamilyData*) data;
122 familyData->currentFontInfo->fFileName.set(s, len);
125 void fontElementHandler(XML_Parser* parser, FontFileInfo* file, const char** attributes) {
126 // A <font> should have weight (integer) and style (normal, italic) attributes.
127 // NOTE: we ignore the style.
128 // The element should contain a filename.
129 for (size_t i = 0; attributes[i] != NULL &&
130 attributes[i+1] != NULL; i += 2) {
131 const char* name = attributes[i];
132 const char* value = attributes[i+1];
133 size_t nameLen = strlen(name);
134 if (nameLen == 6 && !strncmp("weight", name, nameLen)) {
135 if (!parseNonNegativeInteger(value, &file->fWeight)) {
136 SkDebugf("---- Font weight %s (INVALID)", value);
141 XML_SetCharacterDataHandler(*parser, fontFileNameHandler);
144 FontFamily* findFamily(FamilyData* familyData, const char* familyName) {
145 size_t nameLen = strlen(familyName);
146 for (int i = 0; i < familyData->families.count(); i++) {
147 FontFamily* candidate = familyData->families[i];
148 for (int j = 0; j < candidate->fNames.count(); j++) {
149 if (!strncmp(candidate->fNames[j].c_str(), familyName, nameLen) &&
150 nameLen == strlen(candidate->fNames[j].c_str())) {
159 void aliasElementHandler(FamilyData* familyData, const char** attributes) {
160 // An <alias> must have name and to attributes.
161 // It may have weight (integer).
162 // If it *does not* have a weight, it is a variant name for a <family>.
163 // If it *does* have a weight, it names the <font>(s) of a specific weight
169 for (size_t i = 0; attributes[i] != NULL &&
170 attributes[i+1] != NULL; i += 2) {
171 const char* name = attributes[i];
172 const char* value = attributes[i+1];
173 size_t nameLen = strlen(name);
174 if (nameLen == 4 && !strncmp("name", name, nameLen)) {
175 SkAutoAsciiToLC tolc(value);
176 aliasName.set(tolc.lc());
177 } else if (nameLen == 2 && !strncmp("to", name, nameLen)) {
179 } else if (nameLen == 6 && !strncmp("weight", name, nameLen)) {
180 parseNonNegativeInteger(value, &weight);
184 // Assumes that the named family is already declared
185 FontFamily* targetFamily = findFamily(familyData, to.c_str());
187 SkDebugf("---- Font alias target %s (NOT FOUND)", to.c_str());
192 FontFamily* family = new FontFamily();
193 family->fNames.push_back().set(aliasName);
195 for (int i = 0; i < targetFamily->fFonts.count(); i++) {
196 if (targetFamily->fFonts[i].fWeight == weight) {
197 family->fFonts.push_back(targetFamily->fFonts[i]);
200 *familyData->families.append() = family;
202 targetFamily->fNames.push_back().set(aliasName);
206 void startElementHandler(void* data, const char* tag, const char** attributes) {
207 FamilyData* familyData = (FamilyData*) data;
208 size_t len = strlen(tag);
209 if (len == 6 && !strncmp(tag, "family", len)) {
210 familyData->currentFamily = new FontFamily();
211 familyElementHandler(familyData->currentFamily, attributes);
212 } else if (len == 4 && !strncmp(tag, "font", len)) {
213 FontFileInfo* file = &familyData->currentFamily->fFonts.push_back();
214 familyData->currentFontInfo = file;
215 fontElementHandler(familyData->parser, file, attributes);
216 } else if (len == 5 && !strncmp(tag, "alias", len)) {
217 aliasElementHandler(familyData, attributes);
221 void endElementHandler(void* data, const char* tag) {
222 FamilyData* familyData = (FamilyData*) data;
223 size_t len = strlen(tag);
224 if (len == 6 && strncmp(tag, "family", len) == 0) {
225 *familyData->families.append() = familyData->currentFamily;
226 familyData->currentFamily = NULL;
227 } else if (len == 4 && !strncmp(tag, "font", len)) {
228 XML_SetCharacterDataHandler(*familyData->parser, NULL);
237 * Handler for arbitrary text. This is used to parse the text inside each name
238 * or file tag. The resulting strings are put into the fNames or FontFileInfo arrays.
240 static void textHandler(void* data, const char* s, int len) {
241 FamilyData* familyData = (FamilyData*) data;
242 // Make sure we're in the right state to store this name information
243 if (familyData->currentFamily &&
244 (familyData->currentTag == NAMESET_TAG || familyData->currentTag == FILESET_TAG)) {
245 switch (familyData->currentTag) {
247 SkAutoAsciiToLC tolc(s, len);
248 familyData->currentFamily->fNames.push_back().set(tolc.lc(), len);
252 if (familyData->currentFontInfo) {
253 familyData->currentFontInfo->fFileName.set(s, len);
257 // Noop - don't care about any text that's not in the Fonts or Names list
264 * Handler for font files. This processes the attributes for language and
265 * variants then lets textHandler handle the actual file name
267 static void fontFileElementHandler(FamilyData* familyData, const char** attributes) {
268 FontFileInfo& newFileInfo = familyData->currentFamily->fFonts.push_back();
270 size_t currentAttributeIndex = 0;
271 while (attributes[currentAttributeIndex] &&
272 attributes[currentAttributeIndex + 1]) {
273 const char* attributeName = attributes[currentAttributeIndex];
274 const char* attributeValue = attributes[currentAttributeIndex+1];
275 size_t nameLength = strlen(attributeName);
276 size_t valueLength = strlen(attributeValue);
277 if (nameLength == 7 && strncmp(attributeName, "variant", nameLength) == 0) {
278 const FontVariant prevVariant = familyData->currentFamily->fVariant;
279 if (valueLength == 7 && strncmp(attributeValue, "elegant", valueLength) == 0) {
280 familyData->currentFamily->fVariant = kElegant_FontVariant;
281 } else if (valueLength == 7 &&
282 strncmp(attributeValue, "compact", valueLength) == 0) {
283 familyData->currentFamily->fVariant = kCompact_FontVariant;
285 if (familyData->currentFamily->fFonts.count() > 1 &&
286 familyData->currentFamily->fVariant != prevVariant) {
287 SkDebugf("Every font file within a family must have identical variants");
291 } else if (nameLength == 4 && strncmp(attributeName, "lang", nameLength) == 0) {
292 SkLanguage prevLang = familyData->currentFamily->fLanguage;
293 familyData->currentFamily->fLanguage = SkLanguage(attributeValue);
294 if (familyData->currentFamily->fFonts.count() > 1 &&
295 familyData->currentFamily->fLanguage != prevLang) {
296 SkDebugf("Every font file within a family must have identical languages");
299 } else if (nameLength == 5 && strncmp(attributeName, "index", nameLength) == 0) {
301 if (parseNonNegativeInteger(attributeValue, &value)) {
302 newFileInfo.fIndex = value;
304 SkDebugf("---- SystemFonts index=%s (INVALID)", attributeValue);
307 //each element is a pair of attributeName/attributeValue string pairs
308 currentAttributeIndex += 2;
311 familyData->currentFontInfo = &newFileInfo;
312 XML_SetCharacterDataHandler(*familyData->parser, textHandler);
316 * Handler for the start of a tag. The only tags we expect are familyset, family,
317 * nameset, fileset, name, and file.
319 static void startElementHandler(void* data, const char* tag, const char** atts) {
320 FamilyData* familyData = (FamilyData*) data;
321 size_t len = strlen(tag);
322 if (len == 9 && strncmp(tag, "familyset", len) == 0) {
323 // The familyset tag has an optional "version" attribute with an integer value >= 0
324 for (size_t i = 0; atts[i] != NULL &&
325 atts[i+1] != NULL; i += 2) {
326 size_t nameLen = strlen(atts[i]);
327 if (nameLen == 7 && strncmp(atts[i], "version", nameLen)) continue;
328 const char* valueString = atts[i+1];
330 if (parseNonNegativeInteger(valueString, &version) && (version >= 21)) {
331 XML_SetElementHandler(*familyData->parser,
332 lmpParser::startElementHandler,
333 lmpParser::endElementHandler);
336 } else if (len == 6 && strncmp(tag, "family", len) == 0) {
337 familyData->currentFamily = new FontFamily();
338 // The Family tag has an optional "order" attribute with an integer value >= 0
339 // If this attribute does not exist, the default value is -1
340 for (size_t i = 0; atts[i] != NULL &&
341 atts[i+1] != NULL; i += 2) {
342 const char* valueString = atts[i+1];
344 if (parseNonNegativeInteger(valueString, &value)) {
345 familyData->currentFamily->fOrder = value;
348 } else if (len == 7 && strncmp(tag, "nameset", len) == 0) {
349 familyData->currentTag = NAMESET_TAG;
350 } else if (len == 7 && strncmp(tag, "fileset", len) == 0) {
351 familyData->currentTag = FILESET_TAG;
352 } else if (len == 4 && strncmp(tag, "name", len) == 0 && familyData->currentTag == NAMESET_TAG) {
353 // If it's a Name, parse the text inside
354 XML_SetCharacterDataHandler(*familyData->parser, textHandler);
355 } else if (len == 4 && strncmp(tag, "file", len) == 0 && familyData->currentTag == FILESET_TAG) {
356 // If it's a file, parse the attributes, then parse the text inside
357 fontFileElementHandler(familyData, atts);
362 * Handler for the end of tags. We only care about family, nameset, fileset,
365 static void endElementHandler(void* data, const char* tag) {
366 FamilyData* familyData = (FamilyData*) data;
367 size_t len = strlen(tag);
368 if (len == 6 && strncmp(tag, "family", len)== 0) {
369 // Done parsing a Family - store the created currentFamily in the families array
370 *familyData->families.append() = familyData->currentFamily;
371 familyData->currentFamily = NULL;
372 } else if (len == 7 && strncmp(tag, "nameset", len) == 0) {
373 familyData->currentTag = NO_TAG;
374 } else if (len == 7 && strncmp(tag, "fileset", len) == 0) {
375 familyData->currentTag = NO_TAG;
376 } else if ((len == 4 &&
377 strncmp(tag, "name", len) == 0 &&
378 familyData->currentTag == NAMESET_TAG) ||
380 strncmp(tag, "file", len) == 0 &&
381 familyData->currentTag == FILESET_TAG)) {
382 // Disable the arbitrary text handler installed to load Name data
383 XML_SetCharacterDataHandler(*familyData->parser, NULL);
387 } // namespace jbParser
390 * This function parses the given filename and stores the results in the given
393 static void parseConfigFile(const char* filename, SkTDArray<FontFamily*> &families) {
395 FILE* file = fopen(filename, "r");
397 // Some of the files we attempt to parse (in particular, /vendor/etc/fallback_fonts.xml)
398 // are optional - failure here is okay because one of these optional files may not exist.
403 XML_Parser parser = XML_ParserCreate(NULL);
404 FamilyData* familyData = new FamilyData(&parser, families);
405 XML_SetUserData(parser, familyData);
406 // Start parsing oldschool; switch these in flight if we detect a newer version of the file.
407 XML_SetElementHandler(parser, jbParser::startElementHandler, jbParser::endElementHandler);
412 fgets(buffer, sizeof(buffer), file);
413 size_t len = strlen(buffer);
414 if (feof(file) != 0) {
417 XML_Parse(parser, buffer, len, done);
419 XML_ParserFree(parser);
423 static void getSystemFontFamilies(SkTDArray<FontFamily*> &fontFamilies) {
424 int initialCount = fontFamilies.count();
425 parseConfigFile(LMP_SYSTEM_FONTS_FILE, fontFamilies);
427 if (initialCount == fontFamilies.count()) {
428 parseConfigFile(OLD_SYSTEM_FONTS_FILE, fontFamilies);
433 * In some versions of Android prior to Android 4.2 (JellyBean MR1 at API
434 * Level 17) the fallback fonts for certain locales were encoded in their own
435 * XML files with a suffix that identified the locale. We search the provided
436 * directory for those files,add all of their entries to the fallback chain, and
437 * include the locale as part of each entry.
439 static void getFallbackFontFamiliesForLocale(SkTDArray<FontFamily*> &fallbackFonts, const char* dir) {
440 #if defined(SK_BUILD_FOR_ANDROID_FRAMEWORK)
441 // The framework is beyond Android 4.2 and can therefore skip this function
445 DIR* fontDirectory = opendir(dir);
446 if (fontDirectory != NULL){
447 struct dirent* dirEntry = readdir(fontDirectory);
450 // The size of both the prefix, suffix, and a minimum valid language code
451 static const size_t minSize = strlen(LOCALE_FALLBACK_FONTS_PREFIX) +
452 strlen(LOCALE_FALLBACK_FONTS_SUFFIX) + 2;
454 SkString fileName(dirEntry->d_name);
455 if (fileName.size() >= minSize &&
456 fileName.startsWith(LOCALE_FALLBACK_FONTS_PREFIX) &&
457 fileName.endsWith(LOCALE_FALLBACK_FONTS_SUFFIX)) {
459 static const size_t fixedLen = strlen(LOCALE_FALLBACK_FONTS_PREFIX) -
460 strlen(LOCALE_FALLBACK_FONTS_SUFFIX);
462 SkString locale(fileName.c_str() - strlen(LOCALE_FALLBACK_FONTS_PREFIX),
463 fileName.size() - fixedLen);
465 SkString absoluteFilename;
466 absoluteFilename.printf("%s/%s", dir, fileName.c_str());
468 SkTDArray<FontFamily*> langSpecificFonts;
469 parseConfigFile(absoluteFilename.c_str(), langSpecificFonts);
471 for (int i = 0; i < langSpecificFonts.count(); ++i) {
472 FontFamily* family = langSpecificFonts[i];
473 family->fLanguage = SkLanguage(locale);
474 *fallbackFonts.append() = family;
478 // proceed to the next entry in the directory
479 dirEntry = readdir(fontDirectory);
481 // cleanup the directory reference
482 closedir(fontDirectory);
486 static void getFallbackFontFamilies(SkTDArray<FontFamily*> &fallbackFonts) {
487 SkTDArray<FontFamily*> vendorFonts;
488 parseConfigFile(FALLBACK_FONTS_FILE, fallbackFonts);
489 parseConfigFile(VENDOR_FONTS_FILE, vendorFonts);
491 getFallbackFontFamiliesForLocale(fallbackFonts, LOCALE_FALLBACK_FONTS_SYSTEM_DIR);
492 getFallbackFontFamiliesForLocale(vendorFonts, LOCALE_FALLBACK_FONTS_VENDOR_DIR);
494 // This loop inserts the vendor fallback fonts in the correct order in the
495 // overall fallbacks list.
496 int currentOrder = -1;
497 for (int i = 0; i < vendorFonts.count(); ++i) {
498 FontFamily* family = vendorFonts[i];
499 int order = family->fOrder;
501 if (currentOrder < 0) {
502 // Default case - just add it to the end of the fallback list
503 *fallbackFonts.append() = family;
505 // no order specified on this font, but we're incrementing the order
506 // based on an earlier order insertion request
507 *fallbackFonts.insert(currentOrder++) = family;
510 // Add the font into the fallback list in the specified order. Set
511 // currentOrder for correct placement of other fonts in the vendor list.
512 *fallbackFonts.insert(order) = family;
513 currentOrder = order + 1;
519 * Loads data on font families from various expected configuration files. The
520 * resulting data is returned in the given fontFamilies array.
522 void SkFontConfigParser::GetFontFamilies(SkTDArray<FontFamily*> &fontFamilies) {
524 getSystemFontFamilies(fontFamilies);
526 // Append all the fallback fonts to system fonts
527 SkTDArray<FontFamily*> fallbackFonts;
528 getFallbackFontFamilies(fallbackFonts);
529 for (int i = 0; i < fallbackFonts.count(); ++i) {
530 fallbackFonts[i]->fIsFallbackFont = true;
531 *fontFamilies.append() = fallbackFonts[i];
535 void SkFontConfigParser::GetTestFontFamilies(SkTDArray<FontFamily*> &fontFamilies,
536 const char* testMainConfigFile,
537 const char* testFallbackConfigFile) {
538 parseConfigFile(testMainConfigFile, fontFamilies);
540 SkTDArray<FontFamily*> fallbackFonts;
541 if (testFallbackConfigFile) {
542 parseConfigFile(testFallbackConfigFile, fallbackFonts);
545 // Append all fallback fonts to system fonts
546 for (int i = 0; i < fallbackFonts.count(); ++i) {
547 fallbackFonts[i]->fIsFallbackFont = true;
548 *fontFamilies.append() = fallbackFonts[i];
552 SkLanguage SkLanguage::getParent() const {
553 SkASSERT(!fTag.isEmpty());
554 const char* tag = fTag.c_str();
556 // strip off the rightmost "-.*"
557 const char* parentTagEnd = strrchr(tag, '-');
558 if (parentTagEnd == NULL) {
561 size_t parentTagLen = parentTagEnd - tag;
562 return SkLanguage(tag, parentTagLen);