| 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 |