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 "SkTypeface.h"
     11 
     12 #include <expat.h>
     13 #include <stdio.h>
     14 #include <sys/system_properties.h>
     15 
     16 #define SYSTEM_FONTS_FILE "/system/etc/system_fonts.xml"
     17 #define FALLBACK_FONTS_FILE "/system/etc/fallback_fonts.xml"
     18 #define VENDOR_FONTS_FILE "/vendor/etc/fallback_fonts.xml"
     19 
     20 // These defines are used to determine the kind of tag that we're currently
     21 // populating with data. We only care about the sibling tags nameset and fileset
     22 // for now.
     23 #define NO_TAG 0
     24 #define NAMESET_TAG 1
     25 #define FILESET_TAG 2
     26 
     27 /**
     28  * The FamilyData structure is passed around by the parser so that each handler
     29  * can read these variables that are relevant to the current parsing.
     30  */
     31 struct FamilyData {
     32     FamilyData(XML_Parser *parserRef, SkTDArray<FontFamily*> &familiesRef) :
     33         parser(parserRef),
     34         families(familiesRef),
     35         currentFamily(NULL),
     36         currentFontInfo(NULL),
     37         currentTag(NO_TAG) {};
     38 
     39     XML_Parser *parser;                // The expat parser doing the work
     40     SkTDArray<FontFamily*> &families;  // The array that each family is put into as it is parsed
     41     FontFamily *currentFamily;         // The current family being created
     42     FontFileInfo *currentFontInfo;     // The current fontInfo being created
     43     int currentTag;                    // A flag to indicate whether we're in nameset/fileset tags
     44 };
     45 
     46 /**
     47  * Handler for arbitrary text. This is used to parse the text inside each name
     48  * or file tag. The resulting strings are put into the fNames or FontFileInfo arrays.
     49  */
     50 static void textHandler(void *data, const char *s, int len) {
     51     FamilyData *familyData = (FamilyData*) data;
     52     // Make sure we're in the right state to store this name information
     53     if (familyData->currentFamily &&
     54             (familyData->currentTag == NAMESET_TAG || familyData->currentTag == FILESET_TAG)) {
     55         switch (familyData->currentTag) {
     56         case NAMESET_TAG:
     57             familyData->currentFamily->fNames.push_back().set(s, len);
     58             break;
     59         case FILESET_TAG:
     60             if (familyData->currentFontInfo) {
     61                 familyData->currentFontInfo->fFileName.set(s, len);
     62             }
     63             break;
     64         default:
     65             // Noop - don't care about any text that's not in the Fonts or Names list
     66             break;
     67         }
     68     }
     69 }
     70 
     71 /**
     72  * Handler for font files. This processes the attributes for language and
     73  * variants then lets textHandler handle the actual file name
     74  */
     75 static void fontFileElementHandler(FamilyData *familyData, const char **attributes) {
     76 
     77     FontFileInfo& newFileInfo = familyData->currentFamily->fFontFiles.push_back();
     78     if (attributes) {
     79         int currentAttributeIndex = 0;
     80         while (attributes[currentAttributeIndex]) {
     81             const char* attributeName = attributes[currentAttributeIndex];
     82             const char* attributeValue = attributes[currentAttributeIndex+1];
     83             int nameLength = strlen(attributeName);
     84             int valueLength = strlen(attributeValue);
     85             if (strncmp(attributeName, "variant", nameLength) == 0) {
     86                 if (strncmp(attributeValue, "elegant", valueLength) == 0) {
     87                     newFileInfo.fPaintOptions.setFontVariant(SkPaintOptionsAndroid::kElegant_Variant);
     88                 } else if (strncmp(attributeValue, "compact", valueLength) == 0) {
     89                     newFileInfo.fPaintOptions.setFontVariant(SkPaintOptionsAndroid::kCompact_Variant);
     90                 }
     91             } else if (strncmp(attributeName, "lang", nameLength) == 0) {
     92                 newFileInfo.fPaintOptions.setLanguage(attributeValue);
     93             }
     94             //each element is a pair of attributeName/attributeValue string pairs
     95             currentAttributeIndex += 2;
     96         }
     97     }
     98     familyData->currentFontInfo = &newFileInfo;
     99     XML_SetCharacterDataHandler(*familyData->parser, textHandler);
    100 }
    101 
    102 /**
    103  * Handler for the start of a tag. The only tags we expect are family, nameset,
    104  * fileset, name, and file.
    105  */
    106 static void startElementHandler(void *data, const char *tag, const char **atts) {
    107     FamilyData *familyData = (FamilyData*) data;
    108     int len = strlen(tag);
    109     if (strncmp(tag, "family", len)== 0) {
    110         familyData->currentFamily = new FontFamily();
    111         familyData->currentFamily->order = -1;
    112         // The Family tag has an optional "order" attribute with an integer value >= 0
    113         // If this attribute does not exist, the default value is -1
    114         for (int i = 0; atts[i] != NULL; i += 2) {
    115             const char* valueString = atts[i+1];
    116             int value;
    117             int len = sscanf(valueString, "%d", &value);
    118             if (len > 0) {
    119                 familyData->currentFamily->order = value;
    120             }
    121         }
    122     } else if (len == 7 && strncmp(tag, "nameset", len) == 0) {
    123         familyData->currentTag = NAMESET_TAG;
    124     } else if (len == 7 && strncmp(tag, "fileset", len) == 0) {
    125         familyData->currentTag = FILESET_TAG;
    126     } else if (strncmp(tag, "name", len) == 0 && familyData->currentTag == NAMESET_TAG) {
    127         // If it's a Name, parse the text inside
    128         XML_SetCharacterDataHandler(*familyData->parser, textHandler);
    129     } else if (strncmp(tag, "file", len) == 0 && familyData->currentTag == FILESET_TAG) {
    130         // If it's a file, parse the attributes, then parse the text inside
    131         fontFileElementHandler(familyData, atts);
    132     }
    133 }
    134 
    135 /**
    136  * Handler for the end of tags. We only care about family, nameset, fileset,
    137  * name, and file.
    138  */
    139 static void endElementHandler(void *data, const char *tag) {
    140     FamilyData *familyData = (FamilyData*) data;
    141     int len = strlen(tag);
    142     if (strncmp(tag, "family", len)== 0) {
    143         // Done parsing a Family - store the created currentFamily in the families array
    144         *familyData->families.append() = familyData->currentFamily;
    145         familyData->currentFamily = NULL;
    146     } else if (len == 7 && strncmp(tag, "nameset", len) == 0) {
    147         familyData->currentTag = NO_TAG;
    148     } else if (len == 7 && strncmp(tag, "fileset", len) == 0) {
    149         familyData->currentTag = NO_TAG;
    150     } else if ((strncmp(tag, "name", len) == 0 && familyData->currentTag == NAMESET_TAG) ||
    151             (strncmp(tag, "file", len) == 0 && familyData->currentTag == FILESET_TAG)) {
    152         // Disable the arbitrary text handler installed to load Name data
    153         XML_SetCharacterDataHandler(*familyData->parser, NULL);
    154     }
    155 }
    156 
    157 /**
    158  * This function parses the given filename and stores the results in the given
    159  * families array.
    160  */
    161 static void parseConfigFile(const char *filename, SkTDArray<FontFamily*> &families) {
    162 
    163     FILE* file = NULL;
    164 
    165 #if !defined(SK_BUILD_FOR_ANDROID_FRAMEWORK)
    166     // if we are using a version of Android prior to Android 4.2 (JellyBean MR1
    167     // at API Level 17) then we need to look for files with a different suffix.
    168     char sdkVersion[PROP_VALUE_MAX];
    169     __system_property_get("ro.build.version.sdk", sdkVersion);
    170     const int sdkVersionInt = atoi(sdkVersion);
    171 
    172     if (0 != *sdkVersion && sdkVersionInt < 17) {
    173         SkString basename;
    174         SkString updatedFilename;
    175         SkString locale = SkFontConfigParser::GetLocale();
    176 
    177         basename.set(filename);
    178         // Remove the .xml suffix. We'll add it back in a moment.
    179         if (basename.endsWith(".xml")) {
    180             basename.resize(basename.size()-4);
    181         }
    182         // Try first with language and region
    183         updatedFilename.printf("%s-%s.xml", basename.c_str(), locale.c_str());
    184         file = fopen(updatedFilename.c_str(), "r");
    185         if (!file) {
    186             // If not found, try next with just language
    187             updatedFilename.printf("%s-%.2s.xml", basename.c_str(), locale.c_str());
    188             file = fopen(updatedFilename.c_str(), "r");
    189         }
    190     }
    191 #endif
    192 
    193     if (NULL == file) {
    194         file = fopen(filename, "r");
    195     }
    196 
    197     // Some of the files we attempt to parse (in particular, /vendor/etc/fallback_fonts.xml)
    198     // are optional - failure here is okay because one of these optional files may not exist.
    199     if (NULL == file) {
    200         return;
    201     }
    202 
    203     XML_Parser parser = XML_ParserCreate(NULL);
    204     FamilyData *familyData = new FamilyData(&parser, families);
    205     XML_SetUserData(parser, familyData);
    206     XML_SetElementHandler(parser, startElementHandler, endElementHandler);
    207 
    208     char buffer[512];
    209     bool done = false;
    210     while (!done) {
    211         fgets(buffer, sizeof(buffer), file);
    212         int len = strlen(buffer);
    213         if (feof(file) != 0) {
    214             done = true;
    215         }
    216         XML_Parse(parser, buffer, len, done);
    217     }
    218     XML_ParserFree(parser);
    219     fclose(file);
    220 }
    221 
    222 static void getSystemFontFamilies(SkTDArray<FontFamily*> &fontFamilies) {
    223     parseConfigFile(SYSTEM_FONTS_FILE, fontFamilies);
    224 }
    225 
    226 static void getFallbackFontFamilies(SkTDArray<FontFamily*> &fallbackFonts) {
    227     SkTDArray<FontFamily*> vendorFonts;
    228     parseConfigFile(FALLBACK_FONTS_FILE, fallbackFonts);
    229     parseConfigFile(VENDOR_FONTS_FILE, vendorFonts);
    230 
    231     // This loop inserts the vendor fallback fonts in the correct order in the
    232     // overall fallbacks list.
    233     int currentOrder = -1;
    234     for (int i = 0; i < vendorFonts.count(); ++i) {
    235         FontFamily* family = vendorFonts[i];
    236         int order = family->order;
    237         if (order < 0) {
    238             if (currentOrder < 0) {
    239                 // Default case - just add it to the end of the fallback list
    240                 *fallbackFonts.append() = family;
    241             } else {
    242                 // no order specified on this font, but we're incrementing the order
    243                 // based on an earlier order insertion request
    244                 *fallbackFonts.insert(currentOrder++) = family;
    245             }
    246         } else {
    247             // Add the font into the fallback list in the specified order. Set
    248             // currentOrder for correct placement of other fonts in the vendor list.
    249             *fallbackFonts.insert(order) = family;
    250             currentOrder = order + 1;
    251         }
    252     }
    253 }
    254 
    255 /**
    256  * Loads data on font families from various expected configuration files. The
    257  * resulting data is returned in the given fontFamilies array.
    258  */
    259 void SkFontConfigParser::GetFontFamilies(SkTDArray<FontFamily*> &fontFamilies) {
    260 
    261     getSystemFontFamilies(fontFamilies);
    262 
    263     // Append all the fallback fonts to system fonts
    264     SkTDArray<FontFamily*> fallbackFonts;
    265     getFallbackFontFamilies(fallbackFonts);
    266     for (int i = 0; i < fallbackFonts.count(); ++i) {
    267         fallbackFonts[i]->fIsFallbackFont = true;
    268         *fontFamilies.append() = fallbackFonts[i];
    269     }
    270 }
    271 
    272 void SkFontConfigParser::GetTestFontFamilies(SkTDArray<FontFamily*> &fontFamilies,
    273                                              const char* testMainConfigFile,
    274                                              const char* testFallbackConfigFile) {
    275     parseConfigFile(testMainConfigFile, fontFamilies);
    276 
    277     SkTDArray<FontFamily*> fallbackFonts;
    278     parseConfigFile(testFallbackConfigFile, fallbackFonts);
    279 
    280     // Append all fallback fonts to system fonts
    281     for (int i = 0; i < fallbackFonts.count(); ++i) {
    282         fallbackFonts[i]->fIsFallbackFont = true;
    283         *fontFamilies.append() = fallbackFonts[i];
    284     }
    285 }
    286 
    287 /**
    288  * Read the persistent locale.
    289  */
    290 SkString SkFontConfigParser::GetLocale()
    291 {
    292     char propLang[PROP_VALUE_MAX], propRegn[PROP_VALUE_MAX];
    293     __system_property_get("persist.sys.language", propLang);
    294     __system_property_get("persist.sys.country", propRegn);
    295 
    296     if (*propLang == 0 && *propRegn == 0) {
    297         /* Set to ro properties, default is en_US */
    298         __system_property_get("ro.product.locale.language", propLang);
    299         __system_property_get("ro.product.locale.region", propRegn);
    300         if (*propLang == 0 && *propRegn == 0) {
    301             strcpy(propLang, "en");
    302             strcpy(propRegn, "US");
    303         }
    304     }
    305 
    306     SkString locale(6);
    307     char* localeCStr = locale.writable_str();
    308 
    309     strncpy(localeCStr, propLang, 2);
    310     localeCStr[2] = '-';
    311     strncpy(&localeCStr[3], propRegn, 2);
    312     localeCStr[5] = '\0';
    313 
    314     return locale;
    315 }
    316