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

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

Issue 1138073002: Extensible Android font configuration parsing. (Closed) Base URL: https://skia.googlesource.com/skia.git@master
Patch Set: Created 5 years, 7 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 | « no previous file | no next file » | 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 "SkFontMgr_android.h" 9 #include "SkFontMgr_android.h"
10 #include "SkStream.h" 10 #include "SkStream.h"
11 #include "SkTDArray.h" 11 #include "SkTDArray.h"
12 #include "SkTSearch.h" 12 #include "SkTSearch.h"
13 #include "SkTypeface.h" 13 #include "SkTypeface.h"
14 14
15 #include <expat.h> 15 #include <expat.h>
16 #include <dirent.h> 16 #include <dirent.h>
17 #include <stdio.h> 17 #include <stdio.h>
18 18
19 #include <limits> 19 #include <limits>
20 #include <stdlib.h> 20 #include <stdlib.h>
21 21
22 // From Android version LMP onwards, all font files collapse into
23 // /system/etc/fonts.xml. Instead of trying to detect which version
24 // we're on, try to open fonts.xml; if that fails, fall back to the
25 // older filename.
26 #define LMP_SYSTEM_FONTS_FILE "/system/etc/fonts.xml" 22 #define LMP_SYSTEM_FONTS_FILE "/system/etc/fonts.xml"
27 #define OLD_SYSTEM_FONTS_FILE "/system/etc/system_fonts.xml" 23 #define OLD_SYSTEM_FONTS_FILE "/system/etc/system_fonts.xml"
28 #define FALLBACK_FONTS_FILE "/system/etc/fallback_fonts.xml" 24 #define FALLBACK_FONTS_FILE "/system/etc/fallback_fonts.xml"
29 #define VENDOR_FONTS_FILE "/vendor/etc/fallback_fonts.xml" 25 #define VENDOR_FONTS_FILE "/vendor/etc/fallback_fonts.xml"
30 26
31 #define LOCALE_FALLBACK_FONTS_SYSTEM_DIR "/system/etc" 27 #define LOCALE_FALLBACK_FONTS_SYSTEM_DIR "/system/etc"
32 #define LOCALE_FALLBACK_FONTS_VENDOR_DIR "/vendor/etc" 28 #define LOCALE_FALLBACK_FONTS_VENDOR_DIR "/vendor/etc"
33 #define LOCALE_FALLBACK_FONTS_PREFIX "fallback_fonts-" 29 #define LOCALE_FALLBACK_FONTS_PREFIX "fallback_fonts-"
34 #define LOCALE_FALLBACK_FONTS_SUFFIX ".xml" 30 #define LOCALE_FALLBACK_FONTS_SUFFIX ".xml"
35 31
36 #ifndef SK_FONT_FILE_PREFIX 32 #ifndef SK_FONT_FILE_PREFIX
37 # define SK_FONT_FILE_PREFIX "/fonts/" 33 # define SK_FONT_FILE_PREFIX "/fonts/"
38 #endif 34 #endif
39 35
40 /** 36 /**
41 * This file contains TWO parsers: one for JB and earlier (system_fonts.xml / 37 * This file contains TWO 'familyset' handlers:
42 * fallback_fonts.xml), one for LMP and later (fonts.xml). 38 * One for JB and earlier which works with
43 * We start with the JB parser, and if we detect a <familyset> tag with 39 * /system/etc/system_fonts.xml
44 * version 21 or higher we switch to the LMP parser. 40 * /system/etc/fallback_fonts.xml
41 * /vendor/etc/fallback_fonts.xml
42 * /system/etc/fallback_fonts-XX.xml
43 * /vendor/etc/fallback_fonts-XX.xml
44 * and the other for LMP and later which works with
45 * /system/etc/fonts.xml
46 *
47 * If the 'familyset' 'version' attribute is 21 or higher the LMP parser is used , otherwise the JB.
45 */ 48 */
46 49
47 /** Used to track which tag is currently populating with data. 50 struct FamilyData;
48 * Only nameset and fileset are of interest for now. 51
49 */ 52 struct TagHandler {
50 enum CurrentTag { 53 void (*start)(FamilyData* data, const char* tag, const char** attributes);
51 kNo_CurrentTag, 54 void (*end)(FamilyData* data, const char* tag);
52 kNameSet_CurrentTag, 55 TagHandler* (*startSubTag)(FamilyData* data, const char* tag, const char** a ttributes);
53 kFileSet_CurrentTag 56 XML_CharacterDataHandler characterHandler;
54 }; 57 };
55 58
56 /** 59 /** Represents the current parsing state. */
57 * The FamilyData structure is passed around by the parser so that each handler
58 * can read these variables that are relevant to the current parsing.
59 */
60 struct FamilyData { 60 struct FamilyData {
61 FamilyData(XML_Parser parser, SkTDArray<FontFamily*>& families, 61 FamilyData(XML_Parser parser, SkTDArray<FontFamily*>& families,
62 const SkString& basePath, bool isFallback, const char* filename) 62 const SkString& basePath, bool isFallback, const char* filename,
63 TagHandler* topLevelHandler)
63 : fParser(parser) 64 : fParser(parser)
64 , fFamilies(families) 65 , fFamilies(families)
65 , fCurrentFamily(NULL) 66 , fCurrentFamily(NULL)
66 , fCurrentFontInfo(NULL) 67 , fCurrentFontInfo(NULL)
67 , fCurrentTag(kNo_CurrentTag)
68 , fVersion(0) 68 , fVersion(0)
69 , fBasePath(basePath) 69 , fBasePath(basePath)
70 , fIsFallback(isFallback) 70 , fIsFallback(isFallback)
71 , fFilename(filename) 71 , fFilename(filename)
72 , fDepth(1)
73 , fSkip(0)
74 , fHandler(&topLevelHandler, 1)
72 { }; 75 { };
73 76
74 XML_Parser fParser; // The expat parser doing the work , owned by caller 77 XML_Parser fParser; // The expat parser doing the work , owned by caller
75 SkTDArray<FontFamily*>& fFamilies; // The array to append families, o wned by caller 78 SkTDArray<FontFamily*>& fFamilies; // The array to append families, o wned by caller
76 SkAutoTDelete<FontFamily> fCurrentFamily; // The family being created, owned by this 79 SkAutoTDelete<FontFamily> fCurrentFamily; // The family being created, owned by this
77 FontFileInfo* fCurrentFontInfo; // The fontInfo being created, own ed by fCurrentFamily 80 FontFileInfo* fCurrentFontInfo; // The fontInfo being created, own ed by fCurrentFamily
78 CurrentTag fCurrentTag; // The kind of tag currently being parsed.
79 int fVersion; // The version of the file parsed. 81 int fVersion; // The version of the file parsed.
80 const SkString& fBasePath; // The current base path. 82 const SkString& fBasePath; // The current base path.
81 const bool fIsFallback; // Indicates the file being parsed is a fallback file 83 const bool fIsFallback; // Indicates the file being parsed is a fallback file
82 const char* fFilename; // The name of the file currently being parsed. 84 const char* fFilename; // The name of the file currently being parsed.
85
86 int fDepth; // The current element depth of th e parse.
87 int fSkip; // The depth to stop skipping, 0 i f not skipping.
88 SkTDArray<TagHandler*> fHandler; // The stack of current tag handle rs.
83 }; 89 };
84 90
85 /** Parses a null terminated string into an integer type, checking for overflow. 91 /** Parses a null terminated string into an integer type, checking for overflow.
86 * http://www.w3.org/TR/html-markup/datatypes.html#common.data.integer.non-nega tive-def 92 * http://www.w3.org/TR/html-markup/datatypes.html#common.data.integer.non-nega tive-def
87 * 93 *
88 * If the string cannot be parsed into 'value', returns false and does not chan ge 'value'. 94 * If the string cannot be parsed into 'value', returns false and does not chan ge 'value'.
89 */ 95 */
90 template <typename T> static bool parse_non_negative_integer(const char* s, T* v alue) { 96 template <typename T> static bool parse_non_negative_integer(const char* s, T* v alue) {
91 SK_COMPILE_ASSERT(std::numeric_limits<T>::is_integer, T_must_be_integer); 97 SK_COMPILE_ASSERT(std::numeric_limits<T>::is_integer, T_must_be_integer);
92 const T nMax = std::numeric_limits<T>::max() / 10; 98 const T nMax = std::numeric_limits<T>::max() / 10;
(...skipping 24 matching lines...) Expand all
117 123
118 #define SK_FONTCONFIGPARSER_PREFIX "[SkFontConfigParser] " 124 #define SK_FONTCONFIGPARSER_PREFIX "[SkFontConfigParser] "
119 125
120 #define SK_FONTCONFIGPARSER_WARNING(message, ...) SkDebugf( \ 126 #define SK_FONTCONFIGPARSER_WARNING(message, ...) SkDebugf( \
121 SK_FONTCONFIGPARSER_PREFIX "%s:%d:%d: warning: " message "\n", \ 127 SK_FONTCONFIGPARSER_PREFIX "%s:%d:%d: warning: " message "\n", \
122 self->fFilename, \ 128 self->fFilename, \
123 XML_GetCurrentLineNumber(self->fParser), \ 129 XML_GetCurrentLineNumber(self->fParser), \
124 XML_GetCurrentColumnNumber(self->fParser), \ 130 XML_GetCurrentColumnNumber(self->fParser), \
125 ##__VA_ARGS__); 131 ##__VA_ARGS__);
126 132
133 static bool is_whitespace(char c) {
134 return c == ' ' || c == '\n'|| c == '\r' || c == '\t';
135 }
136
137 static void trim_string(SkString* s) {
138 char* str = s->writable_str();
139 const char* start = str; // start is inclusive
140 const char* end = start + s->size(); // end is exclusive
141 while (is_whitespace(*start)) { ++start; }
142 if (start != end) {
143 --end; // make end inclusive
144 while (is_whitespace(*end)) { --end; }
145 ++end; // make end exclusive
146 }
147 size_t len = end - start;
148 memmove(str, start, len);
149 s->resize(len);
150 }
151
127 namespace lmpParser { 152 namespace lmpParser {
128 153
129 static void family_element_handler(FamilyData* self, const char** attributes) { 154 static TagHandler fontHandler = {
djsollen 2015/05/11 17:49:54 I find this formatting really hard to parse. Why
130 // A <family> element without a 'name' (string) attribute is a fallback font . 155 [](FamilyData* self, const char* tag, const char** attributes) {
131 // The element may have 'lang' (string) and 'variant' ("elegant", "compact") attributes. 156 // 'weight' (non-negative integer)
132 // The element should contain <font> elements. 157 // 'style' ("normal", "italic")
133 FontFamily* family = new FontFamily(self->fBasePath, true); 158 // 'index' (non-negative integer)
134 self->fCurrentFamily.reset(family); 159 // The element's character data should contain a filename.
135 for (size_t i = 0; ATTS_NON_NULL(attributes, i); i += 2) { 160 FontFileInfo& file = self->fCurrentFamily->fFonts.push_back();
136 const char* name = attributes[i]; 161 self->fCurrentFontInfo = &file;
137 const char* value = attributes[i+1]; 162 for (size_t i = 0; ATTS_NON_NULL(attributes, i); i += 2) {
138 size_t nameLen = strlen(name); 163 const char* name = attributes[i];
139 size_t valueLen = strlen(value); 164 const char* value = attributes[i+1];
140 if (MEMEQ("name", name, nameLen)) { 165 size_t nameLen = strlen(name);
141 SkAutoAsciiToLC tolc(value); 166 if (MEMEQ("weight", name, nameLen)) {
142 family->fNames.push_back().set(tolc.lc()); 167 if (!parse_non_negative_integer(value, &file.fWeight)) {
143 family->fIsFallbackFont = false; 168 SK_FONTCONFIGPARSER_WARNING("'%s' is an invalid weight", val ue);
144 } else if (MEMEQ("lang", name, nameLen)) { 169 }
145 family->fLanguage = SkLanguage(value, valueLen); 170 } else if (MEMEQ("style", name, nameLen)) {
146 } else if (MEMEQ("variant", name, nameLen)) { 171 size_t valueLen = strlen(value);
147 if (MEMEQ("elegant", value, valueLen)) { 172 if (MEMEQ("normal", value, valueLen)) {
148 family->fVariant = kElegant_FontVariant; 173 file.fStyle = FontFileInfo::Style::kNormal;
149 } else if (MEMEQ("compact", value, valueLen)) { 174 } else if (MEMEQ("italic", value, valueLen)) {
150 family->fVariant = kCompact_FontVariant; 175 file.fStyle = FontFileInfo::Style::kItalic;
176 }
177 } else if (MEMEQ("index", name, nameLen)) {
178 if (!parse_non_negative_integer(value, &file.fIndex)) {
179 SK_FONTCONFIGPARSER_WARNING("'%s' is an invalid index", valu e);
180 }
151 } 181 }
152 } 182 }
183 },
184 [](FamilyData* self, const char* tag) {
185 trim_string(&self->fCurrentFontInfo->fFileName);
186 },
187 [](FamilyData* self, const char* tag, const char** attributes) -> TagHandler * { return NULL; },
188 [](void* data, const char* s, int len) {
189 FamilyData* self = static_cast<FamilyData*>(data);
190 self->fCurrentFontInfo->fFileName.append(s, len);
153 } 191 }
154 } 192 };
155 193
156 static void XMLCALL font_file_name_handler(void* data, const char* s, int len) { 194 static TagHandler familyHandler = {
157 FamilyData* self = static_cast<FamilyData*>(data); 195 [](FamilyData* self, const char* tag, const char** attributes) {
158 self->fCurrentFontInfo->fFileName.append(s, len); 196 // A <family> element without a 'name' (string) attribute is a fallback font.
159 } 197 // 'lang' (string)
160 198 // 'variant' ("elegant", "compact")
161 static void font_element_handler(FamilyData* self, const char** attributes) { 199 // The element should contain <font> elements.
162 // A <font> element should be contained in a <family> element. 200 FontFamily* family = new FontFamily(self->fBasePath, true);
163 // The element may have 'weight' (non-negative integer), 'style' ("normal", "italic"), 201 self->fCurrentFamily.reset(family);
164 // and 'index' (non-negative integer) attributes. 202 for (size_t i = 0; ATTS_NON_NULL(attributes, i); i += 2) {
165 // The element should contain a filename. 203 const char* name = attributes[i];
166 FontFileInfo& file = self->fCurrentFamily->fFonts.push_back(); 204 const char* value = attributes[i+1];
167 self->fCurrentFontInfo = &file; 205 size_t nameLen = strlen(name);
168 for (size_t i = 0; ATTS_NON_NULL(attributes, i); i += 2) {
169 const char* name = attributes[i];
170 const char* value = attributes[i+1];
171 size_t nameLen = strlen(name);
172 if (MEMEQ("weight", name, nameLen)) {
173 if (!parse_non_negative_integer(value, &file.fWeight)) {
174 SK_FONTCONFIGPARSER_WARNING("'%s' is an invalid weight", value);
175 }
176 } else if (MEMEQ("style", name, nameLen)) {
177 size_t valueLen = strlen(value); 206 size_t valueLen = strlen(value);
178 if (MEMEQ("normal", value, valueLen)) { 207 if (MEMEQ("name", name, nameLen)) {
179 file.fStyle = FontFileInfo::Style::kNormal; 208 SkAutoAsciiToLC tolc(value);
180 } else if (MEMEQ("italic", value, valueLen)) { 209 family->fNames.push_back().set(tolc.lc());
181 file.fStyle = FontFileInfo::Style::kItalic; 210 family->fIsFallbackFont = false;
182 } 211 } else if (MEMEQ("lang", name, nameLen)) {
183 } else if (MEMEQ("index", name, nameLen)) { 212 family->fLanguage = SkLanguage(value, valueLen);
184 if (!parse_non_negative_integer(value, &file.fIndex)) { 213 } else if (MEMEQ("variant", name, nameLen)) {
185 SK_FONTCONFIGPARSER_WARNING("'%s' is an invalid index", value); 214 if (MEMEQ("elegant", value, valueLen)) {
215 family->fVariant = kElegant_FontVariant;
216 } else if (MEMEQ("compact", value, valueLen)) {
217 family->fVariant = kCompact_FontVariant;
218 }
186 } 219 }
187 } 220 }
188 } 221 },
189 XML_SetCharacterDataHandler(self->fParser, font_file_name_handler); 222 [](FamilyData* self, const char* tag) {
190 } 223 *self->fFamilies.append() = self->fCurrentFamily.detach();
224 },
225 [](FamilyData* self, const char* tag, const char** attributes) -> TagHandler * {
226 size_t len = strlen(tag);
227 if (MEMEQ("font", tag, len)) {
228 return &fontHandler;
229 }
230 return NULL;
231 },
232 NULL,
233 };
191 234
192 static FontFamily* find_family(FamilyData* self, const SkString& familyName) { 235 static FontFamily* find_family(FamilyData* self, const SkString& familyName) {
193 for (int i = 0; i < self->fFamilies.count(); i++) { 236 for (int i = 0; i < self->fFamilies.count(); i++) {
194 FontFamily* candidate = self->fFamilies[i]; 237 FontFamily* candidate = self->fFamilies[i];
195 for (int j = 0; j < candidate->fNames.count(); j++) { 238 for (int j = 0; j < candidate->fNames.count(); j++) {
196 if (candidate->fNames[j] == familyName) { 239 if (candidate->fNames[j] == familyName) {
197 return candidate; 240 return candidate;
198 } 241 }
199 } 242 }
200 } 243 }
201 return NULL; 244 return NULL;
202 } 245 }
203 246
204 static void alias_element_handler(FamilyData* self, const char** attributes) { 247 static TagHandler aliasHandler = {
205 // A <alias> element must have 'name' (string) and 'to' (string) attributes. 248 [](FamilyData* self, const char* tag, const char** attributes) {
206 // The element may also have a 'weight' (non-negative integer) attribute. 249 // A <alias> element must have 'name' (string) and 'to' (string) attribu tes.
207 // The 'name' introduces a new family name. 250 // The element may also have a 'weight' (non-negative integer) attribute .
208 // The 'to' specifies which (previous) <family> to alias. 251 // The 'name' introduces a new family name.
209 // If it *does not* have a weight, 'name' is an alias for the entire 'to' <f amily>. 252 // The 'to' specifies which (previous) <family> to alias.
210 // If it *does* have a weight, 'name' is a new family consisting of the <fon t>(s) with 'weight' 253 // If it *does not* have a weight, 'name' is an alias for the entire 'to ' <family>.
211 // from the 'to' <family>. 254 // If it *does* have a weight, 'name' is a new family consisting of the <font>(s) with 'weight'
212 255 // from the 'to' <family>.
213 SkString aliasName; 256
214 SkString to; 257 SkString aliasName;
215 int weight = 0; 258 SkString to;
216 for (size_t i = 0; ATTS_NON_NULL(attributes, i); i += 2) { 259 int weight = 0;
217 const char* name = attributes[i];
218 const char* value = attributes[i+1];
219 size_t nameLen = strlen(name);
220 if (MEMEQ("name", name, nameLen)) {
221 SkAutoAsciiToLC tolc(value);
222 aliasName.set(tolc.lc());
223 } else if (MEMEQ("to", name, nameLen)) {
224 to.set(value);
225 } else if (MEMEQ("weight", name, nameLen)) {
226 if (!parse_non_negative_integer(value, &weight)) {
227 SK_FONTCONFIGPARSER_WARNING("'%s' is an invalid weight", value);
228 }
229 }
230 }
231
232 // Assumes that the named family is already declared
233 FontFamily* targetFamily = find_family(self, to);
234 if (!targetFamily) {
235 SK_FONTCONFIGPARSER_WARNING("'%s' alias target not found", to.c_str());
236 return;
237 }
238
239 if (weight) {
240 FontFamily* family = new FontFamily(targetFamily->fBasePath, self->fIsFa llback);
241 family->fNames.push_back().set(aliasName);
242
243 for (int i = 0; i < targetFamily->fFonts.count(); i++) {
244 if (targetFamily->fFonts[i].fWeight == weight) {
245 family->fFonts.push_back(targetFamily->fFonts[i]);
246 }
247 }
248 *self->fFamilies.append() = family;
249 } else {
250 targetFamily->fNames.push_back().set(aliasName);
251 }
252 }
253
254 static void XMLCALL start_element_handler(void* data, const char* tag, const cha r** attributes) {
255 FamilyData* self = static_cast<FamilyData*>(data);
256 size_t len = strlen(tag);
257 if (MEMEQ("family", tag, len)) {
258 family_element_handler(self, attributes);
259 } else if (MEMEQ("font", tag, len)) {
260 font_element_handler(self, attributes);
261 } else if (MEMEQ("alias", tag, len)) {
262 alias_element_handler(self, attributes);
263 }
264 }
265
266 static bool is_whitespace(char c) {
267 return c == ' ' || c == '\n'|| c == '\r' || c == '\t';
268 }
269
270 static void trim_string(SkString* s) {
271 char* str = s->writable_str();
272 const char* start = str; // start is inclusive
273 const char* end = start + s->size(); // end is exclusive
274 while (is_whitespace(*start)) { ++start; }
275 if (start != end) {
276 --end; // make end inclusive
277 while (is_whitespace(*end)) { --end; }
278 ++end; // make end exclusive
279 }
280 size_t len = end - start;
281 memmove(str, start, len);
282 s->resize(len);
283 }
284
285 static void XMLCALL end_element_handler(void* data, const char* tag) {
286 FamilyData* self = static_cast<FamilyData*>(data);
287 size_t len = strlen(tag);
288 if (MEMEQ("family", tag, len)) {
289 *self->fFamilies.append() = self->fCurrentFamily.detach();
290 } else if (MEMEQ("font", tag, len)) {
291 XML_SetCharacterDataHandler(self->fParser, NULL);
292 trim_string(&self->fCurrentFontInfo->fFileName);
293 }
294 }
295
296 } // lmpParser
297
298 namespace jbParser {
299
300 /**
301 * Handler for arbitrary text. This is used to parse the text inside each name
302 * or file tag. The resulting strings are put into the fNames or FontFileInfo ar rays.
303 */
304 static void XMLCALL text_handler(void* data, const char* s, int len) {
305 FamilyData* self = static_cast<FamilyData*>(data);
306 // Make sure we're in the right state to store this name information
307 if (self->fCurrentFamily.get()) {
308 switch (self->fCurrentTag) {
309 case kNameSet_CurrentTag: {
310 SkAutoAsciiToLC tolc(s, len);
311 self->fCurrentFamily->fNames.back().append(tolc.lc(), len);
312 break;
313 }
314 case kFileSet_CurrentTag:
315 if (self->fCurrentFontInfo) {
316 self->fCurrentFontInfo->fFileName.append(s, len);
317 }
318 break;
319 default:
320 // Noop - don't care about any text that's not in the Fonts or Names list
321 break;
322 }
323 }
324 }
325
326 /**
327 * Handler for font files. This processes the attributes for language and
328 * variants then lets textHandler handle the actual file name
329 */
330 static void font_file_element_handler(FamilyData* self, const char** attributes) {
331 FontFamily& currentFamily = *self->fCurrentFamily.get();
332 FontFileInfo& newFileInfo = currentFamily.fFonts.push_back();
333 if (attributes) {
334 for (size_t i = 0; ATTS_NON_NULL(attributes, i); i += 2) { 260 for (size_t i = 0; ATTS_NON_NULL(attributes, i); i += 2) {
335 const char* name = attributes[i]; 261 const char* name = attributes[i];
336 const char* value = attributes[i+1]; 262 const char* value = attributes[i+1];
337 size_t nameLen = strlen(name); 263 size_t nameLen = strlen(name);
338 size_t valueLen = strlen(value); 264 if (MEMEQ("name", name, nameLen)) {
339 if (MEMEQ("variant", name, nameLen)) { 265 SkAutoAsciiToLC tolc(value);
340 const FontVariant prevVariant = currentFamily.fVariant; 266 aliasName.set(tolc.lc());
341 if (MEMEQ("elegant", value, valueLen)) { 267 } else if (MEMEQ("to", name, nameLen)) {
342 currentFamily.fVariant = kElegant_FontVariant; 268 to.set(value);
343 } else if (MEMEQ("compact", value, valueLen)) { 269 } else if (MEMEQ("weight", name, nameLen)) {
344 currentFamily.fVariant = kCompact_FontVariant; 270 if (!parse_non_negative_integer(value, &weight)) {
345 } 271 SK_FONTCONFIGPARSER_WARNING("'%s' is an invalid weight", val ue);
346 if (currentFamily.fFonts.count() > 1 && currentFamily.fVariant ! = prevVariant) { 272 }
347 SK_FONTCONFIGPARSER_WARNING("'%s' unexpected variant found\n " 273 }
348 "Note: Every font file within a family must have identic al variants.", 274 }
349 value); 275
350 } 276 // Assumes that the named family is already declared
351 277 FontFamily* targetFamily = find_family(self, to);
352 } else if (MEMEQ("lang", name, nameLen)) { 278 if (!targetFamily) {
353 SkLanguage prevLang = currentFamily.fLanguage; 279 SK_FONTCONFIGPARSER_WARNING("'%s' alias target not found", to.c_str( ));
354 currentFamily.fLanguage = SkLanguage(value, valueLen); 280 return;
355 if (currentFamily.fFonts.count() > 1 && currentFamily.fLanguage != prevLang) { 281 }
356 SK_FONTCONFIGPARSER_WARNING("'%s' unexpected language found\ n" 282
357 "Note: Every font file within a family must have identic al languages.", 283 if (weight) {
358 value); 284 FontFamily* family = new FontFamily(targetFamily->fBasePath, self->f IsFallback);
359 } 285 family->fNames.push_back().set(aliasName);
360 286
361 } else if (MEMEQ("index", name, nameLen)) { 287 for (int i = 0; i < targetFamily->fFonts.count(); i++) {
362 if (!parse_non_negative_integer(value, &newFileInfo.fIndex)) { 288 if (targetFamily->fFonts[i].fWeight == weight) {
363 SK_FONTCONFIGPARSER_WARNING("'%s' is an invalid index", valu e); 289 family->fFonts.push_back(targetFamily->fFonts[i]);
364 } 290 }
365 } 291 }
366 } 292 *self->fFamilies.append() = family;
367 } 293 } else {
368 self->fCurrentFontInfo = &newFileInfo; 294 targetFamily->fNames.push_back().set(aliasName);
369 XML_SetCharacterDataHandler(self->fParser, text_handler); 295 }
296 },
297 [](FamilyData* self, const char* tag) { },
298 [](FamilyData* self, const char* tag, const char** attributes) -> TagHandler * { return NULL; },
299 NULL,
300 };
301
302 static TagHandler familySetHandler = {
303 [](FamilyData* self, const char* tag, const char** attributes) { },
304 [](FamilyData* self, const char* tag) { },
305 [](FamilyData* self, const char* tag, const char** attributes) -> TagHandler * {
306 size_t len = strlen(tag);
307 if (MEMEQ("family", tag, len)) {
308 return &familyHandler;
309 } else if (MEMEQ("alias", tag, len)) {
310 return &aliasHandler;
311 }
312 return NULL;
313 },
314 NULL,
315 };
316
317 } // lmpParser
318
319 namespace jbParser {
320
321 static TagHandler fileHandler = {
322 [](FamilyData* self, const char* tag, const char** attributes) {
323 FontFamily& currentFamily = *self->fCurrentFamily.get();
324 FontFileInfo& newFileInfo = currentFamily.fFonts.push_back();
325 if (attributes) {
326 for (size_t i = 0; ATTS_NON_NULL(attributes, i); i += 2) {
327 const char* name = attributes[i];
328 const char* value = attributes[i+1];
329 size_t nameLen = strlen(name);
330 size_t valueLen = strlen(value);
331 if (MEMEQ("variant", name, nameLen)) {
332 const FontVariant prevVariant = currentFamily.fVariant;
333 if (MEMEQ("elegant", value, valueLen)) {
334 currentFamily.fVariant = kElegant_FontVariant;
335 } else if (MEMEQ("compact", value, valueLen)) {
336 currentFamily.fVariant = kCompact_FontVariant;
337 }
338 if (currentFamily.fFonts.count() > 1 && currentFamily.fVaria nt != prevVariant) {
339 SK_FONTCONFIGPARSER_WARNING("'%s' unexpected variant fou nd\n"
340 "Note: Every font file within a family must have ide ntical variants.",
341 value);
342 }
343
344 } else if (MEMEQ("lang", name, nameLen)) {
345 SkLanguage prevLang = currentFamily.fLanguage;
346 currentFamily.fLanguage = SkLanguage(value, valueLen);
347 if (currentFamily.fFonts.count() > 1 && currentFamily.fLangu age != prevLang) {
348 SK_FONTCONFIGPARSER_WARNING("'%s' unexpected language fo und\n"
349 "Note: Every font file within a family must have ide ntical languages.",
350 value);
351 }
352
353 } else if (MEMEQ("index", name, nameLen)) {
354 if (!parse_non_negative_integer(value, &newFileInfo.fIndex)) {
355 SK_FONTCONFIGPARSER_WARNING("'%s' is an invalid index", value);
356 }
357 }
358 }
359 }
360 self->fCurrentFontInfo = &newFileInfo;
361 },
362 [](FamilyData* self, const char* tag) { },
363 [](FamilyData* self, const char* tag, const char** attributes) -> TagHandler * { return NULL; },
364 [](void* data, const char* s, int len) {
365 FamilyData* self = static_cast<FamilyData*>(data);
366 self->fCurrentFontInfo->fFileName.append(s, len);
367 }
368 };
369
370 static TagHandler fileSetHandler = {
371 [](FamilyData* self, const char* tag, const char** attributes) { },
372 [](FamilyData* self, const char* tag) { },
373 [](FamilyData* self, const char* tag, const char** attributes) -> TagHandler * {
374 size_t len = strlen(tag);
375 if (MEMEQ("file", tag, len)) {
376 return &fileHandler;
377 }
378 return NULL;
379 },
380 NULL,
381 };
382
383 static TagHandler nameHandler = {
384 [](FamilyData* self, const char* tag, const char** attributes) {
385 self->fCurrentFamily->fNames.push_back();
386 },
387 [](FamilyData* self, const char* tag) { },
388 [](FamilyData* self, const char* tag, const char** attributes) -> TagHandler * { return NULL; },
389 [](void* data, const char* s, int len) {
390 FamilyData* self = static_cast<FamilyData*>(data);
391 SkAutoAsciiToLC tolc(s, len);
392 self->fCurrentFamily->fNames.back().append(tolc.lc(), len);
393 }
394 };
395
396 static TagHandler nameSetHandler = {
397 [](FamilyData* self, const char* tag, const char** attributes) { },
398 [](FamilyData* self, const char* tag) { },
399 [](FamilyData* self, const char* tag, const char** attributes) -> TagHandler * {
400 size_t len = strlen(tag);
401 if (MEMEQ("name", tag, len)) {
402 return &nameHandler;
403 }
404 return NULL;
405 },
406 NULL,
407 };
408
409 static TagHandler familyHandler = {
410 [](FamilyData* self, const char* tag, const char** attributes) {
411 self->fCurrentFamily.reset(new FontFamily(self->fBasePath, self->fIsFall back));
412 // 'order' (non-negative integer) [default -1].
413 for (size_t i = 0; ATTS_NON_NULL(attributes, i); i += 2) {
414 const char* value = attributes[i+1];
415 parse_non_negative_integer(value, &self->fCurrentFamily->fOrder);
416 }
417 },
418 [](FamilyData* self, const char* tag) {
419 *self->fFamilies.append() = self->fCurrentFamily.detach();
420 },
421 [](FamilyData* self, const char* tag, const char** attributes) -> TagHandler * {
422 size_t len = strlen(tag);
423 if (MEMEQ("nameset", tag, len)) {
424 return &nameSetHandler;
425 } else if (MEMEQ("fileset", tag, len)) {
426 return &fileSetHandler;
427 }
428 return NULL;
429 },
430 NULL,
431 };
432
433 static TagHandler familySetHandler = {
434 [](FamilyData* self, const char* tag, const char** attributes) { },
435 [](FamilyData* self, const char* tag) { },
436 [](FamilyData* self, const char* tag, const char** attributes) -> TagHandler * {
437 size_t len = strlen(tag);
438 if (MEMEQ("family", tag, len)) {
439 return &familyHandler;
440 }
441 return NULL;
442 },
443 NULL,
444 };
445
446 } // namespace jbParser
447
448 static TagHandler topLevelHandler = {
449 [](FamilyData* self, const char* tag, const char** attributes) { },
450 [](FamilyData* self, const char* tag) { },
451 [](FamilyData* self, const char* tag, const char** attributes) -> TagHandler * {
452 size_t len = strlen(tag);
453 if (MEMEQ("familyset", tag, len)) {
454 // The <familyset> element has an optional 'version' (non-negative i nteger) attribute.
455 for (size_t i = 0; ATTS_NON_NULL(attributes, i); i += 2) {
456 const char* name = attributes[i];
457 size_t nameLen = strlen(name);
458 if (MEMEQ("version", name, nameLen)) {
459 const char* value = attributes[i+1];
460 if (parse_non_negative_integer(value, &self->fVersion)) {
461 if (self->fVersion >= 21) {
462 return &lmpParser::familySetHandler;
463 }
464 }
465 }
466 }
467 return &jbParser::familySetHandler;
468 }
469 return NULL;
470 },
471 NULL,
472 };
473
474 static void XMLCALL start_element_handler(void *data, const char *tag, const cha r **attributes) {
475 FamilyData* self = static_cast<FamilyData*>(data);
476
477 if (!self->fSkip) {
478 TagHandler* subHandler = self->fHandler.top()->startSubTag(self, tag, at tributes);
479 if (subHandler) {
480 subHandler->start(self, tag, attributes);
481 self->fHandler.push(subHandler);
482 XML_SetCharacterDataHandler(self->fParser, subHandler->characterHand ler);
483 } else {
484 SK_FONTCONFIGPARSER_WARNING("'%s' tag not recognized, skipping", tag );
485 XML_SetCharacterDataHandler(self->fParser, NULL);
486 self->fSkip = self->fDepth;
487 }
488 }
489
490 ++self->fDepth;
370 } 491 }
371 492
372 /**
373 * Handler for the start of a tag. The only tags we expect are familyset, family ,
374 * nameset, fileset, name, and file.
375 */
376 static void XMLCALL start_element_handler(void* data, const char* tag, const cha r** attributes) {
377 FamilyData* self = static_cast<FamilyData*>(data);
378 size_t len = strlen(tag);
379 if (MEMEQ("familyset", tag, len)) {
380 // The <familyset> element has an optional 'version' (non-negative integ er) attribute.
381 for (size_t i = 0; ATTS_NON_NULL(attributes, i); i += 2) {
382 size_t nameLen = strlen(attributes[i]);
383 if (!MEMEQ("version", attributes[i], nameLen)) continue;
384 const char* valueString = attributes[i+1];
385 int version;
386 if (parse_non_negative_integer(valueString, &version) && (version >= 21)) {
387 XML_SetElementHandler(self->fParser,
388 lmpParser::start_element_handler,
389 lmpParser::end_element_handler);
390 self->fVersion = version;
391 }
392 }
393 } else if (MEMEQ("family", tag, len)) {
394 self->fCurrentFamily.reset(new FontFamily(self->fBasePath, self->fIsFall back));
395 // The <family> element has an optional 'order' (non-negative integer) a ttribute.
396 // If this attribute does not exist, the default value is -1.
397 for (size_t i = 0; ATTS_NON_NULL(attributes, i); i += 2) {
398 const char* valueString = attributes[i+1];
399 int value;
400 if (parse_non_negative_integer(valueString, &value)) {
401 self->fCurrentFamily->fOrder = value;
402 }
403 }
404 } else if (MEMEQ("nameset", tag, len)) {
405 self->fCurrentTag = kNameSet_CurrentTag;
406 } else if (MEMEQ("fileset", tag, len)) {
407 self->fCurrentTag = kFileSet_CurrentTag;
408 } else if (MEMEQ("name", tag, len) && self->fCurrentTag == kNameSet_CurrentT ag) {
409 // If it's a Name, parse the text inside
410 self->fCurrentFamily->fNames.push_back();
411 XML_SetCharacterDataHandler(self->fParser, text_handler);
412 } else if (MEMEQ("file", tag, len) && self->fCurrentTag == kFileSet_CurrentT ag) {
413 // If it's a file, parse the attributes, then parse the text inside
414 font_file_element_handler(self, attributes);
415 }
416 }
417
418 /**
419 * Handler for the end of tags. We only care about family, nameset, fileset,
420 * name, and file.
421 */
422 static void XMLCALL end_element_handler(void* data, const char* tag) { 493 static void XMLCALL end_element_handler(void* data, const char* tag) {
423 FamilyData* self = static_cast<FamilyData*>(data); 494 FamilyData* self = static_cast<FamilyData*>(data);
424 size_t len = strlen(tag); 495 --self->fDepth;
425 if (MEMEQ("family", tag, len)) { 496
426 // Done parsing a Family - store the created currentFamily in the famili es array 497 if (!self->fSkip) {
427 *self->fFamilies.append() = self->fCurrentFamily.detach(); 498 self->fHandler.top()->end(self, tag);
428 } else if (MEMEQ("nameset", tag, len)) { 499 self->fHandler.pop();
429 self->fCurrentTag = kNo_CurrentTag; 500 XML_SetCharacterDataHandler(self->fParser, self->fHandler.top()->charact erHandler);
430 } else if (MEMEQ("fileset", tag, len)) { 501 }
431 self->fCurrentTag = kNo_CurrentTag; 502
432 } else if ((MEMEQ("name", tag, len) && self->fCurrentTag == kNameSet_Current Tag) || 503 if (self->fSkip == self->fDepth) {
433 (MEMEQ("file", tag, len) && self->fCurrentTag == kFileSet_Current Tag)) { 504 self->fSkip = 0;
434 // Disable the arbitrary text handler installed to load Name data 505 XML_SetCharacterDataHandler(self->fParser, self->fHandler.top()->charact erHandler);
435 XML_SetCharacterDataHandler(self->fParser, NULL);
436 } 506 }
437 } 507 }
438 508
439 } // namespace jbParser
440
441 static void XMLCALL xml_entity_decl_handler(void *data, 509 static void XMLCALL xml_entity_decl_handler(void *data,
442 const XML_Char *entityName, 510 const XML_Char *entityName,
443 int is_parameter_entity, 511 int is_parameter_entity,
444 const XML_Char *value, 512 const XML_Char *value,
445 int value_length, 513 int value_length,
446 const XML_Char *base, 514 const XML_Char *base,
447 const XML_Char *systemId, 515 const XML_Char *systemId,
448 const XML_Char *publicId, 516 const XML_Char *publicId,
449 const XML_Char *notationName) 517 const XML_Char *notationName)
450 { 518 {
(...skipping 27 matching lines...) Expand all
478 return -1; 546 return -1;
479 } 547 }
480 548
481 SkAutoTCallVProc<remove_ptr<XML_Parser>::type, XML_ParserFree> parser( 549 SkAutoTCallVProc<remove_ptr<XML_Parser>::type, XML_ParserFree> parser(
482 XML_ParserCreate_MM(NULL, &sk_XML_alloc, NULL)); 550 XML_ParserCreate_MM(NULL, &sk_XML_alloc, NULL));
483 if (!parser) { 551 if (!parser) {
484 SkDebugf(SK_FONTCONFIGPARSER_PREFIX "could not create XML parser\n"); 552 SkDebugf(SK_FONTCONFIGPARSER_PREFIX "could not create XML parser\n");
485 return -1; 553 return -1;
486 } 554 }
487 555
488 FamilyData self(parser, families, basePath, isFallback, filename); 556 FamilyData self(parser, families, basePath, isFallback, filename, &topLevelH andler);
489 XML_SetUserData(parser, &self); 557 XML_SetUserData(parser, &self);
490 558
491 // Disable entity processing, to inhibit internal entity expansion. See expa t CVE-2013-0340 559 // Disable entity processing, to inhibit internal entity expansion. See expa t CVE-2013-0340
492 XML_SetEntityDeclHandler(parser, xml_entity_decl_handler); 560 XML_SetEntityDeclHandler(parser, xml_entity_decl_handler);
493 561
494 // Start parsing oldschool; switch these in flight if we detect a newer vers ion of the file. 562 // Start parsing oldschool; switch these in flight if we detect a newer vers ion of the file.
495 XML_SetElementHandler(parser, jbParser::start_element_handler, jbParser::end _element_handler); 563 XML_SetElementHandler(parser, start_element_handler, end_element_handler);
496 564
497 // One would assume it would be faster to have a buffer on the stack and cal l XML_Parse. 565 // One would assume it would be faster to have a buffer on the stack and cal l XML_Parse.
498 // But XML_Parse will call XML_GetBuffer anyway and memmove the passed buffe r into it. 566 // But XML_Parse will call XML_GetBuffer anyway and memmove the passed buffe r into it.
499 // (Unless XML_CONTEXT_BYTES is undefined, but all users define it.) 567 // (Unless XML_CONTEXT_BYTES is undefined, but all users define it.)
500 // In debug, buffer a small odd number of bytes to detect slicing in XML_Cha racterDataHandler. 568 // In debug, buffer a small odd number of bytes to detect slicing in XML_Cha racterDataHandler.
501 static const int bufferSize = 512 SkDEBUGCODE( - 507); 569 static const int bufferSize = 512 SkDEBUGCODE( - 507);
502 bool done = false; 570 bool done = false;
503 while (!done) { 571 while (!done) {
504 void* buffer = XML_GetBuffer(parser, bufferSize); 572 void* buffer = XML_GetBuffer(parser, bufferSize);
505 if (!buffer) { 573 if (!buffer) {
(...skipping 164 matching lines...) Expand 10 before | Expand all | Expand 10 after
670 const char* tag = fTag.c_str(); 738 const char* tag = fTag.c_str();
671 739
672 // strip off the rightmost "-.*" 740 // strip off the rightmost "-.*"
673 const char* parentTagEnd = strrchr(tag, '-'); 741 const char* parentTagEnd = strrchr(tag, '-');
674 if (parentTagEnd == NULL) { 742 if (parentTagEnd == NULL) {
675 return SkLanguage(); 743 return SkLanguage();
676 } 744 }
677 size_t parentTagLen = parentTagEnd - tag; 745 size_t parentTagLen = parentTagEnd - tag;
678 return SkLanguage(tag, parentTagLen); 746 return SkLanguage(tag, parentTagLen);
679 } 747 }
OLDNEW
« no previous file with comments | « no previous file | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698