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