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]
252aee878d767a3965ee6409be9c72bb9ae6266bf52Ben Wagner        // 'lang' (space separated 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)) {
267aee878d767a3965ee6409be9c72bb9ae6266bf52Ben Wagner                size_t i = 0;
268aee878d767a3965ee6409be9c72bb9ae6266bf52Ben Wagner                while (true) {
269aee878d767a3965ee6409be9c72bb9ae6266bf52Ben Wagner                    for (; i < valueLen && is_whitespace(value[i]); ++i) { }
270aee878d767a3965ee6409be9c72bb9ae6266bf52Ben Wagner                    if (i == valueLen) { break; }
271aee878d767a3965ee6409be9c72bb9ae6266bf52Ben Wagner                    size_t j;
272aee878d767a3965ee6409be9c72bb9ae6266bf52Ben Wagner                    for (j = i + 1; j < valueLen && !is_whitespace(value[j]); ++j) { }
273aee878d767a3965ee6409be9c72bb9ae6266bf52Ben Wagner                    family->fLanguages.emplace_back(value + i, j - i);
274aee878d767a3965ee6409be9c72bb9ae6266bf52Ben Wagner                    i = j;
275aee878d767a3965ee6409be9c72bb9ae6266bf52Ben Wagner                    if (i == valueLen) { break; }
276aee878d767a3965ee6409be9c72bb9ae6266bf52Ben Wagner                }
27710b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman            } else if (MEMEQ("variant", name, nameLen)) {
27810b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman                if (MEMEQ("elegant", value, valueLen)) {
27910b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman                    family->fVariant = kElegant_FontVariant;
28010b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman                } else if (MEMEQ("compact", value, valueLen)) {
28110b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman                    family->fVariant = kCompact_FontVariant;
28210b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman                }
283b8a1d30a42d13ae83690b2d854a024d9b56e7b71bungeman            }
28407544757c9fcf0f359f1686a3779eb2e75dd5b36tomhudson        }
28510b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman    },
28610b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman    /*end*/[](FamilyData* self, const char* tag) {
28718300a3aa7cb6eb55d21bb0450dffa58b6fc062cmtklein        *self->fFamilies.append() = self->fCurrentFamily.release();
28810b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman    },
28910b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman    /*tag*/[](FamilyData* self, const char* tag, const char** attributes) -> const TagHandler* {
29010b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        size_t len = strlen(tag);
29110b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        if (MEMEQ("font", tag, len)) {
29210b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman            return &fontHandler;
29310b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        }
29496fcdcc219d2a0d3579719b84b28bede76efba64halcanary        return nullptr;
29510b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman    },
29696fcdcc219d2a0d3579719b84b28bede76efba64halcanary    /*chars*/nullptr,
29710b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman};
29807544757c9fcf0f359f1686a3779eb2e75dd5b36tomhudson
299eb2be7fa4413a566212782d8efae5dc225002821bungemanstatic FontFamily* find_family(FamilyData* self, const SkString& familyName) {
3007fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman    for (int i = 0; i < self->fFamilies.count(); i++) {
3017fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman        FontFamily* candidate = self->fFamilies[i];
30207544757c9fcf0f359f1686a3779eb2e75dd5b36tomhudson        for (int j = 0; j < candidate->fNames.count(); j++) {
3037fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman            if (candidate->fNames[j] == familyName) {
30407544757c9fcf0f359f1686a3779eb2e75dd5b36tomhudson                return candidate;
30507544757c9fcf0f359f1686a3779eb2e75dd5b36tomhudson            }
30607544757c9fcf0f359f1686a3779eb2e75dd5b36tomhudson        }
30707544757c9fcf0f359f1686a3779eb2e75dd5b36tomhudson    }
30896fcdcc219d2a0d3579719b84b28bede76efba64halcanary    return nullptr;
30907544757c9fcf0f359f1686a3779eb2e75dd5b36tomhudson}
31007544757c9fcf0f359f1686a3779eb2e75dd5b36tomhudson
31110b063cb91c52fd1f570ee63307fe7e68c1501f1bungemanstatic const TagHandler aliasHandler = {
31210b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman    /*start*/[](FamilyData* self, const char* tag, const char** attributes) {
31310b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        // 'name' (string) introduces a new family name.
31410b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        // 'to' (string) specifies which (previous) family to alias
31510b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        // 'weight' (non-negative integer) [optional]
31610b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        // If it *does not* have a weight, 'name' is an alias for the entire 'to' family.
31710b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        // If it *does* have a weight, 'name' is a new family consisting of
31810b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        // the font(s) with 'weight' from the 'to' family.
31910b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman
32010b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        SkString aliasName;
32110b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        SkString to;
32210b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        int weight = 0;
32310b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        for (size_t i = 0; ATTS_NON_NULL(attributes, i); i += 2) {
32410b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman            const char* name = attributes[i];
32510b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman            const char* value = attributes[i+1];
32610b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman            size_t nameLen = strlen(name);
32710b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman            if (MEMEQ("name", name, nameLen)) {
32810b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman                SkAutoAsciiToLC tolc(value);
32910b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman                aliasName.set(tolc.lc());
33010b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman            } else if (MEMEQ("to", name, nameLen)) {
33110b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman                to.set(value);
33210b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman            } else if (MEMEQ("weight", name, nameLen)) {
33310b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman                if (!parse_non_negative_integer(value, &weight)) {
33410b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman                    SK_FONTCONFIGPARSER_WARNING("'%s' is an invalid weight", value);
33510b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman                }
3367fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman            }
33707544757c9fcf0f359f1686a3779eb2e75dd5b36tomhudson        }
33807544757c9fcf0f359f1686a3779eb2e75dd5b36tomhudson
33910b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        // Assumes that the named family is already declared
34010b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        FontFamily* targetFamily = find_family(self, to);
34110b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        if (!targetFamily) {
34210b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman            SK_FONTCONFIGPARSER_WARNING("'%s' alias target not found", to.c_str());
34310b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman            return;
34410b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        }
34507544757c9fcf0f359f1686a3779eb2e75dd5b36tomhudson
34610b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        if (weight) {
34710b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman            FontFamily* family = new FontFamily(targetFamily->fBasePath, self->fIsFallback);
34810b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman            family->fNames.push_back().set(aliasName);
34907544757c9fcf0f359f1686a3779eb2e75dd5b36tomhudson
35010b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman            for (int i = 0; i < targetFamily->fFonts.count(); i++) {
35110b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman                if (targetFamily->fFonts[i].fWeight == weight) {
35210b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman                    family->fFonts.push_back(targetFamily->fFonts[i]);
35310b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman                }
35407544757c9fcf0f359f1686a3779eb2e75dd5b36tomhudson            }
35510b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman            *self->fFamilies.append() = family;
35610b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        } else {
35710b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman            targetFamily->fNames.push_back().set(aliasName);
35807544757c9fcf0f359f1686a3779eb2e75dd5b36tomhudson        }
35910b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman    },
36096fcdcc219d2a0d3579719b84b28bede76efba64halcanary    /*end*/nullptr,
36196fcdcc219d2a0d3579719b84b28bede76efba64halcanary    /*tag*/nullptr,
36296fcdcc219d2a0d3579719b84b28bede76efba64halcanary    /*chars*/nullptr,
36310b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman};
364c0727d117e67844f0c8794cc7eaa49a96a015347bungeman
36510b063cb91c52fd1f570ee63307fe7e68c1501f1bungemanstatic const TagHandler familySetHandler = {
36610b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman    /*start*/[](FamilyData* self, const char* tag, const char** attributes) { },
36796fcdcc219d2a0d3579719b84b28bede76efba64halcanary    /*end*/nullptr,
36810b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman    /*tag*/[](FamilyData* self, const char* tag, const char** attributes) -> const TagHandler* {
36910b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        size_t len = strlen(tag);
37010b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        if (MEMEQ("family", tag, len)) {
37110b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman            return &familyHandler;
37210b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        } else if (MEMEQ("alias", tag, len)) {
37310b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman            return &aliasHandler;
37410b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        }
37596fcdcc219d2a0d3579719b84b28bede76efba64halcanary        return nullptr;
37610b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman    },
37796fcdcc219d2a0d3579719b84b28bede76efba64halcanary    /*chars*/nullptr,
37810b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman};
379f79673bbae0a662c1428755e2719dadf944e4ba1tomhudson
380f79673bbae0a662c1428755e2719dadf944e4ba1tomhudson} // lmpParser
381f79673bbae0a662c1428755e2719dadf944e4ba1tomhudson
382f79673bbae0a662c1428755e2719dadf944e4ba1tomhudsonnamespace jbParser {
383f79673bbae0a662c1428755e2719dadf944e4ba1tomhudson
38410b063cb91c52fd1f570ee63307fe7e68c1501f1bungemanstatic const TagHandler fileHandler = {
38510b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman    /*start*/[](FamilyData* self, const char* tag, const char** attributes) {
38610b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        // 'variant' ("elegant", "compact") [default "default"]
38710b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        // 'lang' (string) [default ""]
38810b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        // 'index' (non-negative integer) [default 0]
38910b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        // The character data should be a filename.
39010b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        FontFamily& currentFamily = *self->fCurrentFamily.get();
39110b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        FontFileInfo& newFileInfo = currentFamily.fFonts.push_back();
39210b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        if (attributes) {
39310b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman            for (size_t i = 0; ATTS_NON_NULL(attributes, i); i += 2) {
39410b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman                const char* name = attributes[i];
39510b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman                const char* value = attributes[i+1];
39610b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman                size_t nameLen = strlen(name);
39710b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman                size_t valueLen = strlen(value);
39810b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman                if (MEMEQ("variant", name, nameLen)) {
39910b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman                    const FontVariant prevVariant = currentFamily.fVariant;
40010b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman                    if (MEMEQ("elegant", value, valueLen)) {
40110b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman                        currentFamily.fVariant = kElegant_FontVariant;
40210b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman                    } else if (MEMEQ("compact", value, valueLen)) {
40310b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman                        currentFamily.fVariant = kCompact_FontVariant;
40410b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman                    }
40510b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman                    if (currentFamily.fFonts.count() > 1 && currentFamily.fVariant != prevVariant) {
40610b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman                        SK_FONTCONFIGPARSER_WARNING("'%s' unexpected variant found\n"
40710b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman                            "Note: Every font file within a family must have identical variants.",
40810b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman                            value);
40910b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman                    }
41010b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman
41110b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman                } else if (MEMEQ("lang", name, nameLen)) {
412aee878d767a3965ee6409be9c72bb9ae6266bf52Ben Wagner                    SkLanguage currentLanguage = SkLanguage(value, valueLen);
413aee878d767a3965ee6409be9c72bb9ae6266bf52Ben Wagner                    bool showWarning = false;
414aee878d767a3965ee6409be9c72bb9ae6266bf52Ben Wagner                    if (currentFamily.fLanguages.empty()) {
415aee878d767a3965ee6409be9c72bb9ae6266bf52Ben Wagner                        showWarning = (currentFamily.fFonts.count() > 1);
416aee878d767a3965ee6409be9c72bb9ae6266bf52Ben Wagner                        currentFamily.fLanguages.push_back(std::move(currentLanguage));
417aee878d767a3965ee6409be9c72bb9ae6266bf52Ben Wagner                    } else if (currentFamily.fLanguages[0] != currentLanguage) {
418aee878d767a3965ee6409be9c72bb9ae6266bf52Ben Wagner                        showWarning = true;
419aee878d767a3965ee6409be9c72bb9ae6266bf52Ben Wagner                        currentFamily.fLanguages[0] = std::move(currentLanguage);
420aee878d767a3965ee6409be9c72bb9ae6266bf52Ben Wagner                    }
421aee878d767a3965ee6409be9c72bb9ae6266bf52Ben Wagner                    if (showWarning) {
42210b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman                        SK_FONTCONFIGPARSER_WARNING("'%s' unexpected language found\n"
42310b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman                            "Note: Every font file within a family must have identical languages.",
42410b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman                            value);
42510b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman                    }
42610b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman
42710b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman                } else if (MEMEQ("index", name, nameLen)) {
42810b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman                    if (!parse_non_negative_integer(value, &newFileInfo.fIndex)) {
42910b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman                        SK_FONTCONFIGPARSER_WARNING("'%s' is an invalid index", value);
43010b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman                    }
43110b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman                }
432bfae9d373ccc9cf47fd70757092962c7850fadf4djsollen@google.com            }
433bfae9d373ccc9cf47fd70757092962c7850fadf4djsollen@google.com        }
43410b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        self->fCurrentFontInfo = &newFileInfo;
43510b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman    },
43696fcdcc219d2a0d3579719b84b28bede76efba64halcanary    /*end*/nullptr,
43796fcdcc219d2a0d3579719b84b28bede76efba64halcanary    /*tag*/nullptr,
43810b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman    /*chars*/[](void* data, const char* s, int len) {
43910b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        FamilyData* self = static_cast<FamilyData*>(data);
44010b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        self->fCurrentFontInfo->fFileName.append(s, len);
441bfae9d373ccc9cf47fd70757092962c7850fadf4djsollen@google.com    }
44210b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman};
443bfae9d373ccc9cf47fd70757092962c7850fadf4djsollen@google.com
44410b063cb91c52fd1f570ee63307fe7e68c1501f1bungemanstatic const TagHandler fileSetHandler = {
44596fcdcc219d2a0d3579719b84b28bede76efba64halcanary    /*start*/nullptr,
44696fcdcc219d2a0d3579719b84b28bede76efba64halcanary    /*end*/nullptr,
44710b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman    /*tag*/[](FamilyData* self, const char* tag, const char** attributes) -> const TagHandler* {
44810b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        size_t len = strlen(tag);
44910b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        if (MEMEQ("file", tag, len)) {
45010b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman            return &fileHandler;
45110b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        }
45296fcdcc219d2a0d3579719b84b28bede76efba64halcanary        return nullptr;
45310b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman    },
45496fcdcc219d2a0d3579719b84b28bede76efba64halcanary    /*chars*/nullptr,
45510b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman};
45610b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman
45710b063cb91c52fd1f570ee63307fe7e68c1501f1bungemanstatic const TagHandler nameHandler = {
45810b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman    /*start*/[](FamilyData* self, const char* tag, const char** attributes) {
45910b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        // The character data should be a name for the font.
46010b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        self->fCurrentFamily->fNames.push_back();
46110b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman    },
46296fcdcc219d2a0d3579719b84b28bede76efba64halcanary    /*end*/nullptr,
46396fcdcc219d2a0d3579719b84b28bede76efba64halcanary    /*tag*/nullptr,
46410b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman    /*chars*/[](void* data, const char* s, int len) {
46510b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        FamilyData* self = static_cast<FamilyData*>(data);
46610b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        SkAutoAsciiToLC tolc(s, len);
46710b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        self->fCurrentFamily->fNames.back().append(tolc.lc(), len);
46810b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman    }
46910b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman};
47010b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman
47110b063cb91c52fd1f570ee63307fe7e68c1501f1bungemanstatic const TagHandler nameSetHandler = {
47296fcdcc219d2a0d3579719b84b28bede76efba64halcanary    /*start*/nullptr,
47396fcdcc219d2a0d3579719b84b28bede76efba64halcanary    /*end*/nullptr,
47410b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman    /*tag*/[](FamilyData* self, const char* tag, const char** attributes) -> const TagHandler* {
47510b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        size_t len = strlen(tag);
47610b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        if (MEMEQ("name", tag, len)) {
47710b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman            return &nameHandler;
47810b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        }
47996fcdcc219d2a0d3579719b84b28bede76efba64halcanary        return nullptr;
48010b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman    },
48196fcdcc219d2a0d3579719b84b28bede76efba64halcanary    /*chars*/nullptr,
48210b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman};
48310b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman
48410b063cb91c52fd1f570ee63307fe7e68c1501f1bungemanstatic const TagHandler familyHandler = {
48510b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman    /*start*/[](FamilyData* self, const char* tag, const char** attributes) {
48610b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        self->fCurrentFamily.reset(new FontFamily(self->fBasePath, self->fIsFallback));
48710b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        // 'order' (non-negative integer) [default -1]
4887fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman        for (size_t i = 0; ATTS_NON_NULL(attributes, i); i += 2) {
489f61475e95d9bb38f741f9e51221c086250b9ad72bungeman            const char* value = attributes[i+1];
49010b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman            parse_non_negative_integer(value, &self->fCurrentFamily->fOrder);
49110b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        }
49210b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman    },
49310b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman    /*end*/[](FamilyData* self, const char* tag) {
49418300a3aa7cb6eb55d21bb0450dffa58b6fc062cmtklein        *self->fFamilies.append() = self->fCurrentFamily.release();
49510b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman    },
49610b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman    /*tag*/[](FamilyData* self, const char* tag, const char** attributes) -> const TagHandler* {
49710b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        size_t len = strlen(tag);
49810b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        if (MEMEQ("nameset", tag, len)) {
49910b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman            return &nameSetHandler;
50010b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        } else if (MEMEQ("fileset", tag, len)) {
50110b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman            return &fileSetHandler;
50210b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        }
50396fcdcc219d2a0d3579719b84b28bede76efba64halcanary        return nullptr;
50410b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman    },
50596fcdcc219d2a0d3579719b84b28bede76efba64halcanary    /*chars*/nullptr,
50610b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman};
5073b6255493e458c6b2c1412af908581f0bf3f6b70djsollen
50810b063cb91c52fd1f570ee63307fe7e68c1501f1bungemanstatic const TagHandler familySetHandler = {
50996fcdcc219d2a0d3579719b84b28bede76efba64halcanary    /*start*/nullptr,
51096fcdcc219d2a0d3579719b84b28bede76efba64halcanary    /*end*/nullptr,
51110b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman    /*tag*/[](FamilyData* self, const char* tag, const char** attributes) -> const TagHandler* {
51210b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        size_t len = strlen(tag);
51310b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        if (MEMEQ("family", tag, len)) {
51410b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman            return &familyHandler;
51510b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        }
51696fcdcc219d2a0d3579719b84b28bede76efba64halcanary        return nullptr;
51710b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman    },
51896fcdcc219d2a0d3579719b84b28bede76efba64halcanary    /*chars*/nullptr,
51910b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman};
5207fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman
52110b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman} // namespace jbParser
52210b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman
52310b063cb91c52fd1f570ee63307fe7e68c1501f1bungemanstatic const TagHandler topLevelHandler = {
52496fcdcc219d2a0d3579719b84b28bede76efba64halcanary    /*start*/nullptr,
52596fcdcc219d2a0d3579719b84b28bede76efba64halcanary    /*end*/nullptr,
52610b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman    /*tag*/[](FamilyData* self, const char* tag, const char** attributes) -> const TagHandler* {
52710b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        size_t len = strlen(tag);
52810b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        if (MEMEQ("familyset", tag, len)) {
52910b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman            // 'version' (non-negative integer) [default 0]
53010b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman            for (size_t i = 0; ATTS_NON_NULL(attributes, i); i += 2) {
53110b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman                const char* name = attributes[i];
53210b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman                size_t nameLen = strlen(name);
53310b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman                if (MEMEQ("version", name, nameLen)) {
53410b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman                    const char* value = attributes[i+1];
53510b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman                    if (parse_non_negative_integer(value, &self->fVersion)) {
53610b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman                        if (self->fVersion >= 21) {
53710b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman                            return &lmpParser::familySetHandler;
53810b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman                        }
53910b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman                    }
5408d84c995319dd4a82e4f2054bbd19f968c671ca6bungeman                }
541bfae9d373ccc9cf47fd70757092962c7850fadf4djsollen@google.com            }
54210b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman            return &jbParser::familySetHandler;
543bfae9d373ccc9cf47fd70757092962c7850fadf4djsollen@google.com        }
54496fcdcc219d2a0d3579719b84b28bede76efba64halcanary        return nullptr;
54510b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman    },
54696fcdcc219d2a0d3579719b84b28bede76efba64halcanary    /*chars*/nullptr,
54710b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman};
548bfae9d373ccc9cf47fd70757092962c7850fadf4djsollen@google.com
54910b063cb91c52fd1f570ee63307fe7e68c1501f1bungemanstatic void XMLCALL start_element_handler(void *data, const char *tag, const char **attributes) {
5507fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman    FamilyData* self = static_cast<FamilyData*>(data);
55110b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman
55210b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman    if (!self->fSkip) {
55310b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        const TagHandler* parent = self->fHandler.top();
55496fcdcc219d2a0d3579719b84b28bede76efba64halcanary        const TagHandler* child = parent->tag ? parent->tag(self, tag, attributes) : nullptr;
55510b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        if (child) {
55610b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman            if (child->start) {
55710b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman                child->start(self, tag, attributes);
558bfae9d373ccc9cf47fd70757092962c7850fadf4djsollen@google.com            }
55910b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman            self->fHandler.push(child);
56010b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman            XML_SetCharacterDataHandler(self->fParser, child->chars);
56110b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        } else {
56210b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman            SK_FONTCONFIGPARSER_WARNING("'%s' tag not recognized, skipping", tag);
56396fcdcc219d2a0d3579719b84b28bede76efba64halcanary            XML_SetCharacterDataHandler(self->fParser, nullptr);
56410b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman            self->fSkip = self->fDepth;
565bfae9d373ccc9cf47fd70757092962c7850fadf4djsollen@google.com        }
566bfae9d373ccc9cf47fd70757092962c7850fadf4djsollen@google.com    }
56710b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman
56810b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman    ++self->fDepth;
569bfae9d373ccc9cf47fd70757092962c7850fadf4djsollen@google.com}
570bfae9d373ccc9cf47fd70757092962c7850fadf4djsollen@google.com
5717fa87cd09f49f1ee9bc27e263038d0f0ae706241bungemanstatic void XMLCALL end_element_handler(void* data, const char* tag) {
5727fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman    FamilyData* self = static_cast<FamilyData*>(data);
57310b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman    --self->fDepth;
57410b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman
57510b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman    if (!self->fSkip) {
57610b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        const TagHandler* child = self->fHandler.top();
57710b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        if (child->end) {
57810b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman            child->end(self, tag);
57910b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        }
58010b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        self->fHandler.pop();
58110b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        const TagHandler* parent = self->fHandler.top();
58210b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        XML_SetCharacterDataHandler(self->fParser, parent->chars);
583bfae9d373ccc9cf47fd70757092962c7850fadf4djsollen@google.com    }
584bfae9d373ccc9cf47fd70757092962c7850fadf4djsollen@google.com
58510b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman    if (self->fSkip == self->fDepth) {
58610b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        self->fSkip = 0;
58710b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        const TagHandler* parent = self->fHandler.top();
58810b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman        XML_SetCharacterDataHandler(self->fParser, parent->chars);
58910b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman    }
59010b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman}
591f79673bbae0a662c1428755e2719dadf944e4ba1tomhudson
5927fa87cd09f49f1ee9bc27e263038d0f0ae706241bungemanstatic void XMLCALL xml_entity_decl_handler(void *data,
5937fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman                                            const XML_Char *entityName,
5947fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman                                            int is_parameter_entity,
5957fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman                                            const XML_Char *value,
5967fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman                                            int value_length,
5977fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman                                            const XML_Char *base,
5987fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman                                            const XML_Char *systemId,
5997fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman                                            const XML_Char *publicId,
6007fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman                                            const XML_Char *notationName)
6017fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman{
6027fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman    FamilyData* self = static_cast<FamilyData*>(data);
603f61475e95d9bb38f741f9e51221c086250b9ad72bungeman    SK_FONTCONFIGPARSER_WARNING("'%s' entity declaration found, stopping processing", entityName);
6047fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman    XML_StopParser(self->fParser, XML_FALSE);
6057fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman}
6067fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman
607eb2be7fa4413a566212782d8efae5dc225002821bungemanstatic const XML_Memory_Handling_Suite sk_XML_alloc = {
608eb2be7fa4413a566212782d8efae5dc225002821bungeman    sk_malloc_throw,
609eb2be7fa4413a566212782d8efae5dc225002821bungeman    sk_realloc_throw,
610eb2be7fa4413a566212782d8efae5dc225002821bungeman    sk_free
611eb2be7fa4413a566212782d8efae5dc225002821bungeman};
612eb2be7fa4413a566212782d8efae5dc225002821bungeman
613bfae9d373ccc9cf47fd70757092962c7850fadf4djsollen@google.com/**
614bfae9d373ccc9cf47fd70757092962c7850fadf4djsollen@google.com * This function parses the given filename and stores the results in the given
615efbad37180c6b6f90d4b7e96a234e8065d2ec395bungeman * families array. Returns the version of the file, negative if the file does not exist.
616bfae9d373ccc9cf47fd70757092962c7850fadf4djsollen@google.com */
6177fa87cd09f49f1ee9bc27e263038d0f0ae706241bungemanstatic int parse_config_file(const char* filename, SkTDArray<FontFamily*>& families,
6187fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman                             const SkString& basePath, bool isFallback)
6197fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman{
6207fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman    SkFILEStream file(filename);
62150c956791291e7f3cec23721157570b7911336b8djsollen@google.com
622bfae9d373ccc9cf47fd70757092962c7850fadf4djsollen@google.com    // Some of the files we attempt to parse (in particular, /vendor/etc/fallback_fonts.xml)
623bfae9d373ccc9cf47fd70757092962c7850fadf4djsollen@google.com    // are optional - failure here is okay because one of these optional files may not exist.
6247fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman    if (!file.isValid()) {
625c53085413e0b4704aa89cc18396613d59e6ccb4dbungeman        SkDebugf(SK_FONTMGR_ANDROID_PARSER_PREFIX "'%s' could not be opened\n", filename);
626efbad37180c6b6f90d4b7e96a234e8065d2ec395bungeman        return -1;
627bfae9d373ccc9cf47fd70757092962c7850fadf4djsollen@google.com    }
62850c956791291e7f3cec23721157570b7911336b8djsollen@google.com
62954e63082191f337084f96083ca90d7c35273d6ffbungeman    SkAutoTCallVProc<skstd::remove_pointer_t<XML_Parser>, XML_ParserFree> parser(
63096fcdcc219d2a0d3579719b84b28bede76efba64halcanary        XML_ParserCreate_MM(nullptr, &sk_XML_alloc, nullptr));
6317fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman    if (!parser) {
632c53085413e0b4704aa89cc18396613d59e6ccb4dbungeman        SkDebugf(SK_FONTMGR_ANDROID_PARSER_PREFIX "could not create XML parser\n");
6337fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman        return -1;
6347fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman    }
6357fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman
63610b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman    FamilyData self(parser, families, basePath, isFallback, filename, &topLevelHandler);
6377fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman    XML_SetUserData(parser, &self);
6387fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman
6397fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman    // Disable entity processing, to inhibit internal entity expansion. See expat CVE-2013-0340
6407fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman    XML_SetEntityDeclHandler(parser, xml_entity_decl_handler);
6417fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman
642f79673bbae0a662c1428755e2719dadf944e4ba1tomhudson    // Start parsing oldschool; switch these in flight if we detect a newer version of the file.
64310b063cb91c52fd1f570ee63307fe7e68c1501f1bungeman    XML_SetElementHandler(parser, start_element_handler, end_element_handler);
64450c956791291e7f3cec23721157570b7911336b8djsollen@google.com
645eb2be7fa4413a566212782d8efae5dc225002821bungeman    // One would assume it would be faster to have a buffer on the stack and call XML_Parse.
646eb2be7fa4413a566212782d8efae5dc225002821bungeman    // But XML_Parse will call XML_GetBuffer anyway and memmove the passed buffer into it.
647eb2be7fa4413a566212782d8efae5dc225002821bungeman    // (Unless XML_CONTEXT_BYTES is undefined, but all users define it.)
6489a0808fd8e83128403285f391944850d908d7af0bungeman    // In debug, buffer a small odd number of bytes to detect slicing in XML_CharacterDataHandler.
6499a0808fd8e83128403285f391944850d908d7af0bungeman    static const int bufferSize = 512 SkDEBUGCODE( - 507);
650bfae9d373ccc9cf47fd70757092962c7850fadf4djsollen@google.com    bool done = false;
651bfae9d373ccc9cf47fd70757092962c7850fadf4djsollen@google.com    while (!done) {
652eb2be7fa4413a566212782d8efae5dc225002821bungeman        void* buffer = XML_GetBuffer(parser, bufferSize);
653eb2be7fa4413a566212782d8efae5dc225002821bungeman        if (!buffer) {
654c53085413e0b4704aa89cc18396613d59e6ccb4dbungeman            SkDebugf(SK_FONTMGR_ANDROID_PARSER_PREFIX "could not buffer enough to continue\n");
655eb2be7fa4413a566212782d8efae5dc225002821bungeman            return -1;
656eb2be7fa4413a566212782d8efae5dc225002821bungeman        }
657eb2be7fa4413a566212782d8efae5dc225002821bungeman        size_t len = file.read(buffer, bufferSize);
6587fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman        done = file.isAtEnd();
659eb2be7fa4413a566212782d8efae5dc225002821bungeman        XML_Status status = XML_ParseBuffer(parser, len, done);
6607fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman        if (XML_STATUS_ERROR == status) {
6617fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman            XML_Error error = XML_GetErrorCode(parser);
6627fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman            int line = XML_GetCurrentLineNumber(parser);
6637fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman            int column = XML_GetCurrentColumnNumber(parser);
6647fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman            const XML_LChar* errorString = XML_ErrorString(error);
665c53085413e0b4704aa89cc18396613d59e6ccb4dbungeman            SkDebugf(SK_FONTMGR_ANDROID_PARSER_PREFIX "%s:%d:%d error %d: %s.\n",
666f61475e95d9bb38f741f9e51221c086250b9ad72bungeman                     filename, line, column, error, errorString);
6677fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman            return -1;
668bfae9d373ccc9cf47fd70757092962c7850fadf4djsollen@google.com        }
669bfae9d373ccc9cf47fd70757092962c7850fadf4djsollen@google.com    }
6707fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman    return self.fVersion;
671bfae9d373ccc9cf47fd70757092962c7850fadf4djsollen@google.com}
672bfae9d373ccc9cf47fd70757092962c7850fadf4djsollen@google.com
673efbad37180c6b6f90d4b7e96a234e8065d2ec395bungeman/** Returns the version of the system font file actually found, negative if none. */
6747fa87cd09f49f1ee9bc27e263038d0f0ae706241bungemanstatic int append_system_font_families(SkTDArray<FontFamily*>& fontFamilies,
6757fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman                                       const SkString& basePath)
6767fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman{
6777a4747f4f62e4896d8f8469e1939b8191fff8d4etomhudson    int initialCount = fontFamilies.count();
6787fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman    int version = parse_config_file(LMP_SYSTEM_FONTS_FILE, fontFamilies, basePath, false);
679efbad37180c6b6f90d4b7e96a234e8065d2ec395bungeman    if (version < 0 || fontFamilies.count() == initialCount) {
6807fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman        version = parse_config_file(OLD_SYSTEM_FONTS_FILE, fontFamilies, basePath, false);
68194fa4b99e2a53e997a90c7808cc5263f1bf40c9ftomhudson    }
682efbad37180c6b6f90d4b7e96a234e8065d2ec395bungeman    return version;
683bfae9d373ccc9cf47fd70757092962c7850fadf4djsollen@google.com}
684bfae9d373ccc9cf47fd70757092962c7850fadf4djsollen@google.com
685a6c27bc5bd3b213d1e315c0bd9bbdcd75cec6900djsollen/**
686a6c27bc5bd3b213d1e315c0bd9bbdcd75cec6900djsollen * In some versions of Android prior to Android 4.2 (JellyBean MR1 at API
687a6c27bc5bd3b213d1e315c0bd9bbdcd75cec6900djsollen * Level 17) the fallback fonts for certain locales were encoded in their own
688a6c27bc5bd3b213d1e315c0bd9bbdcd75cec6900djsollen * XML files with a suffix that identified the locale.  We search the provided
689a6c27bc5bd3b213d1e315c0bd9bbdcd75cec6900djsollen * directory for those files,add all of their entries to the fallback chain, and
690a6c27bc5bd3b213d1e315c0bd9bbdcd75cec6900djsollen * include the locale as part of each entry.
691a6c27bc5bd3b213d1e315c0bd9bbdcd75cec6900djsollen */
692efbad37180c6b6f90d4b7e96a234e8065d2ec395bungemanstatic void append_fallback_font_families_for_locale(SkTDArray<FontFamily*>& fallbackFonts,
6937fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman                                                     const char* dir,
6947fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman                                                     const SkString& basePath)
695efbad37180c6b6f90d4b7e96a234e8065d2ec395bungeman{
69622cffcace2ad526f55ba2579e65836d38f009d1abungeman    SkOSFile::Iter iter(dir, nullptr);
69722cffcace2ad526f55ba2579e65836d38f009d1abungeman    SkString fileName;
69822cffcace2ad526f55ba2579e65836d38f009d1abungeman    while (iter.next(&fileName, false)) {
6999a0808fd8e83128403285f391944850d908d7af0bungeman        // The size of the prefix and suffix.
7009a0808fd8e83128403285f391944850d908d7af0bungeman        static const size_t fixedLen = sizeof(LOCALE_FALLBACK_FONTS_PREFIX) - 1
7019a0808fd8e83128403285f391944850d908d7af0bungeman                                     + sizeof(LOCALE_FALLBACK_FONTS_SUFFIX) - 1;
7029a0808fd8e83128403285f391944850d908d7af0bungeman
7039a0808fd8e83128403285f391944850d908d7af0bungeman        // The size of the prefix, suffix, and a minimum valid language code
7049a0808fd8e83128403285f391944850d908d7af0bungeman        static const size_t minSize = fixedLen + 2;
705c3c694342ad393b88cee5885395f182082aa2ebbbungeman
706c3c694342ad393b88cee5885395f182082aa2ebbbungeman        if (fileName.size() < minSize ||
707c3c694342ad393b88cee5885395f182082aa2ebbbungeman            !fileName.startsWith(LOCALE_FALLBACK_FONTS_PREFIX) ||
708c3c694342ad393b88cee5885395f182082aa2ebbbungeman            !fileName.endsWith(LOCALE_FALLBACK_FONTS_SUFFIX))
709c3c694342ad393b88cee5885395f182082aa2ebbbungeman        {
710c3c694342ad393b88cee5885395f182082aa2ebbbungeman            continue;
711c3c694342ad393b88cee5885395f182082aa2ebbbungeman        }
712a6c27bc5bd3b213d1e315c0bd9bbdcd75cec6900djsollen
713c3c694342ad393b88cee5885395f182082aa2ebbbungeman        SkString locale(fileName.c_str() + sizeof(LOCALE_FALLBACK_FONTS_PREFIX) - 1,
714c3c694342ad393b88cee5885395f182082aa2ebbbungeman                        fileName.size() - fixedLen);
715a6c27bc5bd3b213d1e315c0bd9bbdcd75cec6900djsollen
716c3c694342ad393b88cee5885395f182082aa2ebbbungeman        SkString absoluteFilename;
717c3c694342ad393b88cee5885395f182082aa2ebbbungeman        absoluteFilename.printf("%s/%s", dir, fileName.c_str());
718a6c27bc5bd3b213d1e315c0bd9bbdcd75cec6900djsollen
719c3c694342ad393b88cee5885395f182082aa2ebbbungeman        SkTDArray<FontFamily*> langSpecificFonts;
720c3c694342ad393b88cee5885395f182082aa2ebbbungeman        parse_config_file(absoluteFilename.c_str(), langSpecificFonts, basePath, true);
721a6c27bc5bd3b213d1e315c0bd9bbdcd75cec6900djsollen
722c3c694342ad393b88cee5885395f182082aa2ebbbungeman        for (int i = 0; i < langSpecificFonts.count(); ++i) {
723c3c694342ad393b88cee5885395f182082aa2ebbbungeman            FontFamily* family = langSpecificFonts[i];
724aee878d767a3965ee6409be9c72bb9ae6266bf52Ben Wagner            family->fLanguages.emplace_back(locale);
725c3c694342ad393b88cee5885395f182082aa2ebbbungeman            *fallbackFonts.append() = family;
726a6c27bc5bd3b213d1e315c0bd9bbdcd75cec6900djsollen        }
727a6c27bc5bd3b213d1e315c0bd9bbdcd75cec6900djsollen    }
728a6c27bc5bd3b213d1e315c0bd9bbdcd75cec6900djsollen}
729a6c27bc5bd3b213d1e315c0bd9bbdcd75cec6900djsollen
7307fa87cd09f49f1ee9bc27e263038d0f0ae706241bungemanstatic void append_system_fallback_font_families(SkTDArray<FontFamily*>& fallbackFonts,
7317fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman                                                 const SkString& basePath)
7327fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman{
7337fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman    parse_config_file(FALLBACK_FONTS_FILE, fallbackFonts, basePath, true);
7347fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman    append_fallback_font_families_for_locale(fallbackFonts,
7357fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman                                             LOCALE_FALLBACK_FONTS_SYSTEM_DIR,
7367fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman                                             basePath);
737efbad37180c6b6f90d4b7e96a234e8065d2ec395bungeman}
738bfae9d373ccc9cf47fd70757092962c7850fadf4djsollen@google.com
7397fa87cd09f49f1ee9bc27e263038d0f0ae706241bungemanstatic void mixin_vendor_fallback_font_families(SkTDArray<FontFamily*>& fallbackFonts,
7407fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman                                                const SkString& basePath)
7417fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman{
742efbad37180c6b6f90d4b7e96a234e8065d2ec395bungeman    SkTDArray<FontFamily*> vendorFonts;
7437fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman    parse_config_file(VENDOR_FONTS_FILE, vendorFonts, basePath, true);
7447fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman    append_fallback_font_families_for_locale(vendorFonts,
7457fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman                                             LOCALE_FALLBACK_FONTS_VENDOR_DIR,
7467fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman                                             basePath);
747a6c27bc5bd3b213d1e315c0bd9bbdcd75cec6900djsollen
748bfae9d373ccc9cf47fd70757092962c7850fadf4djsollen@google.com    // This loop inserts the vendor fallback fonts in the correct order in the
749bfae9d373ccc9cf47fd70757092962c7850fadf4djsollen@google.com    // overall fallbacks list.
750bfae9d373ccc9cf47fd70757092962c7850fadf4djsollen@google.com    int currentOrder = -1;
751bfae9d373ccc9cf47fd70757092962c7850fadf4djsollen@google.com    for (int i = 0; i < vendorFonts.count(); ++i) {
752bfae9d373ccc9cf47fd70757092962c7850fadf4djsollen@google.com        FontFamily* family = vendorFonts[i];
753d3ddea284ec6611a93a6b75e64de39d0bc7e083ctomhudson        int order = family->fOrder;
754bfae9d373ccc9cf47fd70757092962c7850fadf4djsollen@google.com        if (order < 0) {
755bfae9d373ccc9cf47fd70757092962c7850fadf4djsollen@google.com            if (currentOrder < 0) {
756bfae9d373ccc9cf47fd70757092962c7850fadf4djsollen@google.com                // Default case - just add it to the end of the fallback list
757bfae9d373ccc9cf47fd70757092962c7850fadf4djsollen@google.com                *fallbackFonts.append() = family;
758bfae9d373ccc9cf47fd70757092962c7850fadf4djsollen@google.com            } else {
759bfae9d373ccc9cf47fd70757092962c7850fadf4djsollen@google.com                // no order specified on this font, but we're incrementing the order
760bfae9d373ccc9cf47fd70757092962c7850fadf4djsollen@google.com                // based on an earlier order insertion request
761bfae9d373ccc9cf47fd70757092962c7850fadf4djsollen@google.com                *fallbackFonts.insert(currentOrder++) = family;
762bfae9d373ccc9cf47fd70757092962c7850fadf4djsollen@google.com            }
763bfae9d373ccc9cf47fd70757092962c7850fadf4djsollen@google.com        } else {
764bfae9d373ccc9cf47fd70757092962c7850fadf4djsollen@google.com            // Add the font into the fallback list in the specified order. Set
765bfae9d373ccc9cf47fd70757092962c7850fadf4djsollen@google.com            // currentOrder for correct placement of other fonts in the vendor list.
766bfae9d373ccc9cf47fd70757092962c7850fadf4djsollen@google.com            *fallbackFonts.insert(order) = family;
767bfae9d373ccc9cf47fd70757092962c7850fadf4djsollen@google.com            currentOrder = order + 1;
768bfae9d373ccc9cf47fd70757092962c7850fadf4djsollen@google.com        }
769bfae9d373ccc9cf47fd70757092962c7850fadf4djsollen@google.com    }
770bfae9d373ccc9cf47fd70757092962c7850fadf4djsollen@google.com}
771bfae9d373ccc9cf47fd70757092962c7850fadf4djsollen@google.com
772c53085413e0b4704aa89cc18396613d59e6ccb4dbungemanvoid SkFontMgr_Android_Parser::GetSystemFontFamilies(SkTDArray<FontFamily*>& fontFamilies) {
773efbad37180c6b6f90d4b7e96a234e8065d2ec395bungeman    // Version 21 of the system font configuration does not need any fallback configuration files.
7747fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman    SkString basePath(getenv("ANDROID_ROOT"));
7757fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman    basePath.append(SK_FONT_FILE_PREFIX, sizeof(SK_FONT_FILE_PREFIX) - 1);
7767fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman
7777fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman    if (append_system_font_families(fontFamilies, basePath) >= 21) {
778efbad37180c6b6f90d4b7e96a234e8065d2ec395bungeman        return;
779efbad37180c6b6f90d4b7e96a234e8065d2ec395bungeman    }
780bfae9d373ccc9cf47fd70757092962c7850fadf4djsollen@google.com
781bfae9d373ccc9cf47fd70757092962c7850fadf4djsollen@google.com    // Append all the fallback fonts to system fonts
782bfae9d373ccc9cf47fd70757092962c7850fadf4djsollen@google.com    SkTDArray<FontFamily*> fallbackFonts;
7837fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman    append_system_fallback_font_families(fallbackFonts, basePath);
7847fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman    mixin_vendor_fallback_font_families(fallbackFonts, basePath);
7857fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman    fontFamilies.append(fallbackFonts.count(), fallbackFonts.begin());
786bfae9d373ccc9cf47fd70757092962c7850fadf4djsollen@google.com}
787bfae9d373ccc9cf47fd70757092962c7850fadf4djsollen@google.com
788c53085413e0b4704aa89cc18396613d59e6ccb4dbungemanvoid SkFontMgr_Android_Parser::GetCustomFontFamilies(SkTDArray<FontFamily*>& fontFamilies,
789c53085413e0b4704aa89cc18396613d59e6ccb4dbungeman                                                     const SkString& basePath,
790c53085413e0b4704aa89cc18396613d59e6ccb4dbungeman                                                     const char* fontsXml,
791c53085413e0b4704aa89cc18396613d59e6ccb4dbungeman                                                     const char* fallbackFontsXml,
792c53085413e0b4704aa89cc18396613d59e6ccb4dbungeman                                                     const char* langFallbackFontsDir)
7937fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman{
7947fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman    if (fontsXml) {
7957fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman        parse_config_file(fontsXml, fontFamilies, basePath, false);
796f79673bbae0a662c1428755e2719dadf944e4ba1tomhudson    }
7977fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman    if (fallbackFontsXml) {
7987fa87cd09f49f1ee9bc27e263038d0f0ae706241bungeman        parse_config_file(fallbackFontsXml, fontFamilies, basePath, true);
799bfae9d373ccc9cf47fd70757092962c7850fadf4djsollen@google.com    }
800c3c694342ad393b88cee5885395f182082aa2ebbbungeman    if (langFallbackFontsDir) {
801c3c694342ad393b88cee5885395f182082aa2ebbbungeman        append_fallback_font_families_for_locale(fontFamilies,
802c3c694342ad393b88cee5885395f182082aa2ebbbungeman                                                 langFallbackFontsDir,
803c3c694342ad393b88cee5885395f182082aa2ebbbungeman                                                 basePath);
804c3c694342ad393b88cee5885395f182082aa2ebbbungeman    }
805bfae9d373ccc9cf47fd70757092962c7850fadf4djsollen@google.com}
8063b6255493e458c6b2c1412af908581f0bf3f6b70djsollen
8073b6255493e458c6b2c1412af908581f0bf3f6b70djsollenSkLanguage SkLanguage::getParent() const {
8083b6255493e458c6b2c1412af908581f0bf3f6b70djsollen    SkASSERT(!fTag.isEmpty());
8093b6255493e458c6b2c1412af908581f0bf3f6b70djsollen    const char* tag = fTag.c_str();
8103b6255493e458c6b2c1412af908581f0bf3f6b70djsollen
8113b6255493e458c6b2c1412af908581f0bf3f6b70djsollen    // strip off the rightmost "-.*"
8123b6255493e458c6b2c1412af908581f0bf3f6b70djsollen    const char* parentTagEnd = strrchr(tag, '-');
81396fcdcc219d2a0d3579719b84b28bede76efba64halcanary    if (parentTagEnd == nullptr) {
8143b6255493e458c6b2c1412af908581f0bf3f6b70djsollen        return SkLanguage();
8153b6255493e458c6b2c1412af908581f0bf3f6b70djsollen    }
8163b6255493e458c6b2c1412af908581f0bf3f6b70djsollen    size_t parentTagLen = parentTagEnd - tag;
8173b6255493e458c6b2c1412af908581f0bf3f6b70djsollen    return SkLanguage(tag, parentTagLen);
8183b6255493e458c6b2c1412af908581f0bf3f6b70djsollen}
819