OLD | NEW |
| (Empty) |
1 /* | |
2 * Copyright 2011 The Android Open Source Project | |
3 * | |
4 * Use of this source code is governed by a BSD-style license that can be | |
5 * found in the LICENSE file. | |
6 */ | |
7 | |
8 #include "SkFontConfigParser_android.h" | |
9 #include "SkFontMgr_android.h" | |
10 #include "SkStream.h" | |
11 #include "SkTDArray.h" | |
12 #include "SkTSearch.h" | |
13 #include "SkTypeface.h" | |
14 | |
15 #include <expat.h> | |
16 #include <dirent.h> | |
17 | |
18 #include <stdlib.h> | |
19 | |
20 #define LMP_SYSTEM_FONTS_FILE "/system/etc/fonts.xml" | |
21 #define OLD_SYSTEM_FONTS_FILE "/system/etc/system_fonts.xml" | |
22 #define FALLBACK_FONTS_FILE "/system/etc/fallback_fonts.xml" | |
23 #define VENDOR_FONTS_FILE "/vendor/etc/fallback_fonts.xml" | |
24 | |
25 #define LOCALE_FALLBACK_FONTS_SYSTEM_DIR "/system/etc" | |
26 #define LOCALE_FALLBACK_FONTS_VENDOR_DIR "/vendor/etc" | |
27 #define LOCALE_FALLBACK_FONTS_PREFIX "fallback_fonts-" | |
28 #define LOCALE_FALLBACK_FONTS_SUFFIX ".xml" | |
29 | |
30 #ifndef SK_FONT_FILE_PREFIX | |
31 # define SK_FONT_FILE_PREFIX "/fonts/" | |
32 #endif | |
33 | |
34 /** | |
35 * This file contains TWO 'familyset' handlers: | |
36 * One for JB and earlier which works with | |
37 * /system/etc/system_fonts.xml | |
38 * /system/etc/fallback_fonts.xml | |
39 * /vendor/etc/fallback_fonts.xml | |
40 * /system/etc/fallback_fonts-XX.xml | |
41 * /vendor/etc/fallback_fonts-XX.xml | |
42 * and the other for LMP and later which works with | |
43 * /system/etc/fonts.xml | |
44 * | |
45 * If the 'familyset' 'version' attribute is 21 or higher the LMP parser is used
, otherwise the JB. | |
46 */ | |
47 | |
48 struct FamilyData; | |
49 | |
50 struct TagHandler { | |
51 /** Called at the start tag. | |
52 * Called immediately after the parent tag retuns this handler from a call
to 'tag'. | |
53 * Allows setting up for handling the tag content and processing attributes
. | |
54 * If NULL, will not be called. | |
55 */ | |
56 void (*start)(FamilyData* data, const char* tag, const char** attributes); | |
57 | |
58 /** Called at the end tag. | |
59 * Allows post-processing of any accumulated information. | |
60 * This will be the last call made in relation to the current tag. | |
61 * If NULL, will not be called. | |
62 */ | |
63 void (*end)(FamilyData* data, const char* tag); | |
64 | |
65 /** Called when a nested tag is encountered. | |
66 * This is responsible for determining how to handle the tag. | |
67 * If the tag is not recognized, return NULL to skip the tag. | |
68 * If NULL, all nested tags will be skipped. | |
69 */ | |
70 const TagHandler* (*tag)(FamilyData* data, const char* tag, const char** att
ributes); | |
71 | |
72 /** The character handler for this tag. | |
73 * This is only active for character data contained directly in this tag (n
ot sub-tags). | |
74 * The first parameter will be castable to a FamilyData*. | |
75 * If NULL, any character data in this tag will be ignored. | |
76 */ | |
77 XML_CharacterDataHandler chars; | |
78 }; | |
79 | |
80 /** Represents the current parsing state. */ | |
81 struct FamilyData { | |
82 FamilyData(XML_Parser parser, SkTDArray<FontFamily*>& families, | |
83 const SkString& basePath, bool isFallback, const char* filename, | |
84 const TagHandler* topLevelHandler) | |
85 : fParser(parser) | |
86 , fFamilies(families) | |
87 , fCurrentFamily(NULL) | |
88 , fCurrentFontInfo(NULL) | |
89 , fVersion(0) | |
90 , fBasePath(basePath) | |
91 , fIsFallback(isFallback) | |
92 , fFilename(filename) | |
93 , fDepth(1) | |
94 , fSkip(0) | |
95 , fHandler(&topLevelHandler, 1) | |
96 { }; | |
97 | |
98 XML_Parser fParser; // The expat parser doing the work
, owned by caller | |
99 SkTDArray<FontFamily*>& fFamilies; // The array to append families, o
wned by caller | |
100 SkAutoTDelete<FontFamily> fCurrentFamily; // The family being created, owned
by this | |
101 FontFileInfo* fCurrentFontInfo; // The fontInfo being created, own
ed by fCurrentFamily | |
102 int fVersion; // The version of the file parsed. | |
103 const SkString& fBasePath; // The current base path. | |
104 const bool fIsFallback; // Indicates the file being parsed
is a fallback file | |
105 const char* fFilename; // The name of the file currently
being parsed. | |
106 | |
107 int fDepth; // The current element depth of th
e parse. | |
108 int fSkip; // The depth to stop skipping, 0 i
f not skipping. | |
109 SkTDArray<const TagHandler*> fHandler; // The stack of current tag handle
rs. | |
110 }; | |
111 | |
112 static bool memeq(const char* s1, const char* s2, size_t n1, size_t n2) { | |
113 return n1 == n2 && 0 == memcmp(s1, s2, n1); | |
114 } | |
115 #define MEMEQ(c, s, n) memeq(c, s, sizeof(c) - 1, n) | |
116 | |
117 #define ATTS_NON_NULL(a, i) (a[i] != NULL && a[i+1] != NULL) | |
118 | |
119 #define SK_FONTCONFIGPARSER_PREFIX "[SkFontConfigParser] " | |
120 | |
121 #define SK_FONTCONFIGPARSER_WARNING(message, ...) SkDebugf( \ | |
122 SK_FONTCONFIGPARSER_PREFIX "%s:%d:%d: warning: " message "\n", \ | |
123 self->fFilename, \ | |
124 XML_GetCurrentLineNumber(self->fParser), \ | |
125 XML_GetCurrentColumnNumber(self->fParser), \ | |
126 ##__VA_ARGS__); | |
127 | |
128 static bool is_whitespace(char c) { | |
129 return c == ' ' || c == '\n'|| c == '\r' || c == '\t'; | |
130 } | |
131 | |
132 static void trim_string(SkString* s) { | |
133 char* str = s->writable_str(); | |
134 const char* start = str; // start is inclusive | |
135 const char* end = start + s->size(); // end is exclusive | |
136 while (is_whitespace(*start)) { ++start; } | |
137 if (start != end) { | |
138 --end; // make end inclusive | |
139 while (is_whitespace(*end)) { --end; } | |
140 ++end; // make end exclusive | |
141 } | |
142 size_t len = end - start; | |
143 memmove(str, start, len); | |
144 s->resize(len); | |
145 } | |
146 | |
147 namespace lmpParser { | |
148 | |
149 static const TagHandler axisHandler = { | |
150 /*start*/[](FamilyData* self, const char* tag, const char** attributes) { | |
151 FontFileInfo& file = *self->fCurrentFontInfo; | |
152 FontFileInfo::Axis& axis = file.fAxes.push_back(); | |
153 for (size_t i = 0; ATTS_NON_NULL(attributes, i); i += 2) { | |
154 const char* name = attributes[i]; | |
155 const char* value = attributes[i+1]; | |
156 size_t nameLen = strlen(name); | |
157 if (MEMEQ("tag", name, nameLen)) { | |
158 size_t valueLen = strlen(value); | |
159 if (valueLen == 4) { | |
160 SkFourByteTag tag = SkSetFourByteTag(value[0], value[1], val
ue[2], value[3]); | |
161 for (int j = 0; j < file.fAxes.count() - 1; ++j) { | |
162 if (file.fAxes[j].fTag == tag) { | |
163 SK_FONTCONFIGPARSER_WARNING("'%c%c%c%c' axis specifi
ed more than once", | |
164 (tag >> 24) & 0xFF, | |
165 (tag >> 16) & 0xFF, | |
166 (tag >> 8) & 0xFF, | |
167 (tag ) & 0xFF); | |
168 } | |
169 } | |
170 axis.fTag = SkSetFourByteTag(value[0], value[1], value[2], v
alue[3]); | |
171 } else { | |
172 SK_FONTCONFIGPARSER_WARNING("'%s' is an invalid axis tag", v
alue); | |
173 } | |
174 } else if (MEMEQ("stylevalue", name, nameLen)) { | |
175 if (!parse_fixed<16>(value, &axis.fValue)) { | |
176 SK_FONTCONFIGPARSER_WARNING("'%s' is an invalid axis styleva
lue", value); | |
177 } | |
178 } | |
179 } | |
180 }, | |
181 /*end*/NULL, | |
182 /*tag*/NULL, | |
183 /*chars*/NULL, | |
184 }; | |
185 | |
186 static const TagHandler fontHandler = { | |
187 /*start*/[](FamilyData* self, const char* tag, const char** attributes) { | |
188 // 'weight' (non-negative integer) [default 0] | |
189 // 'style' ("normal", "italic") [default "auto"] | |
190 // 'index' (non-negative integer) [default 0] | |
191 // The character data should be a filename. | |
192 FontFileInfo& file = self->fCurrentFamily->fFonts.push_back(); | |
193 self->fCurrentFontInfo = &file; | |
194 for (size_t i = 0; ATTS_NON_NULL(attributes, i); i += 2) { | |
195 const char* name = attributes[i]; | |
196 const char* value = attributes[i+1]; | |
197 size_t nameLen = strlen(name); | |
198 if (MEMEQ("weight", name, nameLen)) { | |
199 if (!parse_non_negative_integer(value, &file.fWeight)) { | |
200 SK_FONTCONFIGPARSER_WARNING("'%s' is an invalid weight", val
ue); | |
201 } | |
202 } else if (MEMEQ("style", name, nameLen)) { | |
203 size_t valueLen = strlen(value); | |
204 if (MEMEQ("normal", value, valueLen)) { | |
205 file.fStyle = FontFileInfo::Style::kNormal; | |
206 } else if (MEMEQ("italic", value, valueLen)) { | |
207 file.fStyle = FontFileInfo::Style::kItalic; | |
208 } | |
209 } else if (MEMEQ("index", name, nameLen)) { | |
210 if (!parse_non_negative_integer(value, &file.fIndex)) { | |
211 SK_FONTCONFIGPARSER_WARNING("'%s' is an invalid index", valu
e); | |
212 } | |
213 } | |
214 } | |
215 }, | |
216 /*end*/[](FamilyData* self, const char* tag) { | |
217 trim_string(&self->fCurrentFontInfo->fFileName); | |
218 }, | |
219 /*tag*/[](FamilyData* self, const char* tag, const char** attributes) -> con
st TagHandler* { | |
220 size_t len = strlen(tag); | |
221 if (MEMEQ("axis", tag, len)) { | |
222 return &axisHandler; | |
223 } | |
224 return NULL; | |
225 }, | |
226 /*chars*/[](void* data, const char* s, int len) { | |
227 FamilyData* self = static_cast<FamilyData*>(data); | |
228 self->fCurrentFontInfo->fFileName.append(s, len); | |
229 } | |
230 }; | |
231 | |
232 static const TagHandler familyHandler = { | |
233 /*start*/[](FamilyData* self, const char* tag, const char** attributes) { | |
234 // 'name' (string) [optional] | |
235 // 'lang' (string) [default ""] | |
236 // 'variant' ("elegant", "compact") [default "default"] | |
237 // If there is no name, this is a fallback only font. | |
238 FontFamily* family = new FontFamily(self->fBasePath, true); | |
239 self->fCurrentFamily.reset(family); | |
240 for (size_t i = 0; ATTS_NON_NULL(attributes, i); i += 2) { | |
241 const char* name = attributes[i]; | |
242 const char* value = attributes[i+1]; | |
243 size_t nameLen = strlen(name); | |
244 size_t valueLen = strlen(value); | |
245 if (MEMEQ("name", name, nameLen)) { | |
246 SkAutoAsciiToLC tolc(value); | |
247 family->fNames.push_back().set(tolc.lc()); | |
248 family->fIsFallbackFont = false; | |
249 } else if (MEMEQ("lang", name, nameLen)) { | |
250 family->fLanguage = SkLanguage(value, valueLen); | |
251 } else if (MEMEQ("variant", name, nameLen)) { | |
252 if (MEMEQ("elegant", value, valueLen)) { | |
253 family->fVariant = kElegant_FontVariant; | |
254 } else if (MEMEQ("compact", value, valueLen)) { | |
255 family->fVariant = kCompact_FontVariant; | |
256 } | |
257 } | |
258 } | |
259 }, | |
260 /*end*/[](FamilyData* self, const char* tag) { | |
261 *self->fFamilies.append() = self->fCurrentFamily.detach(); | |
262 }, | |
263 /*tag*/[](FamilyData* self, const char* tag, const char** attributes) -> con
st TagHandler* { | |
264 size_t len = strlen(tag); | |
265 if (MEMEQ("font", tag, len)) { | |
266 return &fontHandler; | |
267 } | |
268 return NULL; | |
269 }, | |
270 /*chars*/NULL, | |
271 }; | |
272 | |
273 static FontFamily* find_family(FamilyData* self, const SkString& familyName) { | |
274 for (int i = 0; i < self->fFamilies.count(); i++) { | |
275 FontFamily* candidate = self->fFamilies[i]; | |
276 for (int j = 0; j < candidate->fNames.count(); j++) { | |
277 if (candidate->fNames[j] == familyName) { | |
278 return candidate; | |
279 } | |
280 } | |
281 } | |
282 return NULL; | |
283 } | |
284 | |
285 static const TagHandler aliasHandler = { | |
286 /*start*/[](FamilyData* self, const char* tag, const char** attributes) { | |
287 // 'name' (string) introduces a new family name. | |
288 // 'to' (string) specifies which (previous) family to alias | |
289 // 'weight' (non-negative integer) [optional] | |
290 // If it *does not* have a weight, 'name' is an alias for the entire 'to
' family. | |
291 // If it *does* have a weight, 'name' is a new family consisting of | |
292 // the font(s) with 'weight' from the 'to' family. | |
293 | |
294 SkString aliasName; | |
295 SkString to; | |
296 int weight = 0; | |
297 for (size_t i = 0; ATTS_NON_NULL(attributes, i); i += 2) { | |
298 const char* name = attributes[i]; | |
299 const char* value = attributes[i+1]; | |
300 size_t nameLen = strlen(name); | |
301 if (MEMEQ("name", name, nameLen)) { | |
302 SkAutoAsciiToLC tolc(value); | |
303 aliasName.set(tolc.lc()); | |
304 } else if (MEMEQ("to", name, nameLen)) { | |
305 to.set(value); | |
306 } else if (MEMEQ("weight", name, nameLen)) { | |
307 if (!parse_non_negative_integer(value, &weight)) { | |
308 SK_FONTCONFIGPARSER_WARNING("'%s' is an invalid weight", val
ue); | |
309 } | |
310 } | |
311 } | |
312 | |
313 // Assumes that the named family is already declared | |
314 FontFamily* targetFamily = find_family(self, to); | |
315 if (!targetFamily) { | |
316 SK_FONTCONFIGPARSER_WARNING("'%s' alias target not found", to.c_str(
)); | |
317 return; | |
318 } | |
319 | |
320 if (weight) { | |
321 FontFamily* family = new FontFamily(targetFamily->fBasePath, self->f
IsFallback); | |
322 family->fNames.push_back().set(aliasName); | |
323 | |
324 for (int i = 0; i < targetFamily->fFonts.count(); i++) { | |
325 if (targetFamily->fFonts[i].fWeight == weight) { | |
326 family->fFonts.push_back(targetFamily->fFonts[i]); | |
327 } | |
328 } | |
329 *self->fFamilies.append() = family; | |
330 } else { | |
331 targetFamily->fNames.push_back().set(aliasName); | |
332 } | |
333 }, | |
334 /*end*/NULL, | |
335 /*tag*/NULL, | |
336 /*chars*/NULL, | |
337 }; | |
338 | |
339 static const TagHandler familySetHandler = { | |
340 /*start*/[](FamilyData* self, const char* tag, const char** attributes) { }, | |
341 /*end*/NULL, | |
342 /*tag*/[](FamilyData* self, const char* tag, const char** attributes) -> con
st TagHandler* { | |
343 size_t len = strlen(tag); | |
344 if (MEMEQ("family", tag, len)) { | |
345 return &familyHandler; | |
346 } else if (MEMEQ("alias", tag, len)) { | |
347 return &aliasHandler; | |
348 } | |
349 return NULL; | |
350 }, | |
351 /*chars*/NULL, | |
352 }; | |
353 | |
354 } // lmpParser | |
355 | |
356 namespace jbParser { | |
357 | |
358 static const TagHandler fileHandler = { | |
359 /*start*/[](FamilyData* self, const char* tag, const char** attributes) { | |
360 // 'variant' ("elegant", "compact") [default "default"] | |
361 // 'lang' (string) [default ""] | |
362 // 'index' (non-negative integer) [default 0] | |
363 // The character data should be a filename. | |
364 FontFamily& currentFamily = *self->fCurrentFamily.get(); | |
365 FontFileInfo& newFileInfo = currentFamily.fFonts.push_back(); | |
366 if (attributes) { | |
367 for (size_t i = 0; ATTS_NON_NULL(attributes, i); i += 2) { | |
368 const char* name = attributes[i]; | |
369 const char* value = attributes[i+1]; | |
370 size_t nameLen = strlen(name); | |
371 size_t valueLen = strlen(value); | |
372 if (MEMEQ("variant", name, nameLen)) { | |
373 const FontVariant prevVariant = currentFamily.fVariant; | |
374 if (MEMEQ("elegant", value, valueLen)) { | |
375 currentFamily.fVariant = kElegant_FontVariant; | |
376 } else if (MEMEQ("compact", value, valueLen)) { | |
377 currentFamily.fVariant = kCompact_FontVariant; | |
378 } | |
379 if (currentFamily.fFonts.count() > 1 && currentFamily.fVaria
nt != prevVariant) { | |
380 SK_FONTCONFIGPARSER_WARNING("'%s' unexpected variant fou
nd\n" | |
381 "Note: Every font file within a family must have ide
ntical variants.", | |
382 value); | |
383 } | |
384 | |
385 } else if (MEMEQ("lang", name, nameLen)) { | |
386 SkLanguage prevLang = currentFamily.fLanguage; | |
387 currentFamily.fLanguage = SkLanguage(value, valueLen); | |
388 if (currentFamily.fFonts.count() > 1 && currentFamily.fLangu
age != prevLang) { | |
389 SK_FONTCONFIGPARSER_WARNING("'%s' unexpected language fo
und\n" | |
390 "Note: Every font file within a family must have ide
ntical languages.", | |
391 value); | |
392 } | |
393 | |
394 } else if (MEMEQ("index", name, nameLen)) { | |
395 if (!parse_non_negative_integer(value, &newFileInfo.fIndex))
{ | |
396 SK_FONTCONFIGPARSER_WARNING("'%s' is an invalid index",
value); | |
397 } | |
398 } | |
399 } | |
400 } | |
401 self->fCurrentFontInfo = &newFileInfo; | |
402 }, | |
403 /*end*/NULL, | |
404 /*tag*/NULL, | |
405 /*chars*/[](void* data, const char* s, int len) { | |
406 FamilyData* self = static_cast<FamilyData*>(data); | |
407 self->fCurrentFontInfo->fFileName.append(s, len); | |
408 } | |
409 }; | |
410 | |
411 static const TagHandler fileSetHandler = { | |
412 /*start*/NULL, | |
413 /*end*/NULL, | |
414 /*tag*/[](FamilyData* self, const char* tag, const char** attributes) -> con
st TagHandler* { | |
415 size_t len = strlen(tag); | |
416 if (MEMEQ("file", tag, len)) { | |
417 return &fileHandler; | |
418 } | |
419 return NULL; | |
420 }, | |
421 /*chars*/NULL, | |
422 }; | |
423 | |
424 static const TagHandler nameHandler = { | |
425 /*start*/[](FamilyData* self, const char* tag, const char** attributes) { | |
426 // The character data should be a name for the font. | |
427 self->fCurrentFamily->fNames.push_back(); | |
428 }, | |
429 /*end*/NULL, | |
430 /*tag*/NULL, | |
431 /*chars*/[](void* data, const char* s, int len) { | |
432 FamilyData* self = static_cast<FamilyData*>(data); | |
433 SkAutoAsciiToLC tolc(s, len); | |
434 self->fCurrentFamily->fNames.back().append(tolc.lc(), len); | |
435 } | |
436 }; | |
437 | |
438 static const TagHandler nameSetHandler = { | |
439 /*start*/NULL, | |
440 /*end*/NULL, | |
441 /*tag*/[](FamilyData* self, const char* tag, const char** attributes) -> con
st TagHandler* { | |
442 size_t len = strlen(tag); | |
443 if (MEMEQ("name", tag, len)) { | |
444 return &nameHandler; | |
445 } | |
446 return NULL; | |
447 }, | |
448 /*chars*/NULL, | |
449 }; | |
450 | |
451 static const TagHandler familyHandler = { | |
452 /*start*/[](FamilyData* self, const char* tag, const char** attributes) { | |
453 self->fCurrentFamily.reset(new FontFamily(self->fBasePath, self->fIsFall
back)); | |
454 // 'order' (non-negative integer) [default -1] | |
455 for (size_t i = 0; ATTS_NON_NULL(attributes, i); i += 2) { | |
456 const char* value = attributes[i+1]; | |
457 parse_non_negative_integer(value, &self->fCurrentFamily->fOrder); | |
458 } | |
459 }, | |
460 /*end*/[](FamilyData* self, const char* tag) { | |
461 *self->fFamilies.append() = self->fCurrentFamily.detach(); | |
462 }, | |
463 /*tag*/[](FamilyData* self, const char* tag, const char** attributes) -> con
st TagHandler* { | |
464 size_t len = strlen(tag); | |
465 if (MEMEQ("nameset", tag, len)) { | |
466 return &nameSetHandler; | |
467 } else if (MEMEQ("fileset", tag, len)) { | |
468 return &fileSetHandler; | |
469 } | |
470 return NULL; | |
471 }, | |
472 /*chars*/NULL, | |
473 }; | |
474 | |
475 static const TagHandler familySetHandler = { | |
476 /*start*/NULL, | |
477 /*end*/NULL, | |
478 /*tag*/[](FamilyData* self, const char* tag, const char** attributes) -> con
st TagHandler* { | |
479 size_t len = strlen(tag); | |
480 if (MEMEQ("family", tag, len)) { | |
481 return &familyHandler; | |
482 } | |
483 return NULL; | |
484 }, | |
485 /*chars*/NULL, | |
486 }; | |
487 | |
488 } // namespace jbParser | |
489 | |
490 static const TagHandler topLevelHandler = { | |
491 /*start*/NULL, | |
492 /*end*/NULL, | |
493 /*tag*/[](FamilyData* self, const char* tag, const char** attributes) -> con
st TagHandler* { | |
494 size_t len = strlen(tag); | |
495 if (MEMEQ("familyset", tag, len)) { | |
496 // 'version' (non-negative integer) [default 0] | |
497 for (size_t i = 0; ATTS_NON_NULL(attributes, i); i += 2) { | |
498 const char* name = attributes[i]; | |
499 size_t nameLen = strlen(name); | |
500 if (MEMEQ("version", name, nameLen)) { | |
501 const char* value = attributes[i+1]; | |
502 if (parse_non_negative_integer(value, &self->fVersion)) { | |
503 if (self->fVersion >= 21) { | |
504 return &lmpParser::familySetHandler; | |
505 } | |
506 } | |
507 } | |
508 } | |
509 return &jbParser::familySetHandler; | |
510 } | |
511 return NULL; | |
512 }, | |
513 /*chars*/NULL, | |
514 }; | |
515 | |
516 static void XMLCALL start_element_handler(void *data, const char *tag, const cha
r **attributes) { | |
517 FamilyData* self = static_cast<FamilyData*>(data); | |
518 | |
519 if (!self->fSkip) { | |
520 const TagHandler* parent = self->fHandler.top(); | |
521 const TagHandler* child = parent->tag ? parent->tag(self, tag, attribute
s) : NULL; | |
522 if (child) { | |
523 if (child->start) { | |
524 child->start(self, tag, attributes); | |
525 } | |
526 self->fHandler.push(child); | |
527 XML_SetCharacterDataHandler(self->fParser, child->chars); | |
528 } else { | |
529 SK_FONTCONFIGPARSER_WARNING("'%s' tag not recognized, skipping", tag
); | |
530 XML_SetCharacterDataHandler(self->fParser, NULL); | |
531 self->fSkip = self->fDepth; | |
532 } | |
533 } | |
534 | |
535 ++self->fDepth; | |
536 } | |
537 | |
538 static void XMLCALL end_element_handler(void* data, const char* tag) { | |
539 FamilyData* self = static_cast<FamilyData*>(data); | |
540 --self->fDepth; | |
541 | |
542 if (!self->fSkip) { | |
543 const TagHandler* child = self->fHandler.top(); | |
544 if (child->end) { | |
545 child->end(self, tag); | |
546 } | |
547 self->fHandler.pop(); | |
548 const TagHandler* parent = self->fHandler.top(); | |
549 XML_SetCharacterDataHandler(self->fParser, parent->chars); | |
550 } | |
551 | |
552 if (self->fSkip == self->fDepth) { | |
553 self->fSkip = 0; | |
554 const TagHandler* parent = self->fHandler.top(); | |
555 XML_SetCharacterDataHandler(self->fParser, parent->chars); | |
556 } | |
557 } | |
558 | |
559 static void XMLCALL xml_entity_decl_handler(void *data, | |
560 const XML_Char *entityName, | |
561 int is_parameter_entity, | |
562 const XML_Char *value, | |
563 int value_length, | |
564 const XML_Char *base, | |
565 const XML_Char *systemId, | |
566 const XML_Char *publicId, | |
567 const XML_Char *notationName) | |
568 { | |
569 FamilyData* self = static_cast<FamilyData*>(data); | |
570 SK_FONTCONFIGPARSER_WARNING("'%s' entity declaration found, stopping process
ing", entityName); | |
571 XML_StopParser(self->fParser, XML_FALSE); | |
572 } | |
573 | |
574 static const XML_Memory_Handling_Suite sk_XML_alloc = { | |
575 sk_malloc_throw, | |
576 sk_realloc_throw, | |
577 sk_free | |
578 }; | |
579 | |
580 template<typename T> struct remove_ptr {typedef T type;}; | |
581 template<typename T> struct remove_ptr<T*> {typedef T type;}; | |
582 | |
583 /** | |
584 * This function parses the given filename and stores the results in the given | |
585 * families array. Returns the version of the file, negative if the file does no
t exist. | |
586 */ | |
587 static int parse_config_file(const char* filename, SkTDArray<FontFamily*>& famil
ies, | |
588 const SkString& basePath, bool isFallback) | |
589 { | |
590 SkFILEStream file(filename); | |
591 | |
592 // Some of the files we attempt to parse (in particular, /vendor/etc/fallbac
k_fonts.xml) | |
593 // are optional - failure here is okay because one of these optional files m
ay not exist. | |
594 if (!file.isValid()) { | |
595 SkDebugf(SK_FONTCONFIGPARSER_PREFIX "'%s' could not be opened\n", filena
me); | |
596 return -1; | |
597 } | |
598 | |
599 SkAutoTCallVProc<remove_ptr<XML_Parser>::type, XML_ParserFree> parser( | |
600 XML_ParserCreate_MM(NULL, &sk_XML_alloc, NULL)); | |
601 if (!parser) { | |
602 SkDebugf(SK_FONTCONFIGPARSER_PREFIX "could not create XML parser\n"); | |
603 return -1; | |
604 } | |
605 | |
606 FamilyData self(parser, families, basePath, isFallback, filename, &topLevelH
andler); | |
607 XML_SetUserData(parser, &self); | |
608 | |
609 // Disable entity processing, to inhibit internal entity expansion. See expa
t CVE-2013-0340 | |
610 XML_SetEntityDeclHandler(parser, xml_entity_decl_handler); | |
611 | |
612 // Start parsing oldschool; switch these in flight if we detect a newer vers
ion of the file. | |
613 XML_SetElementHandler(parser, start_element_handler, end_element_handler); | |
614 | |
615 // One would assume it would be faster to have a buffer on the stack and cal
l XML_Parse. | |
616 // But XML_Parse will call XML_GetBuffer anyway and memmove the passed buffe
r into it. | |
617 // (Unless XML_CONTEXT_BYTES is undefined, but all users define it.) | |
618 // In debug, buffer a small odd number of bytes to detect slicing in XML_Cha
racterDataHandler. | |
619 static const int bufferSize = 512 SkDEBUGCODE( - 507); | |
620 bool done = false; | |
621 while (!done) { | |
622 void* buffer = XML_GetBuffer(parser, bufferSize); | |
623 if (!buffer) { | |
624 SkDebugf(SK_FONTCONFIGPARSER_PREFIX "could not buffer enough to cont
inue\n"); | |
625 return -1; | |
626 } | |
627 size_t len = file.read(buffer, bufferSize); | |
628 done = file.isAtEnd(); | |
629 XML_Status status = XML_ParseBuffer(parser, len, done); | |
630 if (XML_STATUS_ERROR == status) { | |
631 XML_Error error = XML_GetErrorCode(parser); | |
632 int line = XML_GetCurrentLineNumber(parser); | |
633 int column = XML_GetCurrentColumnNumber(parser); | |
634 const XML_LChar* errorString = XML_ErrorString(error); | |
635 SkDebugf(SK_FONTCONFIGPARSER_PREFIX "%s:%d:%d error %d: %s.\n", | |
636 filename, line, column, error, errorString); | |
637 return -1; | |
638 } | |
639 } | |
640 return self.fVersion; | |
641 } | |
642 | |
643 /** Returns the version of the system font file actually found, negative if none
. */ | |
644 static int append_system_font_families(SkTDArray<FontFamily*>& fontFamilies, | |
645 const SkString& basePath) | |
646 { | |
647 int initialCount = fontFamilies.count(); | |
648 int version = parse_config_file(LMP_SYSTEM_FONTS_FILE, fontFamilies, basePat
h, false); | |
649 if (version < 0 || fontFamilies.count() == initialCount) { | |
650 version = parse_config_file(OLD_SYSTEM_FONTS_FILE, fontFamilies, basePat
h, false); | |
651 } | |
652 return version; | |
653 } | |
654 | |
655 /** | |
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 */ | |
662 static void append_fallback_font_families_for_locale(SkTDArray<FontFamily*>& fal
lbackFonts, | |
663 const char* dir, | |
664 const SkString& basePath) | |
665 { | |
666 #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 | |
671 SkAutoTCallIProc<DIR, closedir> fontDirectory(opendir(dir)); | |
672 if (NULL == fontDirectory) { | |
673 return; | |
674 } | |
675 | |
676 for (struct dirent* dirEntry; (dirEntry = readdir(fontDirectory));) { | |
677 // 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; | |
683 | |
684 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; | |
690 } | |
691 | |
692 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 } | |
706 } | |
707 } | |
708 | |
709 static void append_system_fallback_font_families(SkTDArray<FontFamily*>& fallbac
kFonts, | |
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); | |
716 } | |
717 | |
718 static void mixin_vendor_fallback_font_families(SkTDArray<FontFamily*>& fallback
Fonts, | |
719 const SkString& basePath) | |
720 { | |
721 SkTDArray<FontFamily*> vendorFonts; | |
722 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); | |
726 | |
727 // 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]; | |
732 int order = family->fOrder; | |
733 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 o
rder | |
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 l
ist. | |
745 *fallbackFonts.insert(order) = family; | |
746 currentOrder = order + 1; | |
747 } | |
748 } | |
749 } | |
750 | |
751 void SkFontConfigParser::GetSystemFontFamilies(SkTDArray<FontFamily*>& fontFamil
ies) { | |
752 // Version 21 of the system font configuration does not need any fallback co
nfiguration files. | |
753 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) { | |
757 return; | |
758 } | |
759 | |
760 // Append all the fallback fonts to system fonts | |
761 SkTDArray<FontFamily*> fallbackFonts; | |
762 append_system_fallback_font_families(fallbackFonts, basePath); | |
763 mixin_vendor_fallback_font_families(fallbackFonts, basePath); | |
764 fontFamilies.append(fallbackFonts.count(), fallbackFonts.begin()); | |
765 } | |
766 | |
767 void SkFontConfigParser::GetCustomFontFamilies(SkTDArray<FontFamily*>& fontFamil
ies, | |
768 const SkString& basePath, | |
769 const char* fontsXml, | |
770 const char* fallbackFontsXml, | |
771 const char* langFallbackFontsDir) | |
772 { | |
773 if (fontsXml) { | |
774 parse_config_file(fontsXml, fontFamilies, basePath, false); | |
775 } | |
776 if (fallbackFontsXml) { | |
777 parse_config_file(fallbackFontsXml, fontFamilies, basePath, true); | |
778 } | |
779 if (langFallbackFontsDir) { | |
780 append_fallback_font_families_for_locale(fontFamilies, | |
781 langFallbackFontsDir, | |
782 basePath); | |
783 } | |
784 } | |
785 | |
786 SkLanguage 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, '-'); | |
792 if (parentTagEnd == NULL) { | |
793 return SkLanguage(); | |
794 } | |
795 size_t parentTagLen = parentTagEnd - tag; | |
796 return SkLanguage(tag, parentTagLen); | |
797 } | |
OLD | NEW |