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
1047a1e96b957b50662274360f1a390d76ab3d02ccbungeman#include "SkFontMgr.h"
11c53085413e0b4704aa89cc18396613d59e6ccb4dbungeman#include "SkFontMgr_android_parser.h"
127fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman#include "SkStream.h"
13bfae9d373ccc9cf47fd70757092962c7850fadf4djsollen@google.com#include "SkTDArray.h"
148d84c995319dd4a82e4f2054bbd19f968c671ca6bungeman#include "SkTSearch.h"
15f20488b4f2139e6ca09fee7e39b731dd8ab467dbbungeman#include "SkTemplates.h"
1654e63082191f337084f96083ca90d7c35273d6ffbungeman#include "SkTLogic.h"
17bfae9d373ccc9cf47fd70757092962c7850fadf4djsollen@google.com
18a6c27bc5bd3b213d1e315c0bd9bbdcd75cec6900djsollen#include <dirent.h>
19f20488b4f2139e6ca09fee7e39b731dd8ab467dbbungeman#include <expat.h>
20bfae9d373ccc9cf47fd70757092962c7850fadf4djsollen@google.com
217fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman#include <stdlib.h>
22f20488b4f2139e6ca09fee7e39b731dd8ab467dbbungeman#include <string.h>
238d84c995319dd4a82e4f2054bbd19f968c671ca6bungeman
2494fa4b99e2a53e997a90c7808cc5263f1bf40c9ftomhudson#define LMP_SYSTEM_FONTS_FILE "/system/etc/fonts.xml"
2594fa4b99e2a53e997a90c7808cc5263f1bf40c9ftomhudson#define OLD_SYSTEM_FONTS_FILE "/system/etc/system_fonts.xml"
26bfae9d373ccc9cf47fd70757092962c7850fadf4djsollen@google.com#define FALLBACK_FONTS_FILE "/system/etc/fallback_fonts.xml"
27bfae9d373ccc9cf47fd70757092962c7850fadf4djsollen@google.com#define VENDOR_FONTS_FILE "/vendor/etc/fallback_fonts.xml"
28bfae9d373ccc9cf47fd70757092962c7850fadf4djsollen@google.com
29a6c27bc5bd3b213d1e315c0bd9bbdcd75cec6900djsollen#define LOCALE_FALLBACK_FONTS_SYSTEM_DIR "/system/etc"
30a6c27bc5bd3b213d1e315c0bd9bbdcd75cec6900djsollen#define LOCALE_FALLBACK_FONTS_VENDOR_DIR "/vendor/etc"
31a6c27bc5bd3b213d1e315c0bd9bbdcd75cec6900djsollen#define LOCALE_FALLBACK_FONTS_PREFIX "fallback_fonts-"
32a6c27bc5bd3b213d1e315c0bd9bbdcd75cec6900djsollen#define LOCALE_FALLBACK_FONTS_SUFFIX ".xml"
33a6c27bc5bd3b213d1e315c0bd9bbdcd75cec6900djsollen
347fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman#ifndef SK_FONT_FILE_PREFIX
357fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman#    define SK_FONT_FILE_PREFIX "/fonts/"
367fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman#endif
377fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman
38f79673bbae0a662c1428755e2719dadf944e4ba1tomhudson/**
3910b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman * This file contains TWO 'familyset' handlers:
4010b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman * One for JB and earlier which works with
4110b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman *   /system/etc/system_fonts.xml
4210b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman *   /system/etc/fallback_fonts.xml
4310b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman *   /vendor/etc/fallback_fonts.xml
4410b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman *   /system/etc/fallback_fonts-XX.xml
4510b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman *   /vendor/etc/fallback_fonts-XX.xml
4610b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman * and the other for LMP and later which works with
4710b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman *   /system/etc/fonts.xml
4810b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman *
4910b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman * If the 'familyset' 'version' attribute is 21 or higher the LMP parser is used, otherwise the JB.
50f79673bbae0a662c1428755e2719dadf944e4ba1tomhudson */
51f79673bbae0a662c1428755e2719dadf944e4ba1tomhudson
5210b063cb91c52fd1f570ee63307fe7e68c1501f1bungemanstruct FamilyData;
5310b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman
5410b063cb91c52fd1f570ee63307fe7e68c1501f1bungemanstruct TagHandler {
5510b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman    /** Called at the start tag.
5610b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman     *  Called immediately after the parent tag retuns this handler from a call to 'tag'.
5710b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman     *  Allows setting up for handling the tag content and processing attributes.
5896fcdcc219d2a0d3579719b84b28bede76efba64halcanary     *  If nullptr, will not be called.
5910b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman     */
6010b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman    void (*start)(FamilyData* data, const char* tag, const char** attributes);
6110b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman
6210b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman    /** Called at the end tag.
6310b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman     *  Allows post-processing of any accumulated information.
6410b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman     *  This will be the last call made in relation to the current tag.
6596fcdcc219d2a0d3579719b84b28bede76efba64halcanary     *  If nullptr, will not be called.
6610b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman     */
6710b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman    void (*end)(FamilyData* data, const char* tag);
6810b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman
6910b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman    /** Called when a nested tag is encountered.
7010b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman     *  This is responsible for determining how to handle the tag.
7196fcdcc219d2a0d3579719b84b28bede76efba64halcanary     *  If the tag is not recognized, return nullptr to skip the tag.
7296fcdcc219d2a0d3579719b84b28bede76efba64halcanary     *  If nullptr, all nested tags will be skipped.
7310b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman     */
7410b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman    const TagHandler* (*tag)(FamilyData* data, const char* tag, const char** attributes);
7510b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman
7610b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman    /** The character handler for this tag.
7710b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman     *  This is only active for character data contained directly in this tag (not sub-tags).
7810b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman     *  The first parameter will be castable to a FamilyData*.
7996fcdcc219d2a0d3579719b84b28bede76efba64halcanary     *  If nullptr, any character data in this tag will be ignored.
8010b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman     */
8110b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman    XML_CharacterDataHandler chars;
827fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman};
83bfae9d373ccc9cf47fd70757092962c7850fadf4djsollen@google.com
8410b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman/** Represents the current parsing state. */
85bfae9d373ccc9cf47fd70757092962c7850fadf4djsollen@google.comstruct FamilyData {
867fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman    FamilyData(XML_Parser parser, SkTDArray<FontFamily*>& families,
8710b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman               const SkString& basePath, bool isFallback, const char* filename,
8810b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman               const TagHandler* topLevelHandler)
89b6bed17ea81ff8fad68a7db79307bdcbcd4738a8bungeman        : fParser(parser)
90b6bed17ea81ff8fad68a7db79307bdcbcd4738a8bungeman        , fFamilies(families)
9196fcdcc219d2a0d3579719b84b28bede76efba64halcanary        , fCurrentFamily(nullptr)
9296fcdcc219d2a0d3579719b84b28bede76efba64halcanary        , fCurrentFontInfo(nullptr)
93efbad37180c6b6f90d4b7e96a234e8065d2ec395bungeman        , fVersion(0)
947fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman        , fBasePath(basePath)
957fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman        , fIsFallback(isFallback)
96f61475e95d9bb38f741f9e51221c086250b9ad72bungeman        , fFilename(filename)
9710b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        , fDepth(1)
9810b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        , fSkip(0)
9910b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        , fHandler(&topLevelHandler, 1)
1007fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman    { };
101b6bed17ea81ff8fad68a7db79307bdcbcd4738a8bungeman
102b6bed17ea81ff8fad68a7db79307bdcbcd4738a8bungeman    XML_Parser fParser;                       // The expat parser doing the work, owned by caller
103b6bed17ea81ff8fad68a7db79307bdcbcd4738a8bungeman    SkTDArray<FontFamily*>& fFamilies;        // The array to append families, owned by caller
104b6bed17ea81ff8fad68a7db79307bdcbcd4738a8bungeman    SkAutoTDelete<FontFamily> fCurrentFamily; // The family being created, owned by this
1057fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman    FontFileInfo* fCurrentFontInfo;           // The fontInfo being created, owned by fCurrentFamily
106efbad37180c6b6f90d4b7e96a234e8065d2ec395bungeman    int fVersion;                             // The version of the file parsed.
1077fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman    const SkString& fBasePath;                // The current base path.
1087fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman    const bool fIsFallback;                   // Indicates the file being parsed is a fallback file
109f61475e95d9bb38f741f9e51221c086250b9ad72bungeman    const char* fFilename;                    // The name of the file currently being parsed.
11010b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman
11110b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman    int fDepth;                               // The current element depth of the parse.
11210b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman    int fSkip;                                // The depth to stop skipping, 0 if not skipping.
11310b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman    SkTDArray<const TagHandler*> fHandler;    // The stack of current tag handlers.
114bfae9d373ccc9cf47fd70757092962c7850fadf4djsollen@google.com};
115bfae9d373ccc9cf47fd70757092962c7850fadf4djsollen@google.com
1167fa87cd09f49f1ee9bc27e263038d0f0ae706241bungemanstatic bool memeq(const char* s1, const char* s2, size_t n1, size_t n2) {
1177fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman    return n1 == n2 && 0 == memcmp(s1, s2, n1);
1187fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman}
1197fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman#define MEMEQ(c, s, n) memeq(c, s, sizeof(c) - 1, n)
1207fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman
12196fcdcc219d2a0d3579719b84b28bede76efba64halcanary#define ATTS_NON_NULL(a, i) (a[i] != nullptr && a[i+1] != nullptr)
1227fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman
123c53085413e0b4704aa89cc18396613d59e6ccb4dbungeman#define SK_FONTMGR_ANDROID_PARSER_PREFIX "[SkFontMgr Android Parser] "
124f61475e95d9bb38f741f9e51221c086250b9ad72bungeman
125f61475e95d9bb38f741f9e51221c086250b9ad72bungeman#define SK_FONTCONFIGPARSER_WARNING(message, ...) SkDebugf( \
126c53085413e0b4704aa89cc18396613d59e6ccb4dbungeman    SK_FONTMGR_ANDROID_PARSER_PREFIX "%s:%d:%d: warning: " message "\n", \
127f61475e95d9bb38f741f9e51221c086250b9ad72bungeman    self->fFilename, \
128f61475e95d9bb38f741f9e51221c086250b9ad72bungeman    XML_GetCurrentLineNumber(self->fParser), \
129f61475e95d9bb38f741f9e51221c086250b9ad72bungeman    XML_GetCurrentColumnNumber(self->fParser), \
130f61475e95d9bb38f741f9e51221c086250b9ad72bungeman    ##__VA_ARGS__);
131f61475e95d9bb38f741f9e51221c086250b9ad72bungeman
13210b063cb91c52fd1f570ee63307fe7e68c1501f1bungemanstatic bool is_whitespace(char c) {
13310b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman    return c == ' ' || c == '\n'|| c == '\r' || c == '\t';
13410b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman}
135f79673bbae0a662c1428755e2719dadf944e4ba1tomhudson
13610b063cb91c52fd1f570ee63307fe7e68c1501f1bungemanstatic void trim_string(SkString* s) {
13710b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman    char* str = s->writable_str();
13810b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman    const char* start = str;  // start is inclusive
13910b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman    const char* end = start + s->size();  // end is exclusive
14010b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman    while (is_whitespace(*start)) { ++start; }
14110b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman    if (start != end) {
14210b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        --end;  // make end inclusive
14310b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        while (is_whitespace(*end)) { --end; }
14410b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        ++end;  // make end exclusive
14507544757c9fcf0f359f1686a3779eb2e75dd5b36tomhudson    }
14610b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman    size_t len = end - start;
14710b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman    memmove(str, start, len);
14810b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman    s->resize(len);
149f79673bbae0a662c1428755e2719dadf944e4ba1tomhudson}
150f79673bbae0a662c1428755e2719dadf944e4ba1tomhudson
15110b063cb91c52fd1f570ee63307fe7e68c1501f1bungemannamespace lmpParser {
152f79673bbae0a662c1428755e2719dadf944e4ba1tomhudson
15341868fe5625fc3bd70daa3f461c881b5db6a9265bungemanstatic const TagHandler axisHandler = {
15441868fe5625fc3bd70daa3f461c881b5db6a9265bungeman    /*start*/[](FamilyData* self, const char* tag, const char** attributes) {
15541868fe5625fc3bd70daa3f461c881b5db6a9265bungeman        FontFileInfo& file = *self->fCurrentFontInfo;
15647a1e96b957b50662274360f1a390d76ab3d02ccbungeman        SkFourByteTag axisTag = SkSetFourByteTag('\0','\0','\0','\0');
15747a1e96b957b50662274360f1a390d76ab3d02ccbungeman        SkFixed axisStyleValue = 0;
15847a1e96b957b50662274360f1a390d76ab3d02ccbungeman        bool axisTagIsValid = false;
15947a1e96b957b50662274360f1a390d76ab3d02ccbungeman        bool axisStyleValueIsValid = false;
16041868fe5625fc3bd70daa3f461c881b5db6a9265bungeman        for (size_t i = 0; ATTS_NON_NULL(attributes, i); i += 2) {
16141868fe5625fc3bd70daa3f461c881b5db6a9265bungeman            const char* name = attributes[i];
16241868fe5625fc3bd70daa3f461c881b5db6a9265bungeman            const char* value = attributes[i+1];
16341868fe5625fc3bd70daa3f461c881b5db6a9265bungeman            size_t nameLen = strlen(name);
16441868fe5625fc3bd70daa3f461c881b5db6a9265bungeman            if (MEMEQ("tag", name, nameLen)) {
16541868fe5625fc3bd70daa3f461c881b5db6a9265bungeman                size_t valueLen = strlen(value);
16641868fe5625fc3bd70daa3f461c881b5db6a9265bungeman                if (valueLen == 4) {
16747a1e96b957b50662274360f1a390d76ab3d02ccbungeman                    axisTag = SkSetFourByteTag(value[0], value[1], value[2], value[3]);
16847a1e96b957b50662274360f1a390d76ab3d02ccbungeman                    axisTagIsValid = true;
16941868fe5625fc3bd70daa3f461c881b5db6a9265bungeman                    for (int j = 0; j < file.fAxes.count() - 1; ++j) {
17047a1e96b957b50662274360f1a390d76ab3d02ccbungeman                        if (file.fAxes[j].fTag == axisTag) {
17147a1e96b957b50662274360f1a390d76ab3d02ccbungeman                            axisTagIsValid = false;
17241868fe5625fc3bd70daa3f461c881b5db6a9265bungeman                            SK_FONTCONFIGPARSER_WARNING("'%c%c%c%c' axis specified more than once",
17347a1e96b957b50662274360f1a390d76ab3d02ccbungeman                                                        (axisTag >> 24) & 0xFF,
17447a1e96b957b50662274360f1a390d76ab3d02ccbungeman                                                        (axisTag >> 16) & 0xFF,
17547a1e96b957b50662274360f1a390d76ab3d02ccbungeman                                                        (axisTag >>  8) & 0xFF,
17647a1e96b957b50662274360f1a390d76ab3d02ccbungeman                                                        (axisTag      ) & 0xFF);
17741868fe5625fc3bd70daa3f461c881b5db6a9265bungeman                        }
17841868fe5625fc3bd70daa3f461c881b5db6a9265bungeman                    }
17941868fe5625fc3bd70daa3f461c881b5db6a9265bungeman                } else {
18041868fe5625fc3bd70daa3f461c881b5db6a9265bungeman                    SK_FONTCONFIGPARSER_WARNING("'%s' is an invalid axis tag", value);
18141868fe5625fc3bd70daa3f461c881b5db6a9265bungeman                }
18241868fe5625fc3bd70daa3f461c881b5db6a9265bungeman            } else if (MEMEQ("stylevalue", name, nameLen)) {
18347a1e96b957b50662274360f1a390d76ab3d02ccbungeman                if (parse_fixed<16>(value, &axisStyleValue)) {
18447a1e96b957b50662274360f1a390d76ab3d02ccbungeman                    axisStyleValueIsValid = true;
18547a1e96b957b50662274360f1a390d76ab3d02ccbungeman                } else {
18641868fe5625fc3bd70daa3f461c881b5db6a9265bungeman                    SK_FONTCONFIGPARSER_WARNING("'%s' is an invalid axis stylevalue", value);
18741868fe5625fc3bd70daa3f461c881b5db6a9265bungeman                }
18841868fe5625fc3bd70daa3f461c881b5db6a9265bungeman            }
18941868fe5625fc3bd70daa3f461c881b5db6a9265bungeman        }
19047a1e96b957b50662274360f1a390d76ab3d02ccbungeman        if (axisTagIsValid && axisStyleValueIsValid) {
19147a1e96b957b50662274360f1a390d76ab3d02ccbungeman            SkFontMgr::FontParameters::Axis& axis = file.fAxes.push_back();
19247a1e96b957b50662274360f1a390d76ab3d02ccbungeman            axis.fTag = axisTag;
19347a1e96b957b50662274360f1a390d76ab3d02ccbungeman            axis.fStyleValue = SkFixedToScalar(axisStyleValue);
19447a1e96b957b50662274360f1a390d76ab3d02ccbungeman        }
19541868fe5625fc3bd70daa3f461c881b5db6a9265bungeman    },
19696fcdcc219d2a0d3579719b84b28bede76efba64halcanary    /*end*/nullptr,
19796fcdcc219d2a0d3579719b84b28bede76efba64halcanary    /*tag*/nullptr,
19896fcdcc219d2a0d3579719b84b28bede76efba64halcanary    /*chars*/nullptr,
19941868fe5625fc3bd70daa3f461c881b5db6a9265bungeman};
20041868fe5625fc3bd70daa3f461c881b5db6a9265bungeman
20110b063cb91c52fd1f570ee63307fe7e68c1501f1bungemanstatic const TagHandler fontHandler = {
20210b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman    /*start*/[](FamilyData* self, const char* tag, const char** attributes) {
20310b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        // 'weight' (non-negative integer) [default 0]
20410b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        // 'style' ("normal", "italic") [default "auto"]
20510b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        // 'index' (non-negative integer) [default 0]
20610b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        // The character data should be a filename.
20710b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        FontFileInfo& file = self->fCurrentFamily->fFonts.push_back();
20810b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        self->fCurrentFontInfo = &file;
20910b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        for (size_t i = 0; ATTS_NON_NULL(attributes, i); i += 2) {
21010b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman            const char* name = attributes[i];
21110b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman            const char* value = attributes[i+1];
21210b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman            size_t nameLen = strlen(name);
21310b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman            if (MEMEQ("weight", name, nameLen)) {
21410b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman                if (!parse_non_negative_integer(value, &file.fWeight)) {
21510b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman                    SK_FONTCONFIGPARSER_WARNING("'%s' is an invalid weight", value);
21610b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman                }
21710b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman            } else if (MEMEQ("style", name, nameLen)) {
21810b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman                size_t valueLen = strlen(value);
21910b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman                if (MEMEQ("normal", value, valueLen)) {
22010b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman                    file.fStyle = FontFileInfo::Style::kNormal;
22110b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman                } else if (MEMEQ("italic", value, valueLen)) {
22210b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman                    file.fStyle = FontFileInfo::Style::kItalic;
22310b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman                }
22410b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman            } else if (MEMEQ("index", name, nameLen)) {
22510b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman                if (!parse_non_negative_integer(value, &file.fIndex)) {
22610b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman                    SK_FONTCONFIGPARSER_WARNING("'%s' is an invalid index", value);
22710b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman                }
228d3ddea284ec6611a93a6b75e64de39d0bc7e083ctomhudson            }
22910b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        }
23010b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman    },
23110b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman    /*end*/[](FamilyData* self, const char* tag) {
23210b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        trim_string(&self->fCurrentFontInfo->fFileName);
23310b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman    },
23441868fe5625fc3bd70daa3f461c881b5db6a9265bungeman    /*tag*/[](FamilyData* self, const char* tag, const char** attributes) -> const TagHandler* {
23541868fe5625fc3bd70daa3f461c881b5db6a9265bungeman        size_t len = strlen(tag);
23641868fe5625fc3bd70daa3f461c881b5db6a9265bungeman        if (MEMEQ("axis", tag, len)) {
23741868fe5625fc3bd70daa3f461c881b5db6a9265bungeman            return &axisHandler;
23841868fe5625fc3bd70daa3f461c881b5db6a9265bungeman        }
23996fcdcc219d2a0d3579719b84b28bede76efba64halcanary        return nullptr;
24041868fe5625fc3bd70daa3f461c881b5db6a9265bungeman    },
24110b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman    /*chars*/[](void* data, const char* s, int len) {
24210b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        FamilyData* self = static_cast<FamilyData*>(data);
24310b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        self->fCurrentFontInfo->fFileName.append(s, len);
24410b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman    }
24510b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman};
24610b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman
24710b063cb91c52fd1f570ee63307fe7e68c1501f1bungemanstatic const TagHandler familyHandler = {
24810b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman    /*start*/[](FamilyData* self, const char* tag, const char** attributes) {
24910b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        // 'name' (string) [optional]
25010b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        // 'lang' (string) [default ""]
25110b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        // 'variant' ("elegant", "compact") [default "default"]
25210b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        // If there is no name, this is a fallback only font.
25310b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        FontFamily* family = new FontFamily(self->fBasePath, true);
25410b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        self->fCurrentFamily.reset(family);
25510b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        for (size_t i = 0; ATTS_NON_NULL(attributes, i); i += 2) {
25610b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman            const char* name = attributes[i];
25710b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman            const char* value = attributes[i+1];
25810b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman            size_t nameLen = strlen(name);
259e85a754a4ce9b279159270faa6717932f7a8548fbungeman            size_t valueLen = strlen(value);
26010b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman            if (MEMEQ("name", name, nameLen)) {
26110b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman                SkAutoAsciiToLC tolc(value);
26210b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman                family->fNames.push_back().set(tolc.lc());
26310b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman                family->fIsFallbackFont = false;
26410b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman            } else if (MEMEQ("lang", name, nameLen)) {
26510b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman                family->fLanguage = SkLanguage(value, valueLen);
26610b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman            } else if (MEMEQ("variant", name, nameLen)) {
26710b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman                if (MEMEQ("elegant", value, valueLen)) {
26810b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman                    family->fVariant = kElegant_FontVariant;
26910b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman                } else if (MEMEQ("compact", value, valueLen)) {
27010b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman                    family->fVariant = kCompact_FontVariant;
27110b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman                }
272b8a1d30a42d13ae83690b2d854a024d9b56e7b71bungeman            }
27307544757c9fcf0f359f1686a3779eb2e75dd5b36tomhudson        }
27410b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman    },
27510b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman    /*end*/[](FamilyData* self, const char* tag) {
27610b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        *self->fFamilies.append() = self->fCurrentFamily.detach();
27710b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman    },
27810b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman    /*tag*/[](FamilyData* self, const char* tag, const char** attributes) -> const TagHandler* {
27910b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        size_t len = strlen(tag);
28010b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        if (MEMEQ("font", tag, len)) {
28110b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman            return &fontHandler;
28210b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        }
28396fcdcc219d2a0d3579719b84b28bede76efba64halcanary        return nullptr;
28410b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman    },
28596fcdcc219d2a0d3579719b84b28bede76efba64halcanary    /*chars*/nullptr,
28610b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman};
28707544757c9fcf0f359f1686a3779eb2e75dd5b36tomhudson
288eb2be7fa4413a566212782d8efae5dc225002821bungemanstatic FontFamily* find_family(FamilyData* self, const SkString& familyName) {
2897fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman    for (int i = 0; i < self->fFamilies.count(); i++) {
2907fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman        FontFamily* candidate = self->fFamilies[i];
29107544757c9fcf0f359f1686a3779eb2e75dd5b36tomhudson        for (int j = 0; j < candidate->fNames.count(); j++) {
2927fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman            if (candidate->fNames[j] == familyName) {
29307544757c9fcf0f359f1686a3779eb2e75dd5b36tomhudson                return candidate;
29407544757c9fcf0f359f1686a3779eb2e75dd5b36tomhudson            }
29507544757c9fcf0f359f1686a3779eb2e75dd5b36tomhudson        }
29607544757c9fcf0f359f1686a3779eb2e75dd5b36tomhudson    }
29796fcdcc219d2a0d3579719b84b28bede76efba64halcanary    return nullptr;
29807544757c9fcf0f359f1686a3779eb2e75dd5b36tomhudson}
29907544757c9fcf0f359f1686a3779eb2e75dd5b36tomhudson
30010b063cb91c52fd1f570ee63307fe7e68c1501f1bungemanstatic const TagHandler aliasHandler = {
30110b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman    /*start*/[](FamilyData* self, const char* tag, const char** attributes) {
30210b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        // 'name' (string) introduces a new family name.
30310b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        // 'to' (string) specifies which (previous) family to alias
30410b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        // 'weight' (non-negative integer) [optional]
30510b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        // If it *does not* have a weight, 'name' is an alias for the entire 'to' family.
30610b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        // If it *does* have a weight, 'name' is a new family consisting of
30710b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        // the font(s) with 'weight' from the 'to' family.
30810b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman
30910b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        SkString aliasName;
31010b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        SkString to;
31110b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        int weight = 0;
31210b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        for (size_t i = 0; ATTS_NON_NULL(attributes, i); i += 2) {
31310b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman            const char* name = attributes[i];
31410b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman            const char* value = attributes[i+1];
31510b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman            size_t nameLen = strlen(name);
31610b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman            if (MEMEQ("name", name, nameLen)) {
31710b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman                SkAutoAsciiToLC tolc(value);
31810b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman                aliasName.set(tolc.lc());
31910b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman            } else if (MEMEQ("to", name, nameLen)) {
32010b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman                to.set(value);
32110b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman            } else if (MEMEQ("weight", name, nameLen)) {
32210b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman                if (!parse_non_negative_integer(value, &weight)) {
32310b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman                    SK_FONTCONFIGPARSER_WARNING("'%s' is an invalid weight", value);
32410b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman                }
3257fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman            }
32607544757c9fcf0f359f1686a3779eb2e75dd5b36tomhudson        }
32707544757c9fcf0f359f1686a3779eb2e75dd5b36tomhudson
32810b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        // Assumes that the named family is already declared
32910b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        FontFamily* targetFamily = find_family(self, to);
33010b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        if (!targetFamily) {
33110b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman            SK_FONTCONFIGPARSER_WARNING("'%s' alias target not found", to.c_str());
33210b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman            return;
33310b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        }
33407544757c9fcf0f359f1686a3779eb2e75dd5b36tomhudson
33510b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        if (weight) {
33610b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman            FontFamily* family = new FontFamily(targetFamily->fBasePath, self->fIsFallback);
33710b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman            family->fNames.push_back().set(aliasName);
33807544757c9fcf0f359f1686a3779eb2e75dd5b36tomhudson
33910b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman            for (int i = 0; i < targetFamily->fFonts.count(); i++) {
34010b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman                if (targetFamily->fFonts[i].fWeight == weight) {
34110b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman                    family->fFonts.push_back(targetFamily->fFonts[i]);
34210b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman                }
34307544757c9fcf0f359f1686a3779eb2e75dd5b36tomhudson            }
34410b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman            *self->fFamilies.append() = family;
34510b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        } else {
34610b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman            targetFamily->fNames.push_back().set(aliasName);
34707544757c9fcf0f359f1686a3779eb2e75dd5b36tomhudson        }
34810b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman    },
34996fcdcc219d2a0d3579719b84b28bede76efba64halcanary    /*end*/nullptr,
35096fcdcc219d2a0d3579719b84b28bede76efba64halcanary    /*tag*/nullptr,
35196fcdcc219d2a0d3579719b84b28bede76efba64halcanary    /*chars*/nullptr,
35210b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman};
353c0727d117e67844f0c8794cc7eaa49a96a015347bungeman
35410b063cb91c52fd1f570ee63307fe7e68c1501f1bungemanstatic const TagHandler familySetHandler = {
35510b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman    /*start*/[](FamilyData* self, const char* tag, const char** attributes) { },
35696fcdcc219d2a0d3579719b84b28bede76efba64halcanary    /*end*/nullptr,
35710b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman    /*tag*/[](FamilyData* self, const char* tag, const char** attributes) -> const TagHandler* {
35810b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        size_t len = strlen(tag);
35910b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        if (MEMEQ("family", tag, len)) {
36010b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman            return &familyHandler;
36110b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        } else if (MEMEQ("alias", tag, len)) {
36210b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman            return &aliasHandler;
36310b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        }
36496fcdcc219d2a0d3579719b84b28bede76efba64halcanary        return nullptr;
36510b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman    },
36696fcdcc219d2a0d3579719b84b28bede76efba64halcanary    /*chars*/nullptr,
36710b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman};
368f79673bbae0a662c1428755e2719dadf944e4ba1tomhudson
369f79673bbae0a662c1428755e2719dadf944e4ba1tomhudson} // lmpParser
370f79673bbae0a662c1428755e2719dadf944e4ba1tomhudson
371f79673bbae0a662c1428755e2719dadf944e4ba1tomhudsonnamespace jbParser {
372f79673bbae0a662c1428755e2719dadf944e4ba1tomhudson
37310b063cb91c52fd1f570ee63307fe7e68c1501f1bungemanstatic const TagHandler fileHandler = {
37410b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman    /*start*/[](FamilyData* self, const char* tag, const char** attributes) {
37510b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        // 'variant' ("elegant", "compact") [default "default"]
37610b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        // 'lang' (string) [default ""]
37710b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        // 'index' (non-negative integer) [default 0]
37810b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        // The character data should be a filename.
37910b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        FontFamily& currentFamily = *self->fCurrentFamily.get();
38010b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        FontFileInfo& newFileInfo = currentFamily.fFonts.push_back();
38110b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        if (attributes) {
38210b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman            for (size_t i = 0; ATTS_NON_NULL(attributes, i); i += 2) {
38310b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman                const char* name = attributes[i];
38410b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman                const char* value = attributes[i+1];
38510b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman                size_t nameLen = strlen(name);
38610b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman                size_t valueLen = strlen(value);
38710b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman                if (MEMEQ("variant", name, nameLen)) {
38810b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman                    const FontVariant prevVariant = currentFamily.fVariant;
38910b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman                    if (MEMEQ("elegant", value, valueLen)) {
39010b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman                        currentFamily.fVariant = kElegant_FontVariant;
39110b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman                    } else if (MEMEQ("compact", value, valueLen)) {
39210b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman                        currentFamily.fVariant = kCompact_FontVariant;
39310b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman                    }
39410b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman                    if (currentFamily.fFonts.count() > 1 && currentFamily.fVariant != prevVariant) {
39510b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman                        SK_FONTCONFIGPARSER_WARNING("'%s' unexpected variant found\n"
39610b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman                            "Note: Every font file within a family must have identical variants.",
39710b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman                            value);
39810b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman                    }
39910b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman
40010b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman                } else if (MEMEQ("lang", name, nameLen)) {
40110b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman                    SkLanguage prevLang = currentFamily.fLanguage;
40210b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman                    currentFamily.fLanguage = SkLanguage(value, valueLen);
40310b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman                    if (currentFamily.fFonts.count() > 1 && currentFamily.fLanguage != prevLang) {
40410b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman                        SK_FONTCONFIGPARSER_WARNING("'%s' unexpected language found\n"
40510b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman                            "Note: Every font file within a family must have identical languages.",
40610b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman                            value);
40710b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman                    }
40810b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman
40910b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman                } else if (MEMEQ("index", name, nameLen)) {
41010b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman                    if (!parse_non_negative_integer(value, &newFileInfo.fIndex)) {
41110b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman                        SK_FONTCONFIGPARSER_WARNING("'%s' is an invalid index", value);
41210b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman                    }
41310b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman                }
414bfae9d373ccc9cf47fd70757092962c7850fadf4djsollen@google.com            }
415bfae9d373ccc9cf47fd70757092962c7850fadf4djsollen@google.com        }
41610b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        self->fCurrentFontInfo = &newFileInfo;
41710b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman    },
41896fcdcc219d2a0d3579719b84b28bede76efba64halcanary    /*end*/nullptr,
41996fcdcc219d2a0d3579719b84b28bede76efba64halcanary    /*tag*/nullptr,
42010b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman    /*chars*/[](void* data, const char* s, int len) {
42110b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        FamilyData* self = static_cast<FamilyData*>(data);
42210b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        self->fCurrentFontInfo->fFileName.append(s, len);
423bfae9d373ccc9cf47fd70757092962c7850fadf4djsollen@google.com    }
42410b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman};
425bfae9d373ccc9cf47fd70757092962c7850fadf4djsollen@google.com
42610b063cb91c52fd1f570ee63307fe7e68c1501f1bungemanstatic const TagHandler fileSetHandler = {
42796fcdcc219d2a0d3579719b84b28bede76efba64halcanary    /*start*/nullptr,
42896fcdcc219d2a0d3579719b84b28bede76efba64halcanary    /*end*/nullptr,
42910b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman    /*tag*/[](FamilyData* self, const char* tag, const char** attributes) -> const TagHandler* {
43010b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        size_t len = strlen(tag);
43110b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        if (MEMEQ("file", tag, len)) {
43210b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman            return &fileHandler;
43310b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        }
43496fcdcc219d2a0d3579719b84b28bede76efba64halcanary        return nullptr;
43510b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman    },
43696fcdcc219d2a0d3579719b84b28bede76efba64halcanary    /*chars*/nullptr,
43710b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman};
43810b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman
43910b063cb91c52fd1f570ee63307fe7e68c1501f1bungemanstatic const TagHandler nameHandler = {
44010b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman    /*start*/[](FamilyData* self, const char* tag, const char** attributes) {
44110b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        // The character data should be a name for the font.
44210b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        self->fCurrentFamily->fNames.push_back();
44310b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman    },
44496fcdcc219d2a0d3579719b84b28bede76efba64halcanary    /*end*/nullptr,
44596fcdcc219d2a0d3579719b84b28bede76efba64halcanary    /*tag*/nullptr,
44610b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman    /*chars*/[](void* data, const char* s, int len) {
44710b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        FamilyData* self = static_cast<FamilyData*>(data);
44810b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        SkAutoAsciiToLC tolc(s, len);
44910b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        self->fCurrentFamily->fNames.back().append(tolc.lc(), len);
45010b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman    }
45110b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman};
45210b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman
45310b063cb91c52fd1f570ee63307fe7e68c1501f1bungemanstatic const TagHandler nameSetHandler = {
45496fcdcc219d2a0d3579719b84b28bede76efba64halcanary    /*start*/nullptr,
45596fcdcc219d2a0d3579719b84b28bede76efba64halcanary    /*end*/nullptr,
45610b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman    /*tag*/[](FamilyData* self, const char* tag, const char** attributes) -> const TagHandler* {
45710b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        size_t len = strlen(tag);
45810b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        if (MEMEQ("name", tag, len)) {
45910b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman            return &nameHandler;
46010b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        }
46196fcdcc219d2a0d3579719b84b28bede76efba64halcanary        return nullptr;
46210b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman    },
46396fcdcc219d2a0d3579719b84b28bede76efba64halcanary    /*chars*/nullptr,
46410b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman};
46510b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman
46610b063cb91c52fd1f570ee63307fe7e68c1501f1bungemanstatic const TagHandler familyHandler = {
46710b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman    /*start*/[](FamilyData* self, const char* tag, const char** attributes) {
46810b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        self->fCurrentFamily.reset(new FontFamily(self->fBasePath, self->fIsFallback));
46910b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        // 'order' (non-negative integer) [default -1]
4707fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman        for (size_t i = 0; ATTS_NON_NULL(attributes, i); i += 2) {
471f61475e95d9bb38f741f9e51221c086250b9ad72bungeman            const char* value = attributes[i+1];
47210b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman            parse_non_negative_integer(value, &self->fCurrentFamily->fOrder);
47310b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        }
47410b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman    },
47510b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman    /*end*/[](FamilyData* self, const char* tag) {
47610b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        *self->fFamilies.append() = self->fCurrentFamily.detach();
47710b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman    },
47810b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman    /*tag*/[](FamilyData* self, const char* tag, const char** attributes) -> const TagHandler* {
47910b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        size_t len = strlen(tag);
48010b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        if (MEMEQ("nameset", tag, len)) {
48110b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman            return &nameSetHandler;
48210b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        } else if (MEMEQ("fileset", tag, len)) {
48310b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman            return &fileSetHandler;
48410b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        }
48596fcdcc219d2a0d3579719b84b28bede76efba64halcanary        return nullptr;
48610b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman    },
48796fcdcc219d2a0d3579719b84b28bede76efba64halcanary    /*chars*/nullptr,
48810b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman};
4893b6255493e458c6b2c1412af908581f0bf3f6b70djsollen
49010b063cb91c52fd1f570ee63307fe7e68c1501f1bungemanstatic const TagHandler familySetHandler = {
49196fcdcc219d2a0d3579719b84b28bede76efba64halcanary    /*start*/nullptr,
49296fcdcc219d2a0d3579719b84b28bede76efba64halcanary    /*end*/nullptr,
49310b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman    /*tag*/[](FamilyData* self, const char* tag, const char** attributes) -> const TagHandler* {
49410b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        size_t len = strlen(tag);
49510b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        if (MEMEQ("family", tag, len)) {
49610b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman            return &familyHandler;
49710b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        }
49896fcdcc219d2a0d3579719b84b28bede76efba64halcanary        return nullptr;
49910b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman    },
50096fcdcc219d2a0d3579719b84b28bede76efba64halcanary    /*chars*/nullptr,
50110b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman};
5027fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman
50310b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman} // namespace jbParser
50410b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman
50510b063cb91c52fd1f570ee63307fe7e68c1501f1bungemanstatic const TagHandler topLevelHandler = {
50696fcdcc219d2a0d3579719b84b28bede76efba64halcanary    /*start*/nullptr,
50796fcdcc219d2a0d3579719b84b28bede76efba64halcanary    /*end*/nullptr,
50810b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman    /*tag*/[](FamilyData* self, const char* tag, const char** attributes) -> const TagHandler* {
50910b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        size_t len = strlen(tag);
51010b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        if (MEMEQ("familyset", tag, len)) {
51110b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman            // 'version' (non-negative integer) [default 0]
51210b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman            for (size_t i = 0; ATTS_NON_NULL(attributes, i); i += 2) {
51310b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman                const char* name = attributes[i];
51410b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman                size_t nameLen = strlen(name);
51510b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman                if (MEMEQ("version", name, nameLen)) {
51610b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman                    const char* value = attributes[i+1];
51710b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman                    if (parse_non_negative_integer(value, &self->fVersion)) {
51810b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman                        if (self->fVersion >= 21) {
51910b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman                            return &lmpParser::familySetHandler;
52010b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman                        }
52110b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman                    }
5228d84c995319dd4a82e4f2054bbd19f968c671ca6bungeman                }
523bfae9d373ccc9cf47fd70757092962c7850fadf4djsollen@google.com            }
52410b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman            return &jbParser::familySetHandler;
525bfae9d373ccc9cf47fd70757092962c7850fadf4djsollen@google.com        }
52696fcdcc219d2a0d3579719b84b28bede76efba64halcanary        return nullptr;
52710b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman    },
52896fcdcc219d2a0d3579719b84b28bede76efba64halcanary    /*chars*/nullptr,
52910b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman};
530bfae9d373ccc9cf47fd70757092962c7850fadf4djsollen@google.com
53110b063cb91c52fd1f570ee63307fe7e68c1501f1bungemanstatic void XMLCALL start_element_handler(void *data, const char *tag, const char **attributes) {
5327fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman    FamilyData* self = static_cast<FamilyData*>(data);
53310b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman
53410b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman    if (!self->fSkip) {
53510b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        const TagHandler* parent = self->fHandler.top();
53696fcdcc219d2a0d3579719b84b28bede76efba64halcanary        const TagHandler* child = parent->tag ? parent->tag(self, tag, attributes) : nullptr;
53710b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        if (child) {
53810b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman            if (child->start) {
53910b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman                child->start(self, tag, attributes);
540bfae9d373ccc9cf47fd70757092962c7850fadf4djsollen@google.com            }
54110b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman            self->fHandler.push(child);
54210b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman            XML_SetCharacterDataHandler(self->fParser, child->chars);
54310b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        } else {
54410b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman            SK_FONTCONFIGPARSER_WARNING("'%s' tag not recognized, skipping", tag);
54596fcdcc219d2a0d3579719b84b28bede76efba64halcanary            XML_SetCharacterDataHandler(self->fParser, nullptr);
54610b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman            self->fSkip = self->fDepth;
547bfae9d373ccc9cf47fd70757092962c7850fadf4djsollen@google.com        }
548bfae9d373ccc9cf47fd70757092962c7850fadf4djsollen@google.com    }
54910b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman
55010b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman    ++self->fDepth;
551bfae9d373ccc9cf47fd70757092962c7850fadf4djsollen@google.com}
552bfae9d373ccc9cf47fd70757092962c7850fadf4djsollen@google.com
5537fa87cd09f49f1ee9bc27e263038d0f0ae706241bungemanstatic void XMLCALL end_element_handler(void* data, const char* tag) {
5547fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman    FamilyData* self = static_cast<FamilyData*>(data);
55510b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman    --self->fDepth;
55610b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman
55710b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman    if (!self->fSkip) {
55810b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        const TagHandler* child = self->fHandler.top();
55910b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        if (child->end) {
56010b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman            child->end(self, tag);
56110b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        }
56210b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        self->fHandler.pop();
56310b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        const TagHandler* parent = self->fHandler.top();
56410b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        XML_SetCharacterDataHandler(self->fParser, parent->chars);
565bfae9d373ccc9cf47fd70757092962c7850fadf4djsollen@google.com    }
566bfae9d373ccc9cf47fd70757092962c7850fadf4djsollen@google.com
56710b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman    if (self->fSkip == self->fDepth) {
56810b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        self->fSkip = 0;
56910b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        const TagHandler* parent = self->fHandler.top();
57010b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        XML_SetCharacterDataHandler(self->fParser, parent->chars);
57110b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman    }
57210b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman}
573f79673bbae0a662c1428755e2719dadf944e4ba1tomhudson
5747fa87cd09f49f1ee9bc27e263038d0f0ae706241bungemanstatic void XMLCALL xml_entity_decl_handler(void *data,
5757fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman                                            const XML_Char *entityName,
5767fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman                                            int is_parameter_entity,
5777fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman                                            const XML_Char *value,
5787fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman                                            int value_length,
5797fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman                                            const XML_Char *base,
5807fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman                                            const XML_Char *systemId,
5817fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman                                            const XML_Char *publicId,
5827fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman                                            const XML_Char *notationName)
5837fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman{
5847fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman    FamilyData* self = static_cast<FamilyData*>(data);
585f61475e95d9bb38f741f9e51221c086250b9ad72bungeman    SK_FONTCONFIGPARSER_WARNING("'%s' entity declaration found, stopping processing", entityName);
5867fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman    XML_StopParser(self->fParser, XML_FALSE);
5877fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman}
5887fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman
589eb2be7fa4413a566212782d8efae5dc225002821bungemanstatic const XML_Memory_Handling_Suite sk_XML_alloc = {
590eb2be7fa4413a566212782d8efae5dc225002821bungeman    sk_malloc_throw,
591eb2be7fa4413a566212782d8efae5dc225002821bungeman    sk_realloc_throw,
592eb2be7fa4413a566212782d8efae5dc225002821bungeman    sk_free
593eb2be7fa4413a566212782d8efae5dc225002821bungeman};
594eb2be7fa4413a566212782d8efae5dc225002821bungeman
595bfae9d373ccc9cf47fd70757092962c7850fadf4djsollen@google.com/**
596bfae9d373ccc9cf47fd70757092962c7850fadf4djsollen@google.com * This function parses the given filename and stores the results in the given
597efbad37180c6b6f90d4b7e96a234e8065d2ec395bungeman * families array. Returns the version of the file, negative if the file does not exist.
598bfae9d373ccc9cf47fd70757092962c7850fadf4djsollen@google.com */
5997fa87cd09f49f1ee9bc27e263038d0f0ae706241bungemanstatic int parse_config_file(const char* filename, SkTDArray<FontFamily*>& families,
6007fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman                             const SkString& basePath, bool isFallback)
6017fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman{
6027fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman    SkFILEStream file(filename);
60350c956791291e7f3cec23721157570b7911336b8djsollen@google.com
604bfae9d373ccc9cf47fd70757092962c7850fadf4djsollen@google.com    // Some of the files we attempt to parse (in particular, /vendor/etc/fallback_fonts.xml)
605bfae9d373ccc9cf47fd70757092962c7850fadf4djsollen@google.com    // are optional - failure here is okay because one of these optional files may not exist.
6067fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman    if (!file.isValid()) {
607c53085413e0b4704aa89cc18396613d59e6ccb4dbungeman        SkDebugf(SK_FONTMGR_ANDROID_PARSER_PREFIX "'%s' could not be opened\n", filename);
608efbad37180c6b6f90d4b7e96a234e8065d2ec395bungeman        return -1;
609bfae9d373ccc9cf47fd70757092962c7850fadf4djsollen@google.com    }
61050c956791291e7f3cec23721157570b7911336b8djsollen@google.com
61154e63082191f337084f96083ca90d7c35273d6ffbungeman    SkAutoTCallVProc<skstd::remove_pointer_t<XML_Parser>, XML_ParserFree> parser(
61296fcdcc219d2a0d3579719b84b28bede76efba64halcanary        XML_ParserCreate_MM(nullptr, &sk_XML_alloc, nullptr));
6137fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman    if (!parser) {
614c53085413e0b4704aa89cc18396613d59e6ccb4dbungeman        SkDebugf(SK_FONTMGR_ANDROID_PARSER_PREFIX "could not create XML parser\n");
6157fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman        return -1;
6167fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman    }
6177fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman
61810b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman    FamilyData self(parser, families, basePath, isFallback, filename, &topLevelHandler);
6197fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman    XML_SetUserData(parser, &self);
6207fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman
6217fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman    // Disable entity processing, to inhibit internal entity expansion. See expat CVE-2013-0340
6227fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman    XML_SetEntityDeclHandler(parser, xml_entity_decl_handler);
6237fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman
624f79673bbae0a662c1428755e2719dadf944e4ba1tomhudson    // Start parsing oldschool; switch these in flight if we detect a newer version of the file.
62510b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman    XML_SetElementHandler(parser, start_element_handler, end_element_handler);
62650c956791291e7f3cec23721157570b7911336b8djsollen@google.com
627eb2be7fa4413a566212782d8efae5dc225002821bungeman    // One would assume it would be faster to have a buffer on the stack and call XML_Parse.
628eb2be7fa4413a566212782d8efae5dc225002821bungeman    // But XML_Parse will call XML_GetBuffer anyway and memmove the passed buffer into it.
629eb2be7fa4413a566212782d8efae5dc225002821bungeman    // (Unless XML_CONTEXT_BYTES is undefined, but all users define it.)
6309a0808fd8e83128403285f391944850d908d7af0bungeman    // In debug, buffer a small odd number of bytes to detect slicing in XML_CharacterDataHandler.
6319a0808fd8e83128403285f391944850d908d7af0bungeman    static const int bufferSize = 512 SkDEBUGCODE( - 507);
632bfae9d373ccc9cf47fd70757092962c7850fadf4djsollen@google.com    bool done = false;
633bfae9d373ccc9cf47fd70757092962c7850fadf4djsollen@google.com    while (!done) {
634eb2be7fa4413a566212782d8efae5dc225002821bungeman        void* buffer = XML_GetBuffer(parser, bufferSize);
635eb2be7fa4413a566212782d8efae5dc225002821bungeman        if (!buffer) {
636c53085413e0b4704aa89cc18396613d59e6ccb4dbungeman            SkDebugf(SK_FONTMGR_ANDROID_PARSER_PREFIX "could not buffer enough to continue\n");
637eb2be7fa4413a566212782d8efae5dc225002821bungeman            return -1;
638eb2be7fa4413a566212782d8efae5dc225002821bungeman        }
639eb2be7fa4413a566212782d8efae5dc225002821bungeman        size_t len = file.read(buffer, bufferSize);
6407fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman        done = file.isAtEnd();
641eb2be7fa4413a566212782d8efae5dc225002821bungeman        XML_Status status = XML_ParseBuffer(parser, len, done);
6427fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman        if (XML_STATUS_ERROR == status) {
6437fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman            XML_Error error = XML_GetErrorCode(parser);
6447fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman            int line = XML_GetCurrentLineNumber(parser);
6457fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman            int column = XML_GetCurrentColumnNumber(parser);
6467fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman            const XML_LChar* errorString = XML_ErrorString(error);
647c53085413e0b4704aa89cc18396613d59e6ccb4dbungeman            SkDebugf(SK_FONTMGR_ANDROID_PARSER_PREFIX "%s:%d:%d error %d: %s.\n",
648f61475e95d9bb38f741f9e51221c086250b9ad72bungeman                     filename, line, column, error, errorString);
6497fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman            return -1;
650bfae9d373ccc9cf47fd70757092962c7850fadf4djsollen@google.com        }
651bfae9d373ccc9cf47fd70757092962c7850fadf4djsollen@google.com    }
6527fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman    return self.fVersion;
653bfae9d373ccc9cf47fd70757092962c7850fadf4djsollen@google.com}
654bfae9d373ccc9cf47fd70757092962c7850fadf4djsollen@google.com
655efbad37180c6b6f90d4b7e96a234e8065d2ec395bungeman/** Returns the version of the system font file actually found, negative if none. */
6567fa87cd09f49f1ee9bc27e263038d0f0ae706241bungemanstatic int append_system_font_families(SkTDArray<FontFamily*>& fontFamilies,
6577fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman                                       const SkString& basePath)
6587fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman{
6597a4747f4f62e4896d8f8469e1939b8191fff8d4etomhudson    int initialCount = fontFamilies.count();
6607fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman    int version = parse_config_file(LMP_SYSTEM_FONTS_FILE, fontFamilies, basePath, false);
661efbad37180c6b6f90d4b7e96a234e8065d2ec395bungeman    if (version < 0 || fontFamilies.count() == initialCount) {
6627fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman        version = parse_config_file(OLD_SYSTEM_FONTS_FILE, fontFamilies, basePath, false);
66394fa4b99e2a53e997a90c7808cc5263f1bf40c9ftomhudson    }
664efbad37180c6b6f90d4b7e96a234e8065d2ec395bungeman    return version;
665bfae9d373ccc9cf47fd70757092962c7850fadf4djsollen@google.com}
666bfae9d373ccc9cf47fd70757092962c7850fadf4djsollen@google.com
667a6c27bc5bd3b213d1e315c0bd9bbdcd75cec6900djsollen/**
668a6c27bc5bd3b213d1e315c0bd9bbdcd75cec6900djsollen * In some versions of Android prior to Android 4.2 (JellyBean MR1 at API
669a6c27bc5bd3b213d1e315c0bd9bbdcd75cec6900djsollen * Level 17) the fallback fonts for certain locales were encoded in their own
670a6c27bc5bd3b213d1e315c0bd9bbdcd75cec6900djsollen * XML files with a suffix that identified the locale.  We search the provided
671a6c27bc5bd3b213d1e315c0bd9bbdcd75cec6900djsollen * directory for those files,add all of their entries to the fallback chain, and
672a6c27bc5bd3b213d1e315c0bd9bbdcd75cec6900djsollen * include the locale as part of each entry.
673a6c27bc5bd3b213d1e315c0bd9bbdcd75cec6900djsollen */
674efbad37180c6b6f90d4b7e96a234e8065d2ec395bungemanstatic void append_fallback_font_families_for_locale(SkTDArray<FontFamily*>& fallbackFonts,
6757fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman                                                     const char* dir,
6767fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman                                                     const SkString& basePath)
677efbad37180c6b6f90d4b7e96a234e8065d2ec395bungeman{
678c3c694342ad393b88cee5885395f182082aa2ebbbungeman    SkAutoTCallIProc<DIR, closedir> fontDirectory(opendir(dir));
67996fcdcc219d2a0d3579719b84b28bede76efba64halcanary    if (nullptr == fontDirectory) {
680c3c694342ad393b88cee5885395f182082aa2ebbbungeman        return;
681c3c694342ad393b88cee5885395f182082aa2ebbbungeman    }
682a6c27bc5bd3b213d1e315c0bd9bbdcd75cec6900djsollen
683c3c694342ad393b88cee5885395f182082aa2ebbbungeman    for (struct dirent* dirEntry; (dirEntry = readdir(fontDirectory));) {
6849a0808fd8e83128403285f391944850d908d7af0bungeman        // The size of the prefix and suffix.
6859a0808fd8e83128403285f391944850d908d7af0bungeman        static const size_t fixedLen = sizeof(LOCALE_FALLBACK_FONTS_PREFIX) - 1
6869a0808fd8e83128403285f391944850d908d7af0bungeman                                     + sizeof(LOCALE_FALLBACK_FONTS_SUFFIX) - 1;
6879a0808fd8e83128403285f391944850d908d7af0bungeman
6889a0808fd8e83128403285f391944850d908d7af0bungeman        // The size of the prefix, suffix, and a minimum valid language code
6899a0808fd8e83128403285f391944850d908d7af0bungeman        static const size_t minSize = fixedLen + 2;
690c3c694342ad393b88cee5885395f182082aa2ebbbungeman
691c3c694342ad393b88cee5885395f182082aa2ebbbungeman        SkString fileName(dirEntry->d_name);
692c3c694342ad393b88cee5885395f182082aa2ebbbungeman        if (fileName.size() < minSize ||
693c3c694342ad393b88cee5885395f182082aa2ebbbungeman            !fileName.startsWith(LOCALE_FALLBACK_FONTS_PREFIX) ||
694c3c694342ad393b88cee5885395f182082aa2ebbbungeman            !fileName.endsWith(LOCALE_FALLBACK_FONTS_SUFFIX))
695c3c694342ad393b88cee5885395f182082aa2ebbbungeman        {
696c3c694342ad393b88cee5885395f182082aa2ebbbungeman            continue;
697c3c694342ad393b88cee5885395f182082aa2ebbbungeman        }
698a6c27bc5bd3b213d1e315c0bd9bbdcd75cec6900djsollen
699c3c694342ad393b88cee5885395f182082aa2ebbbungeman        SkString locale(fileName.c_str() + sizeof(LOCALE_FALLBACK_FONTS_PREFIX) - 1,
700c3c694342ad393b88cee5885395f182082aa2ebbbungeman                        fileName.size() - fixedLen);
701a6c27bc5bd3b213d1e315c0bd9bbdcd75cec6900djsollen
702c3c694342ad393b88cee5885395f182082aa2ebbbungeman        SkString absoluteFilename;
703c3c694342ad393b88cee5885395f182082aa2ebbbungeman        absoluteFilename.printf("%s/%s", dir, fileName.c_str());
704a6c27bc5bd3b213d1e315c0bd9bbdcd75cec6900djsollen
705c3c694342ad393b88cee5885395f182082aa2ebbbungeman        SkTDArray<FontFamily*> langSpecificFonts;
706c3c694342ad393b88cee5885395f182082aa2ebbbungeman        parse_config_file(absoluteFilename.c_str(), langSpecificFonts, basePath, true);
707a6c27bc5bd3b213d1e315c0bd9bbdcd75cec6900djsollen
708c3c694342ad393b88cee5885395f182082aa2ebbbungeman        for (int i = 0; i < langSpecificFonts.count(); ++i) {
709c3c694342ad393b88cee5885395f182082aa2ebbbungeman            FontFamily* family = langSpecificFonts[i];
710c3c694342ad393b88cee5885395f182082aa2ebbbungeman            family->fLanguage = SkLanguage(locale);
711c3c694342ad393b88cee5885395f182082aa2ebbbungeman            *fallbackFonts.append() = family;
712a6c27bc5bd3b213d1e315c0bd9bbdcd75cec6900djsollen        }
713a6c27bc5bd3b213d1e315c0bd9bbdcd75cec6900djsollen    }
714a6c27bc5bd3b213d1e315c0bd9bbdcd75cec6900djsollen}
715a6c27bc5bd3b213d1e315c0bd9bbdcd75cec6900djsollen
7167fa87cd09f49f1ee9bc27e263038d0f0ae706241bungemanstatic void append_system_fallback_font_families(SkTDArray<FontFamily*>& fallbackFonts,
7177fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman                                                 const SkString& basePath)
7187fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman{
7197fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman    parse_config_file(FALLBACK_FONTS_FILE, fallbackFonts, basePath, true);
7207fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman    append_fallback_font_families_for_locale(fallbackFonts,
7217fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman                                             LOCALE_FALLBACK_FONTS_SYSTEM_DIR,
7227fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman                                             basePath);
723efbad37180c6b6f90d4b7e96a234e8065d2ec395bungeman}
724bfae9d373ccc9cf47fd70757092962c7850fadf4djsollen@google.com
7257fa87cd09f49f1ee9bc27e263038d0f0ae706241bungemanstatic void mixin_vendor_fallback_font_families(SkTDArray<FontFamily*>& fallbackFonts,
7267fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman                                                const SkString& basePath)
7277fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman{
728efbad37180c6b6f90d4b7e96a234e8065d2ec395bungeman    SkTDArray<FontFamily*> vendorFonts;
7297fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman    parse_config_file(VENDOR_FONTS_FILE, vendorFonts, basePath, true);
7307fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman    append_fallback_font_families_for_locale(vendorFonts,
7317fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman                                             LOCALE_FALLBACK_FONTS_VENDOR_DIR,
7327fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman                                             basePath);
733a6c27bc5bd3b213d1e315c0bd9bbdcd75cec6900djsollen
734bfae9d373ccc9cf47fd70757092962c7850fadf4djsollen@google.com    // This loop inserts the vendor fallback fonts in the correct order in the
735bfae9d373ccc9cf47fd70757092962c7850fadf4djsollen@google.com    // overall fallbacks list.
736bfae9d373ccc9cf47fd70757092962c7850fadf4djsollen@google.com    int currentOrder = -1;
737bfae9d373ccc9cf47fd70757092962c7850fadf4djsollen@google.com    for (int i = 0; i < vendorFonts.count(); ++i) {
738bfae9d373ccc9cf47fd70757092962c7850fadf4djsollen@google.com        FontFamily* family = vendorFonts[i];
739d3ddea284ec6611a93a6b75e64de39d0bc7e083ctomhudson        int order = family->fOrder;
740bfae9d373ccc9cf47fd70757092962c7850fadf4djsollen@google.com        if (order < 0) {
741bfae9d373ccc9cf47fd70757092962c7850fadf4djsollen@google.com            if (currentOrder < 0) {
742bfae9d373ccc9cf47fd70757092962c7850fadf4djsollen@google.com                // Default case - just add it to the end of the fallback list
743bfae9d373ccc9cf47fd70757092962c7850fadf4djsollen@google.com                *fallbackFonts.append() = family;
744bfae9d373ccc9cf47fd70757092962c7850fadf4djsollen@google.com            } else {
745bfae9d373ccc9cf47fd70757092962c7850fadf4djsollen@google.com                // no order specified on this font, but we're incrementing the order
746bfae9d373ccc9cf47fd70757092962c7850fadf4djsollen@google.com                // based on an earlier order insertion request
747bfae9d373ccc9cf47fd70757092962c7850fadf4djsollen@google.com                *fallbackFonts.insert(currentOrder++) = family;
748bfae9d373ccc9cf47fd70757092962c7850fadf4djsollen@google.com            }
749bfae9d373ccc9cf47fd70757092962c7850fadf4djsollen@google.com        } else {
750bfae9d373ccc9cf47fd70757092962c7850fadf4djsollen@google.com            // Add the font into the fallback list in the specified order. Set
751bfae9d373ccc9cf47fd70757092962c7850fadf4djsollen@google.com            // currentOrder for correct placement of other fonts in the vendor list.
752bfae9d373ccc9cf47fd70757092962c7850fadf4djsollen@google.com            *fallbackFonts.insert(order) = family;
753bfae9d373ccc9cf47fd70757092962c7850fadf4djsollen@google.com            currentOrder = order + 1;
754bfae9d373ccc9cf47fd70757092962c7850fadf4djsollen@google.com        }
755bfae9d373ccc9cf47fd70757092962c7850fadf4djsollen@google.com    }
756bfae9d373ccc9cf47fd70757092962c7850fadf4djsollen@google.com}
757bfae9d373ccc9cf47fd70757092962c7850fadf4djsollen@google.com
758c53085413e0b4704aa89cc18396613d59e6ccb4dbungemanvoid SkFontMgr_Android_Parser::GetSystemFontFamilies(SkTDArray<FontFamily*>& fontFamilies) {
759efbad37180c6b6f90d4b7e96a234e8065d2ec395bungeman    // Version 21 of the system font configuration does not need any fallback configuration files.
7607fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman    SkString basePath(getenv("ANDROID_ROOT"));
7617fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman    basePath.append(SK_FONT_FILE_PREFIX, sizeof(SK_FONT_FILE_PREFIX) - 1);
7627fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman
7637fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman    if (append_system_font_families(fontFamilies, basePath) >= 21) {
764efbad37180c6b6f90d4b7e96a234e8065d2ec395bungeman        return;
765efbad37180c6b6f90d4b7e96a234e8065d2ec395bungeman    }
766bfae9d373ccc9cf47fd70757092962c7850fadf4djsollen@google.com
767bfae9d373ccc9cf47fd70757092962c7850fadf4djsollen@google.com    // Append all the fallback fonts to system fonts
768bfae9d373ccc9cf47fd70757092962c7850fadf4djsollen@google.com    SkTDArray<FontFamily*> fallbackFonts;
7697fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman    append_system_fallback_font_families(fallbackFonts, basePath);
7707fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman    mixin_vendor_fallback_font_families(fallbackFonts, basePath);
7717fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman    fontFamilies.append(fallbackFonts.count(), fallbackFonts.begin());
772bfae9d373ccc9cf47fd70757092962c7850fadf4djsollen@google.com}
773bfae9d373ccc9cf47fd70757092962c7850fadf4djsollen@google.com
774c53085413e0b4704aa89cc18396613d59e6ccb4dbungemanvoid SkFontMgr_Android_Parser::GetCustomFontFamilies(SkTDArray<FontFamily*>& fontFamilies,
775c53085413e0b4704aa89cc18396613d59e6ccb4dbungeman                                                     const SkString& basePath,
776c53085413e0b4704aa89cc18396613d59e6ccb4dbungeman                                                     const char* fontsXml,
777c53085413e0b4704aa89cc18396613d59e6ccb4dbungeman                                                     const char* fallbackFontsXml,
778c53085413e0b4704aa89cc18396613d59e6ccb4dbungeman                                                     const char* langFallbackFontsDir)
7797fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman{
7807fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman    if (fontsXml) {
7817fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman        parse_config_file(fontsXml, fontFamilies, basePath, false);
782f79673bbae0a662c1428755e2719dadf944e4ba1tomhudson    }
7837fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman    if (fallbackFontsXml) {
7847fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman        parse_config_file(fallbackFontsXml, fontFamilies, basePath, true);
785bfae9d373ccc9cf47fd70757092962c7850fadf4djsollen@google.com    }
786c3c694342ad393b88cee5885395f182082aa2ebbbungeman    if (langFallbackFontsDir) {
787c3c694342ad393b88cee5885395f182082aa2ebbbungeman        append_fallback_font_families_for_locale(fontFamilies,
788c3c694342ad393b88cee5885395f182082aa2ebbbungeman                                                 langFallbackFontsDir,
789c3c694342ad393b88cee5885395f182082aa2ebbbungeman                                                 basePath);
790c3c694342ad393b88cee5885395f182082aa2ebbbungeman    }
791bfae9d373ccc9cf47fd70757092962c7850fadf4djsollen@google.com}
7923b6255493e458c6b2c1412af908581f0bf3f6b70djsollen
7933b6255493e458c6b2c1412af908581f0bf3f6b70djsollenSkLanguage SkLanguage::getParent() const {
7943b6255493e458c6b2c1412af908581f0bf3f6b70djsollen    SkASSERT(!fTag.isEmpty());
7953b6255493e458c6b2c1412af908581f0bf3f6b70djsollen    const char* tag = fTag.c_str();
7963b6255493e458c6b2c1412af908581f0bf3f6b70djsollen
7973b6255493e458c6b2c1412af908581f0bf3f6b70djsollen    // strip off the rightmost "-.*"
7983b6255493e458c6b2c1412af908581f0bf3f6b70djsollen    const char* parentTagEnd = strrchr(tag, '-');
79996fcdcc219d2a0d3579719b84b28bede76efba64halcanary    if (parentTagEnd == nullptr) {
8003b6255493e458c6b2c1412af908581f0bf3f6b70djsollen        return SkLanguage();
8013b6255493e458c6b2c1412af908581f0bf3f6b70djsollen    }
8023b6255493e458c6b2c1412af908581f0bf3f6b70djsollen    size_t parentTagLen = parentTagEnd - tag;
8033b6255493e458c6b2c1412af908581f0bf3f6b70djsollen    return SkLanguage(tag, parentTagLen);
8043b6255493e458c6b2c1412af908581f0bf3f6b70djsollen}
805