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