Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(149)

Side by Side Diff: src/ports/SkFontConfigParser_android.cpp

Issue 439813002: Test and generalize font configuration parser (Closed) Base URL: https://skia.googlesource.com/skia.git@master
Patch Set: Respond to reviews Created 6 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « resources/android_fonts/v22/fonts.xml ('k') | tests/FontConfigParser.cpp » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 /* 1 /*
2 * Copyright 2011 The Android Open Source Project 2 * Copyright 2011 The Android Open Source Project
3 * 3 *
4 * Use of this source code is governed by a BSD-style license that can be 4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file. 5 * found in the LICENSE file.
6 */ 6 */
7 7
8 #include "SkFontConfigParser_android.h" 8 #include "SkFontConfigParser_android.h"
9 #include "SkTDArray.h" 9 #include "SkTDArray.h"
10 #include "SkTSearch.h" 10 #include "SkTSearch.h"
11 #include "SkTypeface.h" 11 #include "SkTypeface.h"
12 12
13 #include <expat.h> 13 #include <expat.h>
14 #include <stdio.h> 14 #include <stdio.h>
15 #include <sys/system_properties.h> 15 #include <sys/system_properties.h>
16 16
17 #include <limits> 17 #include <limits>
18 18
19 #define SYSTEM_FONTS_FILE "/system/etc/system_fonts.xml" 19 #define SYSTEM_FONTS_FILE "/system/etc/system_fonts.xml"
20 #define FALLBACK_FONTS_FILE "/system/etc/fallback_fonts.xml" 20 #define FALLBACK_FONTS_FILE "/system/etc/fallback_fonts.xml"
21 #define VENDOR_FONTS_FILE "/vendor/etc/fallback_fonts.xml" 21 #define VENDOR_FONTS_FILE "/vendor/etc/fallback_fonts.xml"
22 22
23 /**
24 * This file contains TWO parsers: one for JB and earlier (system_fonts.xml /
25 * fallback_fonts.xml), one for LMP and later (fonts.xml).
26 * We start with the JB parser, and if we detect a <familyset> tag with
27 * version 21 or higher we switch to the LMP parser.
28 */
29
23 // These defines are used to determine the kind of tag that we're currently 30 // These defines are used to determine the kind of tag that we're currently
24 // populating with data. We only care about the sibling tags nameset and fileset 31 // populating with data. We only care about the sibling tags nameset and fileset
25 // for now. 32 // for now.
26 #define NO_TAG 0 33 #define NO_TAG 0
27 #define NAMESET_TAG 1 34 #define NAMESET_TAG 1
28 #define FILESET_TAG 2 35 #define FILESET_TAG 2
29 36
30 /** 37 /**
31 * The FamilyData structure is passed around by the parser so that each handler 38 * The FamilyData structure is passed around by the parser so that each handler
32 * can read these variables that are relevant to the current parsing. 39 * can read these variables that are relevant to the current parsing.
33 */ 40 */
34 struct FamilyData { 41 struct FamilyData {
35 FamilyData(XML_Parser *parserRef, SkTDArray<FontFamily*> &familiesRef) : 42 FamilyData(XML_Parser *parserRef, SkTDArray<FontFamily*> &familiesRef) :
36 parser(parserRef), 43 parser(parserRef),
37 families(familiesRef), 44 families(familiesRef),
38 currentFamily(NULL), 45 currentFamily(NULL),
39 currentFontInfo(NULL), 46 currentFontInfo(NULL),
40 currentTag(NO_TAG) {}; 47 currentTag(NO_TAG) {};
41 48
42 XML_Parser *parser; // The expat parser doing the work 49 XML_Parser *parser; // The expat parser doing the work
43 SkTDArray<FontFamily*> &families; // The array that each family is put into as it is parsed 50 SkTDArray<FontFamily*> &families; // The array that each family is put into as it is parsed
44 FontFamily *currentFamily; // The current family being created 51 FontFamily *currentFamily; // The current family being created
45 FontFileInfo *currentFontInfo; // The current fontInfo being created 52 FontFileInfo *currentFontInfo; // The current fontInfo being created
46 int currentTag; // A flag to indicate whether we're in na meset/fileset tags 53 int currentTag; // A flag to indicate whether we're in na meset/fileset tags
47 }; 54 };
48 55
56 /** http://www.w3.org/TR/html-markup/datatypes.html#common.data.integer.non-nega tive-def */
57 template <typename T> static bool parseNonNegativeInteger(const char* s, T* valu e) {
58 SK_COMPILE_ASSERT(std::numeric_limits<T>::is_integer, T_must_be_integer);
59 const T nMax = std::numeric_limits<T>::max() / 10;
60 const T dMax = std::numeric_limits<T>::max() - (nMax * 10);
61 T n = 0;
62 for (; *s; ++s) {
63 // Check if digit
64 if (*s < '0' || '9' < *s) {
65 return false;
66 }
67 int d = *s - '0';
68 // Check for overflow
69 if (n > nMax || (n == nMax && d > dMax)) {
70 return false;
71 }
72 n = (n * 10) + d;
73 }
74 *value = n;
75 return true;
76 }
77
78 namespace lmpParser {
79
80 void startElementHandler(void* data, const char* tag,
81 const char** attributes) {
82 //SkDebugf("lmp started %s", tag);
83 }
84
85 void endElementHandler(void* data, const char* tag) {
86
87 //SkDebugf("lmp ended %s", tag);
88
89 }
90
91 } // lmpParser
92
93 namespace jbParser {
94
49 /** 95 /**
50 * Handler for arbitrary text. This is used to parse the text inside each name 96 * Handler for arbitrary text. This is used to parse the text inside each name
51 * or file tag. The resulting strings are put into the fNames or FontFileInfo ar rays. 97 * or file tag. The resulting strings are put into the fNames or FontFileInfo ar rays.
52 */ 98 */
53 static void textHandler(void *data, const char *s, int len) { 99 static void textHandler(void *data, const char *s, int len) {
54 FamilyData *familyData = (FamilyData*) data; 100 FamilyData *familyData = (FamilyData*) data;
55 // Make sure we're in the right state to store this name information 101 // Make sure we're in the right state to store this name information
56 if (familyData->currentFamily && 102 if (familyData->currentFamily &&
57 (familyData->currentTag == NAMESET_TAG || familyData->currentTag == FILESET_TAG)) { 103 (familyData->currentTag == NAMESET_TAG || familyData->currentTag == FILESET_TAG)) {
58 switch (familyData->currentTag) { 104 switch (familyData->currentTag) {
59 case NAMESET_TAG: { 105 case NAMESET_TAG: {
60 SkAutoAsciiToLC tolc(s, len); 106 SkAutoAsciiToLC tolc(s, len);
61 familyData->currentFamily->fNames.push_back().set(tolc.lc(), len); 107 familyData->currentFamily->fNames.push_back().set(tolc.lc(), len);
62 break; 108 break;
63 } 109 }
64 case FILESET_TAG: 110 case FILESET_TAG:
65 if (familyData->currentFontInfo) { 111 if (familyData->currentFontInfo) {
66 familyData->currentFontInfo->fFileName.set(s, len); 112 familyData->currentFontInfo->fFileName.set(s, len);
67 } 113 }
68 break; 114 break;
69 default: 115 default:
70 // Noop - don't care about any text that's not in the Fonts or Names list 116 // Noop - don't care about any text that's not in the Fonts or Names list
71 break; 117 break;
72 } 118 }
73 } 119 }
74 } 120 }
75 121
76 /** http://www.w3.org/TR/html-markup/datatypes.html#common.data.integer.non-nega tive-def */
77 template <typename T> static bool parseNonNegativeInteger(const char* s, T* valu e) {
78 SK_COMPILE_ASSERT(std::numeric_limits<T>::is_integer, T_must_be_integer);
79 const T nMax = std::numeric_limits<T>::max() / 10;
80 const T dMax = std::numeric_limits<T>::max() - (nMax * 10);
81 T n = 0;
82 for (; *s; ++s) {
83 // Check if digit
84 if (*s < '0' || '9' < *s) {
85 return false;
86 }
87 int d = *s - '0';
88 // Check for overflow
89 if (n > nMax || (n == nMax && d > dMax)) {
90 return false;
91 }
92 n = (n * 10) + d;
93 }
94 *value = n;
95 return true;
96 }
97
98 /** 122 /**
99 * Handler for font files. This processes the attributes for language and 123 * Handler for font files. This processes the attributes for language and
100 * variants then lets textHandler handle the actual file name 124 * variants then lets textHandler handle the actual file name
101 */ 125 */
102 static void fontFileElementHandler(FamilyData *familyData, const char **attribut es) { 126 static void fontFileElementHandler(FamilyData *familyData, const char **attribut es) {
103
104 FontFileInfo& newFileInfo = familyData->currentFamily->fFontFiles.push_back( ); 127 FontFileInfo& newFileInfo = familyData->currentFamily->fFontFiles.push_back( );
105 if (attributes) { 128 if (attributes) {
106 int currentAttributeIndex = 0; 129 int currentAttributeIndex = 0;
107 while (attributes[currentAttributeIndex]) { 130 while (attributes[currentAttributeIndex]) {
108 const char* attributeName = attributes[currentAttributeIndex]; 131 const char* attributeName = attributes[currentAttributeIndex];
109 const char* attributeValue = attributes[currentAttributeIndex+1]; 132 const char* attributeValue = attributes[currentAttributeIndex+1];
110 int nameLength = strlen(attributeName); 133 int nameLength = strlen(attributeName);
111 int valueLength = strlen(attributeValue); 134 int valueLength = strlen(attributeValue);
112 if (strncmp(attributeName, "variant", nameLength) == 0) { 135 if (nameLength == 7 && strncmp(attributeName, "variant", nameLength) == 0) {
113 if (strncmp(attributeValue, "elegant", valueLength) == 0) { 136 if (valueLength == 7 && strncmp(attributeValue, "elegant", value Length) == 0) {
114 newFileInfo.fPaintOptions.setFontVariant(SkPaintOptionsAndro id::kElegant_Variant); 137 newFileInfo.fPaintOptions.setFontVariant(SkPaintOptionsAndro id::kElegant_Variant);
115 } else if (strncmp(attributeValue, "compact", valueLength) == 0) { 138 } else if (valueLength == 7 &&
139 strncmp(attributeValue, "compact", valueLength) == 0) {
116 newFileInfo.fPaintOptions.setFontVariant(SkPaintOptionsAndro id::kCompact_Variant); 140 newFileInfo.fPaintOptions.setFontVariant(SkPaintOptionsAndro id::kCompact_Variant);
117 } 141 }
118 } else if (strncmp(attributeName, "lang", nameLength) == 0) { 142 } else if (nameLength == 4 && strncmp(attributeName, "lang", nameLen gth) == 0) {
119 newFileInfo.fPaintOptions.setLanguage(attributeValue); 143 newFileInfo.fPaintOptions.setLanguage(attributeValue);
120 } else if (strncmp(attributeName, "index", nameLength) == 0) { 144 } else if (nameLength == 5 && strncmp(attributeName, "index", nameLe ngth) == 0) {
121 int value; 145 int value;
122 if (parseNonNegativeInteger(attributeValue, &value)) { 146 if (parseNonNegativeInteger(attributeValue, &value)) {
123 newFileInfo.fIndex = value; 147 newFileInfo.fIndex = value;
124 } else { 148 } else {
125 SkDebugf("---- SystemFonts index=%s (INVALID)", attributeVal ue); 149 SkDebugf("---- SystemFonts index=%s (INVALID)", attributeVal ue);
126 } 150 }
127 } 151 }
128 //each element is a pair of attributeName/attributeValue string pair s 152 //each element is a pair of attributeName/attributeValue string pair s
129 currentAttributeIndex += 2; 153 currentAttributeIndex += 2;
130 } 154 }
131 } 155 }
132 familyData->currentFontInfo = &newFileInfo; 156 familyData->currentFontInfo = &newFileInfo;
133 XML_SetCharacterDataHandler(*familyData->parser, textHandler); 157 XML_SetCharacterDataHandler(*familyData->parser, textHandler);
134 } 158 }
135 159
136 /** 160 /**
137 * Handler for the start of a tag. The only tags we expect are family, nameset, 161 * Handler for the start of a tag. The only tags we expect are familyset, family ,
138 * fileset, name, and file. 162 * nameset, fileset, name, and file.
139 */ 163 */
140 static void startElementHandler(void *data, const char *tag, const char **atts) { 164 static void startElementHandler(void *data, const char *tag, const char **atts) {
141 FamilyData *familyData = (FamilyData*) data; 165 FamilyData *familyData = (FamilyData*) data;
142 int len = strlen(tag); 166 int len = strlen(tag);
143 if (strncmp(tag, "family", len)== 0) { 167 if (len == 9 && strncmp(tag, "familyset", len) == 0) {
168 // The familyset tag has an optional "version" attribute with an integer value >= 0
169 for (int i = 0; atts[i] != NULL; i += 2) {
170 int nameLen = strlen(atts[i]);
171 if (nameLen == 7 && strncmp(atts[i], "version", nameLen)) continue;
172 const char* valueString = atts[i+1];
173 int version;
174 bool success = parseNonNegativeInteger(valueString, &version);
175 if (success && version >= 21) {
bungeman-skia 2014/08/04 21:27:55 nit: I think there's enough room here for if (par
176 XML_SetElementHandler(*familyData->parser,
177 lmpParser::startElementHandler,
178 lmpParser::endElementHandler);
179 }
180 }
181 } else if (len == 6 && strncmp(tag, "family", len) == 0) {
144 familyData->currentFamily = new FontFamily(); 182 familyData->currentFamily = new FontFamily();
145 familyData->currentFamily->order = -1; 183 familyData->currentFamily->order = -1;
146 // The Family tag has an optional "order" attribute with an integer valu e >= 0 184 // The Family tag has an optional "order" attribute with an integer valu e >= 0
147 // If this attribute does not exist, the default value is -1 185 // If this attribute does not exist, the default value is -1
148 for (int i = 0; atts[i] != NULL; i += 2) { 186 for (int i = 0; atts[i] != NULL; i += 2) {
149 const char* valueString = atts[i+1]; 187 const char* valueString = atts[i+1];
150 int value; 188 int value;
151 int len = sscanf(valueString, "%d", &value); 189 if (parseNonNegativeInteger(valueString, &value)) {
152 if (len > 0) {
153 familyData->currentFamily->order = value; 190 familyData->currentFamily->order = value;
154 } 191 }
155 } 192 }
156 } else if (len == 7 && strncmp(tag, "nameset", len) == 0) { 193 } else if (len == 7 && strncmp(tag, "nameset", len) == 0) {
157 familyData->currentTag = NAMESET_TAG; 194 familyData->currentTag = NAMESET_TAG;
158 } else if (len == 7 && strncmp(tag, "fileset", len) == 0) { 195 } else if (len == 7 && strncmp(tag, "fileset", len) == 0) {
159 familyData->currentTag = FILESET_TAG; 196 familyData->currentTag = FILESET_TAG;
160 } else if (strncmp(tag, "name", len) == 0 && familyData->currentTag == NAMES ET_TAG) { 197 } else if (len == 4 && strncmp(tag, "name", len) == 0 && familyData->current Tag == NAMESET_TAG) {
161 // If it's a Name, parse the text inside 198 // If it's a Name, parse the text inside
162 XML_SetCharacterDataHandler(*familyData->parser, textHandler); 199 XML_SetCharacterDataHandler(*familyData->parser, textHandler);
163 } else if (strncmp(tag, "file", len) == 0 && familyData->currentTag == FILES ET_TAG) { 200 } else if (len == 4 && strncmp(tag, "file", len) == 0 && familyData->current Tag == FILESET_TAG) {
164 // If it's a file, parse the attributes, then parse the text inside 201 // If it's a file, parse the attributes, then parse the text inside
165 fontFileElementHandler(familyData, atts); 202 fontFileElementHandler(familyData, atts);
166 } 203 }
167 } 204 }
168 205
169 /** 206 /**
170 * Handler for the end of tags. We only care about family, nameset, fileset, 207 * Handler for the end of tags. We only care about family, nameset, fileset,
171 * name, and file. 208 * name, and file.
172 */ 209 */
173 static void endElementHandler(void *data, const char *tag) { 210 static void endElementHandler(void *data, const char *tag) {
174 FamilyData *familyData = (FamilyData*) data; 211 FamilyData *familyData = (FamilyData*) data;
175 int len = strlen(tag); 212 int len = strlen(tag);
176 if (strncmp(tag, "family", len)== 0) { 213 if (len == 6 && strncmp(tag, "family", len)== 0) {
177 // Done parsing a Family - store the created currentFamily in the famili es array 214 // Done parsing a Family - store the created currentFamily in the famili es array
178 *familyData->families.append() = familyData->currentFamily; 215 *familyData->families.append() = familyData->currentFamily;
179 familyData->currentFamily = NULL; 216 familyData->currentFamily = NULL;
180 } else if (len == 7 && strncmp(tag, "nameset", len) == 0) { 217 } else if (len == 7 && strncmp(tag, "nameset", len) == 0) {
181 familyData->currentTag = NO_TAG; 218 familyData->currentTag = NO_TAG;
182 } else if (len == 7 && strncmp(tag, "fileset", len) == 0) { 219 } else if (len == 7 && strncmp(tag, "fileset", len) == 0) {
183 familyData->currentTag = NO_TAG; 220 familyData->currentTag = NO_TAG;
184 } else if ((strncmp(tag, "name", len) == 0 && familyData->currentTag == NAME SET_TAG) || 221 } else if ((len == 4 &&
185 (strncmp(tag, "file", len) == 0 && familyData->currentTag == FILESET _TAG)) { 222 strncmp(tag, "name", len) == 0 &&
223 familyData->currentTag == NAMESET_TAG) ||
224 (len == 4 &&
225 strncmp(tag, "file", len) == 0 &&
226 familyData->currentTag == FILESET_TAG)) {
186 // Disable the arbitrary text handler installed to load Name data 227 // Disable the arbitrary text handler installed to load Name data
187 XML_SetCharacterDataHandler(*familyData->parser, NULL); 228 XML_SetCharacterDataHandler(*familyData->parser, NULL);
188 } 229 }
189 } 230 }
190 231
232 } // namespace jbParser
233
191 /** 234 /**
192 * This function parses the given filename and stores the results in the given 235 * This function parses the given filename and stores the results in the given
193 * families array. 236 * families array.
194 */ 237 */
195 static void parseConfigFile(const char *filename, SkTDArray<FontFamily*> &famili es) { 238 static void parseConfigFile(const char *filename, SkTDArray<FontFamily*> &famili es) {
196 239
197 FILE* file = NULL; 240 FILE* file = NULL;
198 241
199 #if !defined(SK_BUILD_FOR_ANDROID_FRAMEWORK) 242 #if !defined(SK_BUILD_FOR_ANDROID_FRAMEWORK)
200 // if we are using a version of Android prior to Android 4.2 (JellyBean MR1 243 // if we are using a version of Android prior to Android 4.2 (JellyBean MR1
(...skipping 29 matching lines...) Expand all
230 273
231 // Some of the files we attempt to parse (in particular, /vendor/etc/fallbac k_fonts.xml) 274 // Some of the files we attempt to parse (in particular, /vendor/etc/fallbac k_fonts.xml)
232 // are optional - failure here is okay because one of these optional files m ay not exist. 275 // are optional - failure here is okay because one of these optional files m ay not exist.
233 if (NULL == file) { 276 if (NULL == file) {
234 return; 277 return;
235 } 278 }
236 279
237 XML_Parser parser = XML_ParserCreate(NULL); 280 XML_Parser parser = XML_ParserCreate(NULL);
238 FamilyData *familyData = new FamilyData(&parser, families); 281 FamilyData *familyData = new FamilyData(&parser, families);
239 XML_SetUserData(parser, familyData); 282 XML_SetUserData(parser, familyData);
240 XML_SetElementHandler(parser, startElementHandler, endElementHandler); 283 // Start parsing oldschool; switch these in flight if we detect a newer vers ion of the file.
284 XML_SetElementHandler(parser, jbParser::startElementHandler, jbParser::endEl ementHandler);
241 285
242 char buffer[512]; 286 char buffer[512];
243 bool done = false; 287 bool done = false;
244 while (!done) { 288 while (!done) {
245 fgets(buffer, sizeof(buffer), file); 289 fgets(buffer, sizeof(buffer), file);
246 int len = strlen(buffer); 290 int len = strlen(buffer);
247 if (feof(file) != 0) { 291 if (feof(file) != 0) {
248 done = true; 292 done = true;
249 } 293 }
250 XML_Parse(parser, buffer, len, done); 294 XML_Parse(parser, buffer, len, done);
(...skipping 51 matching lines...) Expand 10 before | Expand all | Expand 10 after
302 *fontFamilies.append() = fallbackFonts[i]; 346 *fontFamilies.append() = fallbackFonts[i];
303 } 347 }
304 } 348 }
305 349
306 void SkFontConfigParser::GetTestFontFamilies(SkTDArray<FontFamily*> &fontFamilie s, 350 void SkFontConfigParser::GetTestFontFamilies(SkTDArray<FontFamily*> &fontFamilie s,
307 const char* testMainConfigFile, 351 const char* testMainConfigFile,
308 const char* testFallbackConfigFile) { 352 const char* testFallbackConfigFile) {
309 parseConfigFile(testMainConfigFile, fontFamilies); 353 parseConfigFile(testMainConfigFile, fontFamilies);
310 354
311 SkTDArray<FontFamily*> fallbackFonts; 355 SkTDArray<FontFamily*> fallbackFonts;
312 parseConfigFile(testFallbackConfigFile, fallbackFonts); 356 if (NULL != testFallbackConfigFile) {
357 parseConfigFile(testFallbackConfigFile, fallbackFonts);
358 }
313 359
314 // Append all fallback fonts to system fonts 360 // Append all fallback fonts to system fonts
315 for (int i = 0; i < fallbackFonts.count(); ++i) { 361 for (int i = 0; i < fallbackFonts.count(); ++i) {
316 fallbackFonts[i]->fIsFallbackFont = true; 362 fallbackFonts[i]->fIsFallbackFont = true;
317 *fontFamilies.append() = fallbackFonts[i]; 363 *fontFamilies.append() = fallbackFonts[i];
318 } 364 }
319 } 365 }
320 366
321 /** 367 /**
322 * Read the persistent locale. 368 * Read the persistent locale.
(...skipping 17 matching lines...) Expand all
340 SkString locale(6); 386 SkString locale(6);
341 char* localeCStr = locale.writable_str(); 387 char* localeCStr = locale.writable_str();
342 388
343 strncpy(localeCStr, propLang, 2); 389 strncpy(localeCStr, propLang, 2);
344 localeCStr[2] = '-'; 390 localeCStr[2] = '-';
345 strncpy(&localeCStr[3], propRegn, 2); 391 strncpy(&localeCStr[3], propRegn, 2);
346 localeCStr[5] = '\0'; 392 localeCStr[5] = '\0';
347 393
348 return locale; 394 return locale;
349 } 395 }
OLDNEW
« no previous file with comments | « resources/android_fonts/v22/fonts.xml ('k') | tests/FontConfigParser.cpp » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698