1bfae9d373ccc9cf47fd70757092962c7850fadf4djsollen@google.com/*
2bfae9d373ccc9cf47fd70757092962c7850fadf4djsollen@google.com * Copyright 2011 The Android Open Source Project
3bfae9d373ccc9cf47fd70757092962c7850fadf4djsollen@google.com *
4bfae9d373ccc9cf47fd70757092962c7850fadf4djsollen@google.com * Use of this source code is governed by a BSD-style license that can be
5bfae9d373ccc9cf47fd70757092962c7850fadf4djsollen@google.com * found in the LICENSE file.
6bfae9d373ccc9cf47fd70757092962c7850fadf4djsollen@google.com */
7bfae9d373ccc9cf47fd70757092962c7850fadf4djsollen@google.com
8bfae9d373ccc9cf47fd70757092962c7850fadf4djsollen@google.com#include "SkFontConfigParser_android.h"
97fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman#include "SkFontMgr_android.h"
107fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman#include "SkStream.h"
11bfae9d373ccc9cf47fd70757092962c7850fadf4djsollen@google.com#include "SkTDArray.h"
128d84c995319dd4a82e4f2054bbd19f968c671ca6bungeman#include "SkTSearch.h"
13bfae9d373ccc9cf47fd70757092962c7850fadf4djsollen@google.com#include "SkTypeface.h"
14bfae9d373ccc9cf47fd70757092962c7850fadf4djsollen@google.com
15bfae9d373ccc9cf47fd70757092962c7850fadf4djsollen@google.com#include <expat.h>
16a6c27bc5bd3b213d1e315c0bd9bbdcd75cec6900djsollen#include <dirent.h>
17465706820d0d373f76ab4831c286115ee0d86b7arobertphillips#include <stdio.h>
18bfae9d373ccc9cf47fd70757092962c7850fadf4djsollen@google.com
19465706820d0d373f76ab4831c286115ee0d86b7arobertphillips#include <limits>
207fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman#include <stdlib.h>
218d84c995319dd4a82e4f2054bbd19f968c671ca6bungeman
2294fa4b99e2a53e997a90c7808cc5263f1bf40c9ftomhudson#define LMP_SYSTEM_FONTS_FILE "/system/etc/fonts.xml"
2394fa4b99e2a53e997a90c7808cc5263f1bf40c9ftomhudson#define OLD_SYSTEM_FONTS_FILE "/system/etc/system_fonts.xml"
24bfae9d373ccc9cf47fd70757092962c7850fadf4djsollen@google.com#define FALLBACK_FONTS_FILE "/system/etc/fallback_fonts.xml"
25bfae9d373ccc9cf47fd70757092962c7850fadf4djsollen@google.com#define VENDOR_FONTS_FILE "/vendor/etc/fallback_fonts.xml"
26bfae9d373ccc9cf47fd70757092962c7850fadf4djsollen@google.com
27a6c27bc5bd3b213d1e315c0bd9bbdcd75cec6900djsollen#define LOCALE_FALLBACK_FONTS_SYSTEM_DIR "/system/etc"
28a6c27bc5bd3b213d1e315c0bd9bbdcd75cec6900djsollen#define LOCALE_FALLBACK_FONTS_VENDOR_DIR "/vendor/etc"
29a6c27bc5bd3b213d1e315c0bd9bbdcd75cec6900djsollen#define LOCALE_FALLBACK_FONTS_PREFIX "fallback_fonts-"
30a6c27bc5bd3b213d1e315c0bd9bbdcd75cec6900djsollen#define LOCALE_FALLBACK_FONTS_SUFFIX ".xml"
31a6c27bc5bd3b213d1e315c0bd9bbdcd75cec6900djsollen
327fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman#ifndef SK_FONT_FILE_PREFIX
337fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman#    define SK_FONT_FILE_PREFIX "/fonts/"
347fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman#endif
357fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman
36f79673bbae0a662c1428755e2719dadf944e4ba1tomhudson/**
3710b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman * This file contains TWO 'familyset' handlers:
3810b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman * One for JB and earlier which works with
3910b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman *   /system/etc/system_fonts.xml
4010b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman *   /system/etc/fallback_fonts.xml
4110b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman *   /vendor/etc/fallback_fonts.xml
4210b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman *   /system/etc/fallback_fonts-XX.xml
4310b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman *   /vendor/etc/fallback_fonts-XX.xml
4410b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman * and the other for LMP and later which works with
4510b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman *   /system/etc/fonts.xml
4610b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman *
4710b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman * If the 'familyset' 'version' attribute is 21 or higher the LMP parser is used, otherwise the JB.
48f79673bbae0a662c1428755e2719dadf944e4ba1tomhudson */
49f79673bbae0a662c1428755e2719dadf944e4ba1tomhudson
5010b063cb91c52fd1f570ee63307fe7e68c1501f1bungemanstruct FamilyData;
5110b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman
5210b063cb91c52fd1f570ee63307fe7e68c1501f1bungemanstruct TagHandler {
5310b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman    /** Called at the start tag.
5410b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman     *  Called immediately after the parent tag retuns this handler from a call to 'tag'.
5510b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman     *  Allows setting up for handling the tag content and processing attributes.
5610b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman     *  If NULL, will not be called.
5710b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman     */
5810b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman    void (*start)(FamilyData* data, const char* tag, const char** attributes);
5910b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman
6010b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman    /** Called at the end tag.
6110b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman     *  Allows post-processing of any accumulated information.
6210b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman     *  This will be the last call made in relation to the current tag.
6310b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman     *  If NULL, will not be called.
6410b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman     */
6510b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman    void (*end)(FamilyData* data, const char* tag);
6610b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman
6710b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman    /** Called when a nested tag is encountered.
6810b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman     *  This is responsible for determining how to handle the tag.
6910b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman     *  If the tag is not recognized, return NULL to skip the tag.
7010b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman     *  If NULL, all nested tags will be skipped.
7110b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman     */
7210b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman    const TagHandler* (*tag)(FamilyData* data, const char* tag, const char** attributes);
7310b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman
7410b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman    /** The character handler for this tag.
7510b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman     *  This is only active for character data contained directly in this tag (not sub-tags).
7610b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman     *  The first parameter will be castable to a FamilyData*.
7710b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman     *  If NULL, any character data in this tag will be ignored.
7810b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman     */
7910b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman    XML_CharacterDataHandler chars;
807fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman};
81bfae9d373ccc9cf47fd70757092962c7850fadf4djsollen@google.com
8210b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman/** Represents the current parsing state. */
83bfae9d373ccc9cf47fd70757092962c7850fadf4djsollen@google.comstruct FamilyData {
847fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman    FamilyData(XML_Parser parser, SkTDArray<FontFamily*>& families,
8510b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman               const SkString& basePath, bool isFallback, const char* filename,
8610b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman               const TagHandler* topLevelHandler)
87b6bed17ea81ff8fad68a7db79307bdcbcd4738a8bungeman        : fParser(parser)
88b6bed17ea81ff8fad68a7db79307bdcbcd4738a8bungeman        , fFamilies(families)
89b6bed17ea81ff8fad68a7db79307bdcbcd4738a8bungeman        , fCurrentFamily(NULL)
90b6bed17ea81ff8fad68a7db79307bdcbcd4738a8bungeman        , fCurrentFontInfo(NULL)
91efbad37180c6b6f90d4b7e96a234e8065d2ec395bungeman        , fVersion(0)
927fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman        , fBasePath(basePath)
937fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman        , fIsFallback(isFallback)
94f61475e95d9bb38f741f9e51221c086250b9ad72bungeman        , fFilename(filename)
9510b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        , fDepth(1)
9610b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        , fSkip(0)
9710b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        , fHandler(&topLevelHandler, 1)
987fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman    { };
99b6bed17ea81ff8fad68a7db79307bdcbcd4738a8bungeman
100b6bed17ea81ff8fad68a7db79307bdcbcd4738a8bungeman    XML_Parser fParser;                       // The expat parser doing the work, owned by caller
101b6bed17ea81ff8fad68a7db79307bdcbcd4738a8bungeman    SkTDArray<FontFamily*>& fFamilies;        // The array to append families, owned by caller
102b6bed17ea81ff8fad68a7db79307bdcbcd4738a8bungeman    SkAutoTDelete<FontFamily> fCurrentFamily; // The family being created, owned by this
1037fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman    FontFileInfo* fCurrentFontInfo;           // The fontInfo being created, owned by fCurrentFamily
104efbad37180c6b6f90d4b7e96a234e8065d2ec395bungeman    int fVersion;                             // The version of the file parsed.
1057fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman    const SkString& fBasePath;                // The current base path.
1067fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman    const bool fIsFallback;                   // Indicates the file being parsed is a fallback file
107f61475e95d9bb38f741f9e51221c086250b9ad72bungeman    const char* fFilename;                    // The name of the file currently being parsed.
10810b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman
10910b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman    int fDepth;                               // The current element depth of the parse.
11010b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman    int fSkip;                                // The depth to stop skipping, 0 if not skipping.
11110b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman    SkTDArray<const TagHandler*> fHandler;    // The stack of current tag handlers.
112bfae9d373ccc9cf47fd70757092962c7850fadf4djsollen@google.com};
113bfae9d373ccc9cf47fd70757092962c7850fadf4djsollen@google.com
114465706820d0d373f76ab4831c286115ee0d86b7arobertphillips/** Parses a null terminated string into an integer type, checking for overflow.
115465706820d0d373f76ab4831c286115ee0d86b7arobertphillips *  http://www.w3.org/TR/html-markup/datatypes.html#common.data.integer.non-negative-def
116465706820d0d373f76ab4831c286115ee0d86b7arobertphillips *
117465706820d0d373f76ab4831c286115ee0d86b7arobertphillips *  If the string cannot be parsed into 'value', returns false and does not change 'value'.
118465706820d0d373f76ab4831c286115ee0d86b7arobertphillips */
119465706820d0d373f76ab4831c286115ee0d86b7arobertphillipstemplate <typename T> static bool parse_non_negative_integer(const char* s, T* value) {
120465706820d0d373f76ab4831c286115ee0d86b7arobertphillips    SK_COMPILE_ASSERT(std::numeric_limits<T>::is_integer, T_must_be_integer);
121465706820d0d373f76ab4831c286115ee0d86b7arobertphillips    const T nMax = std::numeric_limits<T>::max() / 10;
122465706820d0d373f76ab4831c286115ee0d86b7arobertphillips    const T dMax = std::numeric_limits<T>::max() - (nMax * 10);
123465706820d0d373f76ab4831c286115ee0d86b7arobertphillips    T n = 0;
124465706820d0d373f76ab4831c286115ee0d86b7arobertphillips    for (; *s; ++s) {
125465706820d0d373f76ab4831c286115ee0d86b7arobertphillips        // Check if digit
126465706820d0d373f76ab4831c286115ee0d86b7arobertphillips        if (*s < '0' || '9' < *s) {
127465706820d0d373f76ab4831c286115ee0d86b7arobertphillips            return false;
128465706820d0d373f76ab4831c286115ee0d86b7arobertphillips        }
129465706820d0d373f76ab4831c286115ee0d86b7arobertphillips        int d = *s - '0';
130465706820d0d373f76ab4831c286115ee0d86b7arobertphillips        // Check for overflow
131465706820d0d373f76ab4831c286115ee0d86b7arobertphillips        if (n > nMax || (n == nMax && d > dMax)) {
132465706820d0d373f76ab4831c286115ee0d86b7arobertphillips            return false;
133465706820d0d373f76ab4831c286115ee0d86b7arobertphillips        }
134465706820d0d373f76ab4831c286115ee0d86b7arobertphillips        n = (n * 10) + d;
135465706820d0d373f76ab4831c286115ee0d86b7arobertphillips    }
136465706820d0d373f76ab4831c286115ee0d86b7arobertphillips    *value = n;
137465706820d0d373f76ab4831c286115ee0d86b7arobertphillips    return true;
138465706820d0d373f76ab4831c286115ee0d86b7arobertphillips}
139465706820d0d373f76ab4831c286115ee0d86b7arobertphillips
1407fa87cd09f49f1ee9bc27e263038d0f0ae706241bungemanstatic bool memeq(const char* s1, const char* s2, size_t n1, size_t n2) {
1417fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman    return n1 == n2 && 0 == memcmp(s1, s2, n1);
1427fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman}
1437fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman#define MEMEQ(c, s, n) memeq(c, s, sizeof(c) - 1, n)
1447fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman
1457fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman#define ATTS_NON_NULL(a, i) (a[i] != NULL && a[i+1] != NULL)
1467fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman
147f61475e95d9bb38f741f9e51221c086250b9ad72bungeman#define SK_FONTCONFIGPARSER_PREFIX "[SkFontConfigParser] "
148f61475e95d9bb38f741f9e51221c086250b9ad72bungeman
149f61475e95d9bb38f741f9e51221c086250b9ad72bungeman#define SK_FONTCONFIGPARSER_WARNING(message, ...) SkDebugf( \
150f61475e95d9bb38f741f9e51221c086250b9ad72bungeman    SK_FONTCONFIGPARSER_PREFIX "%s:%d:%d: warning: " message "\n", \
151f61475e95d9bb38f741f9e51221c086250b9ad72bungeman    self->fFilename, \
152f61475e95d9bb38f741f9e51221c086250b9ad72bungeman    XML_GetCurrentLineNumber(self->fParser), \
153f61475e95d9bb38f741f9e51221c086250b9ad72bungeman    XML_GetCurrentColumnNumber(self->fParser), \
154f61475e95d9bb38f741f9e51221c086250b9ad72bungeman    ##__VA_ARGS__);
155f61475e95d9bb38f741f9e51221c086250b9ad72bungeman
15610b063cb91c52fd1f570ee63307fe7e68c1501f1bungemanstatic bool is_whitespace(char c) {
15710b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman    return c == ' ' || c == '\n'|| c == '\r' || c == '\t';
15810b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman}
159f79673bbae0a662c1428755e2719dadf944e4ba1tomhudson
16010b063cb91c52fd1f570ee63307fe7e68c1501f1bungemanstatic void trim_string(SkString* s) {
16110b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman    char* str = s->writable_str();
16210b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman    const char* start = str;  // start is inclusive
16310b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman    const char* end = start + s->size();  // end is exclusive
16410b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman    while (is_whitespace(*start)) { ++start; }
16510b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman    if (start != end) {
16610b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        --end;  // make end inclusive
16710b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        while (is_whitespace(*end)) { --end; }
16810b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        ++end;  // make end exclusive
16907544757c9fcf0f359f1686a3779eb2e75dd5b36tomhudson    }
17010b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman    size_t len = end - start;
17110b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman    memmove(str, start, len);
17210b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman    s->resize(len);
173f79673bbae0a662c1428755e2719dadf944e4ba1tomhudson}
174f79673bbae0a662c1428755e2719dadf944e4ba1tomhudson
17510b063cb91c52fd1f570ee63307fe7e68c1501f1bungemannamespace lmpParser {
176f79673bbae0a662c1428755e2719dadf944e4ba1tomhudson
17710b063cb91c52fd1f570ee63307fe7e68c1501f1bungemanstatic const TagHandler fontHandler = {
17810b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman    /*start*/[](FamilyData* self, const char* tag, const char** attributes) {
17910b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        // 'weight' (non-negative integer) [default 0]
18010b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        // 'style' ("normal", "italic") [default "auto"]
18110b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        // 'index' (non-negative integer) [default 0]
18210b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        // The character data should be a filename.
18310b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        FontFileInfo& file = self->fCurrentFamily->fFonts.push_back();
18410b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        self->fCurrentFontInfo = &file;
18510b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        for (size_t i = 0; ATTS_NON_NULL(attributes, i); i += 2) {
18610b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman            const char* name = attributes[i];
18710b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman            const char* value = attributes[i+1];
18810b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman            size_t nameLen = strlen(name);
18910b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman            if (MEMEQ("weight", name, nameLen)) {
19010b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman                if (!parse_non_negative_integer(value, &file.fWeight)) {
19110b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman                    SK_FONTCONFIGPARSER_WARNING("'%s' is an invalid weight", value);
19210b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman                }
19310b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman            } else if (MEMEQ("style", name, nameLen)) {
19410b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman                size_t valueLen = strlen(value);
19510b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman                if (MEMEQ("normal", value, valueLen)) {
19610b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman                    file.fStyle = FontFileInfo::Style::kNormal;
19710b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman                } else if (MEMEQ("italic", value, valueLen)) {
19810b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman                    file.fStyle = FontFileInfo::Style::kItalic;
19910b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman                }
20010b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman            } else if (MEMEQ("index", name, nameLen)) {
20110b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman                if (!parse_non_negative_integer(value, &file.fIndex)) {
20210b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman                    SK_FONTCONFIGPARSER_WARNING("'%s' is an invalid index", value);
20310b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman                }
204d3ddea284ec6611a93a6b75e64de39d0bc7e083ctomhudson            }
20510b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        }
20610b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman    },
20710b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman    /*end*/[](FamilyData* self, const char* tag) {
20810b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        trim_string(&self->fCurrentFontInfo->fFileName);
20910b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman    },
210465706820d0d373f76ab4831c286115ee0d86b7arobertphillips    /*tag*/NULL,
21110b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman    /*chars*/[](void* data, const char* s, int len) {
21210b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        FamilyData* self = static_cast<FamilyData*>(data);
21310b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        self->fCurrentFontInfo->fFileName.append(s, len);
21410b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman    }
21510b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman};
21610b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman
21710b063cb91c52fd1f570ee63307fe7e68c1501f1bungemanstatic const TagHandler familyHandler = {
21810b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman    /*start*/[](FamilyData* self, const char* tag, const char** attributes) {
21910b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        // 'name' (string) [optional]
22010b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        // 'lang' (string) [default ""]
22110b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        // 'variant' ("elegant", "compact") [default "default"]
22210b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        // If there is no name, this is a fallback only font.
22310b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        FontFamily* family = new FontFamily(self->fBasePath, true);
22410b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        self->fCurrentFamily.reset(family);
22510b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        for (size_t i = 0; ATTS_NON_NULL(attributes, i); i += 2) {
22610b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman            const char* name = attributes[i];
22710b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman            const char* value = attributes[i+1];
22810b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman            size_t nameLen = strlen(name);
229e85a754a4ce9b279159270faa6717932f7a8548fbungeman            size_t valueLen = strlen(value);
23010b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman            if (MEMEQ("name", name, nameLen)) {
23110b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman                SkAutoAsciiToLC tolc(value);
23210b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman                family->fNames.push_back().set(tolc.lc());
23310b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman                family->fIsFallbackFont = false;
23410b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman            } else if (MEMEQ("lang", name, nameLen)) {
23510b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman                family->fLanguage = SkLanguage(value, valueLen);
23610b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman            } else if (MEMEQ("variant", name, nameLen)) {
23710b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman                if (MEMEQ("elegant", value, valueLen)) {
23810b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman                    family->fVariant = kElegant_FontVariant;
23910b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman                } else if (MEMEQ("compact", value, valueLen)) {
24010b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman                    family->fVariant = kCompact_FontVariant;
24110b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman                }
242b8a1d30a42d13ae83690b2d854a024d9b56e7b71bungeman            }
24307544757c9fcf0f359f1686a3779eb2e75dd5b36tomhudson        }
24410b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman    },
24510b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman    /*end*/[](FamilyData* self, const char* tag) {
24610b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        *self->fFamilies.append() = self->fCurrentFamily.detach();
24710b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman    },
24810b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman    /*tag*/[](FamilyData* self, const char* tag, const char** attributes) -> const TagHandler* {
24910b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        size_t len = strlen(tag);
25010b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        if (MEMEQ("font", tag, len)) {
25110b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman            return &fontHandler;
25210b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        }
25310b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        return NULL;
25410b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman    },
25510b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman    /*chars*/NULL,
25610b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman};
25707544757c9fcf0f359f1686a3779eb2e75dd5b36tomhudson
258eb2be7fa4413a566212782d8efae5dc225002821bungemanstatic FontFamily* find_family(FamilyData* self, const SkString& familyName) {
2597fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman    for (int i = 0; i < self->fFamilies.count(); i++) {
2607fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman        FontFamily* candidate = self->fFamilies[i];
26107544757c9fcf0f359f1686a3779eb2e75dd5b36tomhudson        for (int j = 0; j < candidate->fNames.count(); j++) {
2627fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman            if (candidate->fNames[j] == familyName) {
26307544757c9fcf0f359f1686a3779eb2e75dd5b36tomhudson                return candidate;
26407544757c9fcf0f359f1686a3779eb2e75dd5b36tomhudson            }
26507544757c9fcf0f359f1686a3779eb2e75dd5b36tomhudson        }
26607544757c9fcf0f359f1686a3779eb2e75dd5b36tomhudson    }
26707544757c9fcf0f359f1686a3779eb2e75dd5b36tomhudson    return NULL;
26807544757c9fcf0f359f1686a3779eb2e75dd5b36tomhudson}
26907544757c9fcf0f359f1686a3779eb2e75dd5b36tomhudson
27010b063cb91c52fd1f570ee63307fe7e68c1501f1bungemanstatic const TagHandler aliasHandler = {
27110b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman    /*start*/[](FamilyData* self, const char* tag, const char** attributes) {
27210b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        // 'name' (string) introduces a new family name.
27310b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        // 'to' (string) specifies which (previous) family to alias
27410b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        // 'weight' (non-negative integer) [optional]
27510b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        // If it *does not* have a weight, 'name' is an alias for the entire 'to' family.
27610b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        // If it *does* have a weight, 'name' is a new family consisting of
27710b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        // the font(s) with 'weight' from the 'to' family.
27810b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman
27910b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        SkString aliasName;
28010b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        SkString to;
28110b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        int weight = 0;
28210b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        for (size_t i = 0; ATTS_NON_NULL(attributes, i); i += 2) {
28310b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman            const char* name = attributes[i];
28410b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman            const char* value = attributes[i+1];
28510b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman            size_t nameLen = strlen(name);
28610b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman            if (MEMEQ("name", name, nameLen)) {
28710b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman                SkAutoAsciiToLC tolc(value);
28810b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman                aliasName.set(tolc.lc());
28910b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman            } else if (MEMEQ("to", name, nameLen)) {
29010b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman                to.set(value);
29110b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman            } else if (MEMEQ("weight", name, nameLen)) {
29210b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman                if (!parse_non_negative_integer(value, &weight)) {
29310b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman                    SK_FONTCONFIGPARSER_WARNING("'%s' is an invalid weight", value);
29410b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman                }
2957fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman            }
29607544757c9fcf0f359f1686a3779eb2e75dd5b36tomhudson        }
29707544757c9fcf0f359f1686a3779eb2e75dd5b36tomhudson
29810b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        // Assumes that the named family is already declared
29910b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        FontFamily* targetFamily = find_family(self, to);
30010b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        if (!targetFamily) {
30110b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman            SK_FONTCONFIGPARSER_WARNING("'%s' alias target not found", to.c_str());
30210b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman            return;
30310b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        }
30407544757c9fcf0f359f1686a3779eb2e75dd5b36tomhudson
30510b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        if (weight) {
30610b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman            FontFamily* family = new FontFamily(targetFamily->fBasePath, self->fIsFallback);
30710b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman            family->fNames.push_back().set(aliasName);
30807544757c9fcf0f359f1686a3779eb2e75dd5b36tomhudson
30910b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman            for (int i = 0; i < targetFamily->fFonts.count(); i++) {
31010b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman                if (targetFamily->fFonts[i].fWeight == weight) {
31110b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman                    family->fFonts.push_back(targetFamily->fFonts[i]);
31210b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman                }
31307544757c9fcf0f359f1686a3779eb2e75dd5b36tomhudson            }
31410b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman            *self->fFamilies.append() = family;
31510b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        } else {
31610b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman            targetFamily->fNames.push_back().set(aliasName);
31707544757c9fcf0f359f1686a3779eb2e75dd5b36tomhudson        }
31810b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman    },
31910b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman    /*end*/NULL,
32010b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman    /*tag*/NULL,
32110b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman    /*chars*/NULL,
32210b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman};
323c0727d117e67844f0c8794cc7eaa49a96a015347bungeman
32410b063cb91c52fd1f570ee63307fe7e68c1501f1bungemanstatic const TagHandler familySetHandler = {
32510b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman    /*start*/[](FamilyData* self, const char* tag, const char** attributes) { },
32610b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman    /*end*/NULL,
32710b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman    /*tag*/[](FamilyData* self, const char* tag, const char** attributes) -> const TagHandler* {
32810b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        size_t len = strlen(tag);
32910b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        if (MEMEQ("family", tag, len)) {
33010b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman            return &familyHandler;
33110b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        } else if (MEMEQ("alias", tag, len)) {
33210b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman            return &aliasHandler;
33310b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        }
33410b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        return NULL;
33510b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman    },
33610b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman    /*chars*/NULL,
33710b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman};
338f79673bbae0a662c1428755e2719dadf944e4ba1tomhudson
339f79673bbae0a662c1428755e2719dadf944e4ba1tomhudson} // lmpParser
340f79673bbae0a662c1428755e2719dadf944e4ba1tomhudson
341f79673bbae0a662c1428755e2719dadf944e4ba1tomhudsonnamespace jbParser {
342f79673bbae0a662c1428755e2719dadf944e4ba1tomhudson
34310b063cb91c52fd1f570ee63307fe7e68c1501f1bungemanstatic const TagHandler fileHandler = {
34410b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman    /*start*/[](FamilyData* self, const char* tag, const char** attributes) {
34510b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        // 'variant' ("elegant", "compact") [default "default"]
34610b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        // 'lang' (string) [default ""]
34710b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        // 'index' (non-negative integer) [default 0]
34810b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        // The character data should be a filename.
34910b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        FontFamily& currentFamily = *self->fCurrentFamily.get();
35010b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        FontFileInfo& newFileInfo = currentFamily.fFonts.push_back();
35110b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        if (attributes) {
35210b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman            for (size_t i = 0; ATTS_NON_NULL(attributes, i); i += 2) {
35310b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman                const char* name = attributes[i];
35410b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman                const char* value = attributes[i+1];
35510b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman                size_t nameLen = strlen(name);
35610b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman                size_t valueLen = strlen(value);
35710b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman                if (MEMEQ("variant", name, nameLen)) {
35810b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman                    const FontVariant prevVariant = currentFamily.fVariant;
35910b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman                    if (MEMEQ("elegant", value, valueLen)) {
36010b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman                        currentFamily.fVariant = kElegant_FontVariant;
36110b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman                    } else if (MEMEQ("compact", value, valueLen)) {
36210b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman                        currentFamily.fVariant = kCompact_FontVariant;
36310b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman                    }
36410b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman                    if (currentFamily.fFonts.count() > 1 && currentFamily.fVariant != prevVariant) {
36510b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman                        SK_FONTCONFIGPARSER_WARNING("'%s' unexpected variant found\n"
36610b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman                            "Note: Every font file within a family must have identical variants.",
36710b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman                            value);
36810b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman                    }
36910b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman
37010b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman                } else if (MEMEQ("lang", name, nameLen)) {
37110b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman                    SkLanguage prevLang = currentFamily.fLanguage;
37210b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman                    currentFamily.fLanguage = SkLanguage(value, valueLen);
37310b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman                    if (currentFamily.fFonts.count() > 1 && currentFamily.fLanguage != prevLang) {
37410b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman                        SK_FONTCONFIGPARSER_WARNING("'%s' unexpected language found\n"
37510b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman                            "Note: Every font file within a family must have identical languages.",
37610b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman                            value);
37710b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman                    }
37810b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman
37910b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman                } else if (MEMEQ("index", name, nameLen)) {
38010b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman                    if (!parse_non_negative_integer(value, &newFileInfo.fIndex)) {
38110b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman                        SK_FONTCONFIGPARSER_WARNING("'%s' is an invalid index", value);
38210b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman                    }
38310b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman                }
384bfae9d373ccc9cf47fd70757092962c7850fadf4djsollen@google.com            }
385bfae9d373ccc9cf47fd70757092962c7850fadf4djsollen@google.com        }
38610b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        self->fCurrentFontInfo = &newFileInfo;
38710b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman    },
38810b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman    /*end*/NULL,
38910b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman    /*tag*/NULL,
39010b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman    /*chars*/[](void* data, const char* s, int len) {
39110b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        FamilyData* self = static_cast<FamilyData*>(data);
39210b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        self->fCurrentFontInfo->fFileName.append(s, len);
393bfae9d373ccc9cf47fd70757092962c7850fadf4djsollen@google.com    }
39410b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman};
395bfae9d373ccc9cf47fd70757092962c7850fadf4djsollen@google.com
39610b063cb91c52fd1f570ee63307fe7e68c1501f1bungemanstatic const TagHandler fileSetHandler = {
39710b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman    /*start*/NULL,
39810b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman    /*end*/NULL,
39910b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman    /*tag*/[](FamilyData* self, const char* tag, const char** attributes) -> const TagHandler* {
40010b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        size_t len = strlen(tag);
40110b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        if (MEMEQ("file", tag, len)) {
40210b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman            return &fileHandler;
40310b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        }
40410b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        return NULL;
40510b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman    },
40610b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman    /*chars*/NULL,
40710b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman};
40810b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman
40910b063cb91c52fd1f570ee63307fe7e68c1501f1bungemanstatic const TagHandler nameHandler = {
41010b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman    /*start*/[](FamilyData* self, const char* tag, const char** attributes) {
41110b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        // The character data should be a name for the font.
41210b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        self->fCurrentFamily->fNames.push_back();
41310b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman    },
41410b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman    /*end*/NULL,
41510b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman    /*tag*/NULL,
41610b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman    /*chars*/[](void* data, const char* s, int len) {
41710b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        FamilyData* self = static_cast<FamilyData*>(data);
41810b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        SkAutoAsciiToLC tolc(s, len);
41910b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        self->fCurrentFamily->fNames.back().append(tolc.lc(), len);
42010b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman    }
42110b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman};
42210b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman
42310b063cb91c52fd1f570ee63307fe7e68c1501f1bungemanstatic const TagHandler nameSetHandler = {
42410b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman    /*start*/NULL,
42510b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman    /*end*/NULL,
42610b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman    /*tag*/[](FamilyData* self, const char* tag, const char** attributes) -> const TagHandler* {
42710b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        size_t len = strlen(tag);
42810b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        if (MEMEQ("name", tag, len)) {
42910b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman            return &nameHandler;
43010b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        }
43110b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        return NULL;
43210b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman    },
43310b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman    /*chars*/NULL,
43410b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman};
43510b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman
43610b063cb91c52fd1f570ee63307fe7e68c1501f1bungemanstatic const TagHandler familyHandler = {
43710b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman    /*start*/[](FamilyData* self, const char* tag, const char** attributes) {
43810b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        self->fCurrentFamily.reset(new FontFamily(self->fBasePath, self->fIsFallback));
43910b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        // 'order' (non-negative integer) [default -1]
4407fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman        for (size_t i = 0; ATTS_NON_NULL(attributes, i); i += 2) {
441f61475e95d9bb38f741f9e51221c086250b9ad72bungeman            const char* value = attributes[i+1];
44210b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman            parse_non_negative_integer(value, &self->fCurrentFamily->fOrder);
44310b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        }
44410b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman    },
44510b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman    /*end*/[](FamilyData* self, const char* tag) {
44610b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        *self->fFamilies.append() = self->fCurrentFamily.detach();
44710b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman    },
44810b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman    /*tag*/[](FamilyData* self, const char* tag, const char** attributes) -> const TagHandler* {
44910b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        size_t len = strlen(tag);
45010b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        if (MEMEQ("nameset", tag, len)) {
45110b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman            return &nameSetHandler;
45210b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        } else if (MEMEQ("fileset", tag, len)) {
45310b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman            return &fileSetHandler;
45410b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        }
45510b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        return NULL;
45610b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman    },
45710b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman    /*chars*/NULL,
45810b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman};
4593b6255493e458c6b2c1412af908581f0bf3f6b70djsollen
46010b063cb91c52fd1f570ee63307fe7e68c1501f1bungemanstatic const TagHandler familySetHandler = {
46110b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman    /*start*/NULL,
46210b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman    /*end*/NULL,
46310b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman    /*tag*/[](FamilyData* self, const char* tag, const char** attributes) -> const TagHandler* {
46410b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        size_t len = strlen(tag);
46510b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        if (MEMEQ("family", tag, len)) {
46610b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman            return &familyHandler;
46710b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        }
46810b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        return NULL;
46910b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman    },
47010b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman    /*chars*/NULL,
47110b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman};
4727fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman
47310b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman} // namespace jbParser
47410b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman
47510b063cb91c52fd1f570ee63307fe7e68c1501f1bungemanstatic const TagHandler topLevelHandler = {
47610b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman    /*start*/NULL,
47710b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman    /*end*/NULL,
47810b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman    /*tag*/[](FamilyData* self, const char* tag, const char** attributes) -> const TagHandler* {
47910b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        size_t len = strlen(tag);
48010b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        if (MEMEQ("familyset", tag, len)) {
48110b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman            // 'version' (non-negative integer) [default 0]
48210b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman            for (size_t i = 0; ATTS_NON_NULL(attributes, i); i += 2) {
48310b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman                const char* name = attributes[i];
48410b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman                size_t nameLen = strlen(name);
48510b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman                if (MEMEQ("version", name, nameLen)) {
48610b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman                    const char* value = attributes[i+1];
48710b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman                    if (parse_non_negative_integer(value, &self->fVersion)) {
48810b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman                        if (self->fVersion >= 21) {
48910b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman                            return &lmpParser::familySetHandler;
49010b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman                        }
49110b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman                    }
4928d84c995319dd4a82e4f2054bbd19f968c671ca6bungeman                }
493bfae9d373ccc9cf47fd70757092962c7850fadf4djsollen@google.com            }
49410b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman            return &jbParser::familySetHandler;
495bfae9d373ccc9cf47fd70757092962c7850fadf4djsollen@google.com        }
49610b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        return NULL;
49710b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman    },
49810b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman    /*chars*/NULL,
49910b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman};
500bfae9d373ccc9cf47fd70757092962c7850fadf4djsollen@google.com
50110b063cb91c52fd1f570ee63307fe7e68c1501f1bungemanstatic void XMLCALL start_element_handler(void *data, const char *tag, const char **attributes) {
5027fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman    FamilyData* self = static_cast<FamilyData*>(data);
50310b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman
50410b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman    if (!self->fSkip) {
50510b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        const TagHandler* parent = self->fHandler.top();
50610b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        const TagHandler* child = parent->tag ? parent->tag(self, tag, attributes) : NULL;
50710b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        if (child) {
50810b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman            if (child->start) {
50910b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman                child->start(self, tag, attributes);
510bfae9d373ccc9cf47fd70757092962c7850fadf4djsollen@google.com            }
51110b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman            self->fHandler.push(child);
51210b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman            XML_SetCharacterDataHandler(self->fParser, child->chars);
51310b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        } else {
51410b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman            SK_FONTCONFIGPARSER_WARNING("'%s' tag not recognized, skipping", tag);
51510b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman            XML_SetCharacterDataHandler(self->fParser, NULL);
51610b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman            self->fSkip = self->fDepth;
517bfae9d373ccc9cf47fd70757092962c7850fadf4djsollen@google.com        }
518bfae9d373ccc9cf47fd70757092962c7850fadf4djsollen@google.com    }
51910b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman
52010b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman    ++self->fDepth;
521bfae9d373ccc9cf47fd70757092962c7850fadf4djsollen@google.com}
522bfae9d373ccc9cf47fd70757092962c7850fadf4djsollen@google.com
5237fa87cd09f49f1ee9bc27e263038d0f0ae706241bungemanstatic void XMLCALL end_element_handler(void* data, const char* tag) {
5247fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman    FamilyData* self = static_cast<FamilyData*>(data);
52510b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman    --self->fDepth;
52610b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman
52710b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman    if (!self->fSkip) {
52810b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        const TagHandler* child = self->fHandler.top();
52910b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        if (child->end) {
53010b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman            child->end(self, tag);
53110b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        }
53210b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        self->fHandler.pop();
53310b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        const TagHandler* parent = self->fHandler.top();
53410b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        XML_SetCharacterDataHandler(self->fParser, parent->chars);
535bfae9d373ccc9cf47fd70757092962c7850fadf4djsollen@google.com    }
536bfae9d373ccc9cf47fd70757092962c7850fadf4djsollen@google.com
53710b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman    if (self->fSkip == self->fDepth) {
53810b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        self->fSkip = 0;
53910b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        const TagHandler* parent = self->fHandler.top();
54010b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        XML_SetCharacterDataHandler(self->fParser, parent->chars);
54110b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman    }
54210b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman}
543f79673bbae0a662c1428755e2719dadf944e4ba1tomhudson
5447fa87cd09f49f1ee9bc27e263038d0f0ae706241bungemanstatic void XMLCALL xml_entity_decl_handler(void *data,
5457fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman                                            const XML_Char *entityName,
5467fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman                                            int is_parameter_entity,
5477fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman                                            const XML_Char *value,
5487fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman                                            int value_length,
5497fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman                                            const XML_Char *base,
5507fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman                                            const XML_Char *systemId,
5517fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman                                            const XML_Char *publicId,
5527fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman                                            const XML_Char *notationName)
5537fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman{
5547fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman    FamilyData* self = static_cast<FamilyData*>(data);
555f61475e95d9bb38f741f9e51221c086250b9ad72bungeman    SK_FONTCONFIGPARSER_WARNING("'%s' entity declaration found, stopping processing", entityName);
5567fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman    XML_StopParser(self->fParser, XML_FALSE);
5577fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman}
5587fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman
559eb2be7fa4413a566212782d8efae5dc225002821bungemanstatic const XML_Memory_Handling_Suite sk_XML_alloc = {
560eb2be7fa4413a566212782d8efae5dc225002821bungeman    sk_malloc_throw,
561eb2be7fa4413a566212782d8efae5dc225002821bungeman    sk_realloc_throw,
562eb2be7fa4413a566212782d8efae5dc225002821bungeman    sk_free
563eb2be7fa4413a566212782d8efae5dc225002821bungeman};
564eb2be7fa4413a566212782d8efae5dc225002821bungeman
5657fa87cd09f49f1ee9bc27e263038d0f0ae706241bungemantemplate<typename T> struct remove_ptr {typedef T type;};
5667fa87cd09f49f1ee9bc27e263038d0f0ae706241bungemantemplate<typename T> struct remove_ptr<T*> {typedef T type;};
5677fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman
568bfae9d373ccc9cf47fd70757092962c7850fadf4djsollen@google.com/**
569bfae9d373ccc9cf47fd70757092962c7850fadf4djsollen@google.com * This function parses the given filename and stores the results in the given
570efbad37180c6b6f90d4b7e96a234e8065d2ec395bungeman * families array. Returns the version of the file, negative if the file does not exist.
571bfae9d373ccc9cf47fd70757092962c7850fadf4djsollen@google.com */
5727fa87cd09f49f1ee9bc27e263038d0f0ae706241bungemanstatic int parse_config_file(const char* filename, SkTDArray<FontFamily*>& families,
5737fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman                             const SkString& basePath, bool isFallback)
5747fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman{
5757fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman    SkFILEStream file(filename);
57650c956791291e7f3cec23721157570b7911336b8djsollen@google.com
577bfae9d373ccc9cf47fd70757092962c7850fadf4djsollen@google.com    // Some of the files we attempt to parse (in particular, /vendor/etc/fallback_fonts.xml)
578bfae9d373ccc9cf47fd70757092962c7850fadf4djsollen@google.com    // are optional - failure here is okay because one of these optional files may not exist.
5797fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman    if (!file.isValid()) {
580f61475e95d9bb38f741f9e51221c086250b9ad72bungeman        SkDebugf(SK_FONTCONFIGPARSER_PREFIX "'%s' could not be opened\n", filename);
581efbad37180c6b6f90d4b7e96a234e8065d2ec395bungeman        return -1;
582bfae9d373ccc9cf47fd70757092962c7850fadf4djsollen@google.com    }
58350c956791291e7f3cec23721157570b7911336b8djsollen@google.com
584eb2be7fa4413a566212782d8efae5dc225002821bungeman    SkAutoTCallVProc<remove_ptr<XML_Parser>::type, XML_ParserFree> parser(
585eb2be7fa4413a566212782d8efae5dc225002821bungeman        XML_ParserCreate_MM(NULL, &sk_XML_alloc, NULL));
5867fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman    if (!parser) {
587f61475e95d9bb38f741f9e51221c086250b9ad72bungeman        SkDebugf(SK_FONTCONFIGPARSER_PREFIX "could not create XML parser\n");
5887fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman        return -1;
5897fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman    }
5907fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman
59110b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman    FamilyData self(parser, families, basePath, isFallback, filename, &topLevelHandler);
5927fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman    XML_SetUserData(parser, &self);
5937fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman
5947fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman    // Disable entity processing, to inhibit internal entity expansion. See expat CVE-2013-0340
5957fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman    XML_SetEntityDeclHandler(parser, xml_entity_decl_handler);
5967fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman
597f79673bbae0a662c1428755e2719dadf944e4ba1tomhudson    // Start parsing oldschool; switch these in flight if we detect a newer version of the file.
59810b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman    XML_SetElementHandler(parser, start_element_handler, end_element_handler);
59950c956791291e7f3cec23721157570b7911336b8djsollen@google.com
600eb2be7fa4413a566212782d8efae5dc225002821bungeman    // One would assume it would be faster to have a buffer on the stack and call XML_Parse.
601eb2be7fa4413a566212782d8efae5dc225002821bungeman    // But XML_Parse will call XML_GetBuffer anyway and memmove the passed buffer into it.
602eb2be7fa4413a566212782d8efae5dc225002821bungeman    // (Unless XML_CONTEXT_BYTES is undefined, but all users define it.)
6039a0808fd8e83128403285f391944850d908d7af0bungeman    // In debug, buffer a small odd number of bytes to detect slicing in XML_CharacterDataHandler.
6049a0808fd8e83128403285f391944850d908d7af0bungeman    static const int bufferSize = 512 SkDEBUGCODE( - 507);
605bfae9d373ccc9cf47fd70757092962c7850fadf4djsollen@google.com    bool done = false;
606bfae9d373ccc9cf47fd70757092962c7850fadf4djsollen@google.com    while (!done) {
607eb2be7fa4413a566212782d8efae5dc225002821bungeman        void* buffer = XML_GetBuffer(parser, bufferSize);
608eb2be7fa4413a566212782d8efae5dc225002821bungeman        if (!buffer) {
609f61475e95d9bb38f741f9e51221c086250b9ad72bungeman            SkDebugf(SK_FONTCONFIGPARSER_PREFIX "could not buffer enough to continue\n");
610eb2be7fa4413a566212782d8efae5dc225002821bungeman            return -1;
611eb2be7fa4413a566212782d8efae5dc225002821bungeman        }
612eb2be7fa4413a566212782d8efae5dc225002821bungeman        size_t len = file.read(buffer, bufferSize);
6137fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman        done = file.isAtEnd();
614eb2be7fa4413a566212782d8efae5dc225002821bungeman        XML_Status status = XML_ParseBuffer(parser, len, done);
6157fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman        if (XML_STATUS_ERROR == status) {
6167fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman            XML_Error error = XML_GetErrorCode(parser);
6177fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman            int line = XML_GetCurrentLineNumber(parser);
6187fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman            int column = XML_GetCurrentColumnNumber(parser);
6197fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman            const XML_LChar* errorString = XML_ErrorString(error);
620f61475e95d9bb38f741f9e51221c086250b9ad72bungeman            SkDebugf(SK_FONTCONFIGPARSER_PREFIX "%s:%d:%d error %d: %s.\n",
621f61475e95d9bb38f741f9e51221c086250b9ad72bungeman                     filename, line, column, error, errorString);
6227fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman            return -1;
623bfae9d373ccc9cf47fd70757092962c7850fadf4djsollen@google.com        }
624bfae9d373ccc9cf47fd70757092962c7850fadf4djsollen@google.com    }
6257fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman    return self.fVersion;
626bfae9d373ccc9cf47fd70757092962c7850fadf4djsollen@google.com}
627bfae9d373ccc9cf47fd70757092962c7850fadf4djsollen@google.com
628efbad37180c6b6f90d4b7e96a234e8065d2ec395bungeman/** Returns the version of the system font file actually found, negative if none. */
6297fa87cd09f49f1ee9bc27e263038d0f0ae706241bungemanstatic int append_system_font_families(SkTDArray<FontFamily*>& fontFamilies,
6307fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman                                       const SkString& basePath)
6317fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman{
6327a4747f4f62e4896d8f8469e1939b8191fff8d4etomhudson    int initialCount = fontFamilies.count();
6337fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman    int version = parse_config_file(LMP_SYSTEM_FONTS_FILE, fontFamilies, basePath, false);
634efbad37180c6b6f90d4b7e96a234e8065d2ec395bungeman    if (version < 0 || fontFamilies.count() == initialCount) {
6357fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman        version = parse_config_file(OLD_SYSTEM_FONTS_FILE, fontFamilies, basePath, false);
63694fa4b99e2a53e997a90c7808cc5263f1bf40c9ftomhudson    }
637efbad37180c6b6f90d4b7e96a234e8065d2ec395bungeman    return version;
638bfae9d373ccc9cf47fd70757092962c7850fadf4djsollen@google.com}
639bfae9d373ccc9cf47fd70757092962c7850fadf4djsollen@google.com
640a6c27bc5bd3b213d1e315c0bd9bbdcd75cec6900djsollen/**
641a6c27bc5bd3b213d1e315c0bd9bbdcd75cec6900djsollen * In some versions of Android prior to Android 4.2 (JellyBean MR1 at API
642a6c27bc5bd3b213d1e315c0bd9bbdcd75cec6900djsollen * Level 17) the fallback fonts for certain locales were encoded in their own
643a6c27bc5bd3b213d1e315c0bd9bbdcd75cec6900djsollen * XML files with a suffix that identified the locale.  We search the provided
644a6c27bc5bd3b213d1e315c0bd9bbdcd75cec6900djsollen * directory for those files,add all of their entries to the fallback chain, and
645a6c27bc5bd3b213d1e315c0bd9bbdcd75cec6900djsollen * include the locale as part of each entry.
646a6c27bc5bd3b213d1e315c0bd9bbdcd75cec6900djsollen */
647efbad37180c6b6f90d4b7e96a234e8065d2ec395bungemanstatic void append_fallback_font_families_for_locale(SkTDArray<FontFamily*>& fallbackFonts,
6487fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman                                                     const char* dir,
6497fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman                                                     const SkString& basePath)
650efbad37180c6b6f90d4b7e96a234e8065d2ec395bungeman{
651a6c27bc5bd3b213d1e315c0bd9bbdcd75cec6900djsollen#if defined(SK_BUILD_FOR_ANDROID_FRAMEWORK)
652a6c27bc5bd3b213d1e315c0bd9bbdcd75cec6900djsollen    // The framework is beyond Android 4.2 and can therefore skip this function
653a6c27bc5bd3b213d1e315c0bd9bbdcd75cec6900djsollen    return;
654a6c27bc5bd3b213d1e315c0bd9bbdcd75cec6900djsollen#endif
655a6c27bc5bd3b213d1e315c0bd9bbdcd75cec6900djsollen
656c3c694342ad393b88cee5885395f182082aa2ebbbungeman    SkAutoTCallIProc<DIR, closedir> fontDirectory(opendir(dir));
657c3c694342ad393b88cee5885395f182082aa2ebbbungeman    if (NULL == fontDirectory) {
658c3c694342ad393b88cee5885395f182082aa2ebbbungeman        return;
659c3c694342ad393b88cee5885395f182082aa2ebbbungeman    }
660a6c27bc5bd3b213d1e315c0bd9bbdcd75cec6900djsollen
661c3c694342ad393b88cee5885395f182082aa2ebbbungeman    for (struct dirent* dirEntry; (dirEntry = readdir(fontDirectory));) {
6629a0808fd8e83128403285f391944850d908d7af0bungeman        // The size of the prefix and suffix.
6639a0808fd8e83128403285f391944850d908d7af0bungeman        static const size_t fixedLen = sizeof(LOCALE_FALLBACK_FONTS_PREFIX) - 1
6649a0808fd8e83128403285f391944850d908d7af0bungeman                                     + sizeof(LOCALE_FALLBACK_FONTS_SUFFIX) - 1;
6659a0808fd8e83128403285f391944850d908d7af0bungeman
6669a0808fd8e83128403285f391944850d908d7af0bungeman        // The size of the prefix, suffix, and a minimum valid language code
6679a0808fd8e83128403285f391944850d908d7af0bungeman        static const size_t minSize = fixedLen + 2;
668c3c694342ad393b88cee5885395f182082aa2ebbbungeman
669c3c694342ad393b88cee5885395f182082aa2ebbbungeman        SkString fileName(dirEntry->d_name);
670c3c694342ad393b88cee5885395f182082aa2ebbbungeman        if (fileName.size() < minSize ||
671c3c694342ad393b88cee5885395f182082aa2ebbbungeman            !fileName.startsWith(LOCALE_FALLBACK_FONTS_PREFIX) ||
672c3c694342ad393b88cee5885395f182082aa2ebbbungeman            !fileName.endsWith(LOCALE_FALLBACK_FONTS_SUFFIX))
673c3c694342ad393b88cee5885395f182082aa2ebbbungeman        {
674c3c694342ad393b88cee5885395f182082aa2ebbbungeman            continue;
675c3c694342ad393b88cee5885395f182082aa2ebbbungeman        }
676a6c27bc5bd3b213d1e315c0bd9bbdcd75cec6900djsollen
677c3c694342ad393b88cee5885395f182082aa2ebbbungeman        SkString locale(fileName.c_str() + sizeof(LOCALE_FALLBACK_FONTS_PREFIX) - 1,
678c3c694342ad393b88cee5885395f182082aa2ebbbungeman                        fileName.size() - fixedLen);
679a6c27bc5bd3b213d1e315c0bd9bbdcd75cec6900djsollen
680c3c694342ad393b88cee5885395f182082aa2ebbbungeman        SkString absoluteFilename;
681c3c694342ad393b88cee5885395f182082aa2ebbbungeman        absoluteFilename.printf("%s/%s", dir, fileName.c_str());
682a6c27bc5bd3b213d1e315c0bd9bbdcd75cec6900djsollen
683c3c694342ad393b88cee5885395f182082aa2ebbbungeman        SkTDArray<FontFamily*> langSpecificFonts;
684c3c694342ad393b88cee5885395f182082aa2ebbbungeman        parse_config_file(absoluteFilename.c_str(), langSpecificFonts, basePath, true);
685a6c27bc5bd3b213d1e315c0bd9bbdcd75cec6900djsollen
686c3c694342ad393b88cee5885395f182082aa2ebbbungeman        for (int i = 0; i < langSpecificFonts.count(); ++i) {
687c3c694342ad393b88cee5885395f182082aa2ebbbungeman            FontFamily* family = langSpecificFonts[i];
688c3c694342ad393b88cee5885395f182082aa2ebbbungeman            family->fLanguage = SkLanguage(locale);
689c3c694342ad393b88cee5885395f182082aa2ebbbungeman            *fallbackFonts.append() = family;
690a6c27bc5bd3b213d1e315c0bd9bbdcd75cec6900djsollen        }
691a6c27bc5bd3b213d1e315c0bd9bbdcd75cec6900djsollen    }
692a6c27bc5bd3b213d1e315c0bd9bbdcd75cec6900djsollen}
693a6c27bc5bd3b213d1e315c0bd9bbdcd75cec6900djsollen
6947fa87cd09f49f1ee9bc27e263038d0f0ae706241bungemanstatic void append_system_fallback_font_families(SkTDArray<FontFamily*>& fallbackFonts,
6957fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman                                                 const SkString& basePath)
6967fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman{
6977fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman    parse_config_file(FALLBACK_FONTS_FILE, fallbackFonts, basePath, true);
6987fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman    append_fallback_font_families_for_locale(fallbackFonts,
6997fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman                                             LOCALE_FALLBACK_FONTS_SYSTEM_DIR,
7007fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman                                             basePath);
701efbad37180c6b6f90d4b7e96a234e8065d2ec395bungeman}
702bfae9d373ccc9cf47fd70757092962c7850fadf4djsollen@google.com
7037fa87cd09f49f1ee9bc27e263038d0f0ae706241bungemanstatic void mixin_vendor_fallback_font_families(SkTDArray<FontFamily*>& fallbackFonts,
7047fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman                                                const SkString& basePath)
7057fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman{
706efbad37180c6b6f90d4b7e96a234e8065d2ec395bungeman    SkTDArray<FontFamily*> vendorFonts;
7077fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman    parse_config_file(VENDOR_FONTS_FILE, vendorFonts, basePath, true);
7087fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman    append_fallback_font_families_for_locale(vendorFonts,
7097fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman                                             LOCALE_FALLBACK_FONTS_VENDOR_DIR,
7107fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman                                             basePath);
711a6c27bc5bd3b213d1e315c0bd9bbdcd75cec6900djsollen
712bfae9d373ccc9cf47fd70757092962c7850fadf4djsollen@google.com    // This loop inserts the vendor fallback fonts in the correct order in the
713bfae9d373ccc9cf47fd70757092962c7850fadf4djsollen@google.com    // overall fallbacks list.
714bfae9d373ccc9cf47fd70757092962c7850fadf4djsollen@google.com    int currentOrder = -1;
715bfae9d373ccc9cf47fd70757092962c7850fadf4djsollen@google.com    for (int i = 0; i < vendorFonts.count(); ++i) {
716bfae9d373ccc9cf47fd70757092962c7850fadf4djsollen@google.com        FontFamily* family = vendorFonts[i];
717d3ddea284ec6611a93a6b75e64de39d0bc7e083ctomhudson        int order = family->fOrder;
718bfae9d373ccc9cf47fd70757092962c7850fadf4djsollen@google.com        if (order < 0) {
719bfae9d373ccc9cf47fd70757092962c7850fadf4djsollen@google.com            if (currentOrder < 0) {
720bfae9d373ccc9cf47fd70757092962c7850fadf4djsollen@google.com                // Default case - just add it to the end of the fallback list
721bfae9d373ccc9cf47fd70757092962c7850fadf4djsollen@google.com                *fallbackFonts.append() = family;
722bfae9d373ccc9cf47fd70757092962c7850fadf4djsollen@google.com            } else {
723bfae9d373ccc9cf47fd70757092962c7850fadf4djsollen@google.com                // no order specified on this font, but we're incrementing the order
724bfae9d373ccc9cf47fd70757092962c7850fadf4djsollen@google.com                // based on an earlier order insertion request
725bfae9d373ccc9cf47fd70757092962c7850fadf4djsollen@google.com                *fallbackFonts.insert(currentOrder++) = family;
726bfae9d373ccc9cf47fd70757092962c7850fadf4djsollen@google.com            }
727bfae9d373ccc9cf47fd70757092962c7850fadf4djsollen@google.com        } else {
728bfae9d373ccc9cf47fd70757092962c7850fadf4djsollen@google.com            // Add the font into the fallback list in the specified order. Set
729bfae9d373ccc9cf47fd70757092962c7850fadf4djsollen@google.com            // currentOrder for correct placement of other fonts in the vendor list.
730bfae9d373ccc9cf47fd70757092962c7850fadf4djsollen@google.com            *fallbackFonts.insert(order) = family;
731bfae9d373ccc9cf47fd70757092962c7850fadf4djsollen@google.com            currentOrder = order + 1;
732bfae9d373ccc9cf47fd70757092962c7850fadf4djsollen@google.com        }
733bfae9d373ccc9cf47fd70757092962c7850fadf4djsollen@google.com    }
734bfae9d373ccc9cf47fd70757092962c7850fadf4djsollen@google.com}
735bfae9d373ccc9cf47fd70757092962c7850fadf4djsollen@google.com
7367fa87cd09f49f1ee9bc27e263038d0f0ae706241bungemanvoid SkFontConfigParser::GetSystemFontFamilies(SkTDArray<FontFamily*>& fontFamilies) {
737efbad37180c6b6f90d4b7e96a234e8065d2ec395bungeman    // Version 21 of the system font configuration does not need any fallback configuration files.
7387fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman    SkString basePath(getenv("ANDROID_ROOT"));
7397fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman    basePath.append(SK_FONT_FILE_PREFIX, sizeof(SK_FONT_FILE_PREFIX) - 1);
7407fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman
7417fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman    if (append_system_font_families(fontFamilies, basePath) >= 21) {
742efbad37180c6b6f90d4b7e96a234e8065d2ec395bungeman        return;
743efbad37180c6b6f90d4b7e96a234e8065d2ec395bungeman    }
744bfae9d373ccc9cf47fd70757092962c7850fadf4djsollen@google.com
745bfae9d373ccc9cf47fd70757092962c7850fadf4djsollen@google.com    // Append all the fallback fonts to system fonts
746bfae9d373ccc9cf47fd70757092962c7850fadf4djsollen@google.com    SkTDArray<FontFamily*> fallbackFonts;
7477fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman    append_system_fallback_font_families(fallbackFonts, basePath);
7487fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman    mixin_vendor_fallback_font_families(fallbackFonts, basePath);
7497fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman    fontFamilies.append(fallbackFonts.count(), fallbackFonts.begin());
750bfae9d373ccc9cf47fd70757092962c7850fadf4djsollen@google.com}
751bfae9d373ccc9cf47fd70757092962c7850fadf4djsollen@google.com
7527fa87cd09f49f1ee9bc27e263038d0f0ae706241bungemanvoid SkFontConfigParser::GetCustomFontFamilies(SkTDArray<FontFamily*>& fontFamilies,
7537fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman                                               const SkString& basePath,
7547fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman                                               const char* fontsXml,
755c3c694342ad393b88cee5885395f182082aa2ebbbungeman                                               const char* fallbackFontsXml,
756c3c694342ad393b88cee5885395f182082aa2ebbbungeman                                               const char* langFallbackFontsDir)
7577fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman{
7587fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman    if (fontsXml) {
7597fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman        parse_config_file(fontsXml, fontFamilies, basePath, false);
760f79673bbae0a662c1428755e2719dadf944e4ba1tomhudson    }
7617fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman    if (fallbackFontsXml) {
7627fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman        parse_config_file(fallbackFontsXml, fontFamilies, basePath, true);
763bfae9d373ccc9cf47fd70757092962c7850fadf4djsollen@google.com    }
764c3c694342ad393b88cee5885395f182082aa2ebbbungeman    if (langFallbackFontsDir) {
765c3c694342ad393b88cee5885395f182082aa2ebbbungeman        append_fallback_font_families_for_locale(fontFamilies,
766c3c694342ad393b88cee5885395f182082aa2ebbbungeman                                                 langFallbackFontsDir,
767c3c694342ad393b88cee5885395f182082aa2ebbbungeman                                                 basePath);
768c3c694342ad393b88cee5885395f182082aa2ebbbungeman    }
769bfae9d373ccc9cf47fd70757092962c7850fadf4djsollen@google.com}
7703b6255493e458c6b2c1412af908581f0bf3f6b70djsollen
7713b6255493e458c6b2c1412af908581f0bf3f6b70djsollenSkLanguage SkLanguage::getParent() const {
7723b6255493e458c6b2c1412af908581f0bf3f6b70djsollen    SkASSERT(!fTag.isEmpty());
7733b6255493e458c6b2c1412af908581f0bf3f6b70djsollen    const char* tag = fTag.c_str();
7743b6255493e458c6b2c1412af908581f0bf3f6b70djsollen
7753b6255493e458c6b2c1412af908581f0bf3f6b70djsollen    // strip off the rightmost "-.*"
7763b6255493e458c6b2c1412af908581f0bf3f6b70djsollen    const char* parentTagEnd = strrchr(tag, '-');
7773b6255493e458c6b2c1412af908581f0bf3f6b70djsollen    if (parentTagEnd == NULL) {
7783b6255493e458c6b2c1412af908581f0bf3f6b70djsollen        return SkLanguage();
7793b6255493e458c6b2c1412af908581f0bf3f6b70djsollen    }
7803b6255493e458c6b2c1412af908581f0bf3f6b70djsollen    size_t parentTagLen = parentTagEnd - tag;
7813b6255493e458c6b2c1412af908581f0bf3f6b70djsollen    return SkLanguage(tag, parentTagLen);
7823b6255493e458c6b2c1412af908581f0bf3f6b70djsollen}
783