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