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