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