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 * 2021.9.9 SkFontMgr_config_parser on previewer of ohos.
7 * Copyright (c) 2021 Huawei Device Co., Ltd. All rights reserved.
8 */
9
10 // Despite the name and location, this is portable code.
11
12 #include "src/ports/SkFontMgr_config_parser.h"
13
14 #include <expat.h>
15
16 #include "include/core/SkStream.h"
17 #include "include/private/SkFixed.h"
18 #include "src/core/SkTSearch.h"
19
20 #define SK_FONT_CONFIG_FILE_NAME "fonts.xml"
21 std::string SkFontMgr::containerFontPath = "";
22
23 namespace {
24
25 std::string g_lmpSystemFontsFile = "INVALID_FILE_PATH";
26
27 }
28
29 /**
30 * This parser file for android contains TWO 'familyset' handlers:
31 * One for JB and earlier and the other for LMP and later.
32 * For previewer, we only use the fonts.xml in LMP, so the LMP 'familyset' handler is only needed.
33 */
34
35 struct FamilyData;
36
37 struct TagHandler {
38 /** Called at the start tag.
39 * Called immediately after the parent tag retuns this handler from a call to 'tag'.
40 * Allows setting up for handling the tag content and processing attributes.
41 * If nullptr, will not be called.
42 */
43 void (*start)(FamilyData* data, const char* tag, const char** attributes);
44
45 /** Called at the end tag.
46 * Allows post-processing of any accumulated information.
47 * This will be the last call made in relation to the current tag.
48 * If nullptr, will not be called.
49 */
50 void (*end)(FamilyData* data, const char* tag);
51
52 /** Called when a nested tag is encountered.
53 * This is responsible for determining how to handle the tag.
54 * If the tag is not recognized, return nullptr to skip the tag.
55 * If nullptr, all nested tags will be skipped.
56 */
57 const TagHandler* (*tag)(FamilyData* data, const char* tag, const char** attributes);
58
59 /** The character handler for this tag.
60 * This is only active for character data contained directly in this tag (not sub-tags).
61 * The first parameter will be castable to a FamilyData*.
62 * If nullptr, any character data in this tag will be ignored.
63 */
64 XML_CharacterDataHandler chars;
65 };
66
67 /** Represents the current parsing state. */
68 struct FamilyData {
FamilyDataFamilyData69 FamilyData(XML_Parser parser, SkTDArray<FontFamily*>& families,
70 const SkString& basePath, bool isFallback, const char* filename,
71 const TagHandler* topLevelHandler)
72 : fParser(parser)
73 , fFamilies(families)
74 , fCurrentFamily(nullptr)
75 , fCurrentFontInfo(nullptr)
76 , fVersion(0)
77 , fBasePath(basePath)
78 , fIsFallback(isFallback)
79 , fFilename(filename)
80 , fDepth(1)
81 , fSkip(0)
82 , fHandler(&topLevelHandler, 1)
83 { }
84
85 XML_Parser fParser; // The expat parser doing the work, owned by caller
86 SkTDArray<FontFamily*>& fFamilies; // The array to append families, owned by caller
87 std::unique_ptr<FontFamily> fCurrentFamily; // The family being created, owned by this
88 FontFileInfo* fCurrentFontInfo; // The info being created, owned by fCurrentFamily
89 int fVersion; // The version of the file parsed.
90 const SkString& fBasePath; // The current base path.
91 const bool fIsFallback; // The file being parsed is a fallback file
92 const char* fFilename; // The name of the file currently being parsed.
93
94 int fDepth; // The current element depth of the parse.
95 int fSkip; // The depth to stop skipping, 0 if not skipping.
96 SkTDArray<const TagHandler*> fHandler; // The stack of current tag handlers.
97 };
98
memeq(const char * s1,const char * s2,size_t n1,size_t n2)99 static bool memeq(const char *s1, const char *s2, size_t n1, size_t n2)
100 {
101 return n1 == n2 && 0 == memcmp(s1, s2, n1);
102 }
103 #define MEMEQ(c, s, n) memeq(c, s, sizeof(c) - 1, n)
104
105 #define ATTS_NON_NULL(a, i) (a[i] != nullptr && a[i + 1] != nullptr)
106
107 #define SK_FONTMGR_CONFIG_PARSER_PREFIX "[SkFontMgr Config Parser] "
108
109 #define SK_FONTCONFIGPARSER_WARNING(message, ...) \
110 SkDebugf(SK_FONTMGR_CONFIG_PARSER_PREFIX "%s:%d:%d: warning: " message "\n", self->fFilename, \
111 XML_GetCurrentLineNumber(self->fParser), XML_GetCurrentColumnNumber(self->fParser), ##__VA_ARGS__)
112
is_whitespace(char c)113 static bool is_whitespace(char c)
114 {
115 return c == ' ' || c == '\n' || c == '\r' || c == '\t';
116 }
117
trim_string(SkString * s)118 static void trim_string(SkString* s)
119 {
120 char* str = s->writable_str();
121 const char* start = str; // start is inclusive
122 const char* end = start + s->size(); // end is exclusive
123 while (is_whitespace(*start)) { ++start; }
124 if (start != end) {
125 --end; // make end inclusive
126 while (is_whitespace(*end)) {
127 --end;
128 }
129 ++end; // make end exclusive
130 }
131 size_t len = end - start;
132 memmove(str, start, len);
133 s->resize(len);
134 }
135
136 namespace lmpParser {
137
138 static const TagHandler axisHandler = {
__anon718442630202() 139 /*start*/[](FamilyData* self, const char* tag, const char** attributes) {
140 FontFileInfo& file = *self->fCurrentFontInfo;
141 SkFourByteTag axisTag = SkSetFourByteTag('\0', '\0', '\0', '\0');
142 SkFixed axisStyleValue = 0;
143 bool axisTagIsValid = false;
144 bool axisStyleValueIsValid = false;
145 for (size_t i = 0; ATTS_NON_NULL(attributes, i); i += 2) {
146 const char* name = attributes[i];
147 const char* value = attributes[i + 1];
148 size_t nameLen = strlen(name);
149 if (MEMEQ("tag", name, nameLen)) {
150 size_t valueLen = strlen(value);
151 if (valueLen == 4) {
152 axisTag = SkSetFourByteTag(value[0], value[1], value[2], value[3]);
153 axisTagIsValid = true;
154 for (int j = 0; j < file.fVariationDesignPosition.count() - 1; ++j) {
155 if (file.fVariationDesignPosition[j].axis == axisTag) {
156 axisTagIsValid = false;
157 SK_FONTCONFIGPARSER_WARNING("'%c%c%c%c' axis specified more than once",
158 (axisTag >> 24) & 0xFF,
159 (axisTag >> 16) & 0xFF,
160 (axisTag >> 8) & 0xFF,
161 (axisTag ) & 0xFF);
162 }
163 }
164 } else {
165 SK_FONTCONFIGPARSER_WARNING("'%s' is an invalid axis tag", value);
166 }
167 } else if (MEMEQ("stylevalue", name, nameLen)) {
168 if (parse_fixed<16>(value, &axisStyleValue)) {
169 axisStyleValueIsValid = true;
170 } else {
171 SK_FONTCONFIGPARSER_WARNING("'%s' is an invalid axis stylevalue", value);
172 }
173 }
174 }
175 if (axisTagIsValid && axisStyleValueIsValid) {
176 auto& coordinate = file.fVariationDesignPosition.push_back();
177 coordinate.axis = axisTag;
178 coordinate.value = SkFixedToScalar(axisStyleValue);
179 }
180 },
181 /*end*/nullptr,
182 /*tag*/nullptr,
183 /*chars*/nullptr,
184 };
185
186 static const TagHandler fontHandler = {
__anon718442630302() 187 /*start*/[](FamilyData* self, const char* tag, const char** attributes) {
188 // 'weight' (non-negative integer) [default 0]
189 // 'style' ("normal", "italic") [default "auto"]
190 // 'index' (non-negative integer) [default 0]
191 // The character data should be a filename.
192 FontFileInfo& file = self->fCurrentFamily->fFonts.push_back();
193 self->fCurrentFontInfo = &file;
194 SkString fallbackFor;
195 for (size_t i = 0; ATTS_NON_NULL(attributes, i); i += 2) {
196 const char* name = attributes[i];
197 const char* value = attributes[i + 1];
198 size_t nameLen = strlen(name);
199 if (MEMEQ("weight", name, nameLen)) {
200 if (!parse_non_negative_integer(value, &file.fWeight)) {
201 SK_FONTCONFIGPARSER_WARNING("'%s' is an invalid weight", value);
202 }
203 } else if (MEMEQ("style", name, nameLen)) {
204 size_t valueLen = strlen(value);
205 if (MEMEQ("normal", value, valueLen)) {
206 file.fStyle = FontFileInfo::Style::kNormal;
207 } else if (MEMEQ("italic", value, valueLen)) {
208 file.fStyle = FontFileInfo::Style::kItalic;
209 }
210 } else if (MEMEQ("index", name, nameLen)) {
211 if (!parse_non_negative_integer(value, &file.fIndex)) {
212 SK_FONTCONFIGPARSER_WARNING("'%s' is an invalid index", value);
213 }
214 } else if (MEMEQ("fallbackFor", name, nameLen)) {
215 /** fallbackFor specifies a family fallback and should have been on family. */
216 fallbackFor = value;
217 }
218 }
219 if (!fallbackFor.isEmpty()) {
220 std::unique_ptr<FontFamily>* fallbackFamily =
221 self->fCurrentFamily->fallbackFamilies.find(fallbackFor);
222 if (!fallbackFamily) {
223 std::unique_ptr<FontFamily> newFallbackFamily(
224 new FontFamily(self->fCurrentFamily->fBasePath, true));
225 fallbackFamily = self->fCurrentFamily->fallbackFamilies.set(
226 fallbackFor, std::move(newFallbackFamily));
227 (*fallbackFamily)->fLanguages = self->fCurrentFamily->fLanguages;
228 (*fallbackFamily)->fVariant = self->fCurrentFamily->fVariant;
229 (*fallbackFamily)->fOrder = self->fCurrentFamily->fOrder;
230 (*fallbackFamily)->fFallbackFor = fallbackFor;
231 }
232 self->fCurrentFontInfo = &(*fallbackFamily)->fFonts.emplace_back(file);
233 self->fCurrentFamily->fFonts.pop_back();
234 }
235 },
__anon718442630402() 236 /*end*/[](FamilyData* self, const char* tag) {
237 trim_string(&self->fCurrentFontInfo->fFileName);
238 },
__anon718442630502() 239 /*tag*/[](FamilyData* self, const char* tag, const char** attributes) -> const TagHandler* {
240 size_t len = strlen(tag);
241 if (MEMEQ("axis", tag, len)) {
242 return &axisHandler;
243 }
244 return nullptr;
245 },
__anon718442630602() 246 /*chars*/[](void* data, const char* s, int len) {
247 FamilyData* self = static_cast<FamilyData*>(data);
248 self->fCurrentFontInfo->fFileName.append(s, len);
249 }
250 };
251
252 static const TagHandler familyHandler = {
__anon718442630702() 253 /*start*/[](FamilyData* self, const char* tag, const char** attributes) {
254 // 'name' (string) [optional]
255 // 'lang' (space separated string) [default ""]
256 // 'variant' ("elegant", "compact") [default "default"]
257 // If there is no name, this is a fallback only font.
258 FontFamily* family = new FontFamily(self->fBasePath, true);
259 self->fCurrentFamily.reset(family);
260 for (size_t i = 0; ATTS_NON_NULL(attributes, i); i += 2) {
261 const char* name = attributes[i];
262 const char* value = attributes[i + 1];
263 size_t nameLen = strlen(name);
264 size_t valueLen = strlen(value);
265 if (MEMEQ("name", name, nameLen)) {
266 SkAutoAsciiToLC tolc(value);
267 family->fNames.push_back().set(tolc.lc());
268 family->fIsFallbackFont = false;
269 } else if (MEMEQ("lang", name, nameLen)) {
270 size_t i = 0;
271 while (true) {
272 for (; i < valueLen && is_whitespace(value[i]); ++i) { }
273 if (i == valueLen) {
274 break;
275 }
276 size_t j;
277 for (j = i + 1; j < valueLen && !is_whitespace(value[j]); ++j) { }
278 family->fLanguages.emplace_back(value + i, j - i);
279 i = j;
280 if (i == valueLen) {
281 break;
282 }
283 }
284 } else if (MEMEQ("variant", name, nameLen)) {
285 if (MEMEQ("elegant", value, valueLen)) {
286 family->fVariant = kElegant_FontVariant;
287 } else if (MEMEQ("compact", value, valueLen)) {
288 family->fVariant = kCompact_FontVariant;
289 }
290 }
291 }
292 },
__anon718442630802() 293 /*end*/[](FamilyData* self, const char* tag) {
294 *self->fFamilies.append() = self->fCurrentFamily.release();
295 },
__anon718442630902() 296 /*tag*/[](FamilyData* self, const char* tag, const char** attributes) -> const TagHandler* {
297 size_t len = strlen(tag);
298 if (MEMEQ("font", tag, len)) {
299 return &fontHandler;
300 }
301 return nullptr;
302 },
303 /*chars*/nullptr,
304 };
305
find_family(FamilyData * self,const SkString & familyName)306 static FontFamily* find_family(FamilyData* self, const SkString& familyName)
307 {
308 for (int i = 0; i < self->fFamilies.count(); i++) {
309 FontFamily* candidate = self->fFamilies[i];
310 for (int j = 0; j < candidate->fNames.count(); j++) {
311 if (candidate->fNames[j] == familyName) {
312 return candidate;
313 }
314 }
315 }
316 return nullptr;
317 }
318
319 static const TagHandler aliasHandler = {
__anon718442630a02() 320 /*start*/[](FamilyData* self, const char* tag, const char** attributes) {
321 // 'name' (string) introduces a new family name.
322 // 'to' (string) specifies which (previous) family to alias
323 // 'weight' (non-negative integer) [optional]
324 // If it *does not* have a weight, 'name' is an alias for the entire 'to' family.
325 // If it *does* have a weight, 'name' is a new family consisting of
326 // the font(s) with 'weight' from the 'to' family.
327
328 SkString aliasName;
329 SkString to;
330 int weight = 0;
331 for (size_t i = 0; ATTS_NON_NULL(attributes, i); i += 2) {
332 const char* name = attributes[i];
333 const char* value = attributes[i + 1];
334 size_t nameLen = strlen(name);
335 if (MEMEQ("name", name, nameLen)) {
336 SkAutoAsciiToLC tolc(value);
337 aliasName.set(tolc.lc());
338 } else if (MEMEQ("to", name, nameLen)) {
339 to.set(value);
340 } else if (MEMEQ("weight", name, nameLen)) {
341 if (!parse_non_negative_integer(value, &weight)) {
342 SK_FONTCONFIGPARSER_WARNING("'%s' is an invalid weight", value);
343 }
344 }
345 }
346
347 // Assumes that the named family is already declared
348 FontFamily* targetFamily = find_family(self, to);
349 if (!targetFamily) {
350 SK_FONTCONFIGPARSER_WARNING("'%s' alias target not found", to.c_str());
351 return;
352 }
353
354 if (weight) {
355 FontFamily* family = new FontFamily(targetFamily->fBasePath, self->fIsFallback);
356 family->fNames.push_back().set(aliasName);
357
358 for (int i = 0; i < targetFamily->fFonts.count(); i++) {
359 if (targetFamily->fFonts[i].fWeight == weight) {
360 family->fFonts.push_back(targetFamily->fFonts[i]);
361 }
362 }
363 *self->fFamilies.append() = family;
364 } else {
365 targetFamily->fNames.push_back().set(aliasName);
366 }
367 },
368 /*end*/nullptr,
369 /*tag*/nullptr,
370 /*chars*/nullptr,
371 };
372
373 static const TagHandler familySetHandler = {
__anon718442630b02() 374 /*start*/[](FamilyData* self, const char* tag, const char** attributes) { },
375 /*end*/nullptr,
__anon718442630c02() 376 /*tag*/[](FamilyData* self, const char* tag, const char** attributes) -> const TagHandler* {
377 size_t len = strlen(tag);
378 if (MEMEQ("family", tag, len)) {
379 return &familyHandler;
380 } else if (MEMEQ("alias", tag, len)) {
381 return &aliasHandler;
382 }
383 return nullptr;
384 },
385 /*chars*/nullptr,
386 };
387
388 } // namespace lmpParser
389
390 static const TagHandler topLevelHandler = {
391 /*start*/nullptr,
392 /*end*/nullptr,
__anon718442630d02() 393 /*tag*/[](FamilyData* self, const char* tag, const char** attributes) -> const TagHandler* {
394 size_t len = strlen(tag);
395 if (MEMEQ("familyset", tag, len)) {
396 // 'version' (non-negative integer) [default 0]
397 for (size_t i = 0; ATTS_NON_NULL(attributes, i); i += 2) {
398 const char* name = attributes[i];
399 size_t nameLen = strlen(name);
400 if (MEMEQ("version", name, nameLen)) {
401 const char* value = attributes[i + 1];
402 if (parse_non_negative_integer(value, &self->fVersion)) {
403 if (self->fVersion >= 21) {
404 return &lmpParser::familySetHandler;
405 }
406 }
407 }
408 }
409 }
410 return nullptr;
411 },
412 /*chars*/ nullptr,
413 };
414
start_element_handler(void * data,const char * tag,const char ** attributes)415 static void XMLCALL start_element_handler(void *data, const char *tag, const char **attributes)
416 {
417 FamilyData* self = static_cast<FamilyData*>(data);
418
419 if (!self->fSkip) {
420 const TagHandler* parent = self->fHandler.top();
421 const TagHandler* child = parent->tag ? parent->tag(self, tag, attributes) : nullptr;
422 if (child) {
423 if (child->start) {
424 child->start(self, tag, attributes);
425 }
426 self->fHandler.push_back(child);
427 XML_SetCharacterDataHandler(self->fParser, child->chars);
428 } else {
429 SK_FONTCONFIGPARSER_WARNING("'%s' tag not recognized, skipping", tag);
430 XML_SetCharacterDataHandler(self->fParser, nullptr);
431 self->fSkip = self->fDepth;
432 }
433 }
434
435 ++self->fDepth;
436 }
437
end_element_handler(void * data,const char * tag)438 static void XMLCALL end_element_handler(void* data, const char* tag)
439 {
440 FamilyData* self = static_cast<FamilyData*>(data);
441 --self->fDepth;
442
443 if (!self->fSkip) {
444 const TagHandler* child = self->fHandler.top();
445 if (child->end) {
446 child->end(self, tag);
447 }
448 self->fHandler.pop();
449 const TagHandler* parent = self->fHandler.top();
450 XML_SetCharacterDataHandler(self->fParser, parent->chars);
451 }
452
453 if (self->fSkip == self->fDepth) {
454 self->fSkip = 0;
455 const TagHandler* parent = self->fHandler.top();
456 XML_SetCharacterDataHandler(self->fParser, parent->chars);
457 }
458 }
459
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)460 static void XMLCALL xml_entity_decl_handler(void *data,
461 const XML_Char *entityName,
462 int is_parameter_entity,
463 const XML_Char *value,
464 int value_length,
465 const XML_Char *base,
466 const XML_Char *systemId,
467 const XML_Char *publicId,
468 const XML_Char *notationName)
469 {
470 FamilyData* self = static_cast<FamilyData*>(data);
471 SK_FONTCONFIGPARSER_WARNING("'%s' entity declaration found, stopping processing", entityName);
472 XML_StopParser(self->fParser, XML_FALSE);
473 }
474
475 static const XML_Memory_Handling_Suite sk_XML_alloc = {
476 sk_malloc_throw,
477 sk_realloc_throw,
478 sk_free
479 };
480
481 /**
482 * This function parses the given filename and stores the results in the given
483 * families array. Returns the version of the file, negative if the file does not exist.
484 */
parse_config_file(const char * filename,SkTDArray<FontFamily * > & families,const SkString & basePath,bool isFallback)485 static int parse_config_file(const char* filename, SkTDArray<FontFamily*>& families,
486 const SkString& basePath, bool isFallback)
487 {
488 SkFILEStream file(filename);
489
490 // Some of the files we attempt to parse (in particular, /vendor/etc/fallback_fonts.xml)
491 // are optional - failure here is okay because one of these optional files may not exist.
492 if (!file.isValid()) {
493 SkDebugf(SK_FONTMGR_CONFIG_PARSER_PREFIX "'%s' could not be opened\n", filename);
494 return -1;
495 }
496
497 SkAutoTCallVProc<std::remove_pointer_t<XML_Parser>, XML_ParserFree> parser(
498 XML_ParserCreate_MM(nullptr, &sk_XML_alloc, nullptr));
499 if (!parser) {
500 SkDebugf(SK_FONTMGR_CONFIG_PARSER_PREFIX "could not create XML parser\n");
501 return -1;
502 }
503
504 FamilyData self(parser, families, basePath, isFallback, filename, &topLevelHandler);
505 XML_SetUserData(parser, &self);
506
507 // Disable entity processing, to inhibit internal entity expansion. See expat CVE-2013-0340
508 XML_SetEntityDeclHandler(parser, xml_entity_decl_handler);
509
510 // Start parsing oldschool; switch these in flight if we detect a newer version of the file.
511 XML_SetElementHandler(parser, start_element_handler, end_element_handler);
512
513 // One would assume it would be faster to have a buffer on the stack and call XML_Parse.
514 // But XML_Parse will call XML_GetBuffer anyway and memmove the passed buffer into it.
515 // (Unless XML_CONTEXT_BYTES is undefined, but all users define it.)
516 // In debug, buffer a small odd number of bytes to detect slicing in XML_CharacterDataHandler.
517 static const int bufferSize = 512 SkDEBUGCODE(-507);
518 bool done = false;
519 while (!done) {
520 void* buffer = XML_GetBuffer(parser, bufferSize);
521 if (!buffer) {
522 SkDebugf(SK_FONTMGR_CONFIG_PARSER_PREFIX "could not buffer enough to continue\n");
523 return -1;
524 }
525 size_t len = file.read(buffer, bufferSize);
526 done = file.isAtEnd();
527 XML_Status status = XML_ParseBuffer(parser, len, done);
528 if (XML_STATUS_ERROR == status) {
529 XML_Error error = XML_GetErrorCode(parser);
530 int line = XML_GetCurrentLineNumber(parser);
531 int column = XML_GetCurrentColumnNumber(parser);
532 const XML_LChar* errorString = XML_ErrorString(error);
533 SkDebugf(SK_FONTMGR_CONFIG_PARSER_PREFIX "%s:%d:%d error %d: %s.\n",
534 filename, line, column, error, errorString);
535 return -1;
536 }
537 }
538 return self.fVersion;
539 }
540
541 /** Returns the version of the system font file actually found, negative if none. */
append_system_font_families(SkTDArray<FontFamily * > & fontFamilies,const SkString & basePath)542 static int append_system_font_families(SkTDArray<FontFamily*>& fontFamilies,
543 const SkString& basePath)
544 {
545 int version = parse_config_file(g_lmpSystemFontsFile.c_str(), fontFamilies, basePath, false);
546 return version;
547 }
548
GetSystemFontFamilies(SkTDArray<FontFamily * > & fontFamilies)549 void SkFontMgr_Config_Parser::GetSystemFontFamilies(SkTDArray<FontFamily*>& fontFamilies)
550 {
551 std::string containerFontBasePath = SkFontMgr::containerFontPath;
552 if (containerFontBasePath.empty()) {
553 printf("error getting font base path '%s'\n", containerFontBasePath.c_str());
554 }
555 SkString basePath(containerFontBasePath.c_str());
556 g_lmpSystemFontsFile = containerFontBasePath.append(SK_FONT_CONFIG_FILE_NAME);
557 append_system_font_families(fontFamilies, basePath);
558 }
559
getParent() const560 SkLanguage SkLanguage::getParent() const
561 {
562 SkASSERT(!fTag.isEmpty());
563 const char* tag = fTag.c_str();
564
565 // strip off the rightmost "-.*"
566 const char* parentTagEnd = strrchr(tag, '-');
567 if (parentTagEnd == nullptr) {
568 return SkLanguage();
569 }
570 size_t parentTagLen = parentTagEnd - tag;
571 return SkLanguage(tag, parentTagLen);
572 }