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