blob: 126952306280885106ca57e4d3c7836b14b87390 [file] [log] [blame]
djsollen@google.combfae9d32013-05-21 16:53:50 +00001/*
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
mtklein1ee76512015-11-02 10:20:27 -08008// Despite the name and location, this is portable code.
9
benjaminwagner6c71e0a2016-04-07 08:49:31 -070010#include "SkFixed.h"
bungeman47a1e962016-02-25 11:20:01 -080011#include "SkFontMgr.h"
bungemanc5308542015-06-23 13:25:46 -070012#include "SkFontMgr_android_parser.h"
Herb Derbyb549cc32017-03-27 13:35:15 -040013#include "SkMalloc.h"
bungeman22cffca2016-08-26 07:15:50 -070014#include "SkOSFile.h"
bungeman7fa87cd2015-02-06 07:59:19 -080015#include "SkStream.h"
djsollen@google.combfae9d32013-05-21 16:53:50 +000016#include "SkTDArray.h"
bungeman8d84c992014-07-24 08:05:09 -070017#include "SkTSearch.h"
bungemanf20488b2015-07-29 11:49:40 -070018#include "SkTemplates.h"
bungeman54e63082016-01-06 08:30:59 -080019#include "SkTLogic.h"
djsollen@google.combfae9d32013-05-21 16:53:50 +000020
bungemanf20488b2015-07-29 11:49:40 -070021#include <expat.h>
djsollen@google.combfae9d32013-05-21 16:53:50 +000022
bungeman7fa87cd2015-02-06 07:59:19 -080023#include <stdlib.h>
bungemanf20488b2015-07-29 11:49:40 -070024#include <string.h>
bungeman8d84c992014-07-24 08:05:09 -070025
tomhudson94fa4b92014-08-12 11:05:29 -070026#define LMP_SYSTEM_FONTS_FILE "/system/etc/fonts.xml"
27#define OLD_SYSTEM_FONTS_FILE "/system/etc/system_fonts.xml"
djsollen@google.combfae9d32013-05-21 16:53:50 +000028#define FALLBACK_FONTS_FILE "/system/etc/fallback_fonts.xml"
29#define VENDOR_FONTS_FILE "/vendor/etc/fallback_fonts.xml"
30
djsollena6c27bc2014-08-06 11:01:58 -070031#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
bungeman7fa87cd2015-02-06 07:59:19 -080036#ifndef SK_FONT_FILE_PREFIX
37# define SK_FONT_FILE_PREFIX "/fonts/"
38#endif
39
tomhudsonf79673b2014-08-05 06:36:11 -070040/**
bungeman10b063c2015-05-13 08:52:16 -070041 * 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.
tomhudsonf79673b2014-08-05 06:36:11 -070052 */
53
bungeman10b063c2015-05-13 08:52:16 -070054struct FamilyData;
55
56struct 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.
halcanary96fcdcc2015-08-27 07:41:13 -070060 * If nullptr, will not be called.
bungeman10b063c2015-05-13 08:52:16 -070061 */
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.
halcanary96fcdcc2015-08-27 07:41:13 -070067 * If nullptr, will not be called.
bungeman10b063c2015-05-13 08:52:16 -070068 */
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.
halcanary96fcdcc2015-08-27 07:41:13 -070073 * If the tag is not recognized, return nullptr to skip the tag.
74 * If nullptr, all nested tags will be skipped.
bungeman10b063c2015-05-13 08:52:16 -070075 */
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*.
halcanary96fcdcc2015-08-27 07:41:13 -070081 * If nullptr, any character data in this tag will be ignored.
bungeman10b063c2015-05-13 08:52:16 -070082 */
83 XML_CharacterDataHandler chars;
bungeman7fa87cd2015-02-06 07:59:19 -080084};
djsollen@google.combfae9d32013-05-21 16:53:50 +000085
bungeman10b063c2015-05-13 08:52:16 -070086/** Represents the current parsing state. */
djsollen@google.combfae9d32013-05-21 16:53:50 +000087struct FamilyData {
bungeman7fa87cd2015-02-06 07:59:19 -080088 FamilyData(XML_Parser parser, SkTDArray<FontFamily*>& families,
bungeman10b063c2015-05-13 08:52:16 -070089 const SkString& basePath, bool isFallback, const char* filename,
90 const TagHandler* topLevelHandler)
bungemanb6bed172015-01-30 15:35:09 -080091 : fParser(parser)
92 , fFamilies(families)
halcanary96fcdcc2015-08-27 07:41:13 -070093 , fCurrentFamily(nullptr)
94 , fCurrentFontInfo(nullptr)
bungemanefbad372015-02-02 11:19:44 -080095 , fVersion(0)
bungeman7fa87cd2015-02-06 07:59:19 -080096 , fBasePath(basePath)
97 , fIsFallback(isFallback)
bungemanf61475e2015-04-20 11:16:48 -070098 , fFilename(filename)
bungeman10b063c2015-05-13 08:52:16 -070099 , fDepth(1)
100 , fSkip(0)
101 , fHandler(&topLevelHandler, 1)
Mike Kleinfc6c37b2016-09-27 09:34:10 -0400102 { }
djsollen@google.combfae9d32013-05-21 16:53:50 +0000103
Ben Wagner145dbcd2016-11-03 14:40:50 -0400104 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.
bungeman10b063c2015-05-13 08:52:16 -0700112
Ben Wagner145dbcd2016-11-03 14:40:50 -0400113 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.
djsollen@google.combfae9d32013-05-21 16:53:50 +0000116};
117
bungeman7fa87cd2015-02-06 07:59:19 -0800118static 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
halcanary96fcdcc2015-08-27 07:41:13 -0700123#define ATTS_NON_NULL(a, i) (a[i] != nullptr && a[i+1] != nullptr)
bungeman7fa87cd2015-02-06 07:59:19 -0800124
bungemanc5308542015-06-23 13:25:46 -0700125#define SK_FONTMGR_ANDROID_PARSER_PREFIX "[SkFontMgr Android Parser] "
bungemanf61475e2015-04-20 11:16:48 -0700126
127#define SK_FONTCONFIGPARSER_WARNING(message, ...) SkDebugf( \
bungemanc5308542015-06-23 13:25:46 -0700128 SK_FONTMGR_ANDROID_PARSER_PREFIX "%s:%d:%d: warning: " message "\n", \
bungemanf61475e2015-04-20 11:16:48 -0700129 self->fFilename, \
130 XML_GetCurrentLineNumber(self->fParser), \
131 XML_GetCurrentColumnNumber(self->fParser), \
132 ##__VA_ARGS__);
133
bungemanc0727d12015-05-08 08:31:54 -0700134static bool is_whitespace(char c) {
135 return c == ' ' || c == '\n'|| c == '\r' || c == '\t';
136}
137
138static 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
bungeman10b063c2015-05-13 08:52:16 -0700153namespace lmpParser {
tomhudsonf79673b2014-08-05 06:36:11 -0700154
bungeman41868fe2015-05-20 09:21:04 -0700155static const TagHandler axisHandler = {
156 /*start*/[](FamilyData* self, const char* tag, const char** attributes) {
157 FontFileInfo& file = *self->fCurrentFontInfo;
bungeman47a1e962016-02-25 11:20:01 -0800158 SkFourByteTag axisTag = SkSetFourByteTag('\0','\0','\0','\0');
159 SkFixed axisStyleValue = 0;
160 bool axisTagIsValid = false;
161 bool axisStyleValueIsValid = false;
bungeman41868fe2015-05-20 09:21:04 -0700162 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) {
bungeman47a1e962016-02-25 11:20:01 -0800169 axisTag = SkSetFourByteTag(value[0], value[1], value[2], value[3]);
170 axisTagIsValid = true;
Ben Wagnerfc497342017-02-24 11:15:26 -0500171 for (int j = 0; j < file.fVariationDesignPosition.count() - 1; ++j) {
172 if (file.fVariationDesignPosition[j].axis == axisTag) {
bungeman47a1e962016-02-25 11:20:01 -0800173 axisTagIsValid = false;
bungeman41868fe2015-05-20 09:21:04 -0700174 SK_FONTCONFIGPARSER_WARNING("'%c%c%c%c' axis specified more than once",
bungeman47a1e962016-02-25 11:20:01 -0800175 (axisTag >> 24) & 0xFF,
176 (axisTag >> 16) & 0xFF,
177 (axisTag >> 8) & 0xFF,
178 (axisTag ) & 0xFF);
bungeman41868fe2015-05-20 09:21:04 -0700179 }
180 }
bungeman41868fe2015-05-20 09:21:04 -0700181 } else {
182 SK_FONTCONFIGPARSER_WARNING("'%s' is an invalid axis tag", value);
183 }
184 } else if (MEMEQ("stylevalue", name, nameLen)) {
bungeman47a1e962016-02-25 11:20:01 -0800185 if (parse_fixed<16>(value, &axisStyleValue)) {
186 axisStyleValueIsValid = true;
187 } else {
bungeman41868fe2015-05-20 09:21:04 -0700188 SK_FONTCONFIGPARSER_WARNING("'%s' is an invalid axis stylevalue", value);
189 }
190 }
191 }
bungeman47a1e962016-02-25 11:20:01 -0800192 if (axisTagIsValid && axisStyleValueIsValid) {
Ben Wagnerfc497342017-02-24 11:15:26 -0500193 auto& coordinate = file.fVariationDesignPosition.push_back();
194 coordinate.axis = axisTag;
195 coordinate.value = SkFixedToScalar(axisStyleValue);
bungeman47a1e962016-02-25 11:20:01 -0800196 }
bungeman41868fe2015-05-20 09:21:04 -0700197 },
halcanary96fcdcc2015-08-27 07:41:13 -0700198 /*end*/nullptr,
199 /*tag*/nullptr,
200 /*chars*/nullptr,
bungeman41868fe2015-05-20 09:21:04 -0700201};
202
bungeman10b063c2015-05-13 08:52:16 -0700203static const TagHandler fontHandler = {
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 }
djsollen@google.combfae9d32013-05-21 16:53:50 +0000230 }
djsollen@google.combfae9d32013-05-21 16:53:50 +0000231 }
bungeman10b063c2015-05-13 08:52:16 -0700232 },
233 /*end*/[](FamilyData* self, const char* tag) {
234 trim_string(&self->fCurrentFontInfo->fFileName);
235 },
bungeman41868fe2015-05-20 09:21:04 -0700236 /*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 }
halcanary96fcdcc2015-08-27 07:41:13 -0700241 return nullptr;
bungeman41868fe2015-05-20 09:21:04 -0700242 },
bungeman10b063c2015-05-13 08:52:16 -0700243 /*chars*/[](void* data, const char* s, int len) {
244 FamilyData* self = static_cast<FamilyData*>(data);
245 self->fCurrentFontInfo->fFileName.append(s, len);
djsollen@google.combfae9d32013-05-21 16:53:50 +0000246 }
bungeman10b063c2015-05-13 08:52:16 -0700247};
djsollen@google.combfae9d32013-05-21 16:53:50 +0000248
bungeman10b063c2015-05-13 08:52:16 -0700249static const TagHandler familyHandler = {
250 /*start*/[](FamilyData* self, const char* tag, const char** attributes) {
251 // 'name' (string) [optional]
Ben Wagneraee878d2017-08-10 13:49:41 -0400252 // 'lang' (space separated string) [default ""]
bungeman10b063c2015-05-13 08:52:16 -0700253 // '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);
bungeman7fa87cd2015-02-06 07:59:19 -0800257 for (size_t i = 0; ATTS_NON_NULL(attributes, i); i += 2) {
bungemanf61475e2015-04-20 11:16:48 -0700258 const char* name = attributes[i];
259 const char* value = attributes[i+1];
260 size_t nameLen = strlen(name);
261 size_t valueLen = strlen(value);
bungeman10b063c2015-05-13 08:52:16 -0700262 if (MEMEQ("name", name, nameLen)) {
263 SkAutoAsciiToLC tolc(value);
264 family->fNames.push_back().set(tolc.lc());
265 family->fIsFallbackFont = false;
bungemanf61475e2015-04-20 11:16:48 -0700266 } else if (MEMEQ("lang", name, nameLen)) {
Ben Wagneraee878d2017-08-10 13:49:41 -0400267 size_t i = 0;
268 while (true) {
269 for (; i < valueLen && is_whitespace(value[i]); ++i) { }
270 if (i == valueLen) { break; }
271 size_t j;
272 for (j = i + 1; j < valueLen && !is_whitespace(value[j]); ++j) { }
273 family->fLanguages.emplace_back(value + i, j - i);
274 i = j;
275 if (i == valueLen) { break; }
276 }
bungeman10b063c2015-05-13 08:52:16 -0700277 } else if (MEMEQ("variant", name, nameLen)) {
278 if (MEMEQ("elegant", value, valueLen)) {
279 family->fVariant = kElegant_FontVariant;
280 } else if (MEMEQ("compact", value, valueLen)) {
281 family->fVariant = kCompact_FontVariant;
bungeman8d84c992014-07-24 08:05:09 -0700282 }
djsollen@google.combfae9d32013-05-21 16:53:50 +0000283 }
djsollen@google.combfae9d32013-05-21 16:53:50 +0000284 }
bungeman10b063c2015-05-13 08:52:16 -0700285 },
286 /*end*/[](FamilyData* self, const char* tag) {
mtklein18300a32016-03-16 13:53:35 -0700287 *self->fFamilies.append() = self->fCurrentFamily.release();
bungeman10b063c2015-05-13 08:52:16 -0700288 },
289 /*tag*/[](FamilyData* self, const char* tag, const char** attributes) -> const TagHandler* {
290 size_t len = strlen(tag);
291 if (MEMEQ("font", tag, len)) {
292 return &fontHandler;
293 }
halcanary96fcdcc2015-08-27 07:41:13 -0700294 return nullptr;
bungeman10b063c2015-05-13 08:52:16 -0700295 },
halcanary96fcdcc2015-08-27 07:41:13 -0700296 /*chars*/nullptr,
bungeman10b063c2015-05-13 08:52:16 -0700297};
298
299static FontFamily* find_family(FamilyData* self, const SkString& familyName) {
300 for (int i = 0; i < self->fFamilies.count(); i++) {
301 FontFamily* candidate = self->fFamilies[i];
302 for (int j = 0; j < candidate->fNames.count(); j++) {
303 if (candidate->fNames[j] == familyName) {
304 return candidate;
305 }
306 }
djsollen@google.combfae9d32013-05-21 16:53:50 +0000307 }
halcanary96fcdcc2015-08-27 07:41:13 -0700308 return nullptr;
djsollen@google.combfae9d32013-05-21 16:53:50 +0000309}
310
bungeman10b063c2015-05-13 08:52:16 -0700311static const TagHandler aliasHandler = {
312 /*start*/[](FamilyData* self, const char* tag, const char** attributes) {
313 // 'name' (string) introduces a new family name.
314 // 'to' (string) specifies which (previous) family to alias
315 // 'weight' (non-negative integer) [optional]
316 // If it *does not* have a weight, 'name' is an alias for the entire 'to' family.
317 // If it *does* have a weight, 'name' is a new family consisting of
318 // the font(s) with 'weight' from the 'to' family.
319
320 SkString aliasName;
321 SkString to;
322 int weight = 0;
323 for (size_t i = 0; ATTS_NON_NULL(attributes, i); i += 2) {
324 const char* name = attributes[i];
325 const char* value = attributes[i+1];
326 size_t nameLen = strlen(name);
327 if (MEMEQ("name", name, nameLen)) {
328 SkAutoAsciiToLC tolc(value);
329 aliasName.set(tolc.lc());
330 } else if (MEMEQ("to", name, nameLen)) {
331 to.set(value);
332 } else if (MEMEQ("weight", name, nameLen)) {
333 if (!parse_non_negative_integer(value, &weight)) {
334 SK_FONTCONFIGPARSER_WARNING("'%s' is an invalid weight", value);
335 }
336 }
337 }
338
339 // Assumes that the named family is already declared
340 FontFamily* targetFamily = find_family(self, to);
341 if (!targetFamily) {
342 SK_FONTCONFIGPARSER_WARNING("'%s' alias target not found", to.c_str());
343 return;
344 }
345
346 if (weight) {
347 FontFamily* family = new FontFamily(targetFamily->fBasePath, self->fIsFallback);
348 family->fNames.push_back().set(aliasName);
349
350 for (int i = 0; i < targetFamily->fFonts.count(); i++) {
351 if (targetFamily->fFonts[i].fWeight == weight) {
352 family->fFonts.push_back(targetFamily->fFonts[i]);
353 }
354 }
355 *self->fFamilies.append() = family;
356 } else {
357 targetFamily->fNames.push_back().set(aliasName);
358 }
359 },
halcanary96fcdcc2015-08-27 07:41:13 -0700360 /*end*/nullptr,
361 /*tag*/nullptr,
362 /*chars*/nullptr,
bungeman10b063c2015-05-13 08:52:16 -0700363};
364
365static const TagHandler familySetHandler = {
366 /*start*/[](FamilyData* self, const char* tag, const char** attributes) { },
halcanary96fcdcc2015-08-27 07:41:13 -0700367 /*end*/nullptr,
bungeman10b063c2015-05-13 08:52:16 -0700368 /*tag*/[](FamilyData* self, const char* tag, const char** attributes) -> const TagHandler* {
369 size_t len = strlen(tag);
370 if (MEMEQ("family", tag, len)) {
371 return &familyHandler;
372 } else if (MEMEQ("alias", tag, len)) {
373 return &aliasHandler;
374 }
halcanary96fcdcc2015-08-27 07:41:13 -0700375 return nullptr;
bungeman10b063c2015-05-13 08:52:16 -0700376 },
halcanary96fcdcc2015-08-27 07:41:13 -0700377 /*chars*/nullptr,
bungeman10b063c2015-05-13 08:52:16 -0700378};
379
380} // lmpParser
381
382namespace jbParser {
383
384static const TagHandler fileHandler = {
385 /*start*/[](FamilyData* self, const char* tag, const char** attributes) {
386 // 'variant' ("elegant", "compact") [default "default"]
387 // 'lang' (string) [default ""]
388 // 'index' (non-negative integer) [default 0]
389 // The character data should be a filename.
390 FontFamily& currentFamily = *self->fCurrentFamily.get();
391 FontFileInfo& newFileInfo = currentFamily.fFonts.push_back();
392 if (attributes) {
393 for (size_t i = 0; ATTS_NON_NULL(attributes, i); i += 2) {
394 const char* name = attributes[i];
395 const char* value = attributes[i+1];
396 size_t nameLen = strlen(name);
397 size_t valueLen = strlen(value);
398 if (MEMEQ("variant", name, nameLen)) {
399 const FontVariant prevVariant = currentFamily.fVariant;
400 if (MEMEQ("elegant", value, valueLen)) {
401 currentFamily.fVariant = kElegant_FontVariant;
402 } else if (MEMEQ("compact", value, valueLen)) {
403 currentFamily.fVariant = kCompact_FontVariant;
404 }
405 if (currentFamily.fFonts.count() > 1 && currentFamily.fVariant != prevVariant) {
406 SK_FONTCONFIGPARSER_WARNING("'%s' unexpected variant found\n"
407 "Note: Every font file within a family must have identical variants.",
408 value);
409 }
410
411 } else if (MEMEQ("lang", name, nameLen)) {
Ben Wagneraee878d2017-08-10 13:49:41 -0400412 SkLanguage currentLanguage = SkLanguage(value, valueLen);
413 bool showWarning = false;
414 if (currentFamily.fLanguages.empty()) {
415 showWarning = (currentFamily.fFonts.count() > 1);
416 currentFamily.fLanguages.push_back(std::move(currentLanguage));
417 } else if (currentFamily.fLanguages[0] != currentLanguage) {
418 showWarning = true;
419 currentFamily.fLanguages[0] = std::move(currentLanguage);
420 }
421 if (showWarning) {
bungeman10b063c2015-05-13 08:52:16 -0700422 SK_FONTCONFIGPARSER_WARNING("'%s' unexpected language found\n"
423 "Note: Every font file within a family must have identical languages.",
424 value);
425 }
426
427 } else if (MEMEQ("index", name, nameLen)) {
428 if (!parse_non_negative_integer(value, &newFileInfo.fIndex)) {
429 SK_FONTCONFIGPARSER_WARNING("'%s' is an invalid index", value);
430 }
431 }
432 }
433 }
434 self->fCurrentFontInfo = &newFileInfo;
435 },
halcanary96fcdcc2015-08-27 07:41:13 -0700436 /*end*/nullptr,
437 /*tag*/nullptr,
bungeman10b063c2015-05-13 08:52:16 -0700438 /*chars*/[](void* data, const char* s, int len) {
439 FamilyData* self = static_cast<FamilyData*>(data);
440 self->fCurrentFontInfo->fFileName.append(s, len);
441 }
442};
443
444static const TagHandler fileSetHandler = {
halcanary96fcdcc2015-08-27 07:41:13 -0700445 /*start*/nullptr,
446 /*end*/nullptr,
bungeman10b063c2015-05-13 08:52:16 -0700447 /*tag*/[](FamilyData* self, const char* tag, const char** attributes) -> const TagHandler* {
448 size_t len = strlen(tag);
449 if (MEMEQ("file", tag, len)) {
450 return &fileHandler;
451 }
halcanary96fcdcc2015-08-27 07:41:13 -0700452 return nullptr;
bungeman10b063c2015-05-13 08:52:16 -0700453 },
halcanary96fcdcc2015-08-27 07:41:13 -0700454 /*chars*/nullptr,
bungeman10b063c2015-05-13 08:52:16 -0700455};
456
457static const TagHandler nameHandler = {
458 /*start*/[](FamilyData* self, const char* tag, const char** attributes) {
459 // The character data should be a name for the font.
460 self->fCurrentFamily->fNames.push_back();
461 },
halcanary96fcdcc2015-08-27 07:41:13 -0700462 /*end*/nullptr,
463 /*tag*/nullptr,
bungeman10b063c2015-05-13 08:52:16 -0700464 /*chars*/[](void* data, const char* s, int len) {
465 FamilyData* self = static_cast<FamilyData*>(data);
466 SkAutoAsciiToLC tolc(s, len);
467 self->fCurrentFamily->fNames.back().append(tolc.lc(), len);
468 }
469};
470
471static const TagHandler nameSetHandler = {
halcanary96fcdcc2015-08-27 07:41:13 -0700472 /*start*/nullptr,
473 /*end*/nullptr,
bungeman10b063c2015-05-13 08:52:16 -0700474 /*tag*/[](FamilyData* self, const char* tag, const char** attributes) -> const TagHandler* {
475 size_t len = strlen(tag);
476 if (MEMEQ("name", tag, len)) {
477 return &nameHandler;
478 }
halcanary96fcdcc2015-08-27 07:41:13 -0700479 return nullptr;
bungeman10b063c2015-05-13 08:52:16 -0700480 },
halcanary96fcdcc2015-08-27 07:41:13 -0700481 /*chars*/nullptr,
bungeman10b063c2015-05-13 08:52:16 -0700482};
483
484static const TagHandler familyHandler = {
485 /*start*/[](FamilyData* self, const char* tag, const char** attributes) {
486 self->fCurrentFamily.reset(new FontFamily(self->fBasePath, self->fIsFallback));
487 // 'order' (non-negative integer) [default -1]
488 for (size_t i = 0; ATTS_NON_NULL(attributes, i); i += 2) {
489 const char* value = attributes[i+1];
490 parse_non_negative_integer(value, &self->fCurrentFamily->fOrder);
491 }
492 },
493 /*end*/[](FamilyData* self, const char* tag) {
mtklein18300a32016-03-16 13:53:35 -0700494 *self->fFamilies.append() = self->fCurrentFamily.release();
bungeman10b063c2015-05-13 08:52:16 -0700495 },
496 /*tag*/[](FamilyData* self, const char* tag, const char** attributes) -> const TagHandler* {
497 size_t len = strlen(tag);
498 if (MEMEQ("nameset", tag, len)) {
499 return &nameSetHandler;
500 } else if (MEMEQ("fileset", tag, len)) {
501 return &fileSetHandler;
502 }
halcanary96fcdcc2015-08-27 07:41:13 -0700503 return nullptr;
bungeman10b063c2015-05-13 08:52:16 -0700504 },
halcanary96fcdcc2015-08-27 07:41:13 -0700505 /*chars*/nullptr,
bungeman10b063c2015-05-13 08:52:16 -0700506};
507
508static const TagHandler familySetHandler = {
halcanary96fcdcc2015-08-27 07:41:13 -0700509 /*start*/nullptr,
510 /*end*/nullptr,
bungeman10b063c2015-05-13 08:52:16 -0700511 /*tag*/[](FamilyData* self, const char* tag, const char** attributes) -> const TagHandler* {
512 size_t len = strlen(tag);
513 if (MEMEQ("family", tag, len)) {
514 return &familyHandler;
515 }
halcanary96fcdcc2015-08-27 07:41:13 -0700516 return nullptr;
bungeman10b063c2015-05-13 08:52:16 -0700517 },
halcanary96fcdcc2015-08-27 07:41:13 -0700518 /*chars*/nullptr,
bungeman10b063c2015-05-13 08:52:16 -0700519};
520
tomhudsonf79673b2014-08-05 06:36:11 -0700521} // namespace jbParser
522
bungeman10b063c2015-05-13 08:52:16 -0700523static const TagHandler topLevelHandler = {
halcanary96fcdcc2015-08-27 07:41:13 -0700524 /*start*/nullptr,
525 /*end*/nullptr,
bungeman10b063c2015-05-13 08:52:16 -0700526 /*tag*/[](FamilyData* self, const char* tag, const char** attributes) -> const TagHandler* {
527 size_t len = strlen(tag);
528 if (MEMEQ("familyset", tag, len)) {
529 // 'version' (non-negative integer) [default 0]
530 for (size_t i = 0; ATTS_NON_NULL(attributes, i); i += 2) {
531 const char* name = attributes[i];
532 size_t nameLen = strlen(name);
533 if (MEMEQ("version", name, nameLen)) {
534 const char* value = attributes[i+1];
535 if (parse_non_negative_integer(value, &self->fVersion)) {
536 if (self->fVersion >= 21) {
537 return &lmpParser::familySetHandler;
538 }
539 }
540 }
541 }
542 return &jbParser::familySetHandler;
543 }
halcanary96fcdcc2015-08-27 07:41:13 -0700544 return nullptr;
bungeman10b063c2015-05-13 08:52:16 -0700545 },
halcanary96fcdcc2015-08-27 07:41:13 -0700546 /*chars*/nullptr,
bungeman10b063c2015-05-13 08:52:16 -0700547};
548
549static void XMLCALL start_element_handler(void *data, const char *tag, const char **attributes) {
550 FamilyData* self = static_cast<FamilyData*>(data);
551
552 if (!self->fSkip) {
553 const TagHandler* parent = self->fHandler.top();
halcanary96fcdcc2015-08-27 07:41:13 -0700554 const TagHandler* child = parent->tag ? parent->tag(self, tag, attributes) : nullptr;
bungeman10b063c2015-05-13 08:52:16 -0700555 if (child) {
556 if (child->start) {
557 child->start(self, tag, attributes);
558 }
559 self->fHandler.push(child);
560 XML_SetCharacterDataHandler(self->fParser, child->chars);
561 } else {
562 SK_FONTCONFIGPARSER_WARNING("'%s' tag not recognized, skipping", tag);
halcanary96fcdcc2015-08-27 07:41:13 -0700563 XML_SetCharacterDataHandler(self->fParser, nullptr);
bungeman10b063c2015-05-13 08:52:16 -0700564 self->fSkip = self->fDepth;
565 }
566 }
567
568 ++self->fDepth;
569}
570
571static void XMLCALL end_element_handler(void* data, const char* tag) {
572 FamilyData* self = static_cast<FamilyData*>(data);
573 --self->fDepth;
574
575 if (!self->fSkip) {
576 const TagHandler* child = self->fHandler.top();
577 if (child->end) {
578 child->end(self, tag);
579 }
580 self->fHandler.pop();
581 const TagHandler* parent = self->fHandler.top();
582 XML_SetCharacterDataHandler(self->fParser, parent->chars);
583 }
584
585 if (self->fSkip == self->fDepth) {
586 self->fSkip = 0;
587 const TagHandler* parent = self->fHandler.top();
588 XML_SetCharacterDataHandler(self->fParser, parent->chars);
589 }
590}
591
bungeman7fa87cd2015-02-06 07:59:19 -0800592static void XMLCALL xml_entity_decl_handler(void *data,
593 const XML_Char *entityName,
594 int is_parameter_entity,
595 const XML_Char *value,
596 int value_length,
597 const XML_Char *base,
598 const XML_Char *systemId,
599 const XML_Char *publicId,
600 const XML_Char *notationName)
601{
602 FamilyData* self = static_cast<FamilyData*>(data);
bungemanf61475e2015-04-20 11:16:48 -0700603 SK_FONTCONFIGPARSER_WARNING("'%s' entity declaration found, stopping processing", entityName);
bungeman7fa87cd2015-02-06 07:59:19 -0800604 XML_StopParser(self->fParser, XML_FALSE);
605}
606
bungemaneb2be7f2015-02-10 07:51:12 -0800607static const XML_Memory_Handling_Suite sk_XML_alloc = {
608 sk_malloc_throw,
609 sk_realloc_throw,
610 sk_free
611};
612
djsollen@google.combfae9d32013-05-21 16:53:50 +0000613/**
614 * This function parses the given filename and stores the results in the given
bungemanefbad372015-02-02 11:19:44 -0800615 * families array. Returns the version of the file, negative if the file does not exist.
djsollen@google.combfae9d32013-05-21 16:53:50 +0000616 */
bungeman7fa87cd2015-02-06 07:59:19 -0800617static int parse_config_file(const char* filename, SkTDArray<FontFamily*>& families,
618 const SkString& basePath, bool isFallback)
619{
620 SkFILEStream file(filename);
djsollen@google.com50c95672013-08-28 12:29:45 +0000621
622 // Some of the files we attempt to parse (in particular, /vendor/etc/fallback_fonts.xml)
623 // are optional - failure here is okay because one of these optional files may not exist.
bungeman7fa87cd2015-02-06 07:59:19 -0800624 if (!file.isValid()) {
bungemanc5308542015-06-23 13:25:46 -0700625 SkDebugf(SK_FONTMGR_ANDROID_PARSER_PREFIX "'%s' could not be opened\n", filename);
bungemanefbad372015-02-02 11:19:44 -0800626 return -1;
djsollen@google.com50c95672013-08-28 12:29:45 +0000627 }
628
bungeman54e63082016-01-06 08:30:59 -0800629 SkAutoTCallVProc<skstd::remove_pointer_t<XML_Parser>, XML_ParserFree> parser(
halcanary96fcdcc2015-08-27 07:41:13 -0700630 XML_ParserCreate_MM(nullptr, &sk_XML_alloc, nullptr));
bungeman7fa87cd2015-02-06 07:59:19 -0800631 if (!parser) {
bungemanc5308542015-06-23 13:25:46 -0700632 SkDebugf(SK_FONTMGR_ANDROID_PARSER_PREFIX "could not create XML parser\n");
bungeman7fa87cd2015-02-06 07:59:19 -0800633 return -1;
634 }
635
bungeman10b063c2015-05-13 08:52:16 -0700636 FamilyData self(parser, families, basePath, isFallback, filename, &topLevelHandler);
bungeman7fa87cd2015-02-06 07:59:19 -0800637 XML_SetUserData(parser, &self);
638
639 // Disable entity processing, to inhibit internal entity expansion. See expat CVE-2013-0340
640 XML_SetEntityDeclHandler(parser, xml_entity_decl_handler);
641
tomhudsonf79673b2014-08-05 06:36:11 -0700642 // Start parsing oldschool; switch these in flight if we detect a newer version of the file.
bungeman10b063c2015-05-13 08:52:16 -0700643 XML_SetElementHandler(parser, start_element_handler, end_element_handler);
djsollen@google.com50c95672013-08-28 12:29:45 +0000644
bungemaneb2be7f2015-02-10 07:51:12 -0800645 // One would assume it would be faster to have a buffer on the stack and call XML_Parse.
646 // But XML_Parse will call XML_GetBuffer anyway and memmove the passed buffer into it.
647 // (Unless XML_CONTEXT_BYTES is undefined, but all users define it.)
bungeman9a0808f2015-02-13 08:55:16 -0800648 // In debug, buffer a small odd number of bytes to detect slicing in XML_CharacterDataHandler.
649 static const int bufferSize = 512 SkDEBUGCODE( - 507);
djsollen@google.combfae9d32013-05-21 16:53:50 +0000650 bool done = false;
651 while (!done) {
bungemaneb2be7f2015-02-10 07:51:12 -0800652 void* buffer = XML_GetBuffer(parser, bufferSize);
653 if (!buffer) {
bungemanc5308542015-06-23 13:25:46 -0700654 SkDebugf(SK_FONTMGR_ANDROID_PARSER_PREFIX "could not buffer enough to continue\n");
bungemaneb2be7f2015-02-10 07:51:12 -0800655 return -1;
656 }
657 size_t len = file.read(buffer, bufferSize);
bungeman7fa87cd2015-02-06 07:59:19 -0800658 done = file.isAtEnd();
bungemaneb2be7f2015-02-10 07:51:12 -0800659 XML_Status status = XML_ParseBuffer(parser, len, done);
bungeman7fa87cd2015-02-06 07:59:19 -0800660 if (XML_STATUS_ERROR == status) {
661 XML_Error error = XML_GetErrorCode(parser);
662 int line = XML_GetCurrentLineNumber(parser);
663 int column = XML_GetCurrentColumnNumber(parser);
bungeman7fa87cd2015-02-06 07:59:19 -0800664 const XML_LChar* errorString = XML_ErrorString(error);
bungemanc5308542015-06-23 13:25:46 -0700665 SkDebugf(SK_FONTMGR_ANDROID_PARSER_PREFIX "%s:%d:%d error %d: %s.\n",
bungemanf61475e2015-04-20 11:16:48 -0700666 filename, line, column, error, errorString);
bungeman7fa87cd2015-02-06 07:59:19 -0800667 return -1;
djsollen@google.combfae9d32013-05-21 16:53:50 +0000668 }
djsollen@google.combfae9d32013-05-21 16:53:50 +0000669 }
bungeman7fa87cd2015-02-06 07:59:19 -0800670 return self.fVersion;
djsollen@google.combfae9d32013-05-21 16:53:50 +0000671}
672
bungemanefbad372015-02-02 11:19:44 -0800673/** Returns the version of the system font file actually found, negative if none. */
bungeman7fa87cd2015-02-06 07:59:19 -0800674static int append_system_font_families(SkTDArray<FontFamily*>& fontFamilies,
675 const SkString& basePath)
676{
tomhudson7a4747f2014-08-13 11:06:43 -0700677 int initialCount = fontFamilies.count();
bungeman7fa87cd2015-02-06 07:59:19 -0800678 int version = parse_config_file(LMP_SYSTEM_FONTS_FILE, fontFamilies, basePath, false);
bungemanefbad372015-02-02 11:19:44 -0800679 if (version < 0 || fontFamilies.count() == initialCount) {
bungeman7fa87cd2015-02-06 07:59:19 -0800680 version = parse_config_file(OLD_SYSTEM_FONTS_FILE, fontFamilies, basePath, false);
tomhudson94fa4b92014-08-12 11:05:29 -0700681 }
bungemanefbad372015-02-02 11:19:44 -0800682 return version;
djsollen@google.combfae9d32013-05-21 16:53:50 +0000683}
684
djsollena6c27bc2014-08-06 11:01:58 -0700685/**
686 * In some versions of Android prior to Android 4.2 (JellyBean MR1 at API
687 * Level 17) the fallback fonts for certain locales were encoded in their own
688 * XML files with a suffix that identified the locale. We search the provided
689 * directory for those files,add all of their entries to the fallback chain, and
690 * include the locale as part of each entry.
691 */
bungemanefbad372015-02-02 11:19:44 -0800692static void append_fallback_font_families_for_locale(SkTDArray<FontFamily*>& fallbackFonts,
bungeman7fa87cd2015-02-06 07:59:19 -0800693 const char* dir,
694 const SkString& basePath)
bungemanefbad372015-02-02 11:19:44 -0800695{
bungeman22cffca2016-08-26 07:15:50 -0700696 SkOSFile::Iter iter(dir, nullptr);
697 SkString fileName;
698 while (iter.next(&fileName, false)) {
bungeman9a0808f2015-02-13 08:55:16 -0800699 // The size of the prefix and suffix.
700 static const size_t fixedLen = sizeof(LOCALE_FALLBACK_FONTS_PREFIX) - 1
701 + sizeof(LOCALE_FALLBACK_FONTS_SUFFIX) - 1;
702
703 // The size of the prefix, suffix, and a minimum valid language code
704 static const size_t minSize = fixedLen + 2;
djsollena6c27bc2014-08-06 11:01:58 -0700705
bungemanc3c69432015-02-11 07:18:51 -0800706 if (fileName.size() < minSize ||
707 !fileName.startsWith(LOCALE_FALLBACK_FONTS_PREFIX) ||
708 !fileName.endsWith(LOCALE_FALLBACK_FONTS_SUFFIX))
709 {
710 continue;
djsollena6c27bc2014-08-06 11:01:58 -0700711 }
bungemanc3c69432015-02-11 07:18:51 -0800712
bungemanc3c69432015-02-11 07:18:51 -0800713 SkString locale(fileName.c_str() + sizeof(LOCALE_FALLBACK_FONTS_PREFIX) - 1,
714 fileName.size() - fixedLen);
715
716 SkString absoluteFilename;
717 absoluteFilename.printf("%s/%s", dir, fileName.c_str());
718
719 SkTDArray<FontFamily*> langSpecificFonts;
720 parse_config_file(absoluteFilename.c_str(), langSpecificFonts, basePath, true);
721
722 for (int i = 0; i < langSpecificFonts.count(); ++i) {
723 FontFamily* family = langSpecificFonts[i];
Ben Wagneraee878d2017-08-10 13:49:41 -0400724 family->fLanguages.emplace_back(locale);
bungemanc3c69432015-02-11 07:18:51 -0800725 *fallbackFonts.append() = family;
726 }
djsollena6c27bc2014-08-06 11:01:58 -0700727 }
728}
729
bungeman7fa87cd2015-02-06 07:59:19 -0800730static void append_system_fallback_font_families(SkTDArray<FontFamily*>& fallbackFonts,
731 const SkString& basePath)
732{
733 parse_config_file(FALLBACK_FONTS_FILE, fallbackFonts, basePath, true);
734 append_fallback_font_families_for_locale(fallbackFonts,
735 LOCALE_FALLBACK_FONTS_SYSTEM_DIR,
736 basePath);
bungemanefbad372015-02-02 11:19:44 -0800737}
djsollen@google.combfae9d32013-05-21 16:53:50 +0000738
bungeman7fa87cd2015-02-06 07:59:19 -0800739static void mixin_vendor_fallback_font_families(SkTDArray<FontFamily*>& fallbackFonts,
740 const SkString& basePath)
741{
bungemanefbad372015-02-02 11:19:44 -0800742 SkTDArray<FontFamily*> vendorFonts;
bungeman7fa87cd2015-02-06 07:59:19 -0800743 parse_config_file(VENDOR_FONTS_FILE, vendorFonts, basePath, true);
744 append_fallback_font_families_for_locale(vendorFonts,
745 LOCALE_FALLBACK_FONTS_VENDOR_DIR,
746 basePath);
djsollena6c27bc2014-08-06 11:01:58 -0700747
djsollen@google.combfae9d32013-05-21 16:53:50 +0000748 // This loop inserts the vendor fallback fonts in the correct order in the
749 // overall fallbacks list.
750 int currentOrder = -1;
751 for (int i = 0; i < vendorFonts.count(); ++i) {
752 FontFamily* family = vendorFonts[i];
tomhudsond3ddea22014-08-11 11:28:00 -0700753 int order = family->fOrder;
djsollen@google.combfae9d32013-05-21 16:53:50 +0000754 if (order < 0) {
755 if (currentOrder < 0) {
756 // Default case - just add it to the end of the fallback list
757 *fallbackFonts.append() = family;
758 } else {
759 // no order specified on this font, but we're incrementing the order
760 // based on an earlier order insertion request
761 *fallbackFonts.insert(currentOrder++) = family;
762 }
763 } else {
764 // Add the font into the fallback list in the specified order. Set
765 // currentOrder for correct placement of other fonts in the vendor list.
766 *fallbackFonts.insert(order) = family;
767 currentOrder = order + 1;
768 }
769 }
770}
771
bungemanc5308542015-06-23 13:25:46 -0700772void SkFontMgr_Android_Parser::GetSystemFontFamilies(SkTDArray<FontFamily*>& fontFamilies) {
bungemanefbad372015-02-02 11:19:44 -0800773 // Version 21 of the system font configuration does not need any fallback configuration files.
bungeman7fa87cd2015-02-06 07:59:19 -0800774 SkString basePath(getenv("ANDROID_ROOT"));
775 basePath.append(SK_FONT_FILE_PREFIX, sizeof(SK_FONT_FILE_PREFIX) - 1);
776
777 if (append_system_font_families(fontFamilies, basePath) >= 21) {
bungemanefbad372015-02-02 11:19:44 -0800778 return;
779 }
djsollen@google.combfae9d32013-05-21 16:53:50 +0000780
781 // Append all the fallback fonts to system fonts
782 SkTDArray<FontFamily*> fallbackFonts;
bungeman7fa87cd2015-02-06 07:59:19 -0800783 append_system_fallback_font_families(fallbackFonts, basePath);
784 mixin_vendor_fallback_font_families(fallbackFonts, basePath);
785 fontFamilies.append(fallbackFonts.count(), fallbackFonts.begin());
djsollen@google.combfae9d32013-05-21 16:53:50 +0000786}
787
bungemanc5308542015-06-23 13:25:46 -0700788void SkFontMgr_Android_Parser::GetCustomFontFamilies(SkTDArray<FontFamily*>& fontFamilies,
789 const SkString& basePath,
790 const char* fontsXml,
791 const char* fallbackFontsXml,
792 const char* langFallbackFontsDir)
bungeman7fa87cd2015-02-06 07:59:19 -0800793{
794 if (fontsXml) {
795 parse_config_file(fontsXml, fontFamilies, basePath, false);
tomhudsonf79673b2014-08-05 06:36:11 -0700796 }
bungeman7fa87cd2015-02-06 07:59:19 -0800797 if (fallbackFontsXml) {
798 parse_config_file(fallbackFontsXml, fontFamilies, basePath, true);
djsollen@google.combfae9d32013-05-21 16:53:50 +0000799 }
bungemanc3c69432015-02-11 07:18:51 -0800800 if (langFallbackFontsDir) {
801 append_fallback_font_families_for_locale(fontFamilies,
802 langFallbackFontsDir,
803 basePath);
804 }
djsollen@google.combfae9d32013-05-21 16:53:50 +0000805}
djsollen3b625542014-08-14 06:29:02 -0700806
807SkLanguage SkLanguage::getParent() const {
808 SkASSERT(!fTag.isEmpty());
809 const char* tag = fTag.c_str();
810
811 // strip off the rightmost "-.*"
812 const char* parentTagEnd = strrchr(tag, '-');
halcanary96fcdcc2015-08-27 07:41:13 -0700813 if (parentTagEnd == nullptr) {
djsollen3b625542014-08-14 06:29:02 -0700814 return SkLanguage();
815 }
816 size_t parentTagLen = parentTagEnd - tag;
817 return SkLanguage(tag, parentTagLen);
818}