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