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