blob: e9c912f4b012e0e9608eab2cf5d7713d6cc48d49 [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
8#include "SkFontConfigParser_android.h"
9#include "SkTDArray.h"
bungeman8d84c992014-07-24 08:05:09 -070010#include "SkTSearch.h"
djsollen@google.combfae9d32013-05-21 16:53:50 +000011#include "SkTypeface.h"
12
13#include <expat.h>
djsollena6c27bc2014-08-06 11:01:58 -070014#include <dirent.h>
bungeman@google.com18252fe2013-10-11 20:13:41 +000015#include <stdio.h>
djsollen@google.combfae9d32013-05-21 16:53:50 +000016
bungeman8d84c992014-07-24 08:05:09 -070017#include <limits>
18
tomhudson94fa4b92014-08-12 11:05:29 -070019
20
21// From Android version LMP onwards, all font files collapse into
22// /system/fonts/fonts.xml. Instead of trying to detect which version
23// we're on, try to open fonts.xml; if that fails, fall back to the
24// older filename.
25#define LMP_SYSTEM_FONTS_FILE "/system/etc/fonts.xml"
26#define OLD_SYSTEM_FONTS_FILE "/system/etc/system_fonts.xml"
djsollen@google.combfae9d32013-05-21 16:53:50 +000027#define FALLBACK_FONTS_FILE "/system/etc/fallback_fonts.xml"
28#define VENDOR_FONTS_FILE "/vendor/etc/fallback_fonts.xml"
29
djsollena6c27bc2014-08-06 11:01:58 -070030#define LOCALE_FALLBACK_FONTS_SYSTEM_DIR "/system/etc"
31#define LOCALE_FALLBACK_FONTS_VENDOR_DIR "/vendor/etc"
32#define LOCALE_FALLBACK_FONTS_PREFIX "fallback_fonts-"
33#define LOCALE_FALLBACK_FONTS_SUFFIX ".xml"
34
tomhudsonf79673b2014-08-05 06:36:11 -070035/**
36 * This file contains TWO parsers: one for JB and earlier (system_fonts.xml /
37 * fallback_fonts.xml), one for LMP and later (fonts.xml).
38 * We start with the JB parser, and if we detect a <familyset> tag with
39 * version 21 or higher we switch to the LMP parser.
40 */
41
djsollen@google.combfae9d32013-05-21 16:53:50 +000042// These defines are used to determine the kind of tag that we're currently
43// populating with data. We only care about the sibling tags nameset and fileset
44// for now.
45#define NO_TAG 0
46#define NAMESET_TAG 1
47#define FILESET_TAG 2
48
49/**
50 * The FamilyData structure is passed around by the parser so that each handler
51 * can read these variables that are relevant to the current parsing.
52 */
53struct FamilyData {
tomhudsond3ddea22014-08-11 11:28:00 -070054 FamilyData(XML_Parser* parserRef, SkTDArray<FontFamily*> &familiesRef) :
djsollen@google.combfae9d32013-05-21 16:53:50 +000055 parser(parserRef),
56 families(familiesRef),
57 currentFamily(NULL),
58 currentFontInfo(NULL),
59 currentTag(NO_TAG) {};
60
tomhudsond3ddea22014-08-11 11:28:00 -070061 XML_Parser* parser; // The expat parser doing the work
djsollen@google.combfae9d32013-05-21 16:53:50 +000062 SkTDArray<FontFamily*> &families; // The array that each family is put into as it is parsed
tomhudsond3ddea22014-08-11 11:28:00 -070063 FontFamily* currentFamily; // The current family being created
64 FontFileInfo* currentFontInfo; // The current fontInfo being created
djsollen@google.combfae9d32013-05-21 16:53:50 +000065 int currentTag; // A flag to indicate whether we're in nameset/fileset tags
66};
67
tomhudsonf79673b2014-08-05 06:36:11 -070068/** http://www.w3.org/TR/html-markup/datatypes.html#common.data.integer.non-negative-def */
69template <typename T> static bool parseNonNegativeInteger(const char* s, T* value) {
70 SK_COMPILE_ASSERT(std::numeric_limits<T>::is_integer, T_must_be_integer);
71 const T nMax = std::numeric_limits<T>::max() / 10;
72 const T dMax = std::numeric_limits<T>::max() - (nMax * 10);
73 T n = 0;
74 for (; *s; ++s) {
75 // Check if digit
76 if (*s < '0' || '9' < *s) {
77 return false;
78 }
79 int d = *s - '0';
80 // Check for overflow
81 if (n > nMax || (n == nMax && d > dMax)) {
82 return false;
83 }
84 n = (n * 10) + d;
85 }
86 *value = n;
87 return true;
88}
89
90namespace lmpParser {
91
tomhudson07544752014-08-05 13:35:00 -070092void familyElementHandler(FontFamily* family, const char** attributes) {
93 // A non-fallback <family> tag must have a canonical name attribute.
tomhudson2ed49a42014-08-13 07:53:48 -070094 // A fallback <family> tag has no name, and may have lang and variant
95 // attributes.
96 family->fIsFallbackFont = true;
tomhudsond3ddea22014-08-11 11:28:00 -070097 for (size_t i = 0; attributes[i] != NULL &&
98 attributes[i+1] != NULL; i += 2) {
tomhudson07544752014-08-05 13:35:00 -070099 const char* name = attributes[i];
100 const char* value = attributes[i+1];
tomhudsond3ddea22014-08-11 11:28:00 -0700101 size_t nameLen = strlen(name);
102 size_t valueLen = strlen(value);
tomhudson07544752014-08-05 13:35:00 -0700103 if (nameLen == 4 && !strncmp("name", name, nameLen)) {
tomhudsonb3d4af52014-08-12 07:42:16 -0700104 SkAutoAsciiToLC tolc(value);
105 family->fNames.push_back().set(tolc.lc());
tomhudson2ed49a42014-08-13 07:53:48 -0700106 family->fIsFallbackFont = false;
tomhudson07544752014-08-05 13:35:00 -0700107 } else if (nameLen == 4 && !strncmp("lang", name, nameLen)) {
108 family->fLanguage = SkLanguage (value);
109 } else if (nameLen == 7 && !strncmp("variant", name, nameLen)) {
110 // Value should be either elegant or compact.
111 if (valueLen == 7 && !strncmp("elegant", value, valueLen)) {
Derek Sollenbergerda7a9442014-08-06 16:34:40 -0400112 family->fVariant = SkPaintOptionsAndroid::kElegant_Variant;
tomhudson07544752014-08-05 13:35:00 -0700113 } else if (valueLen == 7 && !strncmp("compact", value, valueLen)) {
Derek Sollenbergerda7a9442014-08-06 16:34:40 -0400114 family->fVariant = SkPaintOptionsAndroid::kCompact_Variant;
tomhudson07544752014-08-05 13:35:00 -0700115 }
116 }
117 }
118}
119
tomhudsond3ddea22014-08-11 11:28:00 -0700120void fontFileNameHandler(void* data, const char* s, int len) {
121 FamilyData* familyData = (FamilyData*) data;
tomhudson07544752014-08-05 13:35:00 -0700122 familyData->currentFontInfo->fFileName.set(s, len);
123}
124
Derek Sollenbergerda7a9442014-08-06 16:34:40 -0400125void familyElementEndHandler(FontFamily* family) {
tomhudsond3ddea22014-08-11 11:28:00 -0700126 for (int i = 0; i < family->fFonts.count(); i++) {
127 family->fFonts[i].fPaintOptions.setLanguage(family->fLanguage);
128 family->fFonts[i].fPaintOptions.setFontVariant(family->fVariant);
Derek Sollenbergerda7a9442014-08-06 16:34:40 -0400129 }
130}
131
tomhudson07544752014-08-05 13:35:00 -0700132void fontElementHandler(XML_Parser* parser, FontFileInfo* file, const char** attributes) {
133 // A <font> should have weight (integer) and style (normal, italic) attributes.
134 // NOTE: we ignore the style.
135 // The element should contain a filename.
tomhudsond3ddea22014-08-11 11:28:00 -0700136 for (size_t i = 0; attributes[i] != NULL &&
137 attributes[i+1] != NULL; i += 2) {
tomhudson07544752014-08-05 13:35:00 -0700138 const char* name = attributes[i];
139 const char* value = attributes[i+1];
tomhudsond3ddea22014-08-11 11:28:00 -0700140 size_t nameLen = strlen(name);
tomhudson07544752014-08-05 13:35:00 -0700141 if (nameLen == 6 && !strncmp("weight", name, nameLen)) {
tomhudsond3ddea22014-08-11 11:28:00 -0700142 if (!parseNonNegativeInteger(value, &file->fWeight)) {
143 SkDebugf("---- Font weight %s (INVALID)", value);
144 file->fWeight = 0;
145 }
tomhudson07544752014-08-05 13:35:00 -0700146 }
147 }
148 XML_SetCharacterDataHandler(*parser, fontFileNameHandler);
149}
150
151FontFamily* findFamily(FamilyData* familyData, const char* familyName) {
tomhudsond3ddea22014-08-11 11:28:00 -0700152 size_t nameLen = strlen(familyName);
tomhudson07544752014-08-05 13:35:00 -0700153 for (int i = 0; i < familyData->families.count(); i++) {
154 FontFamily* candidate = familyData->families[i];
155 for (int j = 0; j < candidate->fNames.count(); j++) {
156 if (!strncmp(candidate->fNames[j].c_str(), familyName, nameLen) &&
157 nameLen == strlen(candidate->fNames[j].c_str())) {
158 return candidate;
159 }
160 }
161 }
162
163 return NULL;
164}
165
166void aliasElementHandler(FamilyData* familyData, const char** attributes) {
167 // An <alias> must have name and to attributes.
168 // It may have weight (integer).
169 // If it *does not* have a weight, it is a variant name for a <family>.
170 // If it *does* have a weight, it names the <font>(s) of a specific weight
171 // from a <family>.
172
173 SkString aliasName;
174 SkString to;
175 int weight = 0;
tomhudsond3ddea22014-08-11 11:28:00 -0700176 for (size_t i = 0; attributes[i] != NULL &&
177 attributes[i+1] != NULL; i += 2) {
tomhudson07544752014-08-05 13:35:00 -0700178 const char* name = attributes[i];
179 const char* value = attributes[i+1];
tomhudsond3ddea22014-08-11 11:28:00 -0700180 size_t nameLen = strlen(name);
tomhudson07544752014-08-05 13:35:00 -0700181 if (nameLen == 4 && !strncmp("name", name, nameLen)) {
tomhudsonb3d4af52014-08-12 07:42:16 -0700182 SkAutoAsciiToLC tolc(value);
183 aliasName.set(tolc.lc());
tomhudson07544752014-08-05 13:35:00 -0700184 } else if (nameLen == 2 && !strncmp("to", name, nameLen)) {
185 to.set(value);
186 } else if (nameLen == 6 && !strncmp("weight", name, nameLen)) {
187 parseNonNegativeInteger(value, &weight);
188 }
189 }
190
191 // Assumes that the named family is already declared
192 FontFamily* targetFamily = findFamily(familyData, to.c_str());
193 if (!targetFamily) {
194 SkDebugf("---- Font alias target %s (NOT FOUND)", to.c_str());
195 return;
196 }
197
198 if (weight) {
199 FontFamily* family = new FontFamily();
200 family->fNames.push_back().set(aliasName);
201
tomhudsond3ddea22014-08-11 11:28:00 -0700202 for (int i = 0; i < targetFamily->fFonts.count(); i++) {
203 if (targetFamily->fFonts[i].fWeight == weight) {
204 family->fFonts.push_back(targetFamily->fFonts[i]);
tomhudson07544752014-08-05 13:35:00 -0700205 }
206 }
207 *familyData->families.append() = family;
208 } else {
209 targetFamily->fNames.push_back().set(aliasName);
210 }
211}
212
213bool findWeight400(FontFamily* family) {
tomhudsond3ddea22014-08-11 11:28:00 -0700214 for (int i = 0; i < family->fFonts.count(); i++) {
215 if (family->fFonts[i].fWeight == 400) {
tomhudson07544752014-08-05 13:35:00 -0700216 return true;
217 }
218 }
219 return false;
220}
221
222bool desiredWeight(int weight) {
223 return (weight == 400 || weight == 700);
224}
225
226int countDesiredWeight(FontFamily* family) {
227 int count = 0;
tomhudsond3ddea22014-08-11 11:28:00 -0700228 for (int i = 0; i < family->fFonts.count(); i++) {
229 if (desiredWeight(family->fFonts[i].fWeight)) {
tomhudson07544752014-08-05 13:35:00 -0700230 count++;
231 }
232 }
233 return count;
234}
235
236// To meet Skia's expectations, any family that contains weight=400
237// fonts should *only* contain {400,700}
238void purgeUndesiredWeights(FontFamily* family) {
239 int count = countDesiredWeight(family);
tomhudsond3ddea22014-08-11 11:28:00 -0700240 for (int i = 1, j = 0; i < family->fFonts.count(); i++) {
241 if (desiredWeight(family->fFonts[j].fWeight)) {
tomhudson07544752014-08-05 13:35:00 -0700242 j++;
243 }
tomhudsond3ddea22014-08-11 11:28:00 -0700244 if ((i != j) && desiredWeight(family->fFonts[i].fWeight)) {
245 family->fFonts[j] = family->fFonts[i];
tomhudson07544752014-08-05 13:35:00 -0700246 }
247 }
tomhudsond3ddea22014-08-11 11:28:00 -0700248 family->fFonts.resize_back(count);
tomhudson07544752014-08-05 13:35:00 -0700249}
250
251void familysetElementEndHandler(FamilyData* familyData) {
252 for (int i = 0; i < familyData->families.count(); i++) {
253 if (findWeight400(familyData->families[i])) {
254 purgeUndesiredWeights(familyData->families[i]);
255 }
256 }
257}
258
tomhudsonf79673b2014-08-05 06:36:11 -0700259void startElementHandler(void* data, const char* tag,
tomhudson07544752014-08-05 13:35:00 -0700260 const char** attributes) {
261 FamilyData* familyData = (FamilyData*) data;
tomhudsond3ddea22014-08-11 11:28:00 -0700262 size_t len = strlen(tag);
tomhudson07544752014-08-05 13:35:00 -0700263 if (len == 6 && !strncmp(tag, "family", len)) {
264 familyData->currentFamily = new FontFamily();
265 familyElementHandler(familyData->currentFamily, attributes);
266 } else if (len == 4 && !strncmp(tag, "font", len)) {
tomhudsond3ddea22014-08-11 11:28:00 -0700267 FontFileInfo* file = &familyData->currentFamily->fFonts.push_back();
tomhudson07544752014-08-05 13:35:00 -0700268 familyData->currentFontInfo = file;
269 fontElementHandler(familyData->parser, file, attributes);
270 } else if (len == 5 && !strncmp(tag, "alias", len)) {
271 aliasElementHandler(familyData, attributes);
272 }
tomhudsonf79673b2014-08-05 06:36:11 -0700273}
274
275void endElementHandler(void* data, const char* tag) {
tomhudsond3ddea22014-08-11 11:28:00 -0700276 FamilyData* familyData = (FamilyData*) data;
277 size_t len = strlen(tag);
tomhudson07544752014-08-05 13:35:00 -0700278 if (len == 9 && strncmp(tag, "familyset", len) == 0) {
279 familysetElementEndHandler(familyData);
280 } else if (len == 6 && strncmp(tag, "family", len) == 0) {
Derek Sollenbergerda7a9442014-08-06 16:34:40 -0400281 familyElementEndHandler(familyData->currentFamily);
tomhudson07544752014-08-05 13:35:00 -0700282 *familyData->families.append() = familyData->currentFamily;
283 familyData->currentFamily = NULL;
284 } else if (len == 4 && !strncmp(tag, "font", len)) {
285 XML_SetCharacterDataHandler(*familyData->parser, NULL);
286 }
tomhudsonf79673b2014-08-05 06:36:11 -0700287}
288
289} // lmpParser
290
291namespace jbParser {
292
djsollen@google.combfae9d32013-05-21 16:53:50 +0000293/**
294 * Handler for arbitrary text. This is used to parse the text inside each name
295 * or file tag. The resulting strings are put into the fNames or FontFileInfo arrays.
296 */
tomhudsond3ddea22014-08-11 11:28:00 -0700297static void textHandler(void* data, const char* s, int len) {
298 FamilyData* familyData = (FamilyData*) data;
djsollen@google.combfae9d32013-05-21 16:53:50 +0000299 // Make sure we're in the right state to store this name information
300 if (familyData->currentFamily &&
301 (familyData->currentTag == NAMESET_TAG || familyData->currentTag == FILESET_TAG)) {
djsollen@google.combfae9d32013-05-21 16:53:50 +0000302 switch (familyData->currentTag) {
bungemanedb10e72014-07-28 08:19:45 -0700303 case NAMESET_TAG: {
304 SkAutoAsciiToLC tolc(s, len);
bungeman8d84c992014-07-24 08:05:09 -0700305 familyData->currentFamily->fNames.push_back().set(tolc.lc(), len);
djsollen@google.combfae9d32013-05-21 16:53:50 +0000306 break;
bungemanedb10e72014-07-28 08:19:45 -0700307 }
djsollen@google.combfae9d32013-05-21 16:53:50 +0000308 case FILESET_TAG:
309 if (familyData->currentFontInfo) {
commit-bot@chromium.org31db71d2014-04-04 18:14:39 +0000310 familyData->currentFontInfo->fFileName.set(s, len);
djsollen@google.combfae9d32013-05-21 16:53:50 +0000311 }
312 break;
313 default:
314 // Noop - don't care about any text that's not in the Fonts or Names list
315 break;
316 }
317 }
318}
319
320/**
321 * Handler for font files. This processes the attributes for language and
322 * variants then lets textHandler handle the actual file name
323 */
tomhudsond3ddea22014-08-11 11:28:00 -0700324static void fontFileElementHandler(FamilyData* familyData, const char** attributes) {
325 FontFileInfo& newFileInfo = familyData->currentFamily->fFonts.push_back();
djsollen@google.combfae9d32013-05-21 16:53:50 +0000326 if (attributes) {
tomhudsond3ddea22014-08-11 11:28:00 -0700327 size_t currentAttributeIndex = 0;
328 while (attributes[currentAttributeIndex] &&
329 attributes[currentAttributeIndex + 1]) {
djsollen@google.combfae9d32013-05-21 16:53:50 +0000330 const char* attributeName = attributes[currentAttributeIndex];
331 const char* attributeValue = attributes[currentAttributeIndex+1];
tomhudsond3ddea22014-08-11 11:28:00 -0700332 size_t nameLength = strlen(attributeName);
333 size_t valueLength = strlen(attributeValue);
tomhudsonf79673b2014-08-05 06:36:11 -0700334 if (nameLength == 7 && strncmp(attributeName, "variant", nameLength) == 0) {
335 if (valueLength == 7 && strncmp(attributeValue, "elegant", valueLength) == 0) {
Derek Sollenbergerda7a9442014-08-06 16:34:40 -0400336 newFileInfo.fPaintOptions.setFontVariant(SkPaintOptionsAndroid::kElegant_Variant);
tomhudsonf79673b2014-08-05 06:36:11 -0700337 } else if (valueLength == 7 &&
338 strncmp(attributeValue, "compact", valueLength) == 0) {
Derek Sollenbergerda7a9442014-08-06 16:34:40 -0400339 newFileInfo.fPaintOptions.setFontVariant(SkPaintOptionsAndroid::kCompact_Variant);
djsollen@google.combfae9d32013-05-21 16:53:50 +0000340 }
tomhudsonf79673b2014-08-05 06:36:11 -0700341 } else if (nameLength == 4 && strncmp(attributeName, "lang", nameLength) == 0) {
Derek Sollenbergerda7a9442014-08-06 16:34:40 -0400342 newFileInfo.fPaintOptions.setLanguage(attributeValue);
tomhudsonf79673b2014-08-05 06:36:11 -0700343 } else if (nameLength == 5 && strncmp(attributeName, "index", nameLength) == 0) {
bungeman8d84c992014-07-24 08:05:09 -0700344 int value;
345 if (parseNonNegativeInteger(attributeValue, &value)) {
346 newFileInfo.fIndex = value;
347 } else {
348 SkDebugf("---- SystemFonts index=%s (INVALID)", attributeValue);
349 }
djsollen@google.combfae9d32013-05-21 16:53:50 +0000350 }
351 //each element is a pair of attributeName/attributeValue string pairs
352 currentAttributeIndex += 2;
353 }
354 }
commit-bot@chromium.org31db71d2014-04-04 18:14:39 +0000355 familyData->currentFontInfo = &newFileInfo;
djsollen@google.combfae9d32013-05-21 16:53:50 +0000356 XML_SetCharacterDataHandler(*familyData->parser, textHandler);
357}
358
359/**
tomhudsonf79673b2014-08-05 06:36:11 -0700360 * Handler for the start of a tag. The only tags we expect are familyset, family,
361 * nameset, fileset, name, and file.
djsollen@google.combfae9d32013-05-21 16:53:50 +0000362 */
tomhudsond3ddea22014-08-11 11:28:00 -0700363static void startElementHandler(void* data, const char* tag, const char** atts) {
364 FamilyData* familyData = (FamilyData*) data;
365 size_t len = strlen(tag);
tomhudsonf79673b2014-08-05 06:36:11 -0700366 if (len == 9 && strncmp(tag, "familyset", len) == 0) {
367 // The familyset tag has an optional "version" attribute with an integer value >= 0
tomhudsond3ddea22014-08-11 11:28:00 -0700368 for (size_t i = 0; atts[i] != NULL &&
369 atts[i+1] != NULL; i += 2) {
370 size_t nameLen = strlen(atts[i]);
tomhudsonf79673b2014-08-05 06:36:11 -0700371 if (nameLen == 7 && strncmp(atts[i], "version", nameLen)) continue;
372 const char* valueString = atts[i+1];
373 int version;
374 if (parseNonNegativeInteger(valueString, &version) && (version >= 21)) {
375 XML_SetElementHandler(*familyData->parser,
376 lmpParser::startElementHandler,
377 lmpParser::endElementHandler);
378 }
379 }
380 } else if (len == 6 && strncmp(tag, "family", len) == 0) {
djsollen@google.combfae9d32013-05-21 16:53:50 +0000381 familyData->currentFamily = new FontFamily();
djsollen@google.combfae9d32013-05-21 16:53:50 +0000382 // The Family tag has an optional "order" attribute with an integer value >= 0
383 // If this attribute does not exist, the default value is -1
tomhudsond3ddea22014-08-11 11:28:00 -0700384 for (size_t i = 0; atts[i] != NULL &&
385 atts[i+1] != NULL; i += 2) {
djsollen@google.combfae9d32013-05-21 16:53:50 +0000386 const char* valueString = atts[i+1];
387 int value;
tomhudsonf79673b2014-08-05 06:36:11 -0700388 if (parseNonNegativeInteger(valueString, &value)) {
tomhudsond3ddea22014-08-11 11:28:00 -0700389 familyData->currentFamily->fOrder = value;
djsollen@google.combfae9d32013-05-21 16:53:50 +0000390 }
391 }
392 } else if (len == 7 && strncmp(tag, "nameset", len) == 0) {
393 familyData->currentTag = NAMESET_TAG;
394 } else if (len == 7 && strncmp(tag, "fileset", len) == 0) {
395 familyData->currentTag = FILESET_TAG;
tomhudsonf79673b2014-08-05 06:36:11 -0700396 } else if (len == 4 && strncmp(tag, "name", len) == 0 && familyData->currentTag == NAMESET_TAG) {
djsollen@google.combfae9d32013-05-21 16:53:50 +0000397 // If it's a Name, parse the text inside
398 XML_SetCharacterDataHandler(*familyData->parser, textHandler);
tomhudsonf79673b2014-08-05 06:36:11 -0700399 } else if (len == 4 && strncmp(tag, "file", len) == 0 && familyData->currentTag == FILESET_TAG) {
djsollen@google.combfae9d32013-05-21 16:53:50 +0000400 // If it's a file, parse the attributes, then parse the text inside
401 fontFileElementHandler(familyData, atts);
402 }
403}
404
405/**
406 * Handler for the end of tags. We only care about family, nameset, fileset,
407 * name, and file.
408 */
tomhudsond3ddea22014-08-11 11:28:00 -0700409static void endElementHandler(void* data, const char* tag) {
410 FamilyData* familyData = (FamilyData*) data;
411 size_t len = strlen(tag);
tomhudsonf79673b2014-08-05 06:36:11 -0700412 if (len == 6 && strncmp(tag, "family", len)== 0) {
djsollen@google.combfae9d32013-05-21 16:53:50 +0000413 // Done parsing a Family - store the created currentFamily in the families array
414 *familyData->families.append() = familyData->currentFamily;
415 familyData->currentFamily = NULL;
416 } else if (len == 7 && strncmp(tag, "nameset", len) == 0) {
417 familyData->currentTag = NO_TAG;
418 } else if (len == 7 && strncmp(tag, "fileset", len) == 0) {
419 familyData->currentTag = NO_TAG;
tomhudsonf79673b2014-08-05 06:36:11 -0700420 } else if ((len == 4 &&
421 strncmp(tag, "name", len) == 0 &&
422 familyData->currentTag == NAMESET_TAG) ||
423 (len == 4 &&
424 strncmp(tag, "file", len) == 0 &&
425 familyData->currentTag == FILESET_TAG)) {
djsollen@google.combfae9d32013-05-21 16:53:50 +0000426 // Disable the arbitrary text handler installed to load Name data
427 XML_SetCharacterDataHandler(*familyData->parser, NULL);
428 }
429}
430
tomhudsonf79673b2014-08-05 06:36:11 -0700431} // namespace jbParser
432
djsollen@google.combfae9d32013-05-21 16:53:50 +0000433/**
434 * This function parses the given filename and stores the results in the given
435 * families array.
436 */
tomhudsond3ddea22014-08-11 11:28:00 -0700437static void parseConfigFile(const char* filename, SkTDArray<FontFamily*> &families) {
djsollen@google.com50c95672013-08-28 12:29:45 +0000438
djsollena6c27bc2014-08-06 11:01:58 -0700439 FILE* file = fopen(filename, "r");
djsollen@google.com50c95672013-08-28 12:29:45 +0000440
441 // Some of the files we attempt to parse (in particular, /vendor/etc/fallback_fonts.xml)
442 // are optional - failure here is okay because one of these optional files may not exist.
443 if (NULL == file) {
444 return;
445 }
446
djsollen@google.combfae9d32013-05-21 16:53:50 +0000447 XML_Parser parser = XML_ParserCreate(NULL);
tomhudsond3ddea22014-08-11 11:28:00 -0700448 FamilyData* familyData = new FamilyData(&parser, families);
djsollen@google.combfae9d32013-05-21 16:53:50 +0000449 XML_SetUserData(parser, familyData);
tomhudsonf79673b2014-08-05 06:36:11 -0700450 // Start parsing oldschool; switch these in flight if we detect a newer version of the file.
451 XML_SetElementHandler(parser, jbParser::startElementHandler, jbParser::endElementHandler);
djsollen@google.com50c95672013-08-28 12:29:45 +0000452
djsollen@google.combfae9d32013-05-21 16:53:50 +0000453 char buffer[512];
454 bool done = false;
455 while (!done) {
456 fgets(buffer, sizeof(buffer), file);
tomhudsond3ddea22014-08-11 11:28:00 -0700457 size_t len = strlen(buffer);
djsollen@google.combfae9d32013-05-21 16:53:50 +0000458 if (feof(file) != 0) {
459 done = true;
460 }
461 XML_Parse(parser, buffer, len, done);
462 }
djsollen@google.com286a8832013-09-18 18:59:29 +0000463 XML_ParserFree(parser);
djsollen@google.combfae9d32013-05-21 16:53:50 +0000464 fclose(file);
465}
466
467static void getSystemFontFamilies(SkTDArray<FontFamily*> &fontFamilies) {
tomhudson94fa4b92014-08-12 11:05:29 -0700468 parseConfigFile(LMP_SYSTEM_FONTS_FILE, fontFamilies);
469
470 if (0 == fontFamilies.count()) {
471 parseConfigFile(OLD_SYSTEM_FONTS_FILE, fontFamilies);
472 }
djsollen@google.combfae9d32013-05-21 16:53:50 +0000473}
474
djsollena6c27bc2014-08-06 11:01:58 -0700475/**
476 * In some versions of Android prior to Android 4.2 (JellyBean MR1 at API
477 * Level 17) the fallback fonts for certain locales were encoded in their own
478 * XML files with a suffix that identified the locale. We search the provided
479 * directory for those files,add all of their entries to the fallback chain, and
480 * include the locale as part of each entry.
481 */
482static void getFallbackFontFamiliesForLocale(SkTDArray<FontFamily*> &fallbackFonts, const char* dir) {
483#if defined(SK_BUILD_FOR_ANDROID_FRAMEWORK)
484 // The framework is beyond Android 4.2 and can therefore skip this function
485 return;
486#endif
487
488 DIR* fontDirectory = opendir(dir);
489 if (fontDirectory != NULL){
490 struct dirent* dirEntry = readdir(fontDirectory);
491 while (dirEntry) {
492
493 // The size of both the prefix, suffix, and a minimum valid language code
tomhudsond3ddea22014-08-11 11:28:00 -0700494 static const size_t minSize = strlen(LOCALE_FALLBACK_FONTS_PREFIX) +
495 strlen(LOCALE_FALLBACK_FONTS_SUFFIX) + 2;
djsollena6c27bc2014-08-06 11:01:58 -0700496
497 SkString fileName(dirEntry->d_name);
498 if (fileName.size() >= minSize &&
499 fileName.startsWith(LOCALE_FALLBACK_FONTS_PREFIX) &&
500 fileName.endsWith(LOCALE_FALLBACK_FONTS_SUFFIX)) {
501
tomhudsond3ddea22014-08-11 11:28:00 -0700502 static const size_t fixedLen = strlen(LOCALE_FALLBACK_FONTS_PREFIX) -
503 strlen(LOCALE_FALLBACK_FONTS_SUFFIX);
djsollena6c27bc2014-08-06 11:01:58 -0700504
505 SkString locale(fileName.c_str() - strlen(LOCALE_FALLBACK_FONTS_PREFIX),
506 fileName.size() - fixedLen);
507
508 SkString absoluteFilename;
509 absoluteFilename.printf("%s/%s", dir, fileName.c_str());
510
511 SkTDArray<FontFamily*> langSpecificFonts;
512 parseConfigFile(absoluteFilename.c_str(), langSpecificFonts);
513
514 for (int i = 0; i < langSpecificFonts.count(); ++i) {
515 FontFamily* family = langSpecificFonts[i];
tomhudsond3ddea22014-08-11 11:28:00 -0700516 for (int j = 0; j < family->fFonts.count(); ++j) {
517 family->fFonts[j].fPaintOptions.setLanguage(locale);
Derek Sollenbergerda7a9442014-08-06 16:34:40 -0400518 }
djsollena6c27bc2014-08-06 11:01:58 -0700519 *fallbackFonts.append() = family;
520 }
521 }
522
523 // proceed to the next entry in the directory
524 dirEntry = readdir(fontDirectory);
525 }
526 // cleanup the directory reference
527 closedir(fontDirectory);
528 }
529}
530
djsollen@google.combfae9d32013-05-21 16:53:50 +0000531static void getFallbackFontFamilies(SkTDArray<FontFamily*> &fallbackFonts) {
532 SkTDArray<FontFamily*> vendorFonts;
533 parseConfigFile(FALLBACK_FONTS_FILE, fallbackFonts);
534 parseConfigFile(VENDOR_FONTS_FILE, vendorFonts);
535
djsollena6c27bc2014-08-06 11:01:58 -0700536 getFallbackFontFamiliesForLocale(fallbackFonts, LOCALE_FALLBACK_FONTS_SYSTEM_DIR);
537 getFallbackFontFamiliesForLocale(vendorFonts, LOCALE_FALLBACK_FONTS_VENDOR_DIR);
538
djsollen@google.combfae9d32013-05-21 16:53:50 +0000539 // This loop inserts the vendor fallback fonts in the correct order in the
540 // overall fallbacks list.
541 int currentOrder = -1;
542 for (int i = 0; i < vendorFonts.count(); ++i) {
543 FontFamily* family = vendorFonts[i];
tomhudsond3ddea22014-08-11 11:28:00 -0700544 int order = family->fOrder;
djsollen@google.combfae9d32013-05-21 16:53:50 +0000545 if (order < 0) {
546 if (currentOrder < 0) {
547 // Default case - just add it to the end of the fallback list
548 *fallbackFonts.append() = family;
549 } else {
550 // no order specified on this font, but we're incrementing the order
551 // based on an earlier order insertion request
552 *fallbackFonts.insert(currentOrder++) = family;
553 }
554 } else {
555 // Add the font into the fallback list in the specified order. Set
556 // currentOrder for correct placement of other fonts in the vendor list.
557 *fallbackFonts.insert(order) = family;
558 currentOrder = order + 1;
559 }
560 }
561}
562
563/**
564 * Loads data on font families from various expected configuration files. The
565 * resulting data is returned in the given fontFamilies array.
566 */
567void SkFontConfigParser::GetFontFamilies(SkTDArray<FontFamily*> &fontFamilies) {
568
569 getSystemFontFamilies(fontFamilies);
570
571 // Append all the fallback fonts to system fonts
572 SkTDArray<FontFamily*> fallbackFonts;
573 getFallbackFontFamilies(fallbackFonts);
574 for (int i = 0; i < fallbackFonts.count(); ++i) {
575 fallbackFonts[i]->fIsFallbackFont = true;
576 *fontFamilies.append() = fallbackFonts[i];
577 }
578}
579
580void SkFontConfigParser::GetTestFontFamilies(SkTDArray<FontFamily*> &fontFamilies,
581 const char* testMainConfigFile,
582 const char* testFallbackConfigFile) {
583 parseConfigFile(testMainConfigFile, fontFamilies);
584
585 SkTDArray<FontFamily*> fallbackFonts;
tomhudsonf79673b2014-08-05 06:36:11 -0700586 if (NULL != testFallbackConfigFile) {
587 parseConfigFile(testFallbackConfigFile, fallbackFonts);
588 }
djsollen@google.combfae9d32013-05-21 16:53:50 +0000589
590 // Append all fallback fonts to system fonts
591 for (int i = 0; i < fallbackFonts.count(); ++i) {
592 fallbackFonts[i]->fIsFallbackFont = true;
593 *fontFamilies.append() = fallbackFonts[i];
594 }
595}