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