Home | History | Annotate | Download | only in ports
      1 /*
      2  * Copyright 2011 The Android Open Source Project
      3  *
      4  * Use of this source code is governed by a BSD-style license that can be
      5  * found in the LICENSE file.
      6  */
      7 
      8 #include "SkFontConfigParser_android.h"
      9 #include "SkTDArray.h"
     10 #include "SkTSearch.h"
     11 #include "SkTypeface.h"
     12 
     13 #include <expat.h>
     14 #include <dirent.h>
     15 #include <stdio.h>
     16 
     17 #include <limits>
     18 
     19 
     20 
     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
     24 // older filename.
     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"
     29 
     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"
     34 
     35 /**
     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.
     40  */
     41 
     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
     44 // for now.
     45 #define NO_TAG 0
     46 #define NAMESET_TAG 1
     47 #define FILESET_TAG 2
     48 
     49 /**
     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.
     52  */
     53 struct FamilyData {
     54     FamilyData(XML_Parser* parserRef, SkTDArray<FontFamily*> &familiesRef) :
     55         parser(parserRef),
     56         families(familiesRef),
     57         currentFamily(NULL),
     58         currentFontInfo(NULL),
     59         currentTag(NO_TAG) {};
     60 
     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
     66 };
     67 
     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);
     73     T n = 0;
     74     for (; *s; ++s) {
     75         // Check if digit
     76         if (*s < '0' || '9' < *s) {
     77             return false;
     78         }
     79         int d = *s - '0';
     80         // Check for overflow
     81         if (n > nMax || (n == nMax && d > dMax)) {
     82             return false;
     83         }
     84         n = (n * 10) + d;
     85     }
     86     *value = n;
     87     return true;
     88 }
     89 
     90 namespace lmpParser {
     91 
     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
     95     // attributes.
     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;
    115             }
    116         }
    117     }
    118 }
    119 
    120 void fontFileNameHandler(void* data, const char* s, int len) {
    121     FamilyData* familyData = (FamilyData*) data;
    122     familyData->currentFontInfo->fFileName.set(s, len);
    123 }
    124 
    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);
    137                 file->fWeight = 0;
    138             }
    139         }
    140     }
    141     XML_SetCharacterDataHandler(*parser, fontFileNameHandler);
    142 }
    143 
    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())) {
    151                 return candidate;
    152             }
    153         }
    154     }
    155 
    156     return NULL;
    157 }
    158 
    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
    164     //   from a <family>.
    165 
    166     SkString aliasName;
    167     SkString to;
    168     int weight = 0;
    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)) {
    178             to.set(value);
    179         } else if (nameLen == 6 && !strncmp("weight", name, nameLen)) {
    180             parseNonNegativeInteger(value, &weight);
    181         }
    182     }
    183 
    184     // Assumes that the named family is already declared
    185     FontFamily* targetFamily = findFamily(familyData, to.c_str());
    186     if (!targetFamily) {
    187         SkDebugf("---- Font alias target %s (NOT FOUND)", to.c_str());
    188         return;
    189     }
    190 
    191     if (weight) {
    192         FontFamily* family = new FontFamily();
    193         family->fNames.push_back().set(aliasName);
    194 
    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]);
    198             }
    199         }
    200         *familyData->families.append() = family;
    201     } else {
    202         targetFamily->fNames.push_back().set(aliasName);
    203     }
    204 }
    205 
    206 bool findWeight400(FontFamily* family) {
    207     for (int i = 0; i < family->fFonts.count(); i++) {
    208         if (family->fFonts[i].fWeight == 400) {
    209             return true;
    210         }
    211     }
    212     return false;
    213 }
    214 
    215 bool desiredWeight(int weight) {
    216     return (weight == 400 || weight == 700);
    217 }
    218 
    219 int countDesiredWeight(FontFamily* family) {
    220     int count = 0;
    221     for (int i = 0; i < family->fFonts.count(); i++) {
    222         if (desiredWeight(family->fFonts[i].fWeight)) {
    223             count++;
    224         }
    225     }
    226     return count;
    227 }
    228 
    229 // To meet Skia's expectations, any family that contains weight=400
    230 // fonts should *only* contain {400,700}
    231 void purgeUndesiredWeights(FontFamily* family) {
    232     int count = countDesiredWeight(family);
    233     for (int i = 1, j = 0; i < family->fFonts.count(); i++) {
    234         if (desiredWeight(family->fFonts[j].fWeight)) {
    235             j++;
    236         }
    237         if ((i != j) && desiredWeight(family->fFonts[i].fWeight)) {
    238             family->fFonts[j] = family->fFonts[i];
    239         }
    240     }
    241     family->fFonts.resize_back(count);
    242 }
    243 
    244 void familysetElementEndHandler(FamilyData* familyData) {
    245     for (int i = 0; i < familyData->families.count(); i++) {
    246         if (findWeight400(familyData->families[i])) {
    247             purgeUndesiredWeights(familyData->families[i]);
    248         }
    249     }
    250 }
    251 
    252 void startElementHandler(void* data, const char* tag,
    253                          const char** attributes) {
    254     FamilyData* familyData = (FamilyData*) data;
    255     size_t len = strlen(tag);
    256     if (len == 6 && !strncmp(tag, "family", len)) {
    257         familyData->currentFamily = new FontFamily();
    258         familyElementHandler(familyData->currentFamily, attributes);
    259     } else if (len == 4 && !strncmp(tag, "font", len)) {
    260         FontFileInfo* file = &familyData->currentFamily->fFonts.push_back();
    261         familyData->currentFontInfo = file;
    262         fontElementHandler(familyData->parser, file, attributes);
    263     } else if (len == 5 && !strncmp(tag, "alias", len)) {
    264         aliasElementHandler(familyData, attributes);
    265     }
    266 }
    267 
    268 void endElementHandler(void* data, const char* tag) {
    269     FamilyData* familyData = (FamilyData*) data;
    270     size_t len = strlen(tag);
    271     if (len == 9 && strncmp(tag, "familyset", len) == 0) {
    272         familysetElementEndHandler(familyData);
    273     } else if (len == 6 && strncmp(tag, "family", len) == 0) {
    274         *familyData->families.append() = familyData->currentFamily;
    275         familyData->currentFamily = NULL;
    276     } else if (len == 4 && !strncmp(tag, "font", len)) {
    277         XML_SetCharacterDataHandler(*familyData->parser, NULL);
    278     }
    279 }
    280 
    281 } // lmpParser
    282 
    283 namespace jbParser {
    284 
    285 /**
    286  * Handler for arbitrary text. This is used to parse the text inside each name
    287  * or file tag. The resulting strings are put into the fNames or FontFileInfo arrays.
    288  */
    289 static void textHandler(void* data, const char* s, int len) {
    290     FamilyData* familyData = (FamilyData*) data;
    291     // Make sure we're in the right state to store this name information
    292     if (familyData->currentFamily &&
    293             (familyData->currentTag == NAMESET_TAG || familyData->currentTag == FILESET_TAG)) {
    294         switch (familyData->currentTag) {
    295         case NAMESET_TAG: {
    296             SkAutoAsciiToLC tolc(s, len);
    297             familyData->currentFamily->fNames.push_back().set(tolc.lc(), len);
    298             break;
    299         }
    300         case FILESET_TAG:
    301             if (familyData->currentFontInfo) {
    302                 familyData->currentFontInfo->fFileName.set(s, len);
    303             }
    304             break;
    305         default:
    306             // Noop - don't care about any text that's not in the Fonts or Names list
    307             break;
    308         }
    309     }
    310 }
    311 
    312 /**
    313  * Handler for font files. This processes the attributes for language and
    314  * variants then lets textHandler handle the actual file name
    315  */
    316 static void fontFileElementHandler(FamilyData* familyData, const char** attributes) {
    317     FontFileInfo& newFileInfo = familyData->currentFamily->fFonts.push_back();
    318     if (attributes) {
    319         size_t currentAttributeIndex = 0;
    320         while (attributes[currentAttributeIndex] &&
    321                attributes[currentAttributeIndex + 1]) {
    322             const char* attributeName = attributes[currentAttributeIndex];
    323             const char* attributeValue = attributes[currentAttributeIndex+1];
    324             size_t nameLength = strlen(attributeName);
    325             size_t valueLength = strlen(attributeValue);
    326             if (nameLength == 7 && strncmp(attributeName, "variant", nameLength) == 0) {
    327                 const FontVariant prevVariant = familyData->currentFamily->fVariant;
    328                 if (valueLength == 7 && strncmp(attributeValue, "elegant", valueLength) == 0) {
    329                     familyData->currentFamily->fVariant = kElegant_FontVariant;
    330                 } else if (valueLength == 7 &&
    331                            strncmp(attributeValue, "compact", valueLength) == 0) {
    332                     familyData->currentFamily->fVariant = kCompact_FontVariant;
    333                 }
    334                 if (familyData->currentFamily->fFonts.count() > 1 &&
    335                         familyData->currentFamily->fVariant != prevVariant) {
    336                     SkDebugf("Every font file within a family must have identical variants");
    337                     sk_throw();
    338                 }
    339 
    340             } else if (nameLength == 4 && strncmp(attributeName, "lang", nameLength) == 0) {
    341                 SkLanguage prevLang = familyData->currentFamily->fLanguage;
    342                 familyData->currentFamily->fLanguage = SkLanguage(attributeValue);
    343                 if (familyData->currentFamily->fFonts.count() > 1 &&
    344                         familyData->currentFamily->fLanguage != prevLang) {
    345                     SkDebugf("Every font file within a family must have identical languages");
    346                     sk_throw();
    347                 }
    348             } else if (nameLength == 5 && strncmp(attributeName, "index", nameLength) == 0) {
    349                 int value;
    350                 if (parseNonNegativeInteger(attributeValue, &value)) {
    351                     newFileInfo.fIndex = value;
    352                 } else {
    353                     SkDebugf("---- SystemFonts index=%s (INVALID)", attributeValue);
    354                 }
    355             }
    356             //each element is a pair of attributeName/attributeValue string pairs
    357             currentAttributeIndex += 2;
    358         }
    359     }
    360     familyData->currentFontInfo = &newFileInfo;
    361     XML_SetCharacterDataHandler(*familyData->parser, textHandler);
    362 }
    363 
    364 /**
    365  * Handler for the start of a tag. The only tags we expect are familyset, family,
    366  * nameset, fileset, name, and file.
    367  */
    368 static void startElementHandler(void* data, const char* tag, const char** atts) {
    369     FamilyData* familyData = (FamilyData*) data;
    370     size_t len = strlen(tag);
    371     if (len == 9 && strncmp(tag, "familyset", len) == 0) {
    372         // The familyset tag has an optional "version" attribute with an integer value >= 0
    373         for (size_t i = 0; atts[i] != NULL &&
    374                            atts[i+1] != NULL; i += 2) {
    375             size_t nameLen = strlen(atts[i]);
    376             if (nameLen == 7 && strncmp(atts[i], "version", nameLen)) continue;
    377             const char* valueString = atts[i+1];
    378             int version;
    379             if (parseNonNegativeInteger(valueString, &version) && (version >= 21)) {
    380                 XML_SetElementHandler(*familyData->parser,
    381                                       lmpParser::startElementHandler,
    382                                       lmpParser::endElementHandler);
    383             }
    384         }
    385     } else if (len == 6 && strncmp(tag, "family", len) == 0) {
    386         familyData->currentFamily = new FontFamily();
    387         // The Family tag has an optional "order" attribute with an integer value >= 0
    388         // If this attribute does not exist, the default value is -1
    389         for (size_t i = 0; atts[i] != NULL &&
    390                            atts[i+1] != NULL; i += 2) {
    391             const char* valueString = atts[i+1];
    392             int value;
    393             if (parseNonNegativeInteger(valueString, &value)) {
    394                 familyData->currentFamily->fOrder = value;
    395             }
    396         }
    397     } else if (len == 7 && strncmp(tag, "nameset", len) == 0) {
    398         familyData->currentTag = NAMESET_TAG;
    399     } else if (len == 7 && strncmp(tag, "fileset", len) == 0) {
    400         familyData->currentTag = FILESET_TAG;
    401     } else if (len == 4 && strncmp(tag, "name", len) == 0 && familyData->currentTag == NAMESET_TAG) {
    402         // If it's a Name, parse the text inside
    403         XML_SetCharacterDataHandler(*familyData->parser, textHandler);
    404     } else if (len == 4 && strncmp(tag, "file", len) == 0 && familyData->currentTag == FILESET_TAG) {
    405         // If it's a file, parse the attributes, then parse the text inside
    406         fontFileElementHandler(familyData, atts);
    407     }
    408 }
    409 
    410 /**
    411  * Handler for the end of tags. We only care about family, nameset, fileset,
    412  * name, and file.
    413  */
    414 static void endElementHandler(void* data, const char* tag) {
    415     FamilyData* familyData = (FamilyData*) data;
    416     size_t len = strlen(tag);
    417     if (len == 6 && strncmp(tag, "family", len)== 0) {
    418         // Done parsing a Family - store the created currentFamily in the families array
    419         *familyData->families.append() = familyData->currentFamily;
    420         familyData->currentFamily = NULL;
    421     } else if (len == 7 && strncmp(tag, "nameset", len) == 0) {
    422         familyData->currentTag = NO_TAG;
    423     } else if (len == 7 && strncmp(tag, "fileset", len) == 0) {
    424         familyData->currentTag = NO_TAG;
    425     } else if ((len == 4 &&
    426                 strncmp(tag, "name", len) == 0 &&
    427                 familyData->currentTag == NAMESET_TAG) ||
    428                (len == 4 &&
    429                 strncmp(tag, "file", len) == 0 &&
    430                 familyData->currentTag == FILESET_TAG)) {
    431         // Disable the arbitrary text handler installed to load Name data
    432         XML_SetCharacterDataHandler(*familyData->parser, NULL);
    433     }
    434 }
    435 
    436 } // namespace jbParser
    437 
    438 /**
    439  * This function parses the given filename and stores the results in the given
    440  * families array.
    441  */
    442 static void parseConfigFile(const char* filename, SkTDArray<FontFamily*> &families) {
    443 
    444     FILE* file = fopen(filename, "r");
    445 
    446     // Some of the files we attempt to parse (in particular, /vendor/etc/fallback_fonts.xml)
    447     // are optional - failure here is okay because one of these optional files may not exist.
    448     if (NULL == file) {
    449         return;
    450     }
    451 
    452     XML_Parser parser = XML_ParserCreate(NULL);
    453     FamilyData* familyData = new FamilyData(&parser, families);
    454     XML_SetUserData(parser, familyData);
    455     // Start parsing oldschool; switch these in flight if we detect a newer version of the file.
    456     XML_SetElementHandler(parser, jbParser::startElementHandler, jbParser::endElementHandler);
    457 
    458     char buffer[512];
    459     bool done = false;
    460     while (!done) {
    461         fgets(buffer, sizeof(buffer), file);
    462         size_t len = strlen(buffer);
    463         if (feof(file) != 0) {
    464             done = true;
    465         }
    466         XML_Parse(parser, buffer, len, done);
    467     }
    468     XML_ParserFree(parser);
    469     fclose(file);
    470 }
    471 
    472 static void getSystemFontFamilies(SkTDArray<FontFamily*> &fontFamilies) {
    473     int initialCount = fontFamilies.count();
    474     parseConfigFile(LMP_SYSTEM_FONTS_FILE, fontFamilies);
    475 
    476     if (initialCount == fontFamilies.count()) {
    477         parseConfigFile(OLD_SYSTEM_FONTS_FILE, fontFamilies);
    478     }
    479 }
    480 
    481 /**
    482  * In some versions of Android prior to Android 4.2 (JellyBean MR1 at API
    483  * Level 17) the fallback fonts for certain locales were encoded in their own
    484  * XML files with a suffix that identified the locale.  We search the provided
    485  * directory for those files,add all of their entries to the fallback chain, and
    486  * include the locale as part of each entry.
    487  */
    488 static void getFallbackFontFamiliesForLocale(SkTDArray<FontFamily*> &fallbackFonts, const char* dir) {
    489 #if defined(SK_BUILD_FOR_ANDROID_FRAMEWORK)
    490     // The framework is beyond Android 4.2 and can therefore skip this function
    491     return;
    492 #endif
    493 
    494     DIR* fontDirectory = opendir(dir);
    495     if (fontDirectory != NULL){
    496         struct dirent* dirEntry = readdir(fontDirectory);
    497         while (dirEntry) {
    498 
    499             // The size of both the prefix, suffix, and a minimum valid language code
    500             static const size_t minSize = strlen(LOCALE_FALLBACK_FONTS_PREFIX) +
    501                                           strlen(LOCALE_FALLBACK_FONTS_SUFFIX) + 2;
    502 
    503             SkString fileName(dirEntry->d_name);
    504             if (fileName.size() >= minSize &&
    505                     fileName.startsWith(LOCALE_FALLBACK_FONTS_PREFIX) &&
    506                     fileName.endsWith(LOCALE_FALLBACK_FONTS_SUFFIX)) {
    507 
    508                 static const size_t fixedLen = strlen(LOCALE_FALLBACK_FONTS_PREFIX) -
    509                                                strlen(LOCALE_FALLBACK_FONTS_SUFFIX);
    510 
    511                 SkString locale(fileName.c_str() - strlen(LOCALE_FALLBACK_FONTS_PREFIX),
    512                                 fileName.size() - fixedLen);
    513 
    514                 SkString absoluteFilename;
    515                 absoluteFilename.printf("%s/%s", dir, fileName.c_str());
    516 
    517                 SkTDArray<FontFamily*> langSpecificFonts;
    518                 parseConfigFile(absoluteFilename.c_str(), langSpecificFonts);
    519 
    520                 for (int i = 0; i < langSpecificFonts.count(); ++i) {
    521                     FontFamily* family = langSpecificFonts[i];
    522                     family->fLanguage = SkLanguage(locale);
    523                     *fallbackFonts.append() = family;
    524                 }
    525             }
    526 
    527             // proceed to the next entry in the directory
    528             dirEntry = readdir(fontDirectory);
    529         }
    530         // cleanup the directory reference
    531         closedir(fontDirectory);
    532     }
    533 }
    534 
    535 static void getFallbackFontFamilies(SkTDArray<FontFamily*> &fallbackFonts) {
    536     SkTDArray<FontFamily*> vendorFonts;
    537     parseConfigFile(FALLBACK_FONTS_FILE, fallbackFonts);
    538     parseConfigFile(VENDOR_FONTS_FILE, vendorFonts);
    539 
    540     getFallbackFontFamiliesForLocale(fallbackFonts, LOCALE_FALLBACK_FONTS_SYSTEM_DIR);
    541     getFallbackFontFamiliesForLocale(vendorFonts, LOCALE_FALLBACK_FONTS_VENDOR_DIR);
    542 
    543     // This loop inserts the vendor fallback fonts in the correct order in the
    544     // overall fallbacks list.
    545     int currentOrder = -1;
    546     for (int i = 0; i < vendorFonts.count(); ++i) {
    547         FontFamily* family = vendorFonts[i];
    548         int order = family->fOrder;
    549         if (order < 0) {
    550             if (currentOrder < 0) {
    551                 // Default case - just add it to the end of the fallback list
    552                 *fallbackFonts.append() = family;
    553             } else {
    554                 // no order specified on this font, but we're incrementing the order
    555                 // based on an earlier order insertion request
    556                 *fallbackFonts.insert(currentOrder++) = family;
    557             }
    558         } else {
    559             // Add the font into the fallback list in the specified order. Set
    560             // currentOrder for correct placement of other fonts in the vendor list.
    561             *fallbackFonts.insert(order) = family;
    562             currentOrder = order + 1;
    563         }
    564     }
    565 }
    566 
    567 /**
    568  * Loads data on font families from various expected configuration files. The
    569  * resulting data is returned in the given fontFamilies array.
    570  */
    571 void SkFontConfigParser::GetFontFamilies(SkTDArray<FontFamily*> &fontFamilies) {
    572 
    573     getSystemFontFamilies(fontFamilies);
    574 
    575     // Append all the fallback fonts to system fonts
    576     SkTDArray<FontFamily*> fallbackFonts;
    577     getFallbackFontFamilies(fallbackFonts);
    578     for (int i = 0; i < fallbackFonts.count(); ++i) {
    579         fallbackFonts[i]->fIsFallbackFont = true;
    580         *fontFamilies.append() = fallbackFonts[i];
    581     }
    582 }
    583 
    584 void SkFontConfigParser::GetTestFontFamilies(SkTDArray<FontFamily*> &fontFamilies,
    585                                              const char* testMainConfigFile,
    586                                              const char* testFallbackConfigFile) {
    587     parseConfigFile(testMainConfigFile, fontFamilies);
    588 
    589     SkTDArray<FontFamily*> fallbackFonts;
    590     if (testFallbackConfigFile) {
    591         parseConfigFile(testFallbackConfigFile, fallbackFonts);
    592     }
    593 
    594     // Append all fallback fonts to system fonts
    595     for (int i = 0; i < fallbackFonts.count(); ++i) {
    596         fallbackFonts[i]->fIsFallbackFont = true;
    597         *fontFamilies.append() = fallbackFonts[i];
    598     }
    599 }
    600 
    601 SkLanguage SkLanguage::getParent() const {
    602     SkASSERT(!fTag.isEmpty());
    603     const char* tag = fTag.c_str();
    604 
    605     // strip off the rightmost "-.*"
    606     const char* parentTagEnd = strrchr(tag, '-');
    607     if (parentTagEnd == NULL) {
    608         return SkLanguage();
    609     }
    610     size_t parentTagLen = parentTagEnd - tag;
    611     return SkLanguage(tag, parentTagLen);
    612 }
    613