1cb93a386Sopenharmony_ci/*
2cb93a386Sopenharmony_ci * Copyright 2011 The Android Open Source Project
3cb93a386Sopenharmony_ci *
4cb93a386Sopenharmony_ci * Use of this source code is governed by a BSD-style license that can be
5cb93a386Sopenharmony_ci * found in the LICENSE file.
6cb93a386Sopenharmony_ci */
7cb93a386Sopenharmony_ci
8cb93a386Sopenharmony_ci// Despite the name and location, this is portable code.
9cb93a386Sopenharmony_ci
10cb93a386Sopenharmony_ci#include "include/core/SkFontMgr.h"
11cb93a386Sopenharmony_ci#include "include/core/SkStream.h"
12cb93a386Sopenharmony_ci#include "include/private/SkFixed.h"
13cb93a386Sopenharmony_ci#include "include/private/SkMalloc.h"
14cb93a386Sopenharmony_ci#include "include/private/SkTDArray.h"
15cb93a386Sopenharmony_ci#include "include/private/SkTLogic.h"
16cb93a386Sopenharmony_ci#include "include/private/SkTemplates.h"
17cb93a386Sopenharmony_ci#include "src/core/SkOSFile.h"
18cb93a386Sopenharmony_ci#include "src/core/SkTSearch.h"
19cb93a386Sopenharmony_ci#include "src/ports/SkFontMgr_android_parser.h"
20cb93a386Sopenharmony_ci
21cb93a386Sopenharmony_ci#include <expat.h>
22cb93a386Sopenharmony_ci
23cb93a386Sopenharmony_ci#include <stdlib.h>
24cb93a386Sopenharmony_ci#include <string.h>
25cb93a386Sopenharmony_ci
26cb93a386Sopenharmony_ci#include <memory>
27cb93a386Sopenharmony_ci
28cb93a386Sopenharmony_ci#define LMP_SYSTEM_FONTS_FILE "/system/etc/fonts.xml"
29cb93a386Sopenharmony_ci#define OLD_SYSTEM_FONTS_FILE "/system/etc/system_fonts.xml"
30cb93a386Sopenharmony_ci#define FALLBACK_FONTS_FILE "/system/etc/fallback_fonts.xml"
31cb93a386Sopenharmony_ci#define VENDOR_FONTS_FILE "/vendor/etc/fallback_fonts.xml"
32cb93a386Sopenharmony_ci
33cb93a386Sopenharmony_ci#define LOCALE_FALLBACK_FONTS_SYSTEM_DIR "/system/etc"
34cb93a386Sopenharmony_ci#define LOCALE_FALLBACK_FONTS_VENDOR_DIR "/vendor/etc"
35cb93a386Sopenharmony_ci#define LOCALE_FALLBACK_FONTS_PREFIX "fallback_fonts-"
36cb93a386Sopenharmony_ci#define LOCALE_FALLBACK_FONTS_SUFFIX ".xml"
37cb93a386Sopenharmony_ci
38cb93a386Sopenharmony_ci#ifndef SK_FONT_FILE_PREFIX
39cb93a386Sopenharmony_ci#    define SK_FONT_FILE_PREFIX "/fonts/"
40cb93a386Sopenharmony_ci#endif
41cb93a386Sopenharmony_ci
42cb93a386Sopenharmony_ci/**
43cb93a386Sopenharmony_ci * This file contains TWO 'familyset' handlers:
44cb93a386Sopenharmony_ci * One for JB and earlier which works with
45cb93a386Sopenharmony_ci *   /system/etc/system_fonts.xml
46cb93a386Sopenharmony_ci *   /system/etc/fallback_fonts.xml
47cb93a386Sopenharmony_ci *   /vendor/etc/fallback_fonts.xml
48cb93a386Sopenharmony_ci *   /system/etc/fallback_fonts-XX.xml
49cb93a386Sopenharmony_ci *   /vendor/etc/fallback_fonts-XX.xml
50cb93a386Sopenharmony_ci * and the other for LMP and later which works with
51cb93a386Sopenharmony_ci *   /system/etc/fonts.xml
52cb93a386Sopenharmony_ci *
53cb93a386Sopenharmony_ci * If the 'familyset' 'version' attribute is 21 or higher the LMP parser is used, otherwise the JB.
54cb93a386Sopenharmony_ci */
55cb93a386Sopenharmony_ci
56cb93a386Sopenharmony_cistruct FamilyData;
57cb93a386Sopenharmony_ci
58cb93a386Sopenharmony_cistruct TagHandler {
59cb93a386Sopenharmony_ci    /** Called at the start tag.
60cb93a386Sopenharmony_ci     *  Called immediately after the parent tag retuns this handler from a call to 'tag'.
61cb93a386Sopenharmony_ci     *  Allows setting up for handling the tag content and processing attributes.
62cb93a386Sopenharmony_ci     *  If nullptr, will not be called.
63cb93a386Sopenharmony_ci     */
64cb93a386Sopenharmony_ci    void (*start)(FamilyData* data, const char* tag, const char** attributes);
65cb93a386Sopenharmony_ci
66cb93a386Sopenharmony_ci    /** Called at the end tag.
67cb93a386Sopenharmony_ci     *  Allows post-processing of any accumulated information.
68cb93a386Sopenharmony_ci     *  This will be the last call made in relation to the current tag.
69cb93a386Sopenharmony_ci     *  If nullptr, will not be called.
70cb93a386Sopenharmony_ci     */
71cb93a386Sopenharmony_ci    void (*end)(FamilyData* data, const char* tag);
72cb93a386Sopenharmony_ci
73cb93a386Sopenharmony_ci    /** Called when a nested tag is encountered.
74cb93a386Sopenharmony_ci     *  This is responsible for determining how to handle the tag.
75cb93a386Sopenharmony_ci     *  If the tag is not recognized, return nullptr to skip the tag.
76cb93a386Sopenharmony_ci     *  If nullptr, all nested tags will be skipped.
77cb93a386Sopenharmony_ci     */
78cb93a386Sopenharmony_ci    const TagHandler* (*tag)(FamilyData* data, const char* tag, const char** attributes);
79cb93a386Sopenharmony_ci
80cb93a386Sopenharmony_ci    /** The character handler for this tag.
81cb93a386Sopenharmony_ci     *  This is only active for character data contained directly in this tag (not sub-tags).
82cb93a386Sopenharmony_ci     *  The first parameter will be castable to a FamilyData*.
83cb93a386Sopenharmony_ci     *  If nullptr, any character data in this tag will be ignored.
84cb93a386Sopenharmony_ci     */
85cb93a386Sopenharmony_ci    XML_CharacterDataHandler chars;
86cb93a386Sopenharmony_ci};
87cb93a386Sopenharmony_ci
88cb93a386Sopenharmony_ci/** Represents the current parsing state. */
89cb93a386Sopenharmony_cistruct FamilyData {
90cb93a386Sopenharmony_ci    FamilyData(XML_Parser parser, SkTDArray<FontFamily*>& families,
91cb93a386Sopenharmony_ci               const SkString& basePath, bool isFallback, const char* filename,
92cb93a386Sopenharmony_ci               const TagHandler* topLevelHandler)
93cb93a386Sopenharmony_ci        : fParser(parser)
94cb93a386Sopenharmony_ci        , fFamilies(families)
95cb93a386Sopenharmony_ci        , fCurrentFamily(nullptr)
96cb93a386Sopenharmony_ci        , fCurrentFontInfo(nullptr)
97cb93a386Sopenharmony_ci        , fVersion(0)
98cb93a386Sopenharmony_ci        , fBasePath(basePath)
99cb93a386Sopenharmony_ci        , fIsFallback(isFallback)
100cb93a386Sopenharmony_ci        , fFilename(filename)
101cb93a386Sopenharmony_ci        , fDepth(1)
102cb93a386Sopenharmony_ci        , fSkip(0)
103cb93a386Sopenharmony_ci        , fHandler(&topLevelHandler, 1)
104cb93a386Sopenharmony_ci    { }
105cb93a386Sopenharmony_ci
106cb93a386Sopenharmony_ci    XML_Parser fParser;                         // The expat parser doing the work, owned by caller
107cb93a386Sopenharmony_ci    SkTDArray<FontFamily*>& fFamilies;          // The array to append families, owned by caller
108cb93a386Sopenharmony_ci    std::unique_ptr<FontFamily> fCurrentFamily; // The family being created, owned by this
109cb93a386Sopenharmony_ci    FontFileInfo* fCurrentFontInfo;             // The info being created, owned by fCurrentFamily
110cb93a386Sopenharmony_ci    int fVersion;                               // The version of the file parsed.
111cb93a386Sopenharmony_ci    const SkString& fBasePath;                  // The current base path.
112cb93a386Sopenharmony_ci    const bool fIsFallback;                     // The file being parsed is a fallback file
113cb93a386Sopenharmony_ci    const char* fFilename;                      // The name of the file currently being parsed.
114cb93a386Sopenharmony_ci
115cb93a386Sopenharmony_ci    int fDepth;                                 // The current element depth of the parse.
116cb93a386Sopenharmony_ci    int fSkip;                                  // The depth to stop skipping, 0 if not skipping.
117cb93a386Sopenharmony_ci    SkTDArray<const TagHandler*> fHandler;      // The stack of current tag handlers.
118cb93a386Sopenharmony_ci};
119cb93a386Sopenharmony_ci
120cb93a386Sopenharmony_cistatic bool memeq(const char* s1, const char* s2, size_t n1, size_t n2) {
121cb93a386Sopenharmony_ci    return n1 == n2 && 0 == memcmp(s1, s2, n1);
122cb93a386Sopenharmony_ci}
123cb93a386Sopenharmony_ci#define MEMEQ(c, s, n) memeq(c, s, sizeof(c) - 1, n)
124cb93a386Sopenharmony_ci
125cb93a386Sopenharmony_ci#define ATTS_NON_NULL(a, i) (a[i] != nullptr && a[i+1] != nullptr)
126cb93a386Sopenharmony_ci
127cb93a386Sopenharmony_ci#define SK_FONTMGR_ANDROID_PARSER_PREFIX "[SkFontMgr Android Parser] "
128cb93a386Sopenharmony_ci
129cb93a386Sopenharmony_ci#define SK_FONTCONFIGPARSER_WARNING(message, ...)                                 \
130cb93a386Sopenharmony_ci    SkDebugf(SK_FONTMGR_ANDROID_PARSER_PREFIX "%s:%d:%d: warning: " message "\n", \
131cb93a386Sopenharmony_ci             self->fFilename,                                                     \
132cb93a386Sopenharmony_ci             (int)XML_GetCurrentLineNumber(self->fParser),                        \
133cb93a386Sopenharmony_ci             (int)XML_GetCurrentColumnNumber(self->fParser),                      \
134cb93a386Sopenharmony_ci             ##__VA_ARGS__)
135cb93a386Sopenharmony_ci
136cb93a386Sopenharmony_cistatic bool is_whitespace(char c) {
137cb93a386Sopenharmony_ci    return c == ' ' || c == '\n'|| c == '\r' || c == '\t';
138cb93a386Sopenharmony_ci}
139cb93a386Sopenharmony_ci
140cb93a386Sopenharmony_cistatic void trim_string(SkString* s) {
141cb93a386Sopenharmony_ci    char* str = s->writable_str();
142cb93a386Sopenharmony_ci    const char* start = str;  // start is inclusive
143cb93a386Sopenharmony_ci    const char* end = start + s->size();  // end is exclusive
144cb93a386Sopenharmony_ci    while (is_whitespace(*start)) { ++start; }
145cb93a386Sopenharmony_ci    if (start != end) {
146cb93a386Sopenharmony_ci        --end;  // make end inclusive
147cb93a386Sopenharmony_ci        while (is_whitespace(*end)) { --end; }
148cb93a386Sopenharmony_ci        ++end;  // make end exclusive
149cb93a386Sopenharmony_ci    }
150cb93a386Sopenharmony_ci    size_t len = end - start;
151cb93a386Sopenharmony_ci    memmove(str, start, len);
152cb93a386Sopenharmony_ci    s->resize(len);
153cb93a386Sopenharmony_ci}
154cb93a386Sopenharmony_ci
155cb93a386Sopenharmony_cistatic void parse_space_separated_languages(const char* value, size_t valueLen,
156cb93a386Sopenharmony_ci                                            SkTArray<SkLanguage, true>& languages)
157cb93a386Sopenharmony_ci{
158cb93a386Sopenharmony_ci    size_t i = 0;
159cb93a386Sopenharmony_ci    while (true) {
160cb93a386Sopenharmony_ci        for (; i < valueLen && is_whitespace(value[i]); ++i) { }
161cb93a386Sopenharmony_ci        if (i == valueLen) { break; }
162cb93a386Sopenharmony_ci        size_t j;
163cb93a386Sopenharmony_ci        for (j = i + 1; j < valueLen && !is_whitespace(value[j]); ++j) { }
164cb93a386Sopenharmony_ci        languages.emplace_back(value + i, j - i);
165cb93a386Sopenharmony_ci        i = j;
166cb93a386Sopenharmony_ci        if (i == valueLen) { break; }
167cb93a386Sopenharmony_ci    }
168cb93a386Sopenharmony_ci}
169cb93a386Sopenharmony_ci
170cb93a386Sopenharmony_cinamespace lmpParser {
171cb93a386Sopenharmony_ci
172cb93a386Sopenharmony_cistatic const TagHandler axisHandler = {
173cb93a386Sopenharmony_ci    /*start*/[](FamilyData* self, const char* tag, const char** attributes) {
174cb93a386Sopenharmony_ci        FontFileInfo& file = *self->fCurrentFontInfo;
175cb93a386Sopenharmony_ci        SkFourByteTag axisTag = SkSetFourByteTag('\0','\0','\0','\0');
176cb93a386Sopenharmony_ci        SkFixed axisStyleValue = 0;
177cb93a386Sopenharmony_ci        bool axisTagIsValid = false;
178cb93a386Sopenharmony_ci        bool axisStyleValueIsValid = false;
179cb93a386Sopenharmony_ci        for (size_t i = 0; ATTS_NON_NULL(attributes, i); i += 2) {
180cb93a386Sopenharmony_ci            const char* name = attributes[i];
181cb93a386Sopenharmony_ci            const char* value = attributes[i+1];
182cb93a386Sopenharmony_ci            size_t nameLen = strlen(name);
183cb93a386Sopenharmony_ci            if (MEMEQ("tag", name, nameLen)) {
184cb93a386Sopenharmony_ci                size_t valueLen = strlen(value);
185cb93a386Sopenharmony_ci                if (valueLen == 4) {
186cb93a386Sopenharmony_ci                    axisTag = SkSetFourByteTag(value[0], value[1], value[2], value[3]);
187cb93a386Sopenharmony_ci                    axisTagIsValid = true;
188cb93a386Sopenharmony_ci                    for (int j = 0; j < file.fVariationDesignPosition.count() - 1; ++j) {
189cb93a386Sopenharmony_ci                        if (file.fVariationDesignPosition[j].axis == axisTag) {
190cb93a386Sopenharmony_ci                            axisTagIsValid = false;
191cb93a386Sopenharmony_ci                            SK_FONTCONFIGPARSER_WARNING("'%c%c%c%c' axis specified more than once",
192cb93a386Sopenharmony_ci                                                        (axisTag >> 24) & 0xFF,
193cb93a386Sopenharmony_ci                                                        (axisTag >> 16) & 0xFF,
194cb93a386Sopenharmony_ci                                                        (axisTag >>  8) & 0xFF,
195cb93a386Sopenharmony_ci                                                        (axisTag      ) & 0xFF);
196cb93a386Sopenharmony_ci                        }
197cb93a386Sopenharmony_ci                    }
198cb93a386Sopenharmony_ci                } else {
199cb93a386Sopenharmony_ci                    SK_FONTCONFIGPARSER_WARNING("'%s' is an invalid axis tag", value);
200cb93a386Sopenharmony_ci                }
201cb93a386Sopenharmony_ci            } else if (MEMEQ("stylevalue", name, nameLen)) {
202cb93a386Sopenharmony_ci                if (parse_fixed<16>(value, &axisStyleValue)) {
203cb93a386Sopenharmony_ci                    axisStyleValueIsValid = true;
204cb93a386Sopenharmony_ci                } else {
205cb93a386Sopenharmony_ci                    SK_FONTCONFIGPARSER_WARNING("'%s' is an invalid axis stylevalue", value);
206cb93a386Sopenharmony_ci                }
207cb93a386Sopenharmony_ci            }
208cb93a386Sopenharmony_ci        }
209cb93a386Sopenharmony_ci        if (axisTagIsValid && axisStyleValueIsValid) {
210cb93a386Sopenharmony_ci            auto& coordinate = file.fVariationDesignPosition.push_back();
211cb93a386Sopenharmony_ci            coordinate.axis = axisTag;
212cb93a386Sopenharmony_ci            coordinate.value = SkFixedToScalar(axisStyleValue);
213cb93a386Sopenharmony_ci        }
214cb93a386Sopenharmony_ci    },
215cb93a386Sopenharmony_ci    /*end*/nullptr,
216cb93a386Sopenharmony_ci    /*tag*/nullptr,
217cb93a386Sopenharmony_ci    /*chars*/nullptr,
218cb93a386Sopenharmony_ci};
219cb93a386Sopenharmony_ci
220cb93a386Sopenharmony_cistatic const TagHandler fontHandler = {
221cb93a386Sopenharmony_ci    /*start*/[](FamilyData* self, const char* tag, const char** attributes) {
222cb93a386Sopenharmony_ci        // 'weight' (non-negative integer) [default 0]
223cb93a386Sopenharmony_ci        // 'style' ("normal", "italic") [default "auto"]
224cb93a386Sopenharmony_ci        // 'index' (non-negative integer) [default 0]
225cb93a386Sopenharmony_ci        // The character data should be a filename.
226cb93a386Sopenharmony_ci        FontFileInfo& file = self->fCurrentFamily->fFonts.push_back();
227cb93a386Sopenharmony_ci        self->fCurrentFontInfo = &file;
228cb93a386Sopenharmony_ci        SkString fallbackFor;
229cb93a386Sopenharmony_ci        for (size_t i = 0; ATTS_NON_NULL(attributes, i); i += 2) {
230cb93a386Sopenharmony_ci            const char* name = attributes[i];
231cb93a386Sopenharmony_ci            const char* value = attributes[i+1];
232cb93a386Sopenharmony_ci            size_t nameLen = strlen(name);
233cb93a386Sopenharmony_ci            if (MEMEQ("weight", name, nameLen)) {
234cb93a386Sopenharmony_ci                if (!parse_non_negative_integer(value, &file.fWeight)) {
235cb93a386Sopenharmony_ci                    SK_FONTCONFIGPARSER_WARNING("'%s' is an invalid weight", value);
236cb93a386Sopenharmony_ci                }
237cb93a386Sopenharmony_ci            } else if (MEMEQ("style", name, nameLen)) {
238cb93a386Sopenharmony_ci                size_t valueLen = strlen(value);
239cb93a386Sopenharmony_ci                if (MEMEQ("normal", value, valueLen)) {
240cb93a386Sopenharmony_ci                    file.fStyle = FontFileInfo::Style::kNormal;
241cb93a386Sopenharmony_ci                } else if (MEMEQ("italic", value, valueLen)) {
242cb93a386Sopenharmony_ci                    file.fStyle = FontFileInfo::Style::kItalic;
243cb93a386Sopenharmony_ci                }
244cb93a386Sopenharmony_ci            } else if (MEMEQ("index", name, nameLen)) {
245cb93a386Sopenharmony_ci                if (!parse_non_negative_integer(value, &file.fIndex)) {
246cb93a386Sopenharmony_ci                    SK_FONTCONFIGPARSER_WARNING("'%s' is an invalid index", value);
247cb93a386Sopenharmony_ci                }
248cb93a386Sopenharmony_ci            } else if (MEMEQ("fallbackFor", name, nameLen)) {
249cb93a386Sopenharmony_ci                /** fallbackFor specifies a family fallback and should have been on family. */
250cb93a386Sopenharmony_ci                fallbackFor = value;
251cb93a386Sopenharmony_ci            }
252cb93a386Sopenharmony_ci        }
253cb93a386Sopenharmony_ci        if (!fallbackFor.isEmpty()) {
254cb93a386Sopenharmony_ci            std::unique_ptr<FontFamily>* fallbackFamily =
255cb93a386Sopenharmony_ci                    self->fCurrentFamily->fallbackFamilies.find(fallbackFor);
256cb93a386Sopenharmony_ci            if (!fallbackFamily) {
257cb93a386Sopenharmony_ci                std::unique_ptr<FontFamily> newFallbackFamily(
258cb93a386Sopenharmony_ci                        new FontFamily(self->fCurrentFamily->fBasePath, true));
259cb93a386Sopenharmony_ci                fallbackFamily = self->fCurrentFamily->fallbackFamilies.set(
260cb93a386Sopenharmony_ci                        fallbackFor, std::move(newFallbackFamily));
261cb93a386Sopenharmony_ci                (*fallbackFamily)->fLanguages = self->fCurrentFamily->fLanguages;
262cb93a386Sopenharmony_ci                (*fallbackFamily)->fVariant = self->fCurrentFamily->fVariant;
263cb93a386Sopenharmony_ci                (*fallbackFamily)->fOrder = self->fCurrentFamily->fOrder;
264cb93a386Sopenharmony_ci                (*fallbackFamily)->fFallbackFor = fallbackFor;
265cb93a386Sopenharmony_ci            }
266cb93a386Sopenharmony_ci            self->fCurrentFontInfo = &(*fallbackFamily)->fFonts.emplace_back(file);
267cb93a386Sopenharmony_ci            self->fCurrentFamily->fFonts.pop_back();
268cb93a386Sopenharmony_ci        }
269cb93a386Sopenharmony_ci    },
270cb93a386Sopenharmony_ci    /*end*/[](FamilyData* self, const char* tag) {
271cb93a386Sopenharmony_ci        trim_string(&self->fCurrentFontInfo->fFileName);
272cb93a386Sopenharmony_ci    },
273cb93a386Sopenharmony_ci    /*tag*/[](FamilyData* self, const char* tag, const char** attributes) -> const TagHandler* {
274cb93a386Sopenharmony_ci        size_t len = strlen(tag);
275cb93a386Sopenharmony_ci        if (MEMEQ("axis", tag, len)) {
276cb93a386Sopenharmony_ci            return &axisHandler;
277cb93a386Sopenharmony_ci        }
278cb93a386Sopenharmony_ci        return nullptr;
279cb93a386Sopenharmony_ci    },
280cb93a386Sopenharmony_ci    /*chars*/[](void* data, const char* s, int len) {
281cb93a386Sopenharmony_ci        FamilyData* self = static_cast<FamilyData*>(data);
282cb93a386Sopenharmony_ci        self->fCurrentFontInfo->fFileName.append(s, len);
283cb93a386Sopenharmony_ci    }
284cb93a386Sopenharmony_ci};
285cb93a386Sopenharmony_ci
286cb93a386Sopenharmony_cistatic const TagHandler familyHandler = {
287cb93a386Sopenharmony_ci    /*start*/[](FamilyData* self, const char* tag, const char** attributes) {
288cb93a386Sopenharmony_ci        // 'name' (string) [optional]
289cb93a386Sopenharmony_ci        // 'lang' (space separated string) [default ""]
290cb93a386Sopenharmony_ci        // 'variant' ("elegant", "compact") [default "default"]
291cb93a386Sopenharmony_ci        // If there is no name, this is a fallback only font.
292cb93a386Sopenharmony_ci        FontFamily* family = new FontFamily(self->fBasePath, true);
293cb93a386Sopenharmony_ci        self->fCurrentFamily.reset(family);
294cb93a386Sopenharmony_ci        for (size_t i = 0; ATTS_NON_NULL(attributes, i); i += 2) {
295cb93a386Sopenharmony_ci            const char* name = attributes[i];
296cb93a386Sopenharmony_ci            const char* value = attributes[i+1];
297cb93a386Sopenharmony_ci            size_t nameLen = strlen(name);
298cb93a386Sopenharmony_ci            size_t valueLen = strlen(value);
299cb93a386Sopenharmony_ci            if (MEMEQ("name", name, nameLen)) {
300cb93a386Sopenharmony_ci                SkAutoAsciiToLC tolc(value);
301cb93a386Sopenharmony_ci                family->fNames.push_back().set(tolc.lc());
302cb93a386Sopenharmony_ci                family->fIsFallbackFont = false;
303cb93a386Sopenharmony_ci            } else if (MEMEQ("lang", name, nameLen)) {
304cb93a386Sopenharmony_ci                parse_space_separated_languages(value, valueLen, family->fLanguages);
305cb93a386Sopenharmony_ci            } else if (MEMEQ("variant", name, nameLen)) {
306cb93a386Sopenharmony_ci                if (MEMEQ("elegant", value, valueLen)) {
307cb93a386Sopenharmony_ci                    family->fVariant = kElegant_FontVariant;
308cb93a386Sopenharmony_ci                } else if (MEMEQ("compact", value, valueLen)) {
309cb93a386Sopenharmony_ci                    family->fVariant = kCompact_FontVariant;
310cb93a386Sopenharmony_ci                }
311cb93a386Sopenharmony_ci            }
312cb93a386Sopenharmony_ci        }
313cb93a386Sopenharmony_ci    },
314cb93a386Sopenharmony_ci    /*end*/[](FamilyData* self, const char* tag) {
315cb93a386Sopenharmony_ci        *self->fFamilies.append() = self->fCurrentFamily.release();
316cb93a386Sopenharmony_ci    },
317cb93a386Sopenharmony_ci    /*tag*/[](FamilyData* self, const char* tag, const char** attributes) -> const TagHandler* {
318cb93a386Sopenharmony_ci        size_t len = strlen(tag);
319cb93a386Sopenharmony_ci        if (MEMEQ("font", tag, len)) {
320cb93a386Sopenharmony_ci            return &fontHandler;
321cb93a386Sopenharmony_ci        }
322cb93a386Sopenharmony_ci        return nullptr;
323cb93a386Sopenharmony_ci    },
324cb93a386Sopenharmony_ci    /*chars*/nullptr,
325cb93a386Sopenharmony_ci};
326cb93a386Sopenharmony_ci
327cb93a386Sopenharmony_cistatic FontFamily* find_family(FamilyData* self, const SkString& familyName) {
328cb93a386Sopenharmony_ci    for (int i = 0; i < self->fFamilies.count(); i++) {
329cb93a386Sopenharmony_ci        FontFamily* candidate = self->fFamilies[i];
330cb93a386Sopenharmony_ci        for (int j = 0; j < candidate->fNames.count(); j++) {
331cb93a386Sopenharmony_ci            if (candidate->fNames[j] == familyName) {
332cb93a386Sopenharmony_ci                return candidate;
333cb93a386Sopenharmony_ci            }
334cb93a386Sopenharmony_ci        }
335cb93a386Sopenharmony_ci    }
336cb93a386Sopenharmony_ci    return nullptr;
337cb93a386Sopenharmony_ci}
338cb93a386Sopenharmony_ci
339cb93a386Sopenharmony_cistatic const TagHandler aliasHandler = {
340cb93a386Sopenharmony_ci    /*start*/[](FamilyData* self, const char* tag, const char** attributes) {
341cb93a386Sopenharmony_ci        // 'name' (string) introduces a new family name.
342cb93a386Sopenharmony_ci        // 'to' (string) specifies which (previous) family to alias
343cb93a386Sopenharmony_ci        // 'weight' (non-negative integer) [optional]
344cb93a386Sopenharmony_ci        // If it *does not* have a weight, 'name' is an alias for the entire 'to' family.
345cb93a386Sopenharmony_ci        // If it *does* have a weight, 'name' is a new family consisting of
346cb93a386Sopenharmony_ci        // the font(s) with 'weight' from the 'to' family.
347cb93a386Sopenharmony_ci
348cb93a386Sopenharmony_ci        SkString aliasName;
349cb93a386Sopenharmony_ci        SkString to;
350cb93a386Sopenharmony_ci        int weight = 0;
351cb93a386Sopenharmony_ci        for (size_t i = 0; ATTS_NON_NULL(attributes, i); i += 2) {
352cb93a386Sopenharmony_ci            const char* name = attributes[i];
353cb93a386Sopenharmony_ci            const char* value = attributes[i+1];
354cb93a386Sopenharmony_ci            size_t nameLen = strlen(name);
355cb93a386Sopenharmony_ci            if (MEMEQ("name", name, nameLen)) {
356cb93a386Sopenharmony_ci                SkAutoAsciiToLC tolc(value);
357cb93a386Sopenharmony_ci                aliasName.set(tolc.lc());
358cb93a386Sopenharmony_ci            } else if (MEMEQ("to", name, nameLen)) {
359cb93a386Sopenharmony_ci                to.set(value);
360cb93a386Sopenharmony_ci            } else if (MEMEQ("weight", name, nameLen)) {
361cb93a386Sopenharmony_ci                if (!parse_non_negative_integer(value, &weight)) {
362cb93a386Sopenharmony_ci                    SK_FONTCONFIGPARSER_WARNING("'%s' is an invalid weight", value);
363cb93a386Sopenharmony_ci                }
364cb93a386Sopenharmony_ci            }
365cb93a386Sopenharmony_ci        }
366cb93a386Sopenharmony_ci
367cb93a386Sopenharmony_ci        // Assumes that the named family is already declared
368cb93a386Sopenharmony_ci        FontFamily* targetFamily = find_family(self, to);
369cb93a386Sopenharmony_ci        if (!targetFamily) {
370cb93a386Sopenharmony_ci            SK_FONTCONFIGPARSER_WARNING("'%s' alias target not found", to.c_str());
371cb93a386Sopenharmony_ci            return;
372cb93a386Sopenharmony_ci        }
373cb93a386Sopenharmony_ci
374cb93a386Sopenharmony_ci        if (weight) {
375cb93a386Sopenharmony_ci            FontFamily* family = new FontFamily(targetFamily->fBasePath, self->fIsFallback);
376cb93a386Sopenharmony_ci            family->fNames.push_back().set(aliasName);
377cb93a386Sopenharmony_ci
378cb93a386Sopenharmony_ci            for (int i = 0; i < targetFamily->fFonts.count(); i++) {
379cb93a386Sopenharmony_ci                if (targetFamily->fFonts[i].fWeight == weight) {
380cb93a386Sopenharmony_ci                    family->fFonts.push_back(targetFamily->fFonts[i]);
381cb93a386Sopenharmony_ci                }
382cb93a386Sopenharmony_ci            }
383cb93a386Sopenharmony_ci            *self->fFamilies.append() = family;
384cb93a386Sopenharmony_ci        } else {
385cb93a386Sopenharmony_ci            targetFamily->fNames.push_back().set(aliasName);
386cb93a386Sopenharmony_ci        }
387cb93a386Sopenharmony_ci    },
388cb93a386Sopenharmony_ci    /*end*/nullptr,
389cb93a386Sopenharmony_ci    /*tag*/nullptr,
390cb93a386Sopenharmony_ci    /*chars*/nullptr,
391cb93a386Sopenharmony_ci};
392cb93a386Sopenharmony_ci
393cb93a386Sopenharmony_cistatic const TagHandler familySetHandler = {
394cb93a386Sopenharmony_ci    /*start*/[](FamilyData* self, const char* tag, const char** attributes) { },
395cb93a386Sopenharmony_ci    /*end*/nullptr,
396cb93a386Sopenharmony_ci    /*tag*/[](FamilyData* self, const char* tag, const char** attributes) -> const TagHandler* {
397cb93a386Sopenharmony_ci        size_t len = strlen(tag);
398cb93a386Sopenharmony_ci        if (MEMEQ("family", tag, len)) {
399cb93a386Sopenharmony_ci            return &familyHandler;
400cb93a386Sopenharmony_ci        } else if (MEMEQ("alias", tag, len)) {
401cb93a386Sopenharmony_ci            return &aliasHandler;
402cb93a386Sopenharmony_ci        }
403cb93a386Sopenharmony_ci        return nullptr;
404cb93a386Sopenharmony_ci    },
405cb93a386Sopenharmony_ci    /*chars*/nullptr,
406cb93a386Sopenharmony_ci};
407cb93a386Sopenharmony_ci
408cb93a386Sopenharmony_ci}  // namespace lmpParser
409cb93a386Sopenharmony_ci
410cb93a386Sopenharmony_cinamespace jbParser {
411cb93a386Sopenharmony_ci
412cb93a386Sopenharmony_cistatic const TagHandler fileHandler = {
413cb93a386Sopenharmony_ci    /*start*/[](FamilyData* self, const char* tag, const char** attributes) {
414cb93a386Sopenharmony_ci        // 'variant' ("elegant", "compact") [default "default"]
415cb93a386Sopenharmony_ci        // 'lang' (string) [default ""]
416cb93a386Sopenharmony_ci        // 'index' (non-negative integer) [default 0]
417cb93a386Sopenharmony_ci        // The character data should be a filename.
418cb93a386Sopenharmony_ci        FontFamily& currentFamily = *self->fCurrentFamily;
419cb93a386Sopenharmony_ci        FontFileInfo& newFileInfo = currentFamily.fFonts.push_back();
420cb93a386Sopenharmony_ci        if (attributes) {
421cb93a386Sopenharmony_ci            for (size_t i = 0; ATTS_NON_NULL(attributes, i); i += 2) {
422cb93a386Sopenharmony_ci                const char* name = attributes[i];
423cb93a386Sopenharmony_ci                const char* value = attributes[i+1];
424cb93a386Sopenharmony_ci                size_t nameLen = strlen(name);
425cb93a386Sopenharmony_ci                size_t valueLen = strlen(value);
426cb93a386Sopenharmony_ci                if (MEMEQ("variant", name, nameLen)) {
427cb93a386Sopenharmony_ci                    const FontVariant prevVariant = currentFamily.fVariant;
428cb93a386Sopenharmony_ci                    if (MEMEQ("elegant", value, valueLen)) {
429cb93a386Sopenharmony_ci                        currentFamily.fVariant = kElegant_FontVariant;
430cb93a386Sopenharmony_ci                    } else if (MEMEQ("compact", value, valueLen)) {
431cb93a386Sopenharmony_ci                        currentFamily.fVariant = kCompact_FontVariant;
432cb93a386Sopenharmony_ci                    }
433cb93a386Sopenharmony_ci                    if (currentFamily.fFonts.count() > 1 && currentFamily.fVariant != prevVariant) {
434cb93a386Sopenharmony_ci                        SK_FONTCONFIGPARSER_WARNING("'%s' unexpected variant found\n"
435cb93a386Sopenharmony_ci                            "Note: Every font file within a family must have identical variants.",
436cb93a386Sopenharmony_ci                            value);
437cb93a386Sopenharmony_ci                    }
438cb93a386Sopenharmony_ci
439cb93a386Sopenharmony_ci                } else if (MEMEQ("lang", name, nameLen)) {
440cb93a386Sopenharmony_ci                    SkLanguage currentLanguage = SkLanguage(value, valueLen);
441cb93a386Sopenharmony_ci                    bool showWarning = false;
442cb93a386Sopenharmony_ci                    if (currentFamily.fLanguages.empty()) {
443cb93a386Sopenharmony_ci                        showWarning = (currentFamily.fFonts.count() > 1);
444cb93a386Sopenharmony_ci                        currentFamily.fLanguages.push_back(std::move(currentLanguage));
445cb93a386Sopenharmony_ci                    } else if (currentFamily.fLanguages[0] != currentLanguage) {
446cb93a386Sopenharmony_ci                        showWarning = true;
447cb93a386Sopenharmony_ci                        currentFamily.fLanguages[0] = std::move(currentLanguage);
448cb93a386Sopenharmony_ci                    }
449cb93a386Sopenharmony_ci                    if (showWarning) {
450cb93a386Sopenharmony_ci                        SK_FONTCONFIGPARSER_WARNING("'%s' unexpected language found\n"
451cb93a386Sopenharmony_ci                            "Note: Every font file within a family must have identical languages.",
452cb93a386Sopenharmony_ci                            value);
453cb93a386Sopenharmony_ci                    }
454cb93a386Sopenharmony_ci
455cb93a386Sopenharmony_ci                } else if (MEMEQ("index", name, nameLen)) {
456cb93a386Sopenharmony_ci                    if (!parse_non_negative_integer(value, &newFileInfo.fIndex)) {
457cb93a386Sopenharmony_ci                        SK_FONTCONFIGPARSER_WARNING("'%s' is an invalid index", value);
458cb93a386Sopenharmony_ci                    }
459cb93a386Sopenharmony_ci                }
460cb93a386Sopenharmony_ci            }
461cb93a386Sopenharmony_ci        }
462cb93a386Sopenharmony_ci        self->fCurrentFontInfo = &newFileInfo;
463cb93a386Sopenharmony_ci    },
464cb93a386Sopenharmony_ci    /*end*/nullptr,
465cb93a386Sopenharmony_ci    /*tag*/nullptr,
466cb93a386Sopenharmony_ci    /*chars*/[](void* data, const char* s, int len) {
467cb93a386Sopenharmony_ci        FamilyData* self = static_cast<FamilyData*>(data);
468cb93a386Sopenharmony_ci        self->fCurrentFontInfo->fFileName.append(s, len);
469cb93a386Sopenharmony_ci    }
470cb93a386Sopenharmony_ci};
471cb93a386Sopenharmony_ci
472cb93a386Sopenharmony_cistatic const TagHandler fileSetHandler = {
473cb93a386Sopenharmony_ci    /*start*/nullptr,
474cb93a386Sopenharmony_ci    /*end*/nullptr,
475cb93a386Sopenharmony_ci    /*tag*/[](FamilyData* self, const char* tag, const char** attributes) -> const TagHandler* {
476cb93a386Sopenharmony_ci        size_t len = strlen(tag);
477cb93a386Sopenharmony_ci        if (MEMEQ("file", tag, len)) {
478cb93a386Sopenharmony_ci            return &fileHandler;
479cb93a386Sopenharmony_ci        }
480cb93a386Sopenharmony_ci        return nullptr;
481cb93a386Sopenharmony_ci    },
482cb93a386Sopenharmony_ci    /*chars*/nullptr,
483cb93a386Sopenharmony_ci};
484cb93a386Sopenharmony_ci
485cb93a386Sopenharmony_cistatic const TagHandler nameHandler = {
486cb93a386Sopenharmony_ci    /*start*/[](FamilyData* self, const char* tag, const char** attributes) {
487cb93a386Sopenharmony_ci        // The character data should be a name for the font.
488cb93a386Sopenharmony_ci        self->fCurrentFamily->fNames.push_back();
489cb93a386Sopenharmony_ci    },
490cb93a386Sopenharmony_ci    /*end*/nullptr,
491cb93a386Sopenharmony_ci    /*tag*/nullptr,
492cb93a386Sopenharmony_ci    /*chars*/[](void* data, const char* s, int len) {
493cb93a386Sopenharmony_ci        FamilyData* self = static_cast<FamilyData*>(data);
494cb93a386Sopenharmony_ci        SkAutoAsciiToLC tolc(s, len);
495cb93a386Sopenharmony_ci        self->fCurrentFamily->fNames.back().append(tolc.lc(), len);
496cb93a386Sopenharmony_ci    }
497cb93a386Sopenharmony_ci};
498cb93a386Sopenharmony_ci
499cb93a386Sopenharmony_cistatic const TagHandler nameSetHandler = {
500cb93a386Sopenharmony_ci    /*start*/nullptr,
501cb93a386Sopenharmony_ci    /*end*/nullptr,
502cb93a386Sopenharmony_ci    /*tag*/[](FamilyData* self, const char* tag, const char** attributes) -> const TagHandler* {
503cb93a386Sopenharmony_ci        size_t len = strlen(tag);
504cb93a386Sopenharmony_ci        if (MEMEQ("name", tag, len)) {
505cb93a386Sopenharmony_ci            return &nameHandler;
506cb93a386Sopenharmony_ci        }
507cb93a386Sopenharmony_ci        return nullptr;
508cb93a386Sopenharmony_ci    },
509cb93a386Sopenharmony_ci    /*chars*/nullptr,
510cb93a386Sopenharmony_ci};
511cb93a386Sopenharmony_ci
512cb93a386Sopenharmony_cistatic const TagHandler familyHandler = {
513cb93a386Sopenharmony_ci    /*start*/[](FamilyData* self, const char* tag, const char** attributes) {
514cb93a386Sopenharmony_ci        self->fCurrentFamily = std::make_unique<FontFamily>(self->fBasePath, self->fIsFallback);
515cb93a386Sopenharmony_ci        // 'order' (non-negative integer) [default -1]
516cb93a386Sopenharmony_ci        for (size_t i = 0; ATTS_NON_NULL(attributes, i); i += 2) {
517cb93a386Sopenharmony_ci            const char* value = attributes[i+1];
518cb93a386Sopenharmony_ci            parse_non_negative_integer(value, &self->fCurrentFamily->fOrder);
519cb93a386Sopenharmony_ci        }
520cb93a386Sopenharmony_ci    },
521cb93a386Sopenharmony_ci    /*end*/[](FamilyData* self, const char* tag) {
522cb93a386Sopenharmony_ci        *self->fFamilies.append() = self->fCurrentFamily.release();
523cb93a386Sopenharmony_ci    },
524cb93a386Sopenharmony_ci    /*tag*/[](FamilyData* self, const char* tag, const char** attributes) -> const TagHandler* {
525cb93a386Sopenharmony_ci        size_t len = strlen(tag);
526cb93a386Sopenharmony_ci        if (MEMEQ("nameset", tag, len)) {
527cb93a386Sopenharmony_ci            return &nameSetHandler;
528cb93a386Sopenharmony_ci        } else if (MEMEQ("fileset", tag, len)) {
529cb93a386Sopenharmony_ci            return &fileSetHandler;
530cb93a386Sopenharmony_ci        }
531cb93a386Sopenharmony_ci        return nullptr;
532cb93a386Sopenharmony_ci    },
533cb93a386Sopenharmony_ci    /*chars*/nullptr,
534cb93a386Sopenharmony_ci};
535cb93a386Sopenharmony_ci
536cb93a386Sopenharmony_cistatic const TagHandler familySetHandler = {
537cb93a386Sopenharmony_ci    /*start*/nullptr,
538cb93a386Sopenharmony_ci    /*end*/nullptr,
539cb93a386Sopenharmony_ci    /*tag*/[](FamilyData* self, const char* tag, const char** attributes) -> const TagHandler* {
540cb93a386Sopenharmony_ci        size_t len = strlen(tag);
541cb93a386Sopenharmony_ci        if (MEMEQ("family", tag, len)) {
542cb93a386Sopenharmony_ci            return &familyHandler;
543cb93a386Sopenharmony_ci        }
544cb93a386Sopenharmony_ci        return nullptr;
545cb93a386Sopenharmony_ci    },
546cb93a386Sopenharmony_ci    /*chars*/nullptr,
547cb93a386Sopenharmony_ci};
548cb93a386Sopenharmony_ci
549cb93a386Sopenharmony_ci} // namespace jbParser
550cb93a386Sopenharmony_ci
551cb93a386Sopenharmony_cistatic const TagHandler topLevelHandler = {
552cb93a386Sopenharmony_ci    /*start*/nullptr,
553cb93a386Sopenharmony_ci    /*end*/nullptr,
554cb93a386Sopenharmony_ci    /*tag*/[](FamilyData* self, const char* tag, const char** attributes) -> const TagHandler* {
555cb93a386Sopenharmony_ci        size_t len = strlen(tag);
556cb93a386Sopenharmony_ci        if (MEMEQ("familyset", tag, len)) {
557cb93a386Sopenharmony_ci            // 'version' (non-negative integer) [default 0]
558cb93a386Sopenharmony_ci            for (size_t i = 0; ATTS_NON_NULL(attributes, i); i += 2) {
559cb93a386Sopenharmony_ci                const char* name = attributes[i];
560cb93a386Sopenharmony_ci                size_t nameLen = strlen(name);
561cb93a386Sopenharmony_ci                if (MEMEQ("version", name, nameLen)) {
562cb93a386Sopenharmony_ci                    const char* value = attributes[i+1];
563cb93a386Sopenharmony_ci                    if (parse_non_negative_integer(value, &self->fVersion)) {
564cb93a386Sopenharmony_ci                        if (self->fVersion >= 21) {
565cb93a386Sopenharmony_ci                            return &lmpParser::familySetHandler;
566cb93a386Sopenharmony_ci                        }
567cb93a386Sopenharmony_ci                    }
568cb93a386Sopenharmony_ci                }
569cb93a386Sopenharmony_ci            }
570cb93a386Sopenharmony_ci            return &jbParser::familySetHandler;
571cb93a386Sopenharmony_ci        }
572cb93a386Sopenharmony_ci        return nullptr;
573cb93a386Sopenharmony_ci    },
574cb93a386Sopenharmony_ci    /*chars*/nullptr,
575cb93a386Sopenharmony_ci};
576cb93a386Sopenharmony_ci
577cb93a386Sopenharmony_cistatic void XMLCALL start_element_handler(void *data, const char *tag, const char **attributes) {
578cb93a386Sopenharmony_ci    FamilyData* self = static_cast<FamilyData*>(data);
579cb93a386Sopenharmony_ci
580cb93a386Sopenharmony_ci    if (!self->fSkip) {
581cb93a386Sopenharmony_ci        const TagHandler* parent = self->fHandler.top();
582cb93a386Sopenharmony_ci        const TagHandler* child = parent->tag ? parent->tag(self, tag, attributes) : nullptr;
583cb93a386Sopenharmony_ci        if (child) {
584cb93a386Sopenharmony_ci            if (child->start) {
585cb93a386Sopenharmony_ci                child->start(self, tag, attributes);
586cb93a386Sopenharmony_ci            }
587cb93a386Sopenharmony_ci            self->fHandler.push_back(child);
588cb93a386Sopenharmony_ci            XML_SetCharacterDataHandler(self->fParser, child->chars);
589cb93a386Sopenharmony_ci        } else {
590cb93a386Sopenharmony_ci            SK_FONTCONFIGPARSER_WARNING("'%s' tag not recognized, skipping", tag);
591cb93a386Sopenharmony_ci            XML_SetCharacterDataHandler(self->fParser, nullptr);
592cb93a386Sopenharmony_ci            self->fSkip = self->fDepth;
593cb93a386Sopenharmony_ci        }
594cb93a386Sopenharmony_ci    }
595cb93a386Sopenharmony_ci
596cb93a386Sopenharmony_ci    ++self->fDepth;
597cb93a386Sopenharmony_ci}
598cb93a386Sopenharmony_ci
599cb93a386Sopenharmony_cistatic void XMLCALL end_element_handler(void* data, const char* tag) {
600cb93a386Sopenharmony_ci    FamilyData* self = static_cast<FamilyData*>(data);
601cb93a386Sopenharmony_ci    --self->fDepth;
602cb93a386Sopenharmony_ci
603cb93a386Sopenharmony_ci    if (!self->fSkip) {
604cb93a386Sopenharmony_ci        const TagHandler* child = self->fHandler.top();
605cb93a386Sopenharmony_ci        if (child->end) {
606cb93a386Sopenharmony_ci            child->end(self, tag);
607cb93a386Sopenharmony_ci        }
608cb93a386Sopenharmony_ci        self->fHandler.pop();
609cb93a386Sopenharmony_ci        const TagHandler* parent = self->fHandler.top();
610cb93a386Sopenharmony_ci        XML_SetCharacterDataHandler(self->fParser, parent->chars);
611cb93a386Sopenharmony_ci    }
612cb93a386Sopenharmony_ci
613cb93a386Sopenharmony_ci    if (self->fSkip == self->fDepth) {
614cb93a386Sopenharmony_ci        self->fSkip = 0;
615cb93a386Sopenharmony_ci        const TagHandler* parent = self->fHandler.top();
616cb93a386Sopenharmony_ci        XML_SetCharacterDataHandler(self->fParser, parent->chars);
617cb93a386Sopenharmony_ci    }
618cb93a386Sopenharmony_ci}
619cb93a386Sopenharmony_ci
620cb93a386Sopenharmony_cistatic void XMLCALL xml_entity_decl_handler(void *data,
621cb93a386Sopenharmony_ci                                            const XML_Char *entityName,
622cb93a386Sopenharmony_ci                                            int is_parameter_entity,
623cb93a386Sopenharmony_ci                                            const XML_Char *value,
624cb93a386Sopenharmony_ci                                            int value_length,
625cb93a386Sopenharmony_ci                                            const XML_Char *base,
626cb93a386Sopenharmony_ci                                            const XML_Char *systemId,
627cb93a386Sopenharmony_ci                                            const XML_Char *publicId,
628cb93a386Sopenharmony_ci                                            const XML_Char *notationName)
629cb93a386Sopenharmony_ci{
630cb93a386Sopenharmony_ci    FamilyData* self = static_cast<FamilyData*>(data);
631cb93a386Sopenharmony_ci    SK_FONTCONFIGPARSER_WARNING("'%s' entity declaration found, stopping processing", entityName);
632cb93a386Sopenharmony_ci    XML_StopParser(self->fParser, XML_FALSE);
633cb93a386Sopenharmony_ci}
634cb93a386Sopenharmony_ci
635cb93a386Sopenharmony_cistatic const XML_Memory_Handling_Suite sk_XML_alloc = {
636cb93a386Sopenharmony_ci    sk_malloc_throw,
637cb93a386Sopenharmony_ci    sk_realloc_throw,
638cb93a386Sopenharmony_ci    sk_free
639cb93a386Sopenharmony_ci};
640cb93a386Sopenharmony_ci
641cb93a386Sopenharmony_ci/**
642cb93a386Sopenharmony_ci * This function parses the given filename and stores the results in the given
643cb93a386Sopenharmony_ci * families array. Returns the version of the file, negative if the file does not exist.
644cb93a386Sopenharmony_ci */
645cb93a386Sopenharmony_cistatic int parse_config_file(const char* filename, SkTDArray<FontFamily*>& families,
646cb93a386Sopenharmony_ci                             const SkString& basePath, bool isFallback)
647cb93a386Sopenharmony_ci{
648cb93a386Sopenharmony_ci    SkFILEStream file(filename);
649cb93a386Sopenharmony_ci
650cb93a386Sopenharmony_ci    // Some of the files we attempt to parse (in particular, /vendor/etc/fallback_fonts.xml)
651cb93a386Sopenharmony_ci    // are optional - failure here is okay because one of these optional files may not exist.
652cb93a386Sopenharmony_ci    if (!file.isValid()) {
653cb93a386Sopenharmony_ci        SkDebugf(SK_FONTMGR_ANDROID_PARSER_PREFIX "'%s' could not be opened\n", filename);
654cb93a386Sopenharmony_ci        return -1;
655cb93a386Sopenharmony_ci    }
656cb93a386Sopenharmony_ci
657cb93a386Sopenharmony_ci    SkAutoTCallVProc<std::remove_pointer_t<XML_Parser>, XML_ParserFree> parser(
658cb93a386Sopenharmony_ci        XML_ParserCreate_MM(nullptr, &sk_XML_alloc, nullptr));
659cb93a386Sopenharmony_ci    if (!parser) {
660cb93a386Sopenharmony_ci        SkDebugf(SK_FONTMGR_ANDROID_PARSER_PREFIX "could not create XML parser\n");
661cb93a386Sopenharmony_ci        return -1;
662cb93a386Sopenharmony_ci    }
663cb93a386Sopenharmony_ci
664cb93a386Sopenharmony_ci    FamilyData self(parser, families, basePath, isFallback, filename, &topLevelHandler);
665cb93a386Sopenharmony_ci    XML_SetUserData(parser, &self);
666cb93a386Sopenharmony_ci
667cb93a386Sopenharmony_ci    // Disable entity processing, to inhibit internal entity expansion. See expat CVE-2013-0340
668cb93a386Sopenharmony_ci    XML_SetEntityDeclHandler(parser, xml_entity_decl_handler);
669cb93a386Sopenharmony_ci
670cb93a386Sopenharmony_ci    // Start parsing oldschool; switch these in flight if we detect a newer version of the file.
671cb93a386Sopenharmony_ci    XML_SetElementHandler(parser, start_element_handler, end_element_handler);
672cb93a386Sopenharmony_ci
673cb93a386Sopenharmony_ci    // One would assume it would be faster to have a buffer on the stack and call XML_Parse.
674cb93a386Sopenharmony_ci    // But XML_Parse will call XML_GetBuffer anyway and memmove the passed buffer into it.
675cb93a386Sopenharmony_ci    // (Unless XML_CONTEXT_BYTES is undefined, but all users define it.)
676cb93a386Sopenharmony_ci    // In debug, buffer a small odd number of bytes to detect slicing in XML_CharacterDataHandler.
677cb93a386Sopenharmony_ci    static const int bufferSize = 512 SkDEBUGCODE( - 507);
678cb93a386Sopenharmony_ci    bool done = false;
679cb93a386Sopenharmony_ci    while (!done) {
680cb93a386Sopenharmony_ci        void* buffer = XML_GetBuffer(parser, bufferSize);
681cb93a386Sopenharmony_ci        if (!buffer) {
682cb93a386Sopenharmony_ci            SkDebugf(SK_FONTMGR_ANDROID_PARSER_PREFIX "could not buffer enough to continue\n");
683cb93a386Sopenharmony_ci            return -1;
684cb93a386Sopenharmony_ci        }
685cb93a386Sopenharmony_ci        size_t len = file.read(buffer, bufferSize);
686cb93a386Sopenharmony_ci        done = file.isAtEnd();
687cb93a386Sopenharmony_ci        XML_Status status = XML_ParseBuffer(parser, len, done);
688cb93a386Sopenharmony_ci        if (XML_STATUS_ERROR == status) {
689cb93a386Sopenharmony_ci            XML_Error error = XML_GetErrorCode(parser);
690cb93a386Sopenharmony_ci            int line = XML_GetCurrentLineNumber(parser);
691cb93a386Sopenharmony_ci            int column = XML_GetCurrentColumnNumber(parser);
692cb93a386Sopenharmony_ci            const XML_LChar* errorString = XML_ErrorString(error);
693cb93a386Sopenharmony_ci            SkDebugf(SK_FONTMGR_ANDROID_PARSER_PREFIX "%s:%d:%d error %d: %s.\n",
694cb93a386Sopenharmony_ci                     filename, line, column, error, errorString);
695cb93a386Sopenharmony_ci            return -1;
696cb93a386Sopenharmony_ci        }
697cb93a386Sopenharmony_ci    }
698cb93a386Sopenharmony_ci    return self.fVersion;
699cb93a386Sopenharmony_ci}
700cb93a386Sopenharmony_ci
701cb93a386Sopenharmony_ci/** Returns the version of the system font file actually found, negative if none. */
702cb93a386Sopenharmony_cistatic int append_system_font_families(SkTDArray<FontFamily*>& fontFamilies,
703cb93a386Sopenharmony_ci                                       const SkString& basePath)
704cb93a386Sopenharmony_ci{
705cb93a386Sopenharmony_ci    int initialCount = fontFamilies.count();
706cb93a386Sopenharmony_ci    int version = parse_config_file(LMP_SYSTEM_FONTS_FILE, fontFamilies, basePath, false);
707cb93a386Sopenharmony_ci    if (version < 0 || fontFamilies.count() == initialCount) {
708cb93a386Sopenharmony_ci        version = parse_config_file(OLD_SYSTEM_FONTS_FILE, fontFamilies, basePath, false);
709cb93a386Sopenharmony_ci    }
710cb93a386Sopenharmony_ci    return version;
711cb93a386Sopenharmony_ci}
712cb93a386Sopenharmony_ci
713cb93a386Sopenharmony_ci/**
714cb93a386Sopenharmony_ci * In some versions of Android prior to Android 4.2 (JellyBean MR1 at API
715cb93a386Sopenharmony_ci * Level 17) the fallback fonts for certain locales were encoded in their own
716cb93a386Sopenharmony_ci * XML files with a suffix that identified the locale.  We search the provided
717cb93a386Sopenharmony_ci * directory for those files,add all of their entries to the fallback chain, and
718cb93a386Sopenharmony_ci * include the locale as part of each entry.
719cb93a386Sopenharmony_ci */
720cb93a386Sopenharmony_cistatic void append_fallback_font_families_for_locale(SkTDArray<FontFamily*>& fallbackFonts,
721cb93a386Sopenharmony_ci                                                     const char* dir,
722cb93a386Sopenharmony_ci                                                     const SkString& basePath)
723cb93a386Sopenharmony_ci{
724cb93a386Sopenharmony_ci    SkOSFile::Iter iter(dir, nullptr);
725cb93a386Sopenharmony_ci    SkString fileName;
726cb93a386Sopenharmony_ci    while (iter.next(&fileName, false)) {
727cb93a386Sopenharmony_ci        // The size of the prefix and suffix.
728cb93a386Sopenharmony_ci        static const size_t fixedLen = sizeof(LOCALE_FALLBACK_FONTS_PREFIX) - 1
729cb93a386Sopenharmony_ci                                     + sizeof(LOCALE_FALLBACK_FONTS_SUFFIX) - 1;
730cb93a386Sopenharmony_ci
731cb93a386Sopenharmony_ci        // The size of the prefix, suffix, and a minimum valid language code
732cb93a386Sopenharmony_ci        static const size_t minSize = fixedLen + 2;
733cb93a386Sopenharmony_ci
734cb93a386Sopenharmony_ci        if (fileName.size() < minSize ||
735cb93a386Sopenharmony_ci            !fileName.startsWith(LOCALE_FALLBACK_FONTS_PREFIX) ||
736cb93a386Sopenharmony_ci            !fileName.endsWith(LOCALE_FALLBACK_FONTS_SUFFIX))
737cb93a386Sopenharmony_ci        {
738cb93a386Sopenharmony_ci            continue;
739cb93a386Sopenharmony_ci        }
740cb93a386Sopenharmony_ci
741cb93a386Sopenharmony_ci        SkString locale(fileName.c_str() + sizeof(LOCALE_FALLBACK_FONTS_PREFIX) - 1,
742cb93a386Sopenharmony_ci                        fileName.size() - fixedLen);
743cb93a386Sopenharmony_ci
744cb93a386Sopenharmony_ci        SkString absoluteFilename;
745cb93a386Sopenharmony_ci        absoluteFilename.printf("%s/%s", dir, fileName.c_str());
746cb93a386Sopenharmony_ci
747cb93a386Sopenharmony_ci        SkTDArray<FontFamily*> langSpecificFonts;
748cb93a386Sopenharmony_ci        parse_config_file(absoluteFilename.c_str(), langSpecificFonts, basePath, true);
749cb93a386Sopenharmony_ci
750cb93a386Sopenharmony_ci        for (int i = 0; i < langSpecificFonts.count(); ++i) {
751cb93a386Sopenharmony_ci            FontFamily* family = langSpecificFonts[i];
752cb93a386Sopenharmony_ci            family->fLanguages.emplace_back(locale);
753cb93a386Sopenharmony_ci            *fallbackFonts.append() = family;
754cb93a386Sopenharmony_ci        }
755cb93a386Sopenharmony_ci    }
756cb93a386Sopenharmony_ci}
757cb93a386Sopenharmony_ci
758cb93a386Sopenharmony_cistatic void append_system_fallback_font_families(SkTDArray<FontFamily*>& fallbackFonts,
759cb93a386Sopenharmony_ci                                                 const SkString& basePath)
760cb93a386Sopenharmony_ci{
761cb93a386Sopenharmony_ci    parse_config_file(FALLBACK_FONTS_FILE, fallbackFonts, basePath, true);
762cb93a386Sopenharmony_ci    append_fallback_font_families_for_locale(fallbackFonts,
763cb93a386Sopenharmony_ci                                             LOCALE_FALLBACK_FONTS_SYSTEM_DIR,
764cb93a386Sopenharmony_ci                                             basePath);
765cb93a386Sopenharmony_ci}
766cb93a386Sopenharmony_ci
767cb93a386Sopenharmony_cistatic void mixin_vendor_fallback_font_families(SkTDArray<FontFamily*>& fallbackFonts,
768cb93a386Sopenharmony_ci                                                const SkString& basePath)
769cb93a386Sopenharmony_ci{
770cb93a386Sopenharmony_ci    SkTDArray<FontFamily*> vendorFonts;
771cb93a386Sopenharmony_ci    parse_config_file(VENDOR_FONTS_FILE, vendorFonts, basePath, true);
772cb93a386Sopenharmony_ci    append_fallback_font_families_for_locale(vendorFonts,
773cb93a386Sopenharmony_ci                                             LOCALE_FALLBACK_FONTS_VENDOR_DIR,
774cb93a386Sopenharmony_ci                                             basePath);
775cb93a386Sopenharmony_ci
776cb93a386Sopenharmony_ci    // This loop inserts the vendor fallback fonts in the correct order in the
777cb93a386Sopenharmony_ci    // overall fallbacks list.
778cb93a386Sopenharmony_ci    int currentOrder = -1;
779cb93a386Sopenharmony_ci    for (int i = 0; i < vendorFonts.count(); ++i) {
780cb93a386Sopenharmony_ci        FontFamily* family = vendorFonts[i];
781cb93a386Sopenharmony_ci        int order = family->fOrder;
782cb93a386Sopenharmony_ci        if (order < 0) {
783cb93a386Sopenharmony_ci            if (currentOrder < 0) {
784cb93a386Sopenharmony_ci                // Default case - just add it to the end of the fallback list
785cb93a386Sopenharmony_ci                *fallbackFonts.append() = family;
786cb93a386Sopenharmony_ci            } else {
787cb93a386Sopenharmony_ci                // no order specified on this font, but we're incrementing the order
788cb93a386Sopenharmony_ci                // based on an earlier order insertion request
789cb93a386Sopenharmony_ci                *fallbackFonts.insert(currentOrder++) = family;
790cb93a386Sopenharmony_ci            }
791cb93a386Sopenharmony_ci        } else {
792cb93a386Sopenharmony_ci            // Add the font into the fallback list in the specified order. Set
793cb93a386Sopenharmony_ci            // currentOrder for correct placement of other fonts in the vendor list.
794cb93a386Sopenharmony_ci            *fallbackFonts.insert(order) = family;
795cb93a386Sopenharmony_ci            currentOrder = order + 1;
796cb93a386Sopenharmony_ci        }
797cb93a386Sopenharmony_ci    }
798cb93a386Sopenharmony_ci}
799cb93a386Sopenharmony_ci
800cb93a386Sopenharmony_civoid SkFontMgr_Android_Parser::GetSystemFontFamilies(SkTDArray<FontFamily*>& fontFamilies) {
801cb93a386Sopenharmony_ci    // Version 21 of the system font configuration does not need any fallback configuration files.
802cb93a386Sopenharmony_ci    SkString basePath(getenv("ANDROID_ROOT"));
803cb93a386Sopenharmony_ci    basePath.append(SK_FONT_FILE_PREFIX, sizeof(SK_FONT_FILE_PREFIX) - 1);
804cb93a386Sopenharmony_ci
805cb93a386Sopenharmony_ci    if (append_system_font_families(fontFamilies, basePath) >= 21) {
806cb93a386Sopenharmony_ci        return;
807cb93a386Sopenharmony_ci    }
808cb93a386Sopenharmony_ci
809cb93a386Sopenharmony_ci    // Append all the fallback fonts to system fonts
810cb93a386Sopenharmony_ci    SkTDArray<FontFamily*> fallbackFonts;
811cb93a386Sopenharmony_ci    append_system_fallback_font_families(fallbackFonts, basePath);
812cb93a386Sopenharmony_ci    mixin_vendor_fallback_font_families(fallbackFonts, basePath);
813cb93a386Sopenharmony_ci    fontFamilies.append(fallbackFonts.count(), fallbackFonts.begin());
814cb93a386Sopenharmony_ci}
815cb93a386Sopenharmony_ci
816cb93a386Sopenharmony_civoid SkFontMgr_Android_Parser::GetCustomFontFamilies(SkTDArray<FontFamily*>& fontFamilies,
817cb93a386Sopenharmony_ci                                                     const SkString& basePath,
818cb93a386Sopenharmony_ci                                                     const char* fontsXml,
819cb93a386Sopenharmony_ci                                                     const char* fallbackFontsXml,
820cb93a386Sopenharmony_ci                                                     const char* langFallbackFontsDir)
821cb93a386Sopenharmony_ci{
822cb93a386Sopenharmony_ci    if (fontsXml) {
823cb93a386Sopenharmony_ci        parse_config_file(fontsXml, fontFamilies, basePath, false);
824cb93a386Sopenharmony_ci    }
825cb93a386Sopenharmony_ci    if (fallbackFontsXml) {
826cb93a386Sopenharmony_ci        parse_config_file(fallbackFontsXml, fontFamilies, basePath, true);
827cb93a386Sopenharmony_ci    }
828cb93a386Sopenharmony_ci    if (langFallbackFontsDir) {
829cb93a386Sopenharmony_ci        append_fallback_font_families_for_locale(fontFamilies,
830cb93a386Sopenharmony_ci                                                 langFallbackFontsDir,
831cb93a386Sopenharmony_ci                                                 basePath);
832cb93a386Sopenharmony_ci    }
833cb93a386Sopenharmony_ci}
834cb93a386Sopenharmony_ci
835cb93a386Sopenharmony_ciSkLanguage SkLanguage::getParent() const {
836cb93a386Sopenharmony_ci    SkASSERT(!fTag.isEmpty());
837cb93a386Sopenharmony_ci    const char* tag = fTag.c_str();
838cb93a386Sopenharmony_ci
839cb93a386Sopenharmony_ci    // strip off the rightmost "-.*"
840cb93a386Sopenharmony_ci    const char* parentTagEnd = strrchr(tag, '-');
841cb93a386Sopenharmony_ci    if (parentTagEnd == nullptr) {
842cb93a386Sopenharmony_ci        return SkLanguage();
843cb93a386Sopenharmony_ci    }
844cb93a386Sopenharmony_ci    size_t parentTagLen = parentTagEnd - tag;
845cb93a386Sopenharmony_ci    return SkLanguage(tag, parentTagLen);
846cb93a386Sopenharmony_ci}
847