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