blob: 29ea52523dc25d4e9d2ef6a434369409c7a450dc [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
bungemanc5308542015-06-23 13:25:46 -070010#include "SkFontMgr_android_parser.h"
bungeman7fa87cd2015-02-06 07:59:19 -080011#include "SkStream.h"
djsollen@google.combfae9d32013-05-21 16:53:50 +000012#include "SkTDArray.h"
bungeman8d84c992014-07-24 08:05:09 -070013#include "SkTSearch.h"
bungemanf20488b2015-07-29 11:49:40 -070014#include "SkTemplates.h"
bungeman54e63082016-01-06 08:30:59 -080015#include "SkTLogic.h"
djsollen@google.combfae9d32013-05-21 16:53:50 +000016
djsollena6c27bc2014-08-06 11:01:58 -070017#include <dirent.h>
bungemanf20488b2015-07-29 11:49:40 -070018#include <expat.h>
djsollen@google.combfae9d32013-05-21 16:53:50 +000019
bungeman7fa87cd2015-02-06 07:59:19 -080020#include <stdlib.h>
bungemanf20488b2015-07-29 11:49:40 -070021#include <string.h>
bungeman8d84c992014-07-24 08:05:09 -070022
tomhudson94fa4b92014-08-12 11:05:29 -070023#define LMP_SYSTEM_FONTS_FILE "/system/etc/fonts.xml"
24#define OLD_SYSTEM_FONTS_FILE "/system/etc/system_fonts.xml"
djsollen@google.combfae9d32013-05-21 16:53:50 +000025#define FALLBACK_FONTS_FILE "/system/etc/fallback_fonts.xml"
26#define VENDOR_FONTS_FILE "/vendor/etc/fallback_fonts.xml"
27
djsollena6c27bc2014-08-06 11:01:58 -070028#define LOCALE_FALLBACK_FONTS_SYSTEM_DIR "/system/etc"
29#define LOCALE_FALLBACK_FONTS_VENDOR_DIR "/vendor/etc"
30#define LOCALE_FALLBACK_FONTS_PREFIX "fallback_fonts-"
31#define LOCALE_FALLBACK_FONTS_SUFFIX ".xml"
32
bungeman7fa87cd2015-02-06 07:59:19 -080033#ifndef SK_FONT_FILE_PREFIX
34# define SK_FONT_FILE_PREFIX "/fonts/"
35#endif
36
tomhudsonf79673b2014-08-05 06:36:11 -070037/**
bungeman10b063c2015-05-13 08:52:16 -070038 * This file contains TWO 'familyset' handlers:
39 * One for JB and earlier which works with
40 * /system/etc/system_fonts.xml
41 * /system/etc/fallback_fonts.xml
42 * /vendor/etc/fallback_fonts.xml
43 * /system/etc/fallback_fonts-XX.xml
44 * /vendor/etc/fallback_fonts-XX.xml
45 * and the other for LMP and later which works with
46 * /system/etc/fonts.xml
47 *
48 * If the 'familyset' 'version' attribute is 21 or higher the LMP parser is used, otherwise the JB.
tomhudsonf79673b2014-08-05 06:36:11 -070049 */
50
bungeman10b063c2015-05-13 08:52:16 -070051struct FamilyData;
52
53struct TagHandler {
54 /** Called at the start tag.
55 * Called immediately after the parent tag retuns this handler from a call to 'tag'.
56 * Allows setting up for handling the tag content and processing attributes.
halcanary96fcdcc2015-08-27 07:41:13 -070057 * If nullptr, will not be called.
bungeman10b063c2015-05-13 08:52:16 -070058 */
59 void (*start)(FamilyData* data, const char* tag, const char** attributes);
60
61 /** Called at the end tag.
62 * Allows post-processing of any accumulated information.
63 * This will be the last call made in relation to the current tag.
halcanary96fcdcc2015-08-27 07:41:13 -070064 * If nullptr, will not be called.
bungeman10b063c2015-05-13 08:52:16 -070065 */
66 void (*end)(FamilyData* data, const char* tag);
67
68 /** Called when a nested tag is encountered.
69 * This is responsible for determining how to handle the tag.
halcanary96fcdcc2015-08-27 07:41:13 -070070 * If the tag is not recognized, return nullptr to skip the tag.
71 * If nullptr, all nested tags will be skipped.
bungeman10b063c2015-05-13 08:52:16 -070072 */
73 const TagHandler* (*tag)(FamilyData* data, const char* tag, const char** attributes);
74
75 /** The character handler for this tag.
76 * This is only active for character data contained directly in this tag (not sub-tags).
77 * The first parameter will be castable to a FamilyData*.
halcanary96fcdcc2015-08-27 07:41:13 -070078 * If nullptr, any character data in this tag will be ignored.
bungeman10b063c2015-05-13 08:52:16 -070079 */
80 XML_CharacterDataHandler chars;
bungeman7fa87cd2015-02-06 07:59:19 -080081};
djsollen@google.combfae9d32013-05-21 16:53:50 +000082
bungeman10b063c2015-05-13 08:52:16 -070083/** Represents the current parsing state. */
djsollen@google.combfae9d32013-05-21 16:53:50 +000084struct FamilyData {
bungeman7fa87cd2015-02-06 07:59:19 -080085 FamilyData(XML_Parser parser, SkTDArray<FontFamily*>& families,
bungeman10b063c2015-05-13 08:52:16 -070086 const SkString& basePath, bool isFallback, const char* filename,
87 const TagHandler* topLevelHandler)
bungemanb6bed172015-01-30 15:35:09 -080088 : fParser(parser)
89 , fFamilies(families)
halcanary96fcdcc2015-08-27 07:41:13 -070090 , fCurrentFamily(nullptr)
91 , fCurrentFontInfo(nullptr)
bungemanefbad372015-02-02 11:19:44 -080092 , fVersion(0)
bungeman7fa87cd2015-02-06 07:59:19 -080093 , fBasePath(basePath)
94 , fIsFallback(isFallback)
bungemanf61475e2015-04-20 11:16:48 -070095 , fFilename(filename)
bungeman10b063c2015-05-13 08:52:16 -070096 , fDepth(1)
97 , fSkip(0)
98 , fHandler(&topLevelHandler, 1)
bungeman7fa87cd2015-02-06 07:59:19 -080099 { };
djsollen@google.combfae9d32013-05-21 16:53:50 +0000100
bungemanb6bed172015-01-30 15:35:09 -0800101 XML_Parser fParser; // The expat parser doing the work, owned by caller
102 SkTDArray<FontFamily*>& fFamilies; // The array to append families, owned by caller
103 SkAutoTDelete<FontFamily> fCurrentFamily; // The family being created, owned by this
bungeman7fa87cd2015-02-06 07:59:19 -0800104 FontFileInfo* fCurrentFontInfo; // The fontInfo being created, owned by fCurrentFamily
bungemanefbad372015-02-02 11:19:44 -0800105 int fVersion; // The version of the file parsed.
bungeman7fa87cd2015-02-06 07:59:19 -0800106 const SkString& fBasePath; // The current base path.
107 const bool fIsFallback; // Indicates the file being parsed is a fallback file
bungemanf61475e2015-04-20 11:16:48 -0700108 const char* fFilename; // The name of the file currently being parsed.
bungeman10b063c2015-05-13 08:52:16 -0700109
110 int fDepth; // The current element depth of the parse.
111 int fSkip; // The depth to stop skipping, 0 if not skipping.
112 SkTDArray<const TagHandler*> fHandler; // The stack of current tag handlers.
djsollen@google.combfae9d32013-05-21 16:53:50 +0000113};
114
bungeman7fa87cd2015-02-06 07:59:19 -0800115static bool memeq(const char* s1, const char* s2, size_t n1, size_t n2) {
116 return n1 == n2 && 0 == memcmp(s1, s2, n1);
117}
118#define MEMEQ(c, s, n) memeq(c, s, sizeof(c) - 1, n)
119
halcanary96fcdcc2015-08-27 07:41:13 -0700120#define ATTS_NON_NULL(a, i) (a[i] != nullptr && a[i+1] != nullptr)
bungeman7fa87cd2015-02-06 07:59:19 -0800121
bungemanc5308542015-06-23 13:25:46 -0700122#define SK_FONTMGR_ANDROID_PARSER_PREFIX "[SkFontMgr Android Parser] "
bungemanf61475e2015-04-20 11:16:48 -0700123
124#define SK_FONTCONFIGPARSER_WARNING(message, ...) SkDebugf( \
bungemanc5308542015-06-23 13:25:46 -0700125 SK_FONTMGR_ANDROID_PARSER_PREFIX "%s:%d:%d: warning: " message "\n", \
bungemanf61475e2015-04-20 11:16:48 -0700126 self->fFilename, \
127 XML_GetCurrentLineNumber(self->fParser), \
128 XML_GetCurrentColumnNumber(self->fParser), \
129 ##__VA_ARGS__);
130
bungemanc0727d12015-05-08 08:31:54 -0700131static bool is_whitespace(char c) {
132 return c == ' ' || c == '\n'|| c == '\r' || c == '\t';
133}
134
135static void trim_string(SkString* s) {
136 char* str = s->writable_str();
137 const char* start = str; // start is inclusive
138 const char* end = start + s->size(); // end is exclusive
139 while (is_whitespace(*start)) { ++start; }
140 if (start != end) {
141 --end; // make end inclusive
142 while (is_whitespace(*end)) { --end; }
143 ++end; // make end exclusive
144 }
145 size_t len = end - start;
146 memmove(str, start, len);
147 s->resize(len);
148}
149
bungeman10b063c2015-05-13 08:52:16 -0700150namespace lmpParser {
tomhudsonf79673b2014-08-05 06:36:11 -0700151
bungeman41868fe2015-05-20 09:21:04 -0700152static const TagHandler axisHandler = {
153 /*start*/[](FamilyData* self, const char* tag, const char** attributes) {
154 FontFileInfo& file = *self->fCurrentFontInfo;
155 FontFileInfo::Axis& axis = file.fAxes.push_back();
156 for (size_t i = 0; ATTS_NON_NULL(attributes, i); i += 2) {
157 const char* name = attributes[i];
158 const char* value = attributes[i+1];
159 size_t nameLen = strlen(name);
160 if (MEMEQ("tag", name, nameLen)) {
161 size_t valueLen = strlen(value);
162 if (valueLen == 4) {
163 SkFourByteTag tag = SkSetFourByteTag(value[0], value[1], value[2], value[3]);
164 for (int j = 0; j < file.fAxes.count() - 1; ++j) {
165 if (file.fAxes[j].fTag == tag) {
166 SK_FONTCONFIGPARSER_WARNING("'%c%c%c%c' axis specified more than once",
167 (tag >> 24) & 0xFF,
168 (tag >> 16) & 0xFF,
169 (tag >> 8) & 0xFF,
170 (tag ) & 0xFF);
171 }
172 }
173 axis.fTag = SkSetFourByteTag(value[0], value[1], value[2], value[3]);
174 } else {
175 SK_FONTCONFIGPARSER_WARNING("'%s' is an invalid axis tag", value);
176 }
177 } else if (MEMEQ("stylevalue", name, nameLen)) {
178 if (!parse_fixed<16>(value, &axis.fValue)) {
179 SK_FONTCONFIGPARSER_WARNING("'%s' is an invalid axis stylevalue", value);
180 }
181 }
182 }
183 },
halcanary96fcdcc2015-08-27 07:41:13 -0700184 /*end*/nullptr,
185 /*tag*/nullptr,
186 /*chars*/nullptr,
bungeman41868fe2015-05-20 09:21:04 -0700187};
188
bungeman10b063c2015-05-13 08:52:16 -0700189static const TagHandler fontHandler = {
190 /*start*/[](FamilyData* self, const char* tag, const char** attributes) {
191 // 'weight' (non-negative integer) [default 0]
192 // 'style' ("normal", "italic") [default "auto"]
193 // 'index' (non-negative integer) [default 0]
194 // The character data should be a filename.
195 FontFileInfo& file = self->fCurrentFamily->fFonts.push_back();
196 self->fCurrentFontInfo = &file;
197 for (size_t i = 0; ATTS_NON_NULL(attributes, i); i += 2) {
198 const char* name = attributes[i];
199 const char* value = attributes[i+1];
200 size_t nameLen = strlen(name);
201 if (MEMEQ("weight", name, nameLen)) {
202 if (!parse_non_negative_integer(value, &file.fWeight)) {
203 SK_FONTCONFIGPARSER_WARNING("'%s' is an invalid weight", value);
204 }
205 } else if (MEMEQ("style", name, nameLen)) {
206 size_t valueLen = strlen(value);
207 if (MEMEQ("normal", value, valueLen)) {
208 file.fStyle = FontFileInfo::Style::kNormal;
209 } else if (MEMEQ("italic", value, valueLen)) {
210 file.fStyle = FontFileInfo::Style::kItalic;
211 }
212 } else if (MEMEQ("index", name, nameLen)) {
213 if (!parse_non_negative_integer(value, &file.fIndex)) {
214 SK_FONTCONFIGPARSER_WARNING("'%s' is an invalid index", value);
215 }
djsollen@google.combfae9d32013-05-21 16:53:50 +0000216 }
djsollen@google.combfae9d32013-05-21 16:53:50 +0000217 }
bungeman10b063c2015-05-13 08:52:16 -0700218 },
219 /*end*/[](FamilyData* self, const char* tag) {
220 trim_string(&self->fCurrentFontInfo->fFileName);
221 },
bungeman41868fe2015-05-20 09:21:04 -0700222 /*tag*/[](FamilyData* self, const char* tag, const char** attributes) -> const TagHandler* {
223 size_t len = strlen(tag);
224 if (MEMEQ("axis", tag, len)) {
225 return &axisHandler;
226 }
halcanary96fcdcc2015-08-27 07:41:13 -0700227 return nullptr;
bungeman41868fe2015-05-20 09:21:04 -0700228 },
bungeman10b063c2015-05-13 08:52:16 -0700229 /*chars*/[](void* data, const char* s, int len) {
230 FamilyData* self = static_cast<FamilyData*>(data);
231 self->fCurrentFontInfo->fFileName.append(s, len);
djsollen@google.combfae9d32013-05-21 16:53:50 +0000232 }
bungeman10b063c2015-05-13 08:52:16 -0700233};
djsollen@google.combfae9d32013-05-21 16:53:50 +0000234
bungeman10b063c2015-05-13 08:52:16 -0700235static const TagHandler familyHandler = {
236 /*start*/[](FamilyData* self, const char* tag, const char** attributes) {
237 // 'name' (string) [optional]
238 // 'lang' (string) [default ""]
239 // 'variant' ("elegant", "compact") [default "default"]
240 // If there is no name, this is a fallback only font.
241 FontFamily* family = new FontFamily(self->fBasePath, true);
242 self->fCurrentFamily.reset(family);
bungeman7fa87cd2015-02-06 07:59:19 -0800243 for (size_t i = 0; ATTS_NON_NULL(attributes, i); i += 2) {
bungemanf61475e2015-04-20 11:16:48 -0700244 const char* name = attributes[i];
245 const char* value = attributes[i+1];
246 size_t nameLen = strlen(name);
247 size_t valueLen = strlen(value);
bungeman10b063c2015-05-13 08:52:16 -0700248 if (MEMEQ("name", name, nameLen)) {
249 SkAutoAsciiToLC tolc(value);
250 family->fNames.push_back().set(tolc.lc());
251 family->fIsFallbackFont = false;
bungemanf61475e2015-04-20 11:16:48 -0700252 } else if (MEMEQ("lang", name, nameLen)) {
bungeman10b063c2015-05-13 08:52:16 -0700253 family->fLanguage = SkLanguage(value, valueLen);
254 } else if (MEMEQ("variant", name, nameLen)) {
255 if (MEMEQ("elegant", value, valueLen)) {
256 family->fVariant = kElegant_FontVariant;
257 } else if (MEMEQ("compact", value, valueLen)) {
258 family->fVariant = kCompact_FontVariant;
bungeman8d84c992014-07-24 08:05:09 -0700259 }
djsollen@google.combfae9d32013-05-21 16:53:50 +0000260 }
djsollen@google.combfae9d32013-05-21 16:53:50 +0000261 }
bungeman10b063c2015-05-13 08:52:16 -0700262 },
263 /*end*/[](FamilyData* self, const char* tag) {
bungeman7fa87cd2015-02-06 07:59:19 -0800264 *self->fFamilies.append() = self->fCurrentFamily.detach();
bungeman10b063c2015-05-13 08:52:16 -0700265 },
266 /*tag*/[](FamilyData* self, const char* tag, const char** attributes) -> const TagHandler* {
267 size_t len = strlen(tag);
268 if (MEMEQ("font", tag, len)) {
269 return &fontHandler;
270 }
halcanary96fcdcc2015-08-27 07:41:13 -0700271 return nullptr;
bungeman10b063c2015-05-13 08:52:16 -0700272 },
halcanary96fcdcc2015-08-27 07:41:13 -0700273 /*chars*/nullptr,
bungeman10b063c2015-05-13 08:52:16 -0700274};
275
276static FontFamily* find_family(FamilyData* self, const SkString& familyName) {
277 for (int i = 0; i < self->fFamilies.count(); i++) {
278 FontFamily* candidate = self->fFamilies[i];
279 for (int j = 0; j < candidate->fNames.count(); j++) {
280 if (candidate->fNames[j] == familyName) {
281 return candidate;
282 }
283 }
djsollen@google.combfae9d32013-05-21 16:53:50 +0000284 }
halcanary96fcdcc2015-08-27 07:41:13 -0700285 return nullptr;
djsollen@google.combfae9d32013-05-21 16:53:50 +0000286}
287
bungeman10b063c2015-05-13 08:52:16 -0700288static const TagHandler aliasHandler = {
289 /*start*/[](FamilyData* self, const char* tag, const char** attributes) {
290 // 'name' (string) introduces a new family name.
291 // 'to' (string) specifies which (previous) family to alias
292 // 'weight' (non-negative integer) [optional]
293 // If it *does not* have a weight, 'name' is an alias for the entire 'to' family.
294 // If it *does* have a weight, 'name' is a new family consisting of
295 // the font(s) with 'weight' from the 'to' family.
296
297 SkString aliasName;
298 SkString to;
299 int weight = 0;
300 for (size_t i = 0; ATTS_NON_NULL(attributes, i); i += 2) {
301 const char* name = attributes[i];
302 const char* value = attributes[i+1];
303 size_t nameLen = strlen(name);
304 if (MEMEQ("name", name, nameLen)) {
305 SkAutoAsciiToLC tolc(value);
306 aliasName.set(tolc.lc());
307 } else if (MEMEQ("to", name, nameLen)) {
308 to.set(value);
309 } else if (MEMEQ("weight", name, nameLen)) {
310 if (!parse_non_negative_integer(value, &weight)) {
311 SK_FONTCONFIGPARSER_WARNING("'%s' is an invalid weight", value);
312 }
313 }
314 }
315
316 // Assumes that the named family is already declared
317 FontFamily* targetFamily = find_family(self, to);
318 if (!targetFamily) {
319 SK_FONTCONFIGPARSER_WARNING("'%s' alias target not found", to.c_str());
320 return;
321 }
322
323 if (weight) {
324 FontFamily* family = new FontFamily(targetFamily->fBasePath, self->fIsFallback);
325 family->fNames.push_back().set(aliasName);
326
327 for (int i = 0; i < targetFamily->fFonts.count(); i++) {
328 if (targetFamily->fFonts[i].fWeight == weight) {
329 family->fFonts.push_back(targetFamily->fFonts[i]);
330 }
331 }
332 *self->fFamilies.append() = family;
333 } else {
334 targetFamily->fNames.push_back().set(aliasName);
335 }
336 },
halcanary96fcdcc2015-08-27 07:41:13 -0700337 /*end*/nullptr,
338 /*tag*/nullptr,
339 /*chars*/nullptr,
bungeman10b063c2015-05-13 08:52:16 -0700340};
341
342static const TagHandler familySetHandler = {
343 /*start*/[](FamilyData* self, const char* tag, const char** attributes) { },
halcanary96fcdcc2015-08-27 07:41:13 -0700344 /*end*/nullptr,
bungeman10b063c2015-05-13 08:52:16 -0700345 /*tag*/[](FamilyData* self, const char* tag, const char** attributes) -> const TagHandler* {
346 size_t len = strlen(tag);
347 if (MEMEQ("family", tag, len)) {
348 return &familyHandler;
349 } else if (MEMEQ("alias", tag, len)) {
350 return &aliasHandler;
351 }
halcanary96fcdcc2015-08-27 07:41:13 -0700352 return nullptr;
bungeman10b063c2015-05-13 08:52:16 -0700353 },
halcanary96fcdcc2015-08-27 07:41:13 -0700354 /*chars*/nullptr,
bungeman10b063c2015-05-13 08:52:16 -0700355};
356
357} // lmpParser
358
359namespace jbParser {
360
361static const TagHandler fileHandler = {
362 /*start*/[](FamilyData* self, const char* tag, const char** attributes) {
363 // 'variant' ("elegant", "compact") [default "default"]
364 // 'lang' (string) [default ""]
365 // 'index' (non-negative integer) [default 0]
366 // The character data should be a filename.
367 FontFamily& currentFamily = *self->fCurrentFamily.get();
368 FontFileInfo& newFileInfo = currentFamily.fFonts.push_back();
369 if (attributes) {
370 for (size_t i = 0; ATTS_NON_NULL(attributes, i); i += 2) {
371 const char* name = attributes[i];
372 const char* value = attributes[i+1];
373 size_t nameLen = strlen(name);
374 size_t valueLen = strlen(value);
375 if (MEMEQ("variant", name, nameLen)) {
376 const FontVariant prevVariant = currentFamily.fVariant;
377 if (MEMEQ("elegant", value, valueLen)) {
378 currentFamily.fVariant = kElegant_FontVariant;
379 } else if (MEMEQ("compact", value, valueLen)) {
380 currentFamily.fVariant = kCompact_FontVariant;
381 }
382 if (currentFamily.fFonts.count() > 1 && currentFamily.fVariant != prevVariant) {
383 SK_FONTCONFIGPARSER_WARNING("'%s' unexpected variant found\n"
384 "Note: Every font file within a family must have identical variants.",
385 value);
386 }
387
388 } else if (MEMEQ("lang", name, nameLen)) {
389 SkLanguage prevLang = currentFamily.fLanguage;
390 currentFamily.fLanguage = SkLanguage(value, valueLen);
391 if (currentFamily.fFonts.count() > 1 && currentFamily.fLanguage != prevLang) {
392 SK_FONTCONFIGPARSER_WARNING("'%s' unexpected language found\n"
393 "Note: Every font file within a family must have identical languages.",
394 value);
395 }
396
397 } else if (MEMEQ("index", name, nameLen)) {
398 if (!parse_non_negative_integer(value, &newFileInfo.fIndex)) {
399 SK_FONTCONFIGPARSER_WARNING("'%s' is an invalid index", value);
400 }
401 }
402 }
403 }
404 self->fCurrentFontInfo = &newFileInfo;
405 },
halcanary96fcdcc2015-08-27 07:41:13 -0700406 /*end*/nullptr,
407 /*tag*/nullptr,
bungeman10b063c2015-05-13 08:52:16 -0700408 /*chars*/[](void* data, const char* s, int len) {
409 FamilyData* self = static_cast<FamilyData*>(data);
410 self->fCurrentFontInfo->fFileName.append(s, len);
411 }
412};
413
414static const TagHandler fileSetHandler = {
halcanary96fcdcc2015-08-27 07:41:13 -0700415 /*start*/nullptr,
416 /*end*/nullptr,
bungeman10b063c2015-05-13 08:52:16 -0700417 /*tag*/[](FamilyData* self, const char* tag, const char** attributes) -> const TagHandler* {
418 size_t len = strlen(tag);
419 if (MEMEQ("file", tag, len)) {
420 return &fileHandler;
421 }
halcanary96fcdcc2015-08-27 07:41:13 -0700422 return nullptr;
bungeman10b063c2015-05-13 08:52:16 -0700423 },
halcanary96fcdcc2015-08-27 07:41:13 -0700424 /*chars*/nullptr,
bungeman10b063c2015-05-13 08:52:16 -0700425};
426
427static const TagHandler nameHandler = {
428 /*start*/[](FamilyData* self, const char* tag, const char** attributes) {
429 // The character data should be a name for the font.
430 self->fCurrentFamily->fNames.push_back();
431 },
halcanary96fcdcc2015-08-27 07:41:13 -0700432 /*end*/nullptr,
433 /*tag*/nullptr,
bungeman10b063c2015-05-13 08:52:16 -0700434 /*chars*/[](void* data, const char* s, int len) {
435 FamilyData* self = static_cast<FamilyData*>(data);
436 SkAutoAsciiToLC tolc(s, len);
437 self->fCurrentFamily->fNames.back().append(tolc.lc(), len);
438 }
439};
440
441static const TagHandler nameSetHandler = {
halcanary96fcdcc2015-08-27 07:41:13 -0700442 /*start*/nullptr,
443 /*end*/nullptr,
bungeman10b063c2015-05-13 08:52:16 -0700444 /*tag*/[](FamilyData* self, const char* tag, const char** attributes) -> const TagHandler* {
445 size_t len = strlen(tag);
446 if (MEMEQ("name", tag, len)) {
447 return &nameHandler;
448 }
halcanary96fcdcc2015-08-27 07:41:13 -0700449 return nullptr;
bungeman10b063c2015-05-13 08:52:16 -0700450 },
halcanary96fcdcc2015-08-27 07:41:13 -0700451 /*chars*/nullptr,
bungeman10b063c2015-05-13 08:52:16 -0700452};
453
454static const TagHandler familyHandler = {
455 /*start*/[](FamilyData* self, const char* tag, const char** attributes) {
456 self->fCurrentFamily.reset(new FontFamily(self->fBasePath, self->fIsFallback));
457 // 'order' (non-negative integer) [default -1]
458 for (size_t i = 0; ATTS_NON_NULL(attributes, i); i += 2) {
459 const char* value = attributes[i+1];
460 parse_non_negative_integer(value, &self->fCurrentFamily->fOrder);
461 }
462 },
463 /*end*/[](FamilyData* self, const char* tag) {
464 *self->fFamilies.append() = self->fCurrentFamily.detach();
465 },
466 /*tag*/[](FamilyData* self, const char* tag, const char** attributes) -> const TagHandler* {
467 size_t len = strlen(tag);
468 if (MEMEQ("nameset", tag, len)) {
469 return &nameSetHandler;
470 } else if (MEMEQ("fileset", tag, len)) {
471 return &fileSetHandler;
472 }
halcanary96fcdcc2015-08-27 07:41:13 -0700473 return nullptr;
bungeman10b063c2015-05-13 08:52:16 -0700474 },
halcanary96fcdcc2015-08-27 07:41:13 -0700475 /*chars*/nullptr,
bungeman10b063c2015-05-13 08:52:16 -0700476};
477
478static const TagHandler familySetHandler = {
halcanary96fcdcc2015-08-27 07:41:13 -0700479 /*start*/nullptr,
480 /*end*/nullptr,
bungeman10b063c2015-05-13 08:52:16 -0700481 /*tag*/[](FamilyData* self, const char* tag, const char** attributes) -> const TagHandler* {
482 size_t len = strlen(tag);
483 if (MEMEQ("family", tag, len)) {
484 return &familyHandler;
485 }
halcanary96fcdcc2015-08-27 07:41:13 -0700486 return nullptr;
bungeman10b063c2015-05-13 08:52:16 -0700487 },
halcanary96fcdcc2015-08-27 07:41:13 -0700488 /*chars*/nullptr,
bungeman10b063c2015-05-13 08:52:16 -0700489};
490
tomhudsonf79673b2014-08-05 06:36:11 -0700491} // namespace jbParser
492
bungeman10b063c2015-05-13 08:52:16 -0700493static const TagHandler topLevelHandler = {
halcanary96fcdcc2015-08-27 07:41:13 -0700494 /*start*/nullptr,
495 /*end*/nullptr,
bungeman10b063c2015-05-13 08:52:16 -0700496 /*tag*/[](FamilyData* self, const char* tag, const char** attributes) -> const TagHandler* {
497 size_t len = strlen(tag);
498 if (MEMEQ("familyset", tag, len)) {
499 // 'version' (non-negative integer) [default 0]
500 for (size_t i = 0; ATTS_NON_NULL(attributes, i); i += 2) {
501 const char* name = attributes[i];
502 size_t nameLen = strlen(name);
503 if (MEMEQ("version", name, nameLen)) {
504 const char* value = attributes[i+1];
505 if (parse_non_negative_integer(value, &self->fVersion)) {
506 if (self->fVersion >= 21) {
507 return &lmpParser::familySetHandler;
508 }
509 }
510 }
511 }
512 return &jbParser::familySetHandler;
513 }
halcanary96fcdcc2015-08-27 07:41:13 -0700514 return nullptr;
bungeman10b063c2015-05-13 08:52:16 -0700515 },
halcanary96fcdcc2015-08-27 07:41:13 -0700516 /*chars*/nullptr,
bungeman10b063c2015-05-13 08:52:16 -0700517};
518
519static void XMLCALL start_element_handler(void *data, const char *tag, const char **attributes) {
520 FamilyData* self = static_cast<FamilyData*>(data);
521
522 if (!self->fSkip) {
523 const TagHandler* parent = self->fHandler.top();
halcanary96fcdcc2015-08-27 07:41:13 -0700524 const TagHandler* child = parent->tag ? parent->tag(self, tag, attributes) : nullptr;
bungeman10b063c2015-05-13 08:52:16 -0700525 if (child) {
526 if (child->start) {
527 child->start(self, tag, attributes);
528 }
529 self->fHandler.push(child);
530 XML_SetCharacterDataHandler(self->fParser, child->chars);
531 } else {
532 SK_FONTCONFIGPARSER_WARNING("'%s' tag not recognized, skipping", tag);
halcanary96fcdcc2015-08-27 07:41:13 -0700533 XML_SetCharacterDataHandler(self->fParser, nullptr);
bungeman10b063c2015-05-13 08:52:16 -0700534 self->fSkip = self->fDepth;
535 }
536 }
537
538 ++self->fDepth;
539}
540
541static void XMLCALL end_element_handler(void* data, const char* tag) {
542 FamilyData* self = static_cast<FamilyData*>(data);
543 --self->fDepth;
544
545 if (!self->fSkip) {
546 const TagHandler* child = self->fHandler.top();
547 if (child->end) {
548 child->end(self, tag);
549 }
550 self->fHandler.pop();
551 const TagHandler* parent = self->fHandler.top();
552 XML_SetCharacterDataHandler(self->fParser, parent->chars);
553 }
554
555 if (self->fSkip == self->fDepth) {
556 self->fSkip = 0;
557 const TagHandler* parent = self->fHandler.top();
558 XML_SetCharacterDataHandler(self->fParser, parent->chars);
559 }
560}
561
bungeman7fa87cd2015-02-06 07:59:19 -0800562static void XMLCALL xml_entity_decl_handler(void *data,
563 const XML_Char *entityName,
564 int is_parameter_entity,
565 const XML_Char *value,
566 int value_length,
567 const XML_Char *base,
568 const XML_Char *systemId,
569 const XML_Char *publicId,
570 const XML_Char *notationName)
571{
572 FamilyData* self = static_cast<FamilyData*>(data);
bungemanf61475e2015-04-20 11:16:48 -0700573 SK_FONTCONFIGPARSER_WARNING("'%s' entity declaration found, stopping processing", entityName);
bungeman7fa87cd2015-02-06 07:59:19 -0800574 XML_StopParser(self->fParser, XML_FALSE);
575}
576
bungemaneb2be7f2015-02-10 07:51:12 -0800577static const XML_Memory_Handling_Suite sk_XML_alloc = {
578 sk_malloc_throw,
579 sk_realloc_throw,
580 sk_free
581};
582
djsollen@google.combfae9d32013-05-21 16:53:50 +0000583/**
584 * This function parses the given filename and stores the results in the given
bungemanefbad372015-02-02 11:19:44 -0800585 * families array. Returns the version of the file, negative if the file does not exist.
djsollen@google.combfae9d32013-05-21 16:53:50 +0000586 */
bungeman7fa87cd2015-02-06 07:59:19 -0800587static int parse_config_file(const char* filename, SkTDArray<FontFamily*>& families,
588 const SkString& basePath, bool isFallback)
589{
590 SkFILEStream file(filename);
djsollen@google.com50c95672013-08-28 12:29:45 +0000591
592 // Some of the files we attempt to parse (in particular, /vendor/etc/fallback_fonts.xml)
593 // are optional - failure here is okay because one of these optional files may not exist.
bungeman7fa87cd2015-02-06 07:59:19 -0800594 if (!file.isValid()) {
bungemanc5308542015-06-23 13:25:46 -0700595 SkDebugf(SK_FONTMGR_ANDROID_PARSER_PREFIX "'%s' could not be opened\n", filename);
bungemanefbad372015-02-02 11:19:44 -0800596 return -1;
djsollen@google.com50c95672013-08-28 12:29:45 +0000597 }
598
bungeman54e63082016-01-06 08:30:59 -0800599 SkAutoTCallVProc<skstd::remove_pointer_t<XML_Parser>, XML_ParserFree> parser(
halcanary96fcdcc2015-08-27 07:41:13 -0700600 XML_ParserCreate_MM(nullptr, &sk_XML_alloc, nullptr));
bungeman7fa87cd2015-02-06 07:59:19 -0800601 if (!parser) {
bungemanc5308542015-06-23 13:25:46 -0700602 SkDebugf(SK_FONTMGR_ANDROID_PARSER_PREFIX "could not create XML parser\n");
bungeman7fa87cd2015-02-06 07:59:19 -0800603 return -1;
604 }
605
bungeman10b063c2015-05-13 08:52:16 -0700606 FamilyData self(parser, families, basePath, isFallback, filename, &topLevelHandler);
bungeman7fa87cd2015-02-06 07:59:19 -0800607 XML_SetUserData(parser, &self);
608
609 // Disable entity processing, to inhibit internal entity expansion. See expat CVE-2013-0340
610 XML_SetEntityDeclHandler(parser, xml_entity_decl_handler);
611
tomhudsonf79673b2014-08-05 06:36:11 -0700612 // Start parsing oldschool; switch these in flight if we detect a newer version of the file.
bungeman10b063c2015-05-13 08:52:16 -0700613 XML_SetElementHandler(parser, start_element_handler, end_element_handler);
djsollen@google.com50c95672013-08-28 12:29:45 +0000614
bungemaneb2be7f2015-02-10 07:51:12 -0800615 // One would assume it would be faster to have a buffer on the stack and call XML_Parse.
616 // But XML_Parse will call XML_GetBuffer anyway and memmove the passed buffer into it.
617 // (Unless XML_CONTEXT_BYTES is undefined, but all users define it.)
bungeman9a0808f2015-02-13 08:55:16 -0800618 // In debug, buffer a small odd number of bytes to detect slicing in XML_CharacterDataHandler.
619 static const int bufferSize = 512 SkDEBUGCODE( - 507);
djsollen@google.combfae9d32013-05-21 16:53:50 +0000620 bool done = false;
621 while (!done) {
bungemaneb2be7f2015-02-10 07:51:12 -0800622 void* buffer = XML_GetBuffer(parser, bufferSize);
623 if (!buffer) {
bungemanc5308542015-06-23 13:25:46 -0700624 SkDebugf(SK_FONTMGR_ANDROID_PARSER_PREFIX "could not buffer enough to continue\n");
bungemaneb2be7f2015-02-10 07:51:12 -0800625 return -1;
626 }
627 size_t len = file.read(buffer, bufferSize);
bungeman7fa87cd2015-02-06 07:59:19 -0800628 done = file.isAtEnd();
bungemaneb2be7f2015-02-10 07:51:12 -0800629 XML_Status status = XML_ParseBuffer(parser, len, done);
bungeman7fa87cd2015-02-06 07:59:19 -0800630 if (XML_STATUS_ERROR == status) {
631 XML_Error error = XML_GetErrorCode(parser);
632 int line = XML_GetCurrentLineNumber(parser);
633 int column = XML_GetCurrentColumnNumber(parser);
bungeman7fa87cd2015-02-06 07:59:19 -0800634 const XML_LChar* errorString = XML_ErrorString(error);
bungemanc5308542015-06-23 13:25:46 -0700635 SkDebugf(SK_FONTMGR_ANDROID_PARSER_PREFIX "%s:%d:%d error %d: %s.\n",
bungemanf61475e2015-04-20 11:16:48 -0700636 filename, line, column, error, errorString);
bungeman7fa87cd2015-02-06 07:59:19 -0800637 return -1;
djsollen@google.combfae9d32013-05-21 16:53:50 +0000638 }
djsollen@google.combfae9d32013-05-21 16:53:50 +0000639 }
bungeman7fa87cd2015-02-06 07:59:19 -0800640 return self.fVersion;
djsollen@google.combfae9d32013-05-21 16:53:50 +0000641}
642
bungemanefbad372015-02-02 11:19:44 -0800643/** Returns the version of the system font file actually found, negative if none. */
bungeman7fa87cd2015-02-06 07:59:19 -0800644static int append_system_font_families(SkTDArray<FontFamily*>& fontFamilies,
645 const SkString& basePath)
646{
tomhudson7a4747f2014-08-13 11:06:43 -0700647 int initialCount = fontFamilies.count();
bungeman7fa87cd2015-02-06 07:59:19 -0800648 int version = parse_config_file(LMP_SYSTEM_FONTS_FILE, fontFamilies, basePath, false);
bungemanefbad372015-02-02 11:19:44 -0800649 if (version < 0 || fontFamilies.count() == initialCount) {
bungeman7fa87cd2015-02-06 07:59:19 -0800650 version = parse_config_file(OLD_SYSTEM_FONTS_FILE, fontFamilies, basePath, false);
tomhudson94fa4b92014-08-12 11:05:29 -0700651 }
bungemanefbad372015-02-02 11:19:44 -0800652 return version;
djsollen@google.combfae9d32013-05-21 16:53:50 +0000653}
654
djsollena6c27bc2014-08-06 11:01:58 -0700655/**
656 * In some versions of Android prior to Android 4.2 (JellyBean MR1 at API
657 * Level 17) the fallback fonts for certain locales were encoded in their own
658 * XML files with a suffix that identified the locale. We search the provided
659 * directory for those files,add all of their entries to the fallback chain, and
660 * include the locale as part of each entry.
661 */
bungemanefbad372015-02-02 11:19:44 -0800662static void append_fallback_font_families_for_locale(SkTDArray<FontFamily*>& fallbackFonts,
bungeman7fa87cd2015-02-06 07:59:19 -0800663 const char* dir,
664 const SkString& basePath)
bungemanefbad372015-02-02 11:19:44 -0800665{
djsollena6c27bc2014-08-06 11:01:58 -0700666#if defined(SK_BUILD_FOR_ANDROID_FRAMEWORK)
667 // The framework is beyond Android 4.2 and can therefore skip this function
668 return;
669#endif
670
bungemanc3c69432015-02-11 07:18:51 -0800671 SkAutoTCallIProc<DIR, closedir> fontDirectory(opendir(dir));
halcanary96fcdcc2015-08-27 07:41:13 -0700672 if (nullptr == fontDirectory) {
bungemanc3c69432015-02-11 07:18:51 -0800673 return;
674 }
djsollena6c27bc2014-08-06 11:01:58 -0700675
bungemanc3c69432015-02-11 07:18:51 -0800676 for (struct dirent* dirEntry; (dirEntry = readdir(fontDirectory));) {
bungeman9a0808f2015-02-13 08:55:16 -0800677 // The size of the prefix and suffix.
678 static const size_t fixedLen = sizeof(LOCALE_FALLBACK_FONTS_PREFIX) - 1
679 + sizeof(LOCALE_FALLBACK_FONTS_SUFFIX) - 1;
680
681 // The size of the prefix, suffix, and a minimum valid language code
682 static const size_t minSize = fixedLen + 2;
djsollena6c27bc2014-08-06 11:01:58 -0700683
bungemanc3c69432015-02-11 07:18:51 -0800684 SkString fileName(dirEntry->d_name);
685 if (fileName.size() < minSize ||
686 !fileName.startsWith(LOCALE_FALLBACK_FONTS_PREFIX) ||
687 !fileName.endsWith(LOCALE_FALLBACK_FONTS_SUFFIX))
688 {
689 continue;
djsollena6c27bc2014-08-06 11:01:58 -0700690 }
bungemanc3c69432015-02-11 07:18:51 -0800691
bungemanc3c69432015-02-11 07:18:51 -0800692 SkString locale(fileName.c_str() + sizeof(LOCALE_FALLBACK_FONTS_PREFIX) - 1,
693 fileName.size() - fixedLen);
694
695 SkString absoluteFilename;
696 absoluteFilename.printf("%s/%s", dir, fileName.c_str());
697
698 SkTDArray<FontFamily*> langSpecificFonts;
699 parse_config_file(absoluteFilename.c_str(), langSpecificFonts, basePath, true);
700
701 for (int i = 0; i < langSpecificFonts.count(); ++i) {
702 FontFamily* family = langSpecificFonts[i];
703 family->fLanguage = SkLanguage(locale);
704 *fallbackFonts.append() = family;
705 }
djsollena6c27bc2014-08-06 11:01:58 -0700706 }
707}
708
bungeman7fa87cd2015-02-06 07:59:19 -0800709static void append_system_fallback_font_families(SkTDArray<FontFamily*>& fallbackFonts,
710 const SkString& basePath)
711{
712 parse_config_file(FALLBACK_FONTS_FILE, fallbackFonts, basePath, true);
713 append_fallback_font_families_for_locale(fallbackFonts,
714 LOCALE_FALLBACK_FONTS_SYSTEM_DIR,
715 basePath);
bungemanefbad372015-02-02 11:19:44 -0800716}
djsollen@google.combfae9d32013-05-21 16:53:50 +0000717
bungeman7fa87cd2015-02-06 07:59:19 -0800718static void mixin_vendor_fallback_font_families(SkTDArray<FontFamily*>& fallbackFonts,
719 const SkString& basePath)
720{
bungemanefbad372015-02-02 11:19:44 -0800721 SkTDArray<FontFamily*> vendorFonts;
bungeman7fa87cd2015-02-06 07:59:19 -0800722 parse_config_file(VENDOR_FONTS_FILE, vendorFonts, basePath, true);
723 append_fallback_font_families_for_locale(vendorFonts,
724 LOCALE_FALLBACK_FONTS_VENDOR_DIR,
725 basePath);
djsollena6c27bc2014-08-06 11:01:58 -0700726
djsollen@google.combfae9d32013-05-21 16:53:50 +0000727 // This loop inserts the vendor fallback fonts in the correct order in the
728 // overall fallbacks list.
729 int currentOrder = -1;
730 for (int i = 0; i < vendorFonts.count(); ++i) {
731 FontFamily* family = vendorFonts[i];
tomhudsond3ddea22014-08-11 11:28:00 -0700732 int order = family->fOrder;
djsollen@google.combfae9d32013-05-21 16:53:50 +0000733 if (order < 0) {
734 if (currentOrder < 0) {
735 // Default case - just add it to the end of the fallback list
736 *fallbackFonts.append() = family;
737 } else {
738 // no order specified on this font, but we're incrementing the order
739 // based on an earlier order insertion request
740 *fallbackFonts.insert(currentOrder++) = family;
741 }
742 } else {
743 // Add the font into the fallback list in the specified order. Set
744 // currentOrder for correct placement of other fonts in the vendor list.
745 *fallbackFonts.insert(order) = family;
746 currentOrder = order + 1;
747 }
748 }
749}
750
bungemanc5308542015-06-23 13:25:46 -0700751void SkFontMgr_Android_Parser::GetSystemFontFamilies(SkTDArray<FontFamily*>& fontFamilies) {
bungemanefbad372015-02-02 11:19:44 -0800752 // Version 21 of the system font configuration does not need any fallback configuration files.
bungeman7fa87cd2015-02-06 07:59:19 -0800753 SkString basePath(getenv("ANDROID_ROOT"));
754 basePath.append(SK_FONT_FILE_PREFIX, sizeof(SK_FONT_FILE_PREFIX) - 1);
755
756 if (append_system_font_families(fontFamilies, basePath) >= 21) {
bungemanefbad372015-02-02 11:19:44 -0800757 return;
758 }
djsollen@google.combfae9d32013-05-21 16:53:50 +0000759
760 // Append all the fallback fonts to system fonts
761 SkTDArray<FontFamily*> fallbackFonts;
bungeman7fa87cd2015-02-06 07:59:19 -0800762 append_system_fallback_font_families(fallbackFonts, basePath);
763 mixin_vendor_fallback_font_families(fallbackFonts, basePath);
764 fontFamilies.append(fallbackFonts.count(), fallbackFonts.begin());
djsollen@google.combfae9d32013-05-21 16:53:50 +0000765}
766
bungemanc5308542015-06-23 13:25:46 -0700767void SkFontMgr_Android_Parser::GetCustomFontFamilies(SkTDArray<FontFamily*>& fontFamilies,
768 const SkString& basePath,
769 const char* fontsXml,
770 const char* fallbackFontsXml,
771 const char* langFallbackFontsDir)
bungeman7fa87cd2015-02-06 07:59:19 -0800772{
773 if (fontsXml) {
774 parse_config_file(fontsXml, fontFamilies, basePath, false);
tomhudsonf79673b2014-08-05 06:36:11 -0700775 }
bungeman7fa87cd2015-02-06 07:59:19 -0800776 if (fallbackFontsXml) {
777 parse_config_file(fallbackFontsXml, fontFamilies, basePath, true);
djsollen@google.combfae9d32013-05-21 16:53:50 +0000778 }
bungemanc3c69432015-02-11 07:18:51 -0800779 if (langFallbackFontsDir) {
780 append_fallback_font_families_for_locale(fontFamilies,
781 langFallbackFontsDir,
782 basePath);
783 }
djsollen@google.combfae9d32013-05-21 16:53:50 +0000784}
djsollen3b625542014-08-14 06:29:02 -0700785
786SkLanguage SkLanguage::getParent() const {
787 SkASSERT(!fTag.isEmpty());
788 const char* tag = fTag.c_str();
789
790 // strip off the rightmost "-.*"
791 const char* parentTagEnd = strrchr(tag, '-');
halcanary96fcdcc2015-08-27 07:41:13 -0700792 if (parentTagEnd == nullptr) {
djsollen3b625542014-08-14 06:29:02 -0700793 return SkLanguage();
794 }
795 size_t parentTagLen = parentTagEnd - tag;
796 return SkLanguage(tag, parentTagLen);
797}