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