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 "FontHostConfiguration_android.h"
      9 #include "SkLanguage.h"
     10 #include "SkTDArray.h"
     11 #include "SkTypeface.h"
     12 #include <expat.h>
     13 #if !defined(SK_BUILD_FOR_ANDROID_NDK)
     14     #include <cutils/properties.h>
     15 #endif
     16 
     17 #define SYSTEM_FONTS_FILE "/system/etc/system_fonts.xml"
     18 #define FALLBACK_FONTS_FILE "/system/etc/fallback_fonts.xml"
     19 #define VENDOR_FONTS_FILE "/vendor/etc/fallback_fonts.xml"
     20 
     21 // These defines are used to determine the kind of tag that we're currently
     22 // populating with data. We only care about the sibling tags nameset and fileset
     23 // for now.
     24 #define NO_TAG 0
     25 #define NAMESET_TAG 1
     26 #define FILESET_TAG 2
     27 
     28 /**
     29  * The FamilyData structure is passed around by the parser so that each handler
     30  * can read these variables that are relevant to the current parsing.
     31  */
     32 struct FamilyData {
     33     FamilyData(XML_Parser *parserRef, SkTDArray<FontFamily*> &familiesRef) :
     34             parser(parserRef), families(familiesRef), currentTag(NO_TAG) {};
     35 
     36     XML_Parser *parser;                // The expat parser doing the work
     37     SkTDArray<FontFamily*> &families;  // The array that each family is put into as it is parsed
     38     FontFamily *currentFamily;         // The current family being created
     39     FontFileInfo *currentFontInfo;     // The current fontInfo being created
     40     int currentTag;                    // A flag to indicate whether we're in nameset/fileset tags
     41 };
     42 
     43 /**
     44  * Handler for arbitrary text. This is used to parse the text inside each name
     45  * or file tag. The resulting strings are put into the fNames or FontFileInfo arrays.
     46  */
     47 void textHandler(void *data, const char *s, int len) {
     48     FamilyData *familyData = (FamilyData*) data;
     49     // Make sure we're in the right state to store this name information
     50     if (familyData->currentFamily &&
     51             (familyData->currentTag == NAMESET_TAG || familyData->currentTag == FILESET_TAG)) {
     52         // Malloc new buffer to store the string
     53         char *buff;
     54         buff = (char*) malloc((len + 1) * sizeof(char));
     55         strncpy(buff, s, len);
     56         buff[len] = '\0';
     57         switch (familyData->currentTag) {
     58         case NAMESET_TAG:
     59             *(familyData->currentFamily->fNames.append()) = buff;
     60             break;
     61         case FILESET_TAG:
     62             if (familyData->currentFontInfo) {
     63                 familyData->currentFontInfo->fFileName = buff;
     64             }
     65             break;
     66         default:
     67             // Noop - don't care about any text that's not in the Fonts or Names list
     68             break;
     69         }
     70     }
     71 }
     72 
     73 /**
     74  * Handler for font files. This processes the attributes for language and
     75  * variants then lets textHandler handle the actual file name
     76  */
     77 void fontFileElementHandler(FamilyData *familyData, const char **attributes) {
     78     FontFileInfo* newFileInfo = new FontFileInfo();
     79     if (attributes) {
     80         int currentAttributeIndex = 0;
     81         while (attributes[currentAttributeIndex]) {
     82             const char* attributeName = attributes[currentAttributeIndex];
     83             const char* attributeValue = attributes[currentAttributeIndex+1];
     84             int nameLength = strlen(attributeName);
     85             int valueLength = strlen(attributeValue);
     86             if (strncmp(attributeName, "variant", nameLength) == 0) {
     87                 if (strncmp(attributeValue, "elegant", valueLength) == 0) {
     88                     newFileInfo->fVariant = SkPaint::kElegant_Variant;
     89                 } else if (strncmp(attributeValue, "compact", valueLength) == 0) {
     90                     newFileInfo->fVariant = SkPaint::kCompact_Variant;
     91                 }
     92             } else if (strncmp(attributeName, "lang", nameLength) == 0) {
     93                 newFileInfo->fLanguage = SkLanguage(attributeValue);
     94             }
     95             //each element is a pair of attributeName/attributeValue string pairs
     96             currentAttributeIndex += 2;
     97         }
     98     }
     99     *(familyData->currentFamily->fFontFileArray.append()) = newFileInfo;
    100     familyData->currentFontInfo = newFileInfo;
    101     XML_SetCharacterDataHandler(*familyData->parser, textHandler);
    102 }
    103 
    104 /**
    105  * Handler for the start of a tag. The only tags we expect are family, nameset,
    106  * fileset, name, and file.
    107  */
    108 void startElementHandler(void *data, const char *tag, const char **atts) {
    109     FamilyData *familyData = (FamilyData*) data;
    110     int len = strlen(tag);
    111     if (strncmp(tag, "family", len)== 0) {
    112         familyData->currentFamily = new FontFamily();
    113         familyData->currentFamily->order = -1;
    114         // The Family tag has an optional "order" attribute with an integer value >= 0
    115         // If this attribute does not exist, the default value is -1
    116         for (int i = 0; atts[i] != NULL; i += 2) {
    117             const char* attribute = atts[i];
    118             const char* valueString = atts[i+1];
    119             int value;
    120             int len = sscanf(valueString, "%d", &value);
    121             if (len > 0) {
    122                 familyData->currentFamily->order = value;
    123             }
    124         }
    125     } else if (len == 7 && strncmp(tag, "nameset", len) == 0) {
    126         familyData->currentTag = NAMESET_TAG;
    127     } else if (len == 7 && strncmp(tag, "fileset", len) == 0) {
    128         familyData->currentTag = FILESET_TAG;
    129     } else if (strncmp(tag, "name", len) == 0 && familyData->currentTag == NAMESET_TAG) {
    130         // If it's a Name, parse the text inside
    131         XML_SetCharacterDataHandler(*familyData->parser, textHandler);
    132     } else if (strncmp(tag, "file", len) == 0 && familyData->currentTag == FILESET_TAG) {
    133         // If it's a file, parse the attributes, then parse the text inside
    134         fontFileElementHandler(familyData, atts);
    135     }
    136 }
    137 
    138 /**
    139  * Handler for the end of tags. We only care about family, nameset, fileset,
    140  * name, and file.
    141  */
    142 void endElementHandler(void *data, const char *tag) {
    143     FamilyData *familyData = (FamilyData*) data;
    144     int len = strlen(tag);
    145     if (strncmp(tag, "family", len)== 0) {
    146         // Done parsing a Family - store the created currentFamily in the families array
    147         *familyData->families.append() = familyData->currentFamily;
    148         familyData->currentFamily = NULL;
    149     } else if (len == 7 && strncmp(tag, "nameset", len) == 0) {
    150         familyData->currentTag = NO_TAG;
    151     } else if (len == 7 && strncmp(tag, "fileset", len) == 0) {
    152         familyData->currentTag = NO_TAG;
    153     } else if ((strncmp(tag, "name", len) == 0 && familyData->currentTag == NAMESET_TAG) ||
    154             (strncmp(tag, "file", len) == 0 && familyData->currentTag == FILESET_TAG)) {
    155         // Disable the arbitrary text handler installed to load Name data
    156         XML_SetCharacterDataHandler(*familyData->parser, NULL);
    157     }
    158 }
    159 
    160 /**
    161  * This function parses the given filename and stores the results in the given
    162  * families array.
    163  */
    164 void parseConfigFile(const char *filename, SkTDArray<FontFamily*> &families) {
    165     XML_Parser parser = XML_ParserCreate(NULL);
    166     FamilyData *familyData = new FamilyData(&parser, families);
    167     XML_SetUserData(parser, familyData);
    168     XML_SetElementHandler(parser, startElementHandler, endElementHandler);
    169     FILE *file = fopen(filename, "r");
    170     // Some of the files we attempt to parse (in particular, /vendor/etc/fallback_fonts.xml)
    171     // are optional - failure here is okay because one of these optional files may not exist.
    172     if (file == NULL) {
    173         return;
    174     }
    175     char buffer[512];
    176     bool done = false;
    177     while (!done) {
    178         fgets(buffer, sizeof(buffer), file);
    179         int len = strlen(buffer);
    180         if (feof(file) != 0) {
    181             done = true;
    182         }
    183         XML_Parse(parser, buffer, len, done);
    184     }
    185     fclose(file);
    186 }
    187 
    188 void getSystemFontFamilies(SkTDArray<FontFamily*> &fontFamilies) {
    189     parseConfigFile(SYSTEM_FONTS_FILE, fontFamilies);
    190 }
    191 
    192 void getFallbackFontFamilies(SkTDArray<FontFamily*> &fallbackFonts) {
    193     SkTDArray<FontFamily*> vendorFonts;
    194     parseConfigFile(FALLBACK_FONTS_FILE, fallbackFonts);
    195     parseConfigFile(VENDOR_FONTS_FILE, vendorFonts);
    196 
    197     // This loop inserts the vendor fallback fonts in the correct order in the
    198     // overall fallbacks list.
    199     int currentOrder = -1;
    200     for (int i = 0; i < vendorFonts.count(); ++i) {
    201         FontFamily* family = vendorFonts[i];
    202         int order = family->order;
    203         if (order < 0) {
    204             if (currentOrder < 0) {
    205                 // Default case - just add it to the end of the fallback list
    206                 *fallbackFonts.append() = family;
    207             } else {
    208                 // no order specified on this font, but we're incrementing the order
    209                 // based on an earlier order insertion request
    210                 *fallbackFonts.insert(currentOrder++) = family;
    211             }
    212         } else {
    213             // Add the font into the fallback list in the specified order. Set
    214             // currentOrder for correct placement of other fonts in the vendor list.
    215             *fallbackFonts.insert(order) = family;
    216             currentOrder = order + 1;
    217         }
    218     }
    219 }
    220 
    221 /**
    222  * Loads data on font families from various expected configuration files. The
    223  * resulting data is returned in the given fontFamilies array.
    224  */
    225 void getFontFamilies(SkTDArray<FontFamily*> &fontFamilies) {
    226     SkTDArray<FontFamily*> fallbackFonts;
    227 
    228     getSystemFontFamilies(fontFamilies);
    229     getFallbackFontFamilies(fallbackFonts);
    230 
    231     // Append all fallback fonts to system fonts
    232     for (int i = 0; i < fallbackFonts.count(); ++i) {
    233         *fontFamilies.append() = fallbackFonts[i];
    234     }
    235 }
    236