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
81ee76510f5dbf632d30975fc3509ef4f609156d2mtklein// Despite the name and location, this is portable code.
91ee76510f5dbf632d30975fc3509ef4f609156d2mtklein
106c71e0a065c2eb32139682bb1ca1cbbeb02ebcb9benjaminwagner#include "SkFixed.h"
1147a1e96b957b50662274360f1a390d76ab3d02ccbungeman#include "SkFontMgr.h"
12c53085413e0b4704aa89cc18396613d59e6ccb4dbungeman#include "SkFontMgr_android_parser.h"
13b549cc38c8404c58642ada75c0b24907702cc005Herb Derby#include "SkMalloc.h"
1422cffcace2ad526f55ba2579e65836d38f009d1abungeman#include "SkOSFile.h"
157fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman#include "SkStream.h"
16bfae9d373ccc9cf47fd70757092962c7850fadf4djsollen@google.com#include "SkTDArray.h"
178d84c995319dd4a82e4f2054bbd19f968c671ca6bungeman#include "SkTSearch.h"
18f20488b4f2139e6ca09fee7e39b731dd8ab467dbbungeman#include "SkTemplates.h"
1954e63082191f337084f96083ca90d7c35273d6ffbungeman#include "SkTLogic.h"
20bfae9d373ccc9cf47fd70757092962c7850fadf4djsollen@google.com
21f20488b4f2139e6ca09fee7e39b731dd8ab467dbbungeman#include <expat.h>
22bfae9d373ccc9cf47fd70757092962c7850fadf4djsollen@google.com
237fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman#include <stdlib.h>
24f20488b4f2139e6ca09fee7e39b731dd8ab467dbbungeman#include <string.h>
258d84c995319dd4a82e4f2054bbd19f968c671ca6bungeman
2694fa4b99e2a53e997a90c7808cc5263f1bf40c9ftomhudson#define LMP_SYSTEM_FONTS_FILE "/system/etc/fonts.xml"
2794fa4b99e2a53e997a90c7808cc5263f1bf40c9ftomhudson#define OLD_SYSTEM_FONTS_FILE "/system/etc/system_fonts.xml"
28bfae9d373ccc9cf47fd70757092962c7850fadf4djsollen@google.com#define FALLBACK_FONTS_FILE "/system/etc/fallback_fonts.xml"
29bfae9d373ccc9cf47fd70757092962c7850fadf4djsollen@google.com#define VENDOR_FONTS_FILE "/vendor/etc/fallback_fonts.xml"
30bfae9d373ccc9cf47fd70757092962c7850fadf4djsollen@google.com
31a6c27bc5bd3b213d1e315c0bd9bbdcd75cec6900djsollen#define LOCALE_FALLBACK_FONTS_SYSTEM_DIR "/system/etc"
32a6c27bc5bd3b213d1e315c0bd9bbdcd75cec6900djsollen#define LOCALE_FALLBACK_FONTS_VENDOR_DIR "/vendor/etc"
33a6c27bc5bd3b213d1e315c0bd9bbdcd75cec6900djsollen#define LOCALE_FALLBACK_FONTS_PREFIX "fallback_fonts-"
34a6c27bc5bd3b213d1e315c0bd9bbdcd75cec6900djsollen#define LOCALE_FALLBACK_FONTS_SUFFIX ".xml"
35a6c27bc5bd3b213d1e315c0bd9bbdcd75cec6900djsollen
367fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman#ifndef SK_FONT_FILE_PREFIX
377fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman#    define SK_FONT_FILE_PREFIX "/fonts/"
387fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman#endif
397fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman
40f79673bbae0a662c1428755e2719dadf944e4ba1tomhudson/**
4110b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman * This file contains TWO 'familyset' handlers:
4210b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman * One for JB and earlier which works with
4310b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman *   /system/etc/system_fonts.xml
4410b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman *   /system/etc/fallback_fonts.xml
4510b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman *   /vendor/etc/fallback_fonts.xml
4610b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman *   /system/etc/fallback_fonts-XX.xml
4710b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman *   /vendor/etc/fallback_fonts-XX.xml
4810b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman * and the other for LMP and later which works with
4910b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman *   /system/etc/fonts.xml
5010b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman *
5110b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman * If the 'familyset' 'version' attribute is 21 or higher the LMP parser is used, otherwise the JB.
52f79673bbae0a662c1428755e2719dadf944e4ba1tomhudson */
53f79673bbae0a662c1428755e2719dadf944e4ba1tomhudson
5410b063cb91c52fd1f570ee63307fe7e68c1501f1bungemanstruct FamilyData;
5510b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman
5610b063cb91c52fd1f570ee63307fe7e68c1501f1bungemanstruct TagHandler {
5710b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman    /** Called at the start tag.
5810b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman     *  Called immediately after the parent tag retuns this handler from a call to 'tag'.
5910b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman     *  Allows setting up for handling the tag content and processing attributes.
6096fcdcc219d2a0d3579719b84b28bede76efba64halcanary     *  If nullptr, will not be called.
6110b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman     */
6210b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman    void (*start)(FamilyData* data, const char* tag, const char** attributes);
6310b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman
6410b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman    /** Called at the end tag.
6510b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman     *  Allows post-processing of any accumulated information.
6610b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman     *  This will be the last call made in relation to the current tag.
6796fcdcc219d2a0d3579719b84b28bede76efba64halcanary     *  If nullptr, will not be called.
6810b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman     */
6910b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman    void (*end)(FamilyData* data, const char* tag);
7010b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman
7110b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman    /** Called when a nested tag is encountered.
7210b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman     *  This is responsible for determining how to handle the tag.
7396fcdcc219d2a0d3579719b84b28bede76efba64halcanary     *  If the tag is not recognized, return nullptr to skip the tag.
7496fcdcc219d2a0d3579719b84b28bede76efba64halcanary     *  If nullptr, all nested tags will be skipped.
7510b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman     */
7610b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman    const TagHandler* (*tag)(FamilyData* data, const char* tag, const char** attributes);
7710b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman
7810b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman    /** The character handler for this tag.
7910b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman     *  This is only active for character data contained directly in this tag (not sub-tags).
8010b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman     *  The first parameter will be castable to a FamilyData*.
8196fcdcc219d2a0d3579719b84b28bede76efba64halcanary     *  If nullptr, any character data in this tag will be ignored.
8210b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman     */
8310b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman    XML_CharacterDataHandler chars;
847fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman};
85bfae9d373ccc9cf47fd70757092962c7850fadf4djsollen@google.com
8610b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman/** Represents the current parsing state. */
87bfae9d373ccc9cf47fd70757092962c7850fadf4djsollen@google.comstruct FamilyData {
887fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman    FamilyData(XML_Parser parser, SkTDArray<FontFamily*>& families,
8910b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman               const SkString& basePath, bool isFallback, const char* filename,
9010b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman               const TagHandler* topLevelHandler)
91b6bed17ea81ff8fad68a7db79307bdcbcd4738a8bungeman        : fParser(parser)
92b6bed17ea81ff8fad68a7db79307bdcbcd4738a8bungeman        , fFamilies(families)
9396fcdcc219d2a0d3579719b84b28bede76efba64halcanary        , fCurrentFamily(nullptr)
9496fcdcc219d2a0d3579719b84b28bede76efba64halcanary        , fCurrentFontInfo(nullptr)
95efbad37180c6b6f90d4b7e96a234e8065d2ec395bungeman        , fVersion(0)
967fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman        , fBasePath(basePath)
977fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman        , fIsFallback(isFallback)
98f61475e95d9bb38f741f9e51221c086250b9ad72bungeman        , fFilename(filename)
9910b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        , fDepth(1)
10010b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        , fSkip(0)
10110b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        , fHandler(&topLevelHandler, 1)
102fc6c37b981daeece7474ce61070c707c37eefa62Mike Klein    { }
103b6bed17ea81ff8fad68a7db79307bdcbcd4738a8bungeman
104145dbcd165d9d27298eb8888bc240e2d06a95464Ben Wagner    XML_Parser fParser;                         // The expat parser doing the work, owned by caller
105145dbcd165d9d27298eb8888bc240e2d06a95464Ben Wagner    SkTDArray<FontFamily*>& fFamilies;          // The array to append families, owned by caller
106145dbcd165d9d27298eb8888bc240e2d06a95464Ben Wagner    std::unique_ptr<FontFamily> fCurrentFamily; // The family being created, owned by this
107145dbcd165d9d27298eb8888bc240e2d06a95464Ben Wagner    FontFileInfo* fCurrentFontInfo;             // The info being created, owned by fCurrentFamily
108145dbcd165d9d27298eb8888bc240e2d06a95464Ben Wagner    int fVersion;                               // The version of the file parsed.
109145dbcd165d9d27298eb8888bc240e2d06a95464Ben Wagner    const SkString& fBasePath;                  // The current base path.
110145dbcd165d9d27298eb8888bc240e2d06a95464Ben Wagner    const bool fIsFallback;                     // The file being parsed is a fallback file
111145dbcd165d9d27298eb8888bc240e2d06a95464Ben Wagner    const char* fFilename;                      // The name of the file currently being parsed.
112145dbcd165d9d27298eb8888bc240e2d06a95464Ben Wagner
113145dbcd165d9d27298eb8888bc240e2d06a95464Ben Wagner    int fDepth;                                 // The current element depth of the parse.
114145dbcd165d9d27298eb8888bc240e2d06a95464Ben Wagner    int fSkip;                                  // The depth to stop skipping, 0 if not skipping.
115145dbcd165d9d27298eb8888bc240e2d06a95464Ben Wagner    SkTDArray<const TagHandler*> fHandler;      // The stack of current tag handlers.
116bfae9d373ccc9cf47fd70757092962c7850fadf4djsollen@google.com};
117bfae9d373ccc9cf47fd70757092962c7850fadf4djsollen@google.com
1187fa87cd09f49f1ee9bc27e263038d0f0ae706241bungemanstatic bool memeq(const char* s1, const char* s2, size_t n1, size_t n2) {
1197fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman    return n1 == n2 && 0 == memcmp(s1, s2, n1);
1207fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman}
1217fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman#define MEMEQ(c, s, n) memeq(c, s, sizeof(c) - 1, n)
1227fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman
12396fcdcc219d2a0d3579719b84b28bede76efba64halcanary#define ATTS_NON_NULL(a, i) (a[i] != nullptr && a[i+1] != nullptr)
1247fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman
125c53085413e0b4704aa89cc18396613d59e6ccb4dbungeman#define SK_FONTMGR_ANDROID_PARSER_PREFIX "[SkFontMgr Android Parser] "
126f61475e95d9bb38f741f9e51221c086250b9ad72bungeman
127f61475e95d9bb38f741f9e51221c086250b9ad72bungeman#define SK_FONTCONFIGPARSER_WARNING(message, ...) SkDebugf( \
128c53085413e0b4704aa89cc18396613d59e6ccb4dbungeman    SK_FONTMGR_ANDROID_PARSER_PREFIX "%s:%d:%d: warning: " message "\n", \
129f61475e95d9bb38f741f9e51221c086250b9ad72bungeman    self->fFilename, \
130f61475e95d9bb38f741f9e51221c086250b9ad72bungeman    XML_GetCurrentLineNumber(self->fParser), \
131f61475e95d9bb38f741f9e51221c086250b9ad72bungeman    XML_GetCurrentColumnNumber(self->fParser), \
132f61475e95d9bb38f741f9e51221c086250b9ad72bungeman    ##__VA_ARGS__);
133f61475e95d9bb38f741f9e51221c086250b9ad72bungeman
13410b063cb91c52fd1f570ee63307fe7e68c1501f1bungemanstatic bool is_whitespace(char c) {
13510b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman    return c == ' ' || c == '\n'|| c == '\r' || c == '\t';
13610b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman}
137f79673bbae0a662c1428755e2719dadf944e4ba1tomhudson
13810b063cb91c52fd1f570ee63307fe7e68c1501f1bungemanstatic void trim_string(SkString* s) {
13910b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman    char* str = s->writable_str();
14010b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman    const char* start = str;  // start is inclusive
14110b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman    const char* end = start + s->size();  // end is exclusive
14210b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman    while (is_whitespace(*start)) { ++start; }
14310b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman    if (start != end) {
14410b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        --end;  // make end inclusive
14510b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        while (is_whitespace(*end)) { --end; }
14610b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        ++end;  // make end exclusive
14707544757c9fcf0f359f1686a3779eb2e75dd5b36tomhudson    }
14810b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman    size_t len = end - start;
14910b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman    memmove(str, start, len);
15010b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman    s->resize(len);
151f79673bbae0a662c1428755e2719dadf944e4ba1tomhudson}
152f79673bbae0a662c1428755e2719dadf944e4ba1tomhudson
15310b063cb91c52fd1f570ee63307fe7e68c1501f1bungemannamespace lmpParser {
154f79673bbae0a662c1428755e2719dadf944e4ba1tomhudson
15541868fe5625fc3bd70daa3f461c881b5db6a9265bungemanstatic const TagHandler axisHandler = {
15641868fe5625fc3bd70daa3f461c881b5db6a9265bungeman    /*start*/[](FamilyData* self, const char* tag, const char** attributes) {
15741868fe5625fc3bd70daa3f461c881b5db6a9265bungeman        FontFileInfo& file = *self->fCurrentFontInfo;
15847a1e96b957b50662274360f1a390d76ab3d02ccbungeman        SkFourByteTag axisTag = SkSetFourByteTag('\0','\0','\0','\0');
15947a1e96b957b50662274360f1a390d76ab3d02ccbungeman        SkFixed axisStyleValue = 0;
16047a1e96b957b50662274360f1a390d76ab3d02ccbungeman        bool axisTagIsValid = false;
16147a1e96b957b50662274360f1a390d76ab3d02ccbungeman        bool axisStyleValueIsValid = false;
16241868fe5625fc3bd70daa3f461c881b5db6a9265bungeman        for (size_t i = 0; ATTS_NON_NULL(attributes, i); i += 2) {
16341868fe5625fc3bd70daa3f461c881b5db6a9265bungeman            const char* name = attributes[i];
16441868fe5625fc3bd70daa3f461c881b5db6a9265bungeman            const char* value = attributes[i+1];
16541868fe5625fc3bd70daa3f461c881b5db6a9265bungeman            size_t nameLen = strlen(name);
16641868fe5625fc3bd70daa3f461c881b5db6a9265bungeman            if (MEMEQ("tag", name, nameLen)) {
16741868fe5625fc3bd70daa3f461c881b5db6a9265bungeman                size_t valueLen = strlen(value);
16841868fe5625fc3bd70daa3f461c881b5db6a9265bungeman                if (valueLen == 4) {
16947a1e96b957b50662274360f1a390d76ab3d02ccbungeman                    axisTag = SkSetFourByteTag(value[0], value[1], value[2], value[3]);
17047a1e96b957b50662274360f1a390d76ab3d02ccbungeman                    axisTagIsValid = true;
171fc497343cbcbd526f77da913ae2feca0e1b1b866Ben Wagner                    for (int j = 0; j < file.fVariationDesignPosition.count() - 1; ++j) {
172fc497343cbcbd526f77da913ae2feca0e1b1b866Ben Wagner                        if (file.fVariationDesignPosition[j].axis == axisTag) {
17347a1e96b957b50662274360f1a390d76ab3d02ccbungeman                            axisTagIsValid = false;
17441868fe5625fc3bd70daa3f461c881b5db6a9265bungeman                            SK_FONTCONFIGPARSER_WARNING("'%c%c%c%c' axis specified more than once",
17547a1e96b957b50662274360f1a390d76ab3d02ccbungeman                                                        (axisTag >> 24) & 0xFF,
17647a1e96b957b50662274360f1a390d76ab3d02ccbungeman                                                        (axisTag >> 16) & 0xFF,
17747a1e96b957b50662274360f1a390d76ab3d02ccbungeman                                                        (axisTag >>  8) & 0xFF,
17847a1e96b957b50662274360f1a390d76ab3d02ccbungeman                                                        (axisTag      ) & 0xFF);
17941868fe5625fc3bd70daa3f461c881b5db6a9265bungeman                        }
18041868fe5625fc3bd70daa3f461c881b5db6a9265bungeman                    }
18141868fe5625fc3bd70daa3f461c881b5db6a9265bungeman                } else {
18241868fe5625fc3bd70daa3f461c881b5db6a9265bungeman                    SK_FONTCONFIGPARSER_WARNING("'%s' is an invalid axis tag", value);
18341868fe5625fc3bd70daa3f461c881b5db6a9265bungeman                }
18441868fe5625fc3bd70daa3f461c881b5db6a9265bungeman            } else if (MEMEQ("stylevalue", name, nameLen)) {
18547a1e96b957b50662274360f1a390d76ab3d02ccbungeman                if (parse_fixed<16>(value, &axisStyleValue)) {
18647a1e96b957b50662274360f1a390d76ab3d02ccbungeman                    axisStyleValueIsValid = true;
18747a1e96b957b50662274360f1a390d76ab3d02ccbungeman                } else {
18841868fe5625fc3bd70daa3f461c881b5db6a9265bungeman                    SK_FONTCONFIGPARSER_WARNING("'%s' is an invalid axis stylevalue", value);
18941868fe5625fc3bd70daa3f461c881b5db6a9265bungeman                }
19041868fe5625fc3bd70daa3f461c881b5db6a9265bungeman            }
19141868fe5625fc3bd70daa3f461c881b5db6a9265bungeman        }
19247a1e96b957b50662274360f1a390d76ab3d02ccbungeman        if (axisTagIsValid && axisStyleValueIsValid) {
193fc497343cbcbd526f77da913ae2feca0e1b1b866Ben Wagner            auto& coordinate = file.fVariationDesignPosition.push_back();
194fc497343cbcbd526f77da913ae2feca0e1b1b866Ben Wagner            coordinate.axis = axisTag;
195fc497343cbcbd526f77da913ae2feca0e1b1b866Ben Wagner            coordinate.value = SkFixedToScalar(axisStyleValue);
19647a1e96b957b50662274360f1a390d76ab3d02ccbungeman        }
19741868fe5625fc3bd70daa3f461c881b5db6a9265bungeman    },
19896fcdcc219d2a0d3579719b84b28bede76efba64halcanary    /*end*/nullptr,
19996fcdcc219d2a0d3579719b84b28bede76efba64halcanary    /*tag*/nullptr,
20096fcdcc219d2a0d3579719b84b28bede76efba64halcanary    /*chars*/nullptr,
20141868fe5625fc3bd70daa3f461c881b5db6a9265bungeman};
20241868fe5625fc3bd70daa3f461c881b5db6a9265bungeman
20310b063cb91c52fd1f570ee63307fe7e68c1501f1bungemanstatic const TagHandler fontHandler = {
20410b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman    /*start*/[](FamilyData* self, const char* tag, const char** attributes) {
20510b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        // 'weight' (non-negative integer) [default 0]
20610b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        // 'style' ("normal", "italic") [default "auto"]
20710b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        // 'index' (non-negative integer) [default 0]
20810b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        // The character data should be a filename.
20910b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        FontFileInfo& file = self->fCurrentFamily->fFonts.push_back();
21010b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        self->fCurrentFontInfo = &file;
21110b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        for (size_t i = 0; ATTS_NON_NULL(attributes, i); i += 2) {
21210b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman            const char* name = attributes[i];
21310b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman            const char* value = attributes[i+1];
21410b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman            size_t nameLen = strlen(name);
21510b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman            if (MEMEQ("weight", name, nameLen)) {
21610b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman                if (!parse_non_negative_integer(value, &file.fWeight)) {
21710b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman                    SK_FONTCONFIGPARSER_WARNING("'%s' is an invalid weight", value);
21810b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman                }
21910b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman            } else if (MEMEQ("style", name, nameLen)) {
22010b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman                size_t valueLen = strlen(value);
22110b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman                if (MEMEQ("normal", value, valueLen)) {
22210b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman                    file.fStyle = FontFileInfo::Style::kNormal;
22310b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman                } else if (MEMEQ("italic", value, valueLen)) {
22410b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman                    file.fStyle = FontFileInfo::Style::kItalic;
22510b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman                }
22610b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman            } else if (MEMEQ("index", name, nameLen)) {
22710b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman                if (!parse_non_negative_integer(value, &file.fIndex)) {
22810b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman                    SK_FONTCONFIGPARSER_WARNING("'%s' is an invalid index", value);
22910b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman                }
230d3ddea284ec6611a93a6b75e64de39d0bc7e083ctomhudson            }
23110b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        }
23210b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman    },
23310b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman    /*end*/[](FamilyData* self, const char* tag) {
23410b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        trim_string(&self->fCurrentFontInfo->fFileName);
23510b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman    },
23641868fe5625fc3bd70daa3f461c881b5db6a9265bungeman    /*tag*/[](FamilyData* self, const char* tag, const char** attributes) -> const TagHandler* {
23741868fe5625fc3bd70daa3f461c881b5db6a9265bungeman        size_t len = strlen(tag);
23841868fe5625fc3bd70daa3f461c881b5db6a9265bungeman        if (MEMEQ("axis", tag, len)) {
23941868fe5625fc3bd70daa3f461c881b5db6a9265bungeman            return &axisHandler;
24041868fe5625fc3bd70daa3f461c881b5db6a9265bungeman        }
24196fcdcc219d2a0d3579719b84b28bede76efba64halcanary        return nullptr;
24241868fe5625fc3bd70daa3f461c881b5db6a9265bungeman    },
24310b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman    /*chars*/[](void* data, const char* s, int len) {
24410b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        FamilyData* self = static_cast<FamilyData*>(data);
24510b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        self->fCurrentFontInfo->fFileName.append(s, len);
24610b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman    }
24710b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman};
24810b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman
24910b063cb91c52fd1f570ee63307fe7e68c1501f1bungemanstatic const TagHandler familyHandler = {
25010b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman    /*start*/[](FamilyData* self, const char* tag, const char** attributes) {
25110b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        // 'name' (string) [optional]
25210b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        // 'lang' (string) [default ""]
25310b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        // 'variant' ("elegant", "compact") [default "default"]
25410b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        // If there is no name, this is a fallback only font.
25510b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        FontFamily* family = new FontFamily(self->fBasePath, true);
25610b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        self->fCurrentFamily.reset(family);
25710b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        for (size_t i = 0; ATTS_NON_NULL(attributes, i); i += 2) {
25810b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman            const char* name = attributes[i];
25910b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman            const char* value = attributes[i+1];
26010b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman            size_t nameLen = strlen(name);
261e85a754a4ce9b279159270faa6717932f7a8548fbungeman            size_t valueLen = strlen(value);
26210b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman            if (MEMEQ("name", name, nameLen)) {
26310b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman                SkAutoAsciiToLC tolc(value);
26410b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman                family->fNames.push_back().set(tolc.lc());
26510b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman                family->fIsFallbackFont = false;
26610b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman            } else if (MEMEQ("lang", name, nameLen)) {
26710b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman                family->fLanguage = SkLanguage(value, valueLen);
26810b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman            } else if (MEMEQ("variant", name, nameLen)) {
26910b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman                if (MEMEQ("elegant", value, valueLen)) {
27010b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman                    family->fVariant = kElegant_FontVariant;
27110b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman                } else if (MEMEQ("compact", value, valueLen)) {
27210b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman                    family->fVariant = kCompact_FontVariant;
27310b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman                }
274b8a1d30a42d13ae83690b2d854a024d9b56e7b71bungeman            }
27507544757c9fcf0f359f1686a3779eb2e75dd5b36tomhudson        }
27610b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman    },
27710b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman    /*end*/[](FamilyData* self, const char* tag) {
27818300a3aa7cb6eb55d21bb0450dffa58b6fc062cmtklein        *self->fFamilies.append() = self->fCurrentFamily.release();
27910b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman    },
28010b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman    /*tag*/[](FamilyData* self, const char* tag, const char** attributes) -> const TagHandler* {
28110b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        size_t len = strlen(tag);
28210b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        if (MEMEQ("font", tag, len)) {
28310b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman            return &fontHandler;
28410b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        }
28596fcdcc219d2a0d3579719b84b28bede76efba64halcanary        return nullptr;
28610b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman    },
28796fcdcc219d2a0d3579719b84b28bede76efba64halcanary    /*chars*/nullptr,
28810b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman};
28907544757c9fcf0f359f1686a3779eb2e75dd5b36tomhudson
290eb2be7fa4413a566212782d8efae5dc225002821bungemanstatic FontFamily* find_family(FamilyData* self, const SkString& familyName) {
2917fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman    for (int i = 0; i < self->fFamilies.count(); i++) {
2927fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman        FontFamily* candidate = self->fFamilies[i];
29307544757c9fcf0f359f1686a3779eb2e75dd5b36tomhudson        for (int j = 0; j < candidate->fNames.count(); j++) {
2947fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman            if (candidate->fNames[j] == familyName) {
29507544757c9fcf0f359f1686a3779eb2e75dd5b36tomhudson                return candidate;
29607544757c9fcf0f359f1686a3779eb2e75dd5b36tomhudson            }
29707544757c9fcf0f359f1686a3779eb2e75dd5b36tomhudson        }
29807544757c9fcf0f359f1686a3779eb2e75dd5b36tomhudson    }
29996fcdcc219d2a0d3579719b84b28bede76efba64halcanary    return nullptr;
30007544757c9fcf0f359f1686a3779eb2e75dd5b36tomhudson}
30107544757c9fcf0f359f1686a3779eb2e75dd5b36tomhudson
30210b063cb91c52fd1f570ee63307fe7e68c1501f1bungemanstatic const TagHandler aliasHandler = {
30310b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman    /*start*/[](FamilyData* self, const char* tag, const char** attributes) {
30410b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        // 'name' (string) introduces a new family name.
30510b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        // 'to' (string) specifies which (previous) family to alias
30610b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        // 'weight' (non-negative integer) [optional]
30710b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        // If it *does not* have a weight, 'name' is an alias for the entire 'to' family.
30810b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        // If it *does* have a weight, 'name' is a new family consisting of
30910b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        // the font(s) with 'weight' from the 'to' family.
31010b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman
31110b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        SkString aliasName;
31210b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        SkString to;
31310b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        int weight = 0;
31410b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        for (size_t i = 0; ATTS_NON_NULL(attributes, i); i += 2) {
31510b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman            const char* name = attributes[i];
31610b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman            const char* value = attributes[i+1];
31710b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman            size_t nameLen = strlen(name);
31810b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman            if (MEMEQ("name", name, nameLen)) {
31910b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman                SkAutoAsciiToLC tolc(value);
32010b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman                aliasName.set(tolc.lc());
32110b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman            } else if (MEMEQ("to", name, nameLen)) {
32210b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman                to.set(value);
32310b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman            } else if (MEMEQ("weight", name, nameLen)) {
32410b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman                if (!parse_non_negative_integer(value, &weight)) {
32510b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman                    SK_FONTCONFIGPARSER_WARNING("'%s' is an invalid weight", value);
32610b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman                }
3277fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman            }
32807544757c9fcf0f359f1686a3779eb2e75dd5b36tomhudson        }
32907544757c9fcf0f359f1686a3779eb2e75dd5b36tomhudson
33010b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        // Assumes that the named family is already declared
33110b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        FontFamily* targetFamily = find_family(self, to);
33210b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        if (!targetFamily) {
33310b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman            SK_FONTCONFIGPARSER_WARNING("'%s' alias target not found", to.c_str());
33410b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman            return;
33510b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        }
33607544757c9fcf0f359f1686a3779eb2e75dd5b36tomhudson
33710b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        if (weight) {
33810b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman            FontFamily* family = new FontFamily(targetFamily->fBasePath, self->fIsFallback);
33910b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman            family->fNames.push_back().set(aliasName);
34007544757c9fcf0f359f1686a3779eb2e75dd5b36tomhudson
34110b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman            for (int i = 0; i < targetFamily->fFonts.count(); i++) {
34210b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman                if (targetFamily->fFonts[i].fWeight == weight) {
34310b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman                    family->fFonts.push_back(targetFamily->fFonts[i]);
34410b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman                }
34507544757c9fcf0f359f1686a3779eb2e75dd5b36tomhudson            }
34610b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman            *self->fFamilies.append() = family;
34710b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        } else {
34810b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman            targetFamily->fNames.push_back().set(aliasName);
34907544757c9fcf0f359f1686a3779eb2e75dd5b36tomhudson        }
35010b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman    },
35196fcdcc219d2a0d3579719b84b28bede76efba64halcanary    /*end*/nullptr,
35296fcdcc219d2a0d3579719b84b28bede76efba64halcanary    /*tag*/nullptr,
35396fcdcc219d2a0d3579719b84b28bede76efba64halcanary    /*chars*/nullptr,
35410b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman};
355c0727d117e67844f0c8794cc7eaa49a96a015347bungeman
35610b063cb91c52fd1f570ee63307fe7e68c1501f1bungemanstatic const TagHandler familySetHandler = {
35710b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman    /*start*/[](FamilyData* self, const char* tag, const char** attributes) { },
35896fcdcc219d2a0d3579719b84b28bede76efba64halcanary    /*end*/nullptr,
35910b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman    /*tag*/[](FamilyData* self, const char* tag, const char** attributes) -> const TagHandler* {
36010b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        size_t len = strlen(tag);
36110b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        if (MEMEQ("family", tag, len)) {
36210b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman            return &familyHandler;
36310b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        } else if (MEMEQ("alias", tag, len)) {
36410b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman            return &aliasHandler;
36510b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        }
36696fcdcc219d2a0d3579719b84b28bede76efba64halcanary        return nullptr;
36710b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman    },
36896fcdcc219d2a0d3579719b84b28bede76efba64halcanary    /*chars*/nullptr,
36910b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman};
370f79673bbae0a662c1428755e2719dadf944e4ba1tomhudson
371f79673bbae0a662c1428755e2719dadf944e4ba1tomhudson} // lmpParser
372f79673bbae0a662c1428755e2719dadf944e4ba1tomhudson
373f79673bbae0a662c1428755e2719dadf944e4ba1tomhudsonnamespace jbParser {
374f79673bbae0a662c1428755e2719dadf944e4ba1tomhudson
37510b063cb91c52fd1f570ee63307fe7e68c1501f1bungemanstatic const TagHandler fileHandler = {
37610b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman    /*start*/[](FamilyData* self, const char* tag, const char** attributes) {
37710b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        // 'variant' ("elegant", "compact") [default "default"]
37810b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        // 'lang' (string) [default ""]
37910b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        // 'index' (non-negative integer) [default 0]
38010b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        // The character data should be a filename.
38110b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        FontFamily& currentFamily = *self->fCurrentFamily.get();
38210b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        FontFileInfo& newFileInfo = currentFamily.fFonts.push_back();
38310b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        if (attributes) {
38410b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman            for (size_t i = 0; ATTS_NON_NULL(attributes, i); i += 2) {
38510b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman                const char* name = attributes[i];
38610b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman                const char* value = attributes[i+1];
38710b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman                size_t nameLen = strlen(name);
38810b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman                size_t valueLen = strlen(value);
38910b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman                if (MEMEQ("variant", name, nameLen)) {
39010b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman                    const FontVariant prevVariant = currentFamily.fVariant;
39110b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman                    if (MEMEQ("elegant", value, valueLen)) {
39210b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman                        currentFamily.fVariant = kElegant_FontVariant;
39310b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman                    } else if (MEMEQ("compact", value, valueLen)) {
39410b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman                        currentFamily.fVariant = kCompact_FontVariant;
39510b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman                    }
39610b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman                    if (currentFamily.fFonts.count() > 1 && currentFamily.fVariant != prevVariant) {
39710b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman                        SK_FONTCONFIGPARSER_WARNING("'%s' unexpected variant found\n"
39810b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman                            "Note: Every font file within a family must have identical variants.",
39910b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman                            value);
40010b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman                    }
40110b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman
40210b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman                } else if (MEMEQ("lang", name, nameLen)) {
40310b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman                    SkLanguage prevLang = currentFamily.fLanguage;
40410b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman                    currentFamily.fLanguage = SkLanguage(value, valueLen);
40510b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman                    if (currentFamily.fFonts.count() > 1 && currentFamily.fLanguage != prevLang) {
40610b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman                        SK_FONTCONFIGPARSER_WARNING("'%s' unexpected language found\n"
40710b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman                            "Note: Every font file within a family must have identical languages.",
40810b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman                            value);
40910b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman                    }
41010b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman
41110b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman                } else if (MEMEQ("index", name, nameLen)) {
41210b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman                    if (!parse_non_negative_integer(value, &newFileInfo.fIndex)) {
41310b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman                        SK_FONTCONFIGPARSER_WARNING("'%s' is an invalid index", value);
41410b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman                    }
41510b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman                }
416bfae9d373ccc9cf47fd70757092962c7850fadf4djsollen@google.com            }
417bfae9d373ccc9cf47fd70757092962c7850fadf4djsollen@google.com        }
41810b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        self->fCurrentFontInfo = &newFileInfo;
41910b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman    },
42096fcdcc219d2a0d3579719b84b28bede76efba64halcanary    /*end*/nullptr,
42196fcdcc219d2a0d3579719b84b28bede76efba64halcanary    /*tag*/nullptr,
42210b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman    /*chars*/[](void* data, const char* s, int len) {
42310b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        FamilyData* self = static_cast<FamilyData*>(data);
42410b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        self->fCurrentFontInfo->fFileName.append(s, len);
425bfae9d373ccc9cf47fd70757092962c7850fadf4djsollen@google.com    }
42610b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman};
427bfae9d373ccc9cf47fd70757092962c7850fadf4djsollen@google.com
42810b063cb91c52fd1f570ee63307fe7e68c1501f1bungemanstatic const TagHandler fileSetHandler = {
42996fcdcc219d2a0d3579719b84b28bede76efba64halcanary    /*start*/nullptr,
43096fcdcc219d2a0d3579719b84b28bede76efba64halcanary    /*end*/nullptr,
43110b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman    /*tag*/[](FamilyData* self, const char* tag, const char** attributes) -> const TagHandler* {
43210b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        size_t len = strlen(tag);
43310b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        if (MEMEQ("file", tag, len)) {
43410b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman            return &fileHandler;
43510b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        }
43696fcdcc219d2a0d3579719b84b28bede76efba64halcanary        return nullptr;
43710b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman    },
43896fcdcc219d2a0d3579719b84b28bede76efba64halcanary    /*chars*/nullptr,
43910b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman};
44010b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman
44110b063cb91c52fd1f570ee63307fe7e68c1501f1bungemanstatic const TagHandler nameHandler = {
44210b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman    /*start*/[](FamilyData* self, const char* tag, const char** attributes) {
44310b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        // The character data should be a name for the font.
44410b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        self->fCurrentFamily->fNames.push_back();
44510b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman    },
44696fcdcc219d2a0d3579719b84b28bede76efba64halcanary    /*end*/nullptr,
44796fcdcc219d2a0d3579719b84b28bede76efba64halcanary    /*tag*/nullptr,
44810b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman    /*chars*/[](void* data, const char* s, int len) {
44910b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        FamilyData* self = static_cast<FamilyData*>(data);
45010b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        SkAutoAsciiToLC tolc(s, len);
45110b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        self->fCurrentFamily->fNames.back().append(tolc.lc(), len);
45210b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman    }
45310b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman};
45410b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman
45510b063cb91c52fd1f570ee63307fe7e68c1501f1bungemanstatic const TagHandler nameSetHandler = {
45696fcdcc219d2a0d3579719b84b28bede76efba64halcanary    /*start*/nullptr,
45796fcdcc219d2a0d3579719b84b28bede76efba64halcanary    /*end*/nullptr,
45810b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman    /*tag*/[](FamilyData* self, const char* tag, const char** attributes) -> const TagHandler* {
45910b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        size_t len = strlen(tag);
46010b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        if (MEMEQ("name", tag, len)) {
46110b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman            return &nameHandler;
46210b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        }
46396fcdcc219d2a0d3579719b84b28bede76efba64halcanary        return nullptr;
46410b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman    },
46596fcdcc219d2a0d3579719b84b28bede76efba64halcanary    /*chars*/nullptr,
46610b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman};
46710b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman
46810b063cb91c52fd1f570ee63307fe7e68c1501f1bungemanstatic const TagHandler familyHandler = {
46910b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman    /*start*/[](FamilyData* self, const char* tag, const char** attributes) {
47010b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        self->fCurrentFamily.reset(new FontFamily(self->fBasePath, self->fIsFallback));
47110b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        // 'order' (non-negative integer) [default -1]
4727fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman        for (size_t i = 0; ATTS_NON_NULL(attributes, i); i += 2) {
473f61475e95d9bb38f741f9e51221c086250b9ad72bungeman            const char* value = attributes[i+1];
47410b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman            parse_non_negative_integer(value, &self->fCurrentFamily->fOrder);
47510b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        }
47610b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman    },
47710b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman    /*end*/[](FamilyData* self, const char* tag) {
47818300a3aa7cb6eb55d21bb0450dffa58b6fc062cmtklein        *self->fFamilies.append() = self->fCurrentFamily.release();
47910b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman    },
48010b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman    /*tag*/[](FamilyData* self, const char* tag, const char** attributes) -> const TagHandler* {
48110b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        size_t len = strlen(tag);
48210b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        if (MEMEQ("nameset", tag, len)) {
48310b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman            return &nameSetHandler;
48410b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        } else if (MEMEQ("fileset", tag, len)) {
48510b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman            return &fileSetHandler;
48610b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        }
48796fcdcc219d2a0d3579719b84b28bede76efba64halcanary        return nullptr;
48810b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman    },
48996fcdcc219d2a0d3579719b84b28bede76efba64halcanary    /*chars*/nullptr,
49010b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman};
4913b6255493e458c6b2c1412af908581f0bf3f6b70djsollen
49210b063cb91c52fd1f570ee63307fe7e68c1501f1bungemanstatic const TagHandler familySetHandler = {
49396fcdcc219d2a0d3579719b84b28bede76efba64halcanary    /*start*/nullptr,
49496fcdcc219d2a0d3579719b84b28bede76efba64halcanary    /*end*/nullptr,
49510b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman    /*tag*/[](FamilyData* self, const char* tag, const char** attributes) -> const TagHandler* {
49610b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        size_t len = strlen(tag);
49710b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        if (MEMEQ("family", tag, len)) {
49810b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman            return &familyHandler;
49910b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        }
50096fcdcc219d2a0d3579719b84b28bede76efba64halcanary        return nullptr;
50110b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman    },
50296fcdcc219d2a0d3579719b84b28bede76efba64halcanary    /*chars*/nullptr,
50310b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman};
5047fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman
50510b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman} // namespace jbParser
50610b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman
50710b063cb91c52fd1f570ee63307fe7e68c1501f1bungemanstatic const TagHandler topLevelHandler = {
50896fcdcc219d2a0d3579719b84b28bede76efba64halcanary    /*start*/nullptr,
50996fcdcc219d2a0d3579719b84b28bede76efba64halcanary    /*end*/nullptr,
51010b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman    /*tag*/[](FamilyData* self, const char* tag, const char** attributes) -> const TagHandler* {
51110b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        size_t len = strlen(tag);
51210b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        if (MEMEQ("familyset", tag, len)) {
51310b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman            // 'version' (non-negative integer) [default 0]
51410b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman            for (size_t i = 0; ATTS_NON_NULL(attributes, i); i += 2) {
51510b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman                const char* name = attributes[i];
51610b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman                size_t nameLen = strlen(name);
51710b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman                if (MEMEQ("version", name, nameLen)) {
51810b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman                    const char* value = attributes[i+1];
51910b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman                    if (parse_non_negative_integer(value, &self->fVersion)) {
52010b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman                        if (self->fVersion >= 21) {
52110b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman                            return &lmpParser::familySetHandler;
52210b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman                        }
52310b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman                    }
5248d84c995319dd4a82e4f2054bbd19f968c671ca6bungeman                }
525bfae9d373ccc9cf47fd70757092962c7850fadf4djsollen@google.com            }
52610b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman            return &jbParser::familySetHandler;
527bfae9d373ccc9cf47fd70757092962c7850fadf4djsollen@google.com        }
52896fcdcc219d2a0d3579719b84b28bede76efba64halcanary        return nullptr;
52910b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman    },
53096fcdcc219d2a0d3579719b84b28bede76efba64halcanary    /*chars*/nullptr,
53110b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman};
532bfae9d373ccc9cf47fd70757092962c7850fadf4djsollen@google.com
53310b063cb91c52fd1f570ee63307fe7e68c1501f1bungemanstatic void XMLCALL start_element_handler(void *data, const char *tag, const char **attributes) {
5347fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman    FamilyData* self = static_cast<FamilyData*>(data);
53510b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman
53610b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman    if (!self->fSkip) {
53710b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        const TagHandler* parent = self->fHandler.top();
53896fcdcc219d2a0d3579719b84b28bede76efba64halcanary        const TagHandler* child = parent->tag ? parent->tag(self, tag, attributes) : nullptr;
53910b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        if (child) {
54010b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman            if (child->start) {
54110b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman                child->start(self, tag, attributes);
542bfae9d373ccc9cf47fd70757092962c7850fadf4djsollen@google.com            }
54310b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman            self->fHandler.push(child);
54410b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman            XML_SetCharacterDataHandler(self->fParser, child->chars);
54510b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        } else {
54610b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman            SK_FONTCONFIGPARSER_WARNING("'%s' tag not recognized, skipping", tag);
54796fcdcc219d2a0d3579719b84b28bede76efba64halcanary            XML_SetCharacterDataHandler(self->fParser, nullptr);
54810b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman            self->fSkip = self->fDepth;
549bfae9d373ccc9cf47fd70757092962c7850fadf4djsollen@google.com        }
550bfae9d373ccc9cf47fd70757092962c7850fadf4djsollen@google.com    }
55110b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman
55210b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman    ++self->fDepth;
553bfae9d373ccc9cf47fd70757092962c7850fadf4djsollen@google.com}
554bfae9d373ccc9cf47fd70757092962c7850fadf4djsollen@google.com
5557fa87cd09f49f1ee9bc27e263038d0f0ae706241bungemanstatic void XMLCALL end_element_handler(void* data, const char* tag) {
5567fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman    FamilyData* self = static_cast<FamilyData*>(data);
55710b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman    --self->fDepth;
55810b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman
55910b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman    if (!self->fSkip) {
56010b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        const TagHandler* child = self->fHandler.top();
56110b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        if (child->end) {
56210b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman            child->end(self, tag);
56310b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        }
56410b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        self->fHandler.pop();
56510b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        const TagHandler* parent = self->fHandler.top();
56610b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        XML_SetCharacterDataHandler(self->fParser, parent->chars);
567bfae9d373ccc9cf47fd70757092962c7850fadf4djsollen@google.com    }
568bfae9d373ccc9cf47fd70757092962c7850fadf4djsollen@google.com
56910b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman    if (self->fSkip == self->fDepth) {
57010b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        self->fSkip = 0;
57110b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        const TagHandler* parent = self->fHandler.top();
57210b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        XML_SetCharacterDataHandler(self->fParser, parent->chars);
57310b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman    }
57410b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman}
575f79673bbae0a662c1428755e2719dadf944e4ba1tomhudson
5767fa87cd09f49f1ee9bc27e263038d0f0ae706241bungemanstatic void XMLCALL xml_entity_decl_handler(void *data,
5777fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman                                            const XML_Char *entityName,
5787fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman                                            int is_parameter_entity,
5797fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman                                            const XML_Char *value,
5807fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman                                            int value_length,
5817fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman                                            const XML_Char *base,
5827fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman                                            const XML_Char *systemId,
5837fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman                                            const XML_Char *publicId,
5847fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman                                            const XML_Char *notationName)
5857fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman{
5867fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman    FamilyData* self = static_cast<FamilyData*>(data);
587f61475e95d9bb38f741f9e51221c086250b9ad72bungeman    SK_FONTCONFIGPARSER_WARNING("'%s' entity declaration found, stopping processing", entityName);
5887fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman    XML_StopParser(self->fParser, XML_FALSE);
5897fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman}
5907fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman
591eb2be7fa4413a566212782d8efae5dc225002821bungemanstatic const XML_Memory_Handling_Suite sk_XML_alloc = {
592eb2be7fa4413a566212782d8efae5dc225002821bungeman    sk_malloc_throw,
593eb2be7fa4413a566212782d8efae5dc225002821bungeman    sk_realloc_throw,
594eb2be7fa4413a566212782d8efae5dc225002821bungeman    sk_free
595eb2be7fa4413a566212782d8efae5dc225002821bungeman};
596eb2be7fa4413a566212782d8efae5dc225002821bungeman
597bfae9d373ccc9cf47fd70757092962c7850fadf4djsollen@google.com/**
598bfae9d373ccc9cf47fd70757092962c7850fadf4djsollen@google.com * This function parses the given filename and stores the results in the given
599efbad37180c6b6f90d4b7e96a234e8065d2ec395bungeman * families array. Returns the version of the file, negative if the file does not exist.
600bfae9d373ccc9cf47fd70757092962c7850fadf4djsollen@google.com */
6017fa87cd09f49f1ee9bc27e263038d0f0ae706241bungemanstatic int parse_config_file(const char* filename, SkTDArray<FontFamily*>& families,
6027fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman                             const SkString& basePath, bool isFallback)
6037fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman{
6047fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman    SkFILEStream file(filename);
60550c956791291e7f3cec23721157570b7911336b8djsollen@google.com
606bfae9d373ccc9cf47fd70757092962c7850fadf4djsollen@google.com    // Some of the files we attempt to parse (in particular, /vendor/etc/fallback_fonts.xml)
607bfae9d373ccc9cf47fd70757092962c7850fadf4djsollen@google.com    // are optional - failure here is okay because one of these optional files may not exist.
6087fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman    if (!file.isValid()) {
609c53085413e0b4704aa89cc18396613d59e6ccb4dbungeman        SkDebugf(SK_FONTMGR_ANDROID_PARSER_PREFIX "'%s' could not be opened\n", filename);
610efbad37180c6b6f90d4b7e96a234e8065d2ec395bungeman        return -1;
611bfae9d373ccc9cf47fd70757092962c7850fadf4djsollen@google.com    }
61250c956791291e7f3cec23721157570b7911336b8djsollen@google.com
61354e63082191f337084f96083ca90d7c35273d6ffbungeman    SkAutoTCallVProc<skstd::remove_pointer_t<XML_Parser>, XML_ParserFree> parser(
61496fcdcc219d2a0d3579719b84b28bede76efba64halcanary        XML_ParserCreate_MM(nullptr, &sk_XML_alloc, nullptr));
6157fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman    if (!parser) {
616c53085413e0b4704aa89cc18396613d59e6ccb4dbungeman        SkDebugf(SK_FONTMGR_ANDROID_PARSER_PREFIX "could not create XML parser\n");
6177fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman        return -1;
6187fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman    }
6197fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman
62010b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman    FamilyData self(parser, families, basePath, isFallback, filename, &topLevelHandler);
6217fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman    XML_SetUserData(parser, &self);
6227fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman
6237fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman    // Disable entity processing, to inhibit internal entity expansion. See expat CVE-2013-0340
6247fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman    XML_SetEntityDeclHandler(parser, xml_entity_decl_handler);
6257fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman
626f79673bbae0a662c1428755e2719dadf944e4ba1tomhudson    // Start parsing oldschool; switch these in flight if we detect a newer version of the file.
62710b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman    XML_SetElementHandler(parser, start_element_handler, end_element_handler);
62850c956791291e7f3cec23721157570b7911336b8djsollen@google.com
629eb2be7fa4413a566212782d8efae5dc225002821bungeman    // One would assume it would be faster to have a buffer on the stack and call XML_Parse.
630eb2be7fa4413a566212782d8efae5dc225002821bungeman    // But XML_Parse will call XML_GetBuffer anyway and memmove the passed buffer into it.
631eb2be7fa4413a566212782d8efae5dc225002821bungeman    // (Unless XML_CONTEXT_BYTES is undefined, but all users define it.)
6329a0808fd8e83128403285f391944850d908d7af0bungeman    // In debug, buffer a small odd number of bytes to detect slicing in XML_CharacterDataHandler.
6339a0808fd8e83128403285f391944850d908d7af0bungeman    static const int bufferSize = 512 SkDEBUGCODE( - 507);
634bfae9d373ccc9cf47fd70757092962c7850fadf4djsollen@google.com    bool done = false;
635bfae9d373ccc9cf47fd70757092962c7850fadf4djsollen@google.com    while (!done) {
636eb2be7fa4413a566212782d8efae5dc225002821bungeman        void* buffer = XML_GetBuffer(parser, bufferSize);
637eb2be7fa4413a566212782d8efae5dc225002821bungeman        if (!buffer) {
638c53085413e0b4704aa89cc18396613d59e6ccb4dbungeman            SkDebugf(SK_FONTMGR_ANDROID_PARSER_PREFIX "could not buffer enough to continue\n");
639eb2be7fa4413a566212782d8efae5dc225002821bungeman            return -1;
640eb2be7fa4413a566212782d8efae5dc225002821bungeman        }
641eb2be7fa4413a566212782d8efae5dc225002821bungeman        size_t len = file.read(buffer, bufferSize);
6427fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman        done = file.isAtEnd();
643eb2be7fa4413a566212782d8efae5dc225002821bungeman        XML_Status status = XML_ParseBuffer(parser, len, done);
6447fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman        if (XML_STATUS_ERROR == status) {
6457fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman            XML_Error error = XML_GetErrorCode(parser);
6467fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman            int line = XML_GetCurrentLineNumber(parser);
6477fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman            int column = XML_GetCurrentColumnNumber(parser);
6487fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman            const XML_LChar* errorString = XML_ErrorString(error);
649c53085413e0b4704aa89cc18396613d59e6ccb4dbungeman            SkDebugf(SK_FONTMGR_ANDROID_PARSER_PREFIX "%s:%d:%d error %d: %s.\n",
650f61475e95d9bb38f741f9e51221c086250b9ad72bungeman                     filename, line, column, error, errorString);
6517fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman            return -1;
652bfae9d373ccc9cf47fd70757092962c7850fadf4djsollen@google.com        }
653bfae9d373ccc9cf47fd70757092962c7850fadf4djsollen@google.com    }
6547fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman    return self.fVersion;
655bfae9d373ccc9cf47fd70757092962c7850fadf4djsollen@google.com}
656bfae9d373ccc9cf47fd70757092962c7850fadf4djsollen@google.com
657efbad37180c6b6f90d4b7e96a234e8065d2ec395bungeman/** Returns the version of the system font file actually found, negative if none. */
6587fa87cd09f49f1ee9bc27e263038d0f0ae706241bungemanstatic int append_system_font_families(SkTDArray<FontFamily*>& fontFamilies,
6597fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman                                       const SkString& basePath)
6607fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman{
6617a4747f4f62e4896d8f8469e1939b8191fff8d4etomhudson    int initialCount = fontFamilies.count();
6627fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman    int version = parse_config_file(LMP_SYSTEM_FONTS_FILE, fontFamilies, basePath, false);
663efbad37180c6b6f90d4b7e96a234e8065d2ec395bungeman    if (version < 0 || fontFamilies.count() == initialCount) {
6647fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman        version = parse_config_file(OLD_SYSTEM_FONTS_FILE, fontFamilies, basePath, false);
66594fa4b99e2a53e997a90c7808cc5263f1bf40c9ftomhudson    }
666efbad37180c6b6f90d4b7e96a234e8065d2ec395bungeman    return version;
667bfae9d373ccc9cf47fd70757092962c7850fadf4djsollen@google.com}
668bfae9d373ccc9cf47fd70757092962c7850fadf4djsollen@google.com
669a6c27bc5bd3b213d1e315c0bd9bbdcd75cec6900djsollen/**
670a6c27bc5bd3b213d1e315c0bd9bbdcd75cec6900djsollen * In some versions of Android prior to Android 4.2 (JellyBean MR1 at API
671a6c27bc5bd3b213d1e315c0bd9bbdcd75cec6900djsollen * Level 17) the fallback fonts for certain locales were encoded in their own
672a6c27bc5bd3b213d1e315c0bd9bbdcd75cec6900djsollen * XML files with a suffix that identified the locale.  We search the provided
673a6c27bc5bd3b213d1e315c0bd9bbdcd75cec6900djsollen * directory for those files,add all of their entries to the fallback chain, and
674a6c27bc5bd3b213d1e315c0bd9bbdcd75cec6900djsollen * include the locale as part of each entry.
675a6c27bc5bd3b213d1e315c0bd9bbdcd75cec6900djsollen */
676efbad37180c6b6f90d4b7e96a234e8065d2ec395bungemanstatic void append_fallback_font_families_for_locale(SkTDArray<FontFamily*>& fallbackFonts,
6777fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman                                                     const char* dir,
6787fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman                                                     const SkString& basePath)
679efbad37180c6b6f90d4b7e96a234e8065d2ec395bungeman{
68022cffcace2ad526f55ba2579e65836d38f009d1abungeman    SkOSFile::Iter iter(dir, nullptr);
68122cffcace2ad526f55ba2579e65836d38f009d1abungeman    SkString fileName;
68222cffcace2ad526f55ba2579e65836d38f009d1abungeman    while (iter.next(&fileName, false)) {
6839a0808fd8e83128403285f391944850d908d7af0bungeman        // The size of the prefix and suffix.
6849a0808fd8e83128403285f391944850d908d7af0bungeman        static const size_t fixedLen = sizeof(LOCALE_FALLBACK_FONTS_PREFIX) - 1
6859a0808fd8e83128403285f391944850d908d7af0bungeman                                     + sizeof(LOCALE_FALLBACK_FONTS_SUFFIX) - 1;
6869a0808fd8e83128403285f391944850d908d7af0bungeman
6879a0808fd8e83128403285f391944850d908d7af0bungeman        // The size of the prefix, suffix, and a minimum valid language code
6889a0808fd8e83128403285f391944850d908d7af0bungeman        static const size_t minSize = fixedLen + 2;
689c3c694342ad393b88cee5885395f182082aa2ebbbungeman
690c3c694342ad393b88cee5885395f182082aa2ebbbungeman        if (fileName.size() < minSize ||
691c3c694342ad393b88cee5885395f182082aa2ebbbungeman            !fileName.startsWith(LOCALE_FALLBACK_FONTS_PREFIX) ||
692c3c694342ad393b88cee5885395f182082aa2ebbbungeman            !fileName.endsWith(LOCALE_FALLBACK_FONTS_SUFFIX))
693c3c694342ad393b88cee5885395f182082aa2ebbbungeman        {
694c3c694342ad393b88cee5885395f182082aa2ebbbungeman            continue;
695c3c694342ad393b88cee5885395f182082aa2ebbbungeman        }
696a6c27bc5bd3b213d1e315c0bd9bbdcd75cec6900djsollen
697c3c694342ad393b88cee5885395f182082aa2ebbbungeman        SkString locale(fileName.c_str() + sizeof(LOCALE_FALLBACK_FONTS_PREFIX) - 1,
698c3c694342ad393b88cee5885395f182082aa2ebbbungeman                        fileName.size() - fixedLen);
699a6c27bc5bd3b213d1e315c0bd9bbdcd75cec6900djsollen
700c3c694342ad393b88cee5885395f182082aa2ebbbungeman        SkString absoluteFilename;
701c3c694342ad393b88cee5885395f182082aa2ebbbungeman        absoluteFilename.printf("%s/%s", dir, fileName.c_str());
702a6c27bc5bd3b213d1e315c0bd9bbdcd75cec6900djsollen
703c3c694342ad393b88cee5885395f182082aa2ebbbungeman        SkTDArray<FontFamily*> langSpecificFonts;
704c3c694342ad393b88cee5885395f182082aa2ebbbungeman        parse_config_file(absoluteFilename.c_str(), langSpecificFonts, basePath, true);
705a6c27bc5bd3b213d1e315c0bd9bbdcd75cec6900djsollen
706c3c694342ad393b88cee5885395f182082aa2ebbbungeman        for (int i = 0; i < langSpecificFonts.count(); ++i) {
707c3c694342ad393b88cee5885395f182082aa2ebbbungeman            FontFamily* family = langSpecificFonts[i];
708c3c694342ad393b88cee5885395f182082aa2ebbbungeman            family->fLanguage = SkLanguage(locale);
709c3c694342ad393b88cee5885395f182082aa2ebbbungeman            *fallbackFonts.append() = family;
710a6c27bc5bd3b213d1e315c0bd9bbdcd75cec6900djsollen        }
711a6c27bc5bd3b213d1e315c0bd9bbdcd75cec6900djsollen    }
712a6c27bc5bd3b213d1e315c0bd9bbdcd75cec6900djsollen}
713a6c27bc5bd3b213d1e315c0bd9bbdcd75cec6900djsollen
7147fa87cd09f49f1ee9bc27e263038d0f0ae706241bungemanstatic void append_system_fallback_font_families(SkTDArray<FontFamily*>& fallbackFonts,
7157fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman                                                 const SkString& basePath)
7167fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman{
7177fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman    parse_config_file(FALLBACK_FONTS_FILE, fallbackFonts, basePath, true);
7187fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman    append_fallback_font_families_for_locale(fallbackFonts,
7197fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman                                             LOCALE_FALLBACK_FONTS_SYSTEM_DIR,
7207fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman                                             basePath);
721efbad37180c6b6f90d4b7e96a234e8065d2ec395bungeman}
722bfae9d373ccc9cf47fd70757092962c7850fadf4djsollen@google.com
7237fa87cd09f49f1ee9bc27e263038d0f0ae706241bungemanstatic void mixin_vendor_fallback_font_families(SkTDArray<FontFamily*>& fallbackFonts,
7247fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman                                                const SkString& basePath)
7257fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman{
726efbad37180c6b6f90d4b7e96a234e8065d2ec395bungeman    SkTDArray<FontFamily*> vendorFonts;
7277fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman    parse_config_file(VENDOR_FONTS_FILE, vendorFonts, basePath, true);
7287fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman    append_fallback_font_families_for_locale(vendorFonts,
7297fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman                                             LOCALE_FALLBACK_FONTS_VENDOR_DIR,
7307fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman                                             basePath);
731a6c27bc5bd3b213d1e315c0bd9bbdcd75cec6900djsollen
732bfae9d373ccc9cf47fd70757092962c7850fadf4djsollen@google.com    // This loop inserts the vendor fallback fonts in the correct order in the
733bfae9d373ccc9cf47fd70757092962c7850fadf4djsollen@google.com    // overall fallbacks list.
734bfae9d373ccc9cf47fd70757092962c7850fadf4djsollen@google.com    int currentOrder = -1;
735bfae9d373ccc9cf47fd70757092962c7850fadf4djsollen@google.com    for (int i = 0; i < vendorFonts.count(); ++i) {
736bfae9d373ccc9cf47fd70757092962c7850fadf4djsollen@google.com        FontFamily* family = vendorFonts[i];
737d3ddea284ec6611a93a6b75e64de39d0bc7e083ctomhudson        int order = family->fOrder;
738bfae9d373ccc9cf47fd70757092962c7850fadf4djsollen@google.com        if (order < 0) {
739bfae9d373ccc9cf47fd70757092962c7850fadf4djsollen@google.com            if (currentOrder < 0) {
740bfae9d373ccc9cf47fd70757092962c7850fadf4djsollen@google.com                // Default case - just add it to the end of the fallback list
741bfae9d373ccc9cf47fd70757092962c7850fadf4djsollen@google.com                *fallbackFonts.append() = family;
742bfae9d373ccc9cf47fd70757092962c7850fadf4djsollen@google.com            } else {
743bfae9d373ccc9cf47fd70757092962c7850fadf4djsollen@google.com                // no order specified on this font, but we're incrementing the order
744bfae9d373ccc9cf47fd70757092962c7850fadf4djsollen@google.com                // based on an earlier order insertion request
745bfae9d373ccc9cf47fd70757092962c7850fadf4djsollen@google.com                *fallbackFonts.insert(currentOrder++) = family;
746bfae9d373ccc9cf47fd70757092962c7850fadf4djsollen@google.com            }
747bfae9d373ccc9cf47fd70757092962c7850fadf4djsollen@google.com        } else {
748bfae9d373ccc9cf47fd70757092962c7850fadf4djsollen@google.com            // Add the font into the fallback list in the specified order. Set
749bfae9d373ccc9cf47fd70757092962c7850fadf4djsollen@google.com            // currentOrder for correct placement of other fonts in the vendor list.
750bfae9d373ccc9cf47fd70757092962c7850fadf4djsollen@google.com            *fallbackFonts.insert(order) = family;
751bfae9d373ccc9cf47fd70757092962c7850fadf4djsollen@google.com            currentOrder = order + 1;
752bfae9d373ccc9cf47fd70757092962c7850fadf4djsollen@google.com        }
753bfae9d373ccc9cf47fd70757092962c7850fadf4djsollen@google.com    }
754bfae9d373ccc9cf47fd70757092962c7850fadf4djsollen@google.com}
755bfae9d373ccc9cf47fd70757092962c7850fadf4djsollen@google.com
756c53085413e0b4704aa89cc18396613d59e6ccb4dbungemanvoid SkFontMgr_Android_Parser::GetSystemFontFamilies(SkTDArray<FontFamily*>& fontFamilies) {
757efbad37180c6b6f90d4b7e96a234e8065d2ec395bungeman    // Version 21 of the system font configuration does not need any fallback configuration files.
7587fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman    SkString basePath(getenv("ANDROID_ROOT"));
7597fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman    basePath.append(SK_FONT_FILE_PREFIX, sizeof(SK_FONT_FILE_PREFIX) - 1);
7607fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman
7617fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman    if (append_system_font_families(fontFamilies, basePath) >= 21) {
762efbad37180c6b6f90d4b7e96a234e8065d2ec395bungeman        return;
763efbad37180c6b6f90d4b7e96a234e8065d2ec395bungeman    }
764bfae9d373ccc9cf47fd70757092962c7850fadf4djsollen@google.com
765bfae9d373ccc9cf47fd70757092962c7850fadf4djsollen@google.com    // Append all the fallback fonts to system fonts
766bfae9d373ccc9cf47fd70757092962c7850fadf4djsollen@google.com    SkTDArray<FontFamily*> fallbackFonts;
7677fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman    append_system_fallback_font_families(fallbackFonts, basePath);
7687fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman    mixin_vendor_fallback_font_families(fallbackFonts, basePath);
7697fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman    fontFamilies.append(fallbackFonts.count(), fallbackFonts.begin());
770bfae9d373ccc9cf47fd70757092962c7850fadf4djsollen@google.com}
771bfae9d373ccc9cf47fd70757092962c7850fadf4djsollen@google.com
772c53085413e0b4704aa89cc18396613d59e6ccb4dbungemanvoid SkFontMgr_Android_Parser::GetCustomFontFamilies(SkTDArray<FontFamily*>& fontFamilies,
773c53085413e0b4704aa89cc18396613d59e6ccb4dbungeman                                                     const SkString& basePath,
774c53085413e0b4704aa89cc18396613d59e6ccb4dbungeman                                                     const char* fontsXml,
775c53085413e0b4704aa89cc18396613d59e6ccb4dbungeman                                                     const char* fallbackFontsXml,
776c53085413e0b4704aa89cc18396613d59e6ccb4dbungeman                                                     const char* langFallbackFontsDir)
7777fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman{
7787fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman    if (fontsXml) {
7797fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman        parse_config_file(fontsXml, fontFamilies, basePath, false);
780f79673bbae0a662c1428755e2719dadf944e4ba1tomhudson    }
7817fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman    if (fallbackFontsXml) {
7827fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman        parse_config_file(fallbackFontsXml, fontFamilies, basePath, true);
783bfae9d373ccc9cf47fd70757092962c7850fadf4djsollen@google.com    }
784c3c694342ad393b88cee5885395f182082aa2ebbbungeman    if (langFallbackFontsDir) {
785c3c694342ad393b88cee5885395f182082aa2ebbbungeman        append_fallback_font_families_for_locale(fontFamilies,
786c3c694342ad393b88cee5885395f182082aa2ebbbungeman                                                 langFallbackFontsDir,
787c3c694342ad393b88cee5885395f182082aa2ebbbungeman                                                 basePath);
788c3c694342ad393b88cee5885395f182082aa2ebbbungeman    }
789bfae9d373ccc9cf47fd70757092962c7850fadf4djsollen@google.com}
7903b6255493e458c6b2c1412af908581f0bf3f6b70djsollen
7913b6255493e458c6b2c1412af908581f0bf3f6b70djsollenSkLanguage SkLanguage::getParent() const {
7923b6255493e458c6b2c1412af908581f0bf3f6b70djsollen    SkASSERT(!fTag.isEmpty());
7933b6255493e458c6b2c1412af908581f0bf3f6b70djsollen    const char* tag = fTag.c_str();
7943b6255493e458c6b2c1412af908581f0bf3f6b70djsollen
7953b6255493e458c6b2c1412af908581f0bf3f6b70djsollen    // strip off the rightmost "-.*"
7963b6255493e458c6b2c1412af908581f0bf3f6b70djsollen    const char* parentTagEnd = strrchr(tag, '-');
79796fcdcc219d2a0d3579719b84b28bede76efba64halcanary    if (parentTagEnd == nullptr) {
7983b6255493e458c6b2c1412af908581f0bf3f6b70djsollen        return SkLanguage();
7993b6255493e458c6b2c1412af908581f0bf3f6b70djsollen    }
8003b6255493e458c6b2c1412af908581f0bf3f6b70djsollen    size_t parentTagLen = parentTagEnd - tag;
8013b6255493e458c6b2c1412af908581f0bf3f6b70djsollen    return SkLanguage(tag, parentTagLen);
8023b6255493e458c6b2c1412af908581f0bf3f6b70djsollen}
803