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