gfx/skia/trunk/src/ports/SkFontConfigParser_android.cpp

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/gfx/skia/trunk/src/ports/SkFontConfigParser_android.cpp	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,320 @@
     1.4 +/*
     1.5 + * Copyright 2011 The Android Open Source Project
     1.6 + *
     1.7 + * Use of this source code is governed by a BSD-style license that can be
     1.8 + * found in the LICENSE file.
     1.9 + */
    1.10 +
    1.11 +#include "SkFontConfigParser_android.h"
    1.12 +#include "SkTDArray.h"
    1.13 +#include "SkTypeface.h"
    1.14 +
    1.15 +#include <expat.h>
    1.16 +#include <stdio.h>
    1.17 +#include <sys/system_properties.h>
    1.18 +
    1.19 +#define SYSTEM_FONTS_FILE "/system/etc/system_fonts.xml"
    1.20 +#define FALLBACK_FONTS_FILE "/system/etc/fallback_fonts.xml"
    1.21 +#define VENDOR_FONTS_FILE "/vendor/etc/fallback_fonts.xml"
    1.22 +
    1.23 +// These defines are used to determine the kind of tag that we're currently
    1.24 +// populating with data. We only care about the sibling tags nameset and fileset
    1.25 +// for now.
    1.26 +#define NO_TAG 0
    1.27 +#define NAMESET_TAG 1
    1.28 +#define FILESET_TAG 2
    1.29 +
    1.30 +/**
    1.31 + * The FamilyData structure is passed around by the parser so that each handler
    1.32 + * can read these variables that are relevant to the current parsing.
    1.33 + */
    1.34 +struct FamilyData {
    1.35 +    FamilyData(XML_Parser *parserRef, SkTDArray<FontFamily*> &familiesRef) :
    1.36 +        parser(parserRef),
    1.37 +        families(familiesRef),
    1.38 +        currentFamily(NULL),
    1.39 +        currentFontInfo(NULL),
    1.40 +        currentTag(NO_TAG) {};
    1.41 +
    1.42 +    XML_Parser *parser;                // The expat parser doing the work
    1.43 +    SkTDArray<FontFamily*> &families;  // The array that each family is put into as it is parsed
    1.44 +    FontFamily *currentFamily;         // The current family being created
    1.45 +    FontFileInfo *currentFontInfo;     // The current fontInfo being created
    1.46 +    int currentTag;                    // A flag to indicate whether we're in nameset/fileset tags
    1.47 +};
    1.48 +
    1.49 +/**
    1.50 + * Handler for arbitrary text. This is used to parse the text inside each name
    1.51 + * or file tag. The resulting strings are put into the fNames or FontFileInfo arrays.
    1.52 + */
    1.53 +static void textHandler(void *data, const char *s, int len) {
    1.54 +    FamilyData *familyData = (FamilyData*) data;
    1.55 +    // Make sure we're in the right state to store this name information
    1.56 +    if (familyData->currentFamily &&
    1.57 +            (familyData->currentTag == NAMESET_TAG || familyData->currentTag == FILESET_TAG)) {
    1.58 +        // Malloc new buffer to store the string
    1.59 +        char *buff;
    1.60 +        buff = (char*) malloc((len + 1) * sizeof(char));
    1.61 +        strncpy(buff, s, len);
    1.62 +        buff[len] = '\0';
    1.63 +        switch (familyData->currentTag) {
    1.64 +        case NAMESET_TAG:
    1.65 +            *(familyData->currentFamily->fNames.append()) = buff;
    1.66 +            break;
    1.67 +        case FILESET_TAG:
    1.68 +            if (familyData->currentFontInfo) {
    1.69 +                familyData->currentFontInfo->fFileName = buff;
    1.70 +            }
    1.71 +            break;
    1.72 +        default:
    1.73 +            // Noop - don't care about any text that's not in the Fonts or Names list
    1.74 +            break;
    1.75 +        }
    1.76 +    }
    1.77 +}
    1.78 +
    1.79 +/**
    1.80 + * Handler for font files. This processes the attributes for language and
    1.81 + * variants then lets textHandler handle the actual file name
    1.82 + */
    1.83 +static void fontFileElementHandler(FamilyData *familyData, const char **attributes) {
    1.84 +    FontFileInfo* newFileInfo = new FontFileInfo();
    1.85 +    if (attributes) {
    1.86 +        int currentAttributeIndex = 0;
    1.87 +        while (attributes[currentAttributeIndex]) {
    1.88 +            const char* attributeName = attributes[currentAttributeIndex];
    1.89 +            const char* attributeValue = attributes[currentAttributeIndex+1];
    1.90 +            int nameLength = strlen(attributeName);
    1.91 +            int valueLength = strlen(attributeValue);
    1.92 +            if (strncmp(attributeName, "variant", nameLength) == 0) {
    1.93 +                if (strncmp(attributeValue, "elegant", valueLength) == 0) {
    1.94 +                    newFileInfo->fPaintOptions.setFontVariant(SkPaintOptionsAndroid::kElegant_Variant);
    1.95 +                } else if (strncmp(attributeValue, "compact", valueLength) == 0) {
    1.96 +                    newFileInfo->fPaintOptions.setFontVariant(SkPaintOptionsAndroid::kCompact_Variant);
    1.97 +                }
    1.98 +            } else if (strncmp(attributeName, "lang", nameLength) == 0) {
    1.99 +                newFileInfo->fPaintOptions.setLanguage(attributeValue);
   1.100 +            }
   1.101 +            //each element is a pair of attributeName/attributeValue string pairs
   1.102 +            currentAttributeIndex += 2;
   1.103 +        }
   1.104 +    }
   1.105 +    *(familyData->currentFamily->fFontFiles.append()) = newFileInfo;
   1.106 +    familyData->currentFontInfo = newFileInfo;
   1.107 +    XML_SetCharacterDataHandler(*familyData->parser, textHandler);
   1.108 +}
   1.109 +
   1.110 +/**
   1.111 + * Handler for the start of a tag. The only tags we expect are family, nameset,
   1.112 + * fileset, name, and file.
   1.113 + */
   1.114 +static void startElementHandler(void *data, const char *tag, const char **atts) {
   1.115 +    FamilyData *familyData = (FamilyData*) data;
   1.116 +    int len = strlen(tag);
   1.117 +    if (strncmp(tag, "family", len)== 0) {
   1.118 +        familyData->currentFamily = new FontFamily();
   1.119 +        familyData->currentFamily->order = -1;
   1.120 +        // The Family tag has an optional "order" attribute with an integer value >= 0
   1.121 +        // If this attribute does not exist, the default value is -1
   1.122 +        for (int i = 0; atts[i] != NULL; i += 2) {
   1.123 +            const char* valueString = atts[i+1];
   1.124 +            int value;
   1.125 +            int len = sscanf(valueString, "%d", &value);
   1.126 +            if (len > 0) {
   1.127 +                familyData->currentFamily->order = value;
   1.128 +            }
   1.129 +        }
   1.130 +    } else if (len == 7 && strncmp(tag, "nameset", len) == 0) {
   1.131 +        familyData->currentTag = NAMESET_TAG;
   1.132 +    } else if (len == 7 && strncmp(tag, "fileset", len) == 0) {
   1.133 +        familyData->currentTag = FILESET_TAG;
   1.134 +    } else if (strncmp(tag, "name", len) == 0 && familyData->currentTag == NAMESET_TAG) {
   1.135 +        // If it's a Name, parse the text inside
   1.136 +        XML_SetCharacterDataHandler(*familyData->parser, textHandler);
   1.137 +    } else if (strncmp(tag, "file", len) == 0 && familyData->currentTag == FILESET_TAG) {
   1.138 +        // If it's a file, parse the attributes, then parse the text inside
   1.139 +        fontFileElementHandler(familyData, atts);
   1.140 +    }
   1.141 +}
   1.142 +
   1.143 +/**
   1.144 + * Handler for the end of tags. We only care about family, nameset, fileset,
   1.145 + * name, and file.
   1.146 + */
   1.147 +static void endElementHandler(void *data, const char *tag) {
   1.148 +    FamilyData *familyData = (FamilyData*) data;
   1.149 +    int len = strlen(tag);
   1.150 +    if (strncmp(tag, "family", len)== 0) {
   1.151 +        // Done parsing a Family - store the created currentFamily in the families array
   1.152 +        *familyData->families.append() = familyData->currentFamily;
   1.153 +        familyData->currentFamily = NULL;
   1.154 +    } else if (len == 7 && strncmp(tag, "nameset", len) == 0) {
   1.155 +        familyData->currentTag = NO_TAG;
   1.156 +    } else if (len == 7 && strncmp(tag, "fileset", len) == 0) {
   1.157 +        familyData->currentTag = NO_TAG;
   1.158 +    } else if ((strncmp(tag, "name", len) == 0 && familyData->currentTag == NAMESET_TAG) ||
   1.159 +            (strncmp(tag, "file", len) == 0 && familyData->currentTag == FILESET_TAG)) {
   1.160 +        // Disable the arbitrary text handler installed to load Name data
   1.161 +        XML_SetCharacterDataHandler(*familyData->parser, NULL);
   1.162 +    }
   1.163 +}
   1.164 +
   1.165 +/**
   1.166 + * This function parses the given filename and stores the results in the given
   1.167 + * families array.
   1.168 + */
   1.169 +static void parseConfigFile(const char *filename, SkTDArray<FontFamily*> &families) {
   1.170 +
   1.171 +    FILE* file = NULL;
   1.172 +
   1.173 +#if !defined(SK_BUILD_FOR_ANDROID_FRAMEWORK)
   1.174 +    // if we are using a version of Android prior to Android 4.2 (JellyBean MR1
   1.175 +    // at API Level 17) then we need to look for files with a different suffix.
   1.176 +    char sdkVersion[PROP_VALUE_MAX];
   1.177 +    __system_property_get("ro.build.version.sdk", sdkVersion);
   1.178 +    const int sdkVersionInt = atoi(sdkVersion);
   1.179 +
   1.180 +    if (0 != *sdkVersion && sdkVersionInt < 17) {
   1.181 +        SkString basename;
   1.182 +        SkString updatedFilename;
   1.183 +        SkString locale = SkFontConfigParser::GetLocale();
   1.184 +
   1.185 +        basename.set(filename);
   1.186 +        // Remove the .xml suffix. We'll add it back in a moment.
   1.187 +        if (basename.endsWith(".xml")) {
   1.188 +            basename.resize(basename.size()-4);
   1.189 +        }
   1.190 +        // Try first with language and region
   1.191 +        updatedFilename.printf("%s-%s.xml", basename.c_str(), locale.c_str());
   1.192 +        file = fopen(updatedFilename.c_str(), "r");
   1.193 +        if (!file) {
   1.194 +            // If not found, try next with just language
   1.195 +            updatedFilename.printf("%s-%.2s.xml", basename.c_str(), locale.c_str());
   1.196 +            file = fopen(updatedFilename.c_str(), "r");
   1.197 +        }
   1.198 +    }
   1.199 +#endif
   1.200 +
   1.201 +    if (NULL == file) {
   1.202 +        file = fopen(filename, "r");
   1.203 +    }
   1.204 +
   1.205 +    // Some of the files we attempt to parse (in particular, /vendor/etc/fallback_fonts.xml)
   1.206 +    // are optional - failure here is okay because one of these optional files may not exist.
   1.207 +    if (NULL == file) {
   1.208 +        return;
   1.209 +    }
   1.210 +
   1.211 +    XML_Parser parser = XML_ParserCreate(NULL);
   1.212 +    FamilyData *familyData = new FamilyData(&parser, families);
   1.213 +    XML_SetUserData(parser, familyData);
   1.214 +    XML_SetElementHandler(parser, startElementHandler, endElementHandler);
   1.215 +
   1.216 +    char buffer[512];
   1.217 +    bool done = false;
   1.218 +    while (!done) {
   1.219 +        fgets(buffer, sizeof(buffer), file);
   1.220 +        int len = strlen(buffer);
   1.221 +        if (feof(file) != 0) {
   1.222 +            done = true;
   1.223 +        }
   1.224 +        XML_Parse(parser, buffer, len, done);
   1.225 +    }
   1.226 +    XML_ParserFree(parser);
   1.227 +    fclose(file);
   1.228 +}
   1.229 +
   1.230 +static void getSystemFontFamilies(SkTDArray<FontFamily*> &fontFamilies) {
   1.231 +    parseConfigFile(SYSTEM_FONTS_FILE, fontFamilies);
   1.232 +}
   1.233 +
   1.234 +static void getFallbackFontFamilies(SkTDArray<FontFamily*> &fallbackFonts) {
   1.235 +    SkTDArray<FontFamily*> vendorFonts;
   1.236 +    parseConfigFile(FALLBACK_FONTS_FILE, fallbackFonts);
   1.237 +    parseConfigFile(VENDOR_FONTS_FILE, vendorFonts);
   1.238 +
   1.239 +    // This loop inserts the vendor fallback fonts in the correct order in the
   1.240 +    // overall fallbacks list.
   1.241 +    int currentOrder = -1;
   1.242 +    for (int i = 0; i < vendorFonts.count(); ++i) {
   1.243 +        FontFamily* family = vendorFonts[i];
   1.244 +        int order = family->order;
   1.245 +        if (order < 0) {
   1.246 +            if (currentOrder < 0) {
   1.247 +                // Default case - just add it to the end of the fallback list
   1.248 +                *fallbackFonts.append() = family;
   1.249 +            } else {
   1.250 +                // no order specified on this font, but we're incrementing the order
   1.251 +                // based on an earlier order insertion request
   1.252 +                *fallbackFonts.insert(currentOrder++) = family;
   1.253 +            }
   1.254 +        } else {
   1.255 +            // Add the font into the fallback list in the specified order. Set
   1.256 +            // currentOrder for correct placement of other fonts in the vendor list.
   1.257 +            *fallbackFonts.insert(order) = family;
   1.258 +            currentOrder = order + 1;
   1.259 +        }
   1.260 +    }
   1.261 +}
   1.262 +
   1.263 +/**
   1.264 + * Loads data on font families from various expected configuration files. The
   1.265 + * resulting data is returned in the given fontFamilies array.
   1.266 + */
   1.267 +void SkFontConfigParser::GetFontFamilies(SkTDArray<FontFamily*> &fontFamilies) {
   1.268 +
   1.269 +    getSystemFontFamilies(fontFamilies);
   1.270 +
   1.271 +    // Append all the fallback fonts to system fonts
   1.272 +    SkTDArray<FontFamily*> fallbackFonts;
   1.273 +    getFallbackFontFamilies(fallbackFonts);
   1.274 +    for (int i = 0; i < fallbackFonts.count(); ++i) {
   1.275 +        fallbackFonts[i]->fIsFallbackFont = true;
   1.276 +        *fontFamilies.append() = fallbackFonts[i];
   1.277 +    }
   1.278 +}
   1.279 +
   1.280 +void SkFontConfigParser::GetTestFontFamilies(SkTDArray<FontFamily*> &fontFamilies,
   1.281 +                                             const char* testMainConfigFile,
   1.282 +                                             const char* testFallbackConfigFile) {
   1.283 +    parseConfigFile(testMainConfigFile, fontFamilies);
   1.284 +
   1.285 +    SkTDArray<FontFamily*> fallbackFonts;
   1.286 +    parseConfigFile(testFallbackConfigFile, fallbackFonts);
   1.287 +
   1.288 +    // Append all fallback fonts to system fonts
   1.289 +    for (int i = 0; i < fallbackFonts.count(); ++i) {
   1.290 +        fallbackFonts[i]->fIsFallbackFont = true;
   1.291 +        *fontFamilies.append() = fallbackFonts[i];
   1.292 +    }
   1.293 +}
   1.294 +
   1.295 +/**
   1.296 + * Read the persistent locale.
   1.297 + */
   1.298 +SkString SkFontConfigParser::GetLocale()
   1.299 +{
   1.300 +    char propLang[PROP_VALUE_MAX], propRegn[PROP_VALUE_MAX];
   1.301 +    __system_property_get("persist.sys.language", propLang);
   1.302 +    __system_property_get("persist.sys.country", propRegn);
   1.303 +
   1.304 +    if (*propLang == 0 && *propRegn == 0) {
   1.305 +        /* Set to ro properties, default is en_US */
   1.306 +        __system_property_get("ro.product.locale.language", propLang);
   1.307 +        __system_property_get("ro.product.locale.region", propRegn);
   1.308 +        if (*propLang == 0 && *propRegn == 0) {
   1.309 +            strcpy(propLang, "en");
   1.310 +            strcpy(propRegn, "US");
   1.311 +        }
   1.312 +    }
   1.313 +
   1.314 +    SkString locale(6);
   1.315 +    char* localeCStr = locale.writable_str();
   1.316 +
   1.317 +    strncpy(localeCStr, propLang, 2);
   1.318 +    localeCStr[2] = '-';
   1.319 +    strncpy(&localeCStr[3], propRegn, 2);
   1.320 +    localeCStr[5] = '\0';
   1.321 +
   1.322 +    return locale;
   1.323 +}

mercurial