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

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: Add comments, allow NULL. 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 /** Called at the start tag.
51 kNo_CurrentTag, 54 * Called immediately after the parent tag retuns this handler from a call to 'tag'.
52 kNameSet_CurrentTag, 55 * Allows setting up for handling the tag content and processing attributes .
53 kFileSet_CurrentTag 56 * If NULL, will not be called.
57 */
58 void (*start)(FamilyData* data, const char* tag, const char** attributes);
59
60 /** Called at the end tag.
61 * Allows post-processing of any accumulated information.
62 * This will be the last call made in relation to the current tag.
63 * If NULL, will not be called.
64 */
65 void (*end)(FamilyData* data, const char* tag);
66
67 /** Called when a nested tag is encountered.
68 * This is responsible for determining how to handle the tag.
69 * If the tag is not recognized, return NULL to skip the tag.
70 * If NULL, all nested tags will be skipped.
71 */
72 const TagHandler* (*tag)(FamilyData* data, const char* tag, const char** att ributes);
73
74 /** The character handler for this tag.
75 * This is only active for character data contained directly in this tag (n ot sub-tags).
76 * The first parameter will be castable to a FamilyData*.
77 * If NULL, any character data in this tag will be ignored.
78 */
79 XML_CharacterDataHandler chars;
54 }; 80 };
55 81
56 /** 82 /** 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 { 83 struct FamilyData {
61 FamilyData(XML_Parser parser, SkTDArray<FontFamily*>& families, 84 FamilyData(XML_Parser parser, SkTDArray<FontFamily*>& families,
62 const SkString& basePath, bool isFallback, const char* filename) 85 const SkString& basePath, bool isFallback, const char* filename,
86 const TagHandler* topLevelHandler)
63 : fParser(parser) 87 : fParser(parser)
64 , fFamilies(families) 88 , fFamilies(families)
65 , fCurrentFamily(NULL) 89 , fCurrentFamily(NULL)
66 , fCurrentFontInfo(NULL) 90 , fCurrentFontInfo(NULL)
67 , fCurrentTag(kNo_CurrentTag)
68 , fVersion(0) 91 , fVersion(0)
69 , fBasePath(basePath) 92 , fBasePath(basePath)
70 , fIsFallback(isFallback) 93 , fIsFallback(isFallback)
71 , fFilename(filename) 94 , fFilename(filename)
95 , fDepth(1)
96 , fSkip(0)
97 , fHandler(&topLevelHandler, 1)
72 { }; 98 { };
73 99
74 XML_Parser fParser; // The expat parser doing the work , owned by caller 100 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 101 SkTDArray<FontFamily*>& fFamilies; // The array to append families, o wned by caller
76 SkAutoTDelete<FontFamily> fCurrentFamily; // The family being created, owned by this 102 SkAutoTDelete<FontFamily> fCurrentFamily; // The family being created, owned by this
77 FontFileInfo* fCurrentFontInfo; // The fontInfo being created, own ed by fCurrentFamily 103 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. 104 int fVersion; // The version of the file parsed.
80 const SkString& fBasePath; // The current base path. 105 const SkString& fBasePath; // The current base path.
81 const bool fIsFallback; // Indicates the file being parsed is a fallback file 106 const bool fIsFallback; // Indicates the file being parsed is a fallback file
82 const char* fFilename; // The name of the file currently being parsed. 107 const char* fFilename; // The name of the file currently being parsed.
108
109 int fDepth; // The current element depth of th e parse.
110 int fSkip; // The depth to stop skipping, 0 i f not skipping.
111 SkTDArray<const TagHandler*> fHandler; // The stack of current tag handle rs.
83 }; 112 };
84 113
85 /** Parses a null terminated string into an integer type, checking for overflow. 114 /** 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 115 * http://www.w3.org/TR/html-markup/datatypes.html#common.data.integer.non-nega tive-def
87 * 116 *
88 * If the string cannot be parsed into 'value', returns false and does not chan ge 'value'. 117 * If the string cannot be parsed into 'value', returns false and does not chan ge 'value'.
89 */ 118 */
90 template <typename T> static bool parse_non_negative_integer(const char* s, T* v alue) { 119 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); 120 SK_COMPILE_ASSERT(std::numeric_limits<T>::is_integer, T_must_be_integer);
92 const T nMax = std::numeric_limits<T>::max() / 10; 121 const T nMax = std::numeric_limits<T>::max() / 10;
(...skipping 24 matching lines...) Expand all
117 146
118 #define SK_FONTCONFIGPARSER_PREFIX "[SkFontConfigParser] " 147 #define SK_FONTCONFIGPARSER_PREFIX "[SkFontConfigParser] "
119 148
120 #define SK_FONTCONFIGPARSER_WARNING(message, ...) SkDebugf( \ 149 #define SK_FONTCONFIGPARSER_WARNING(message, ...) SkDebugf( \
121 SK_FONTCONFIGPARSER_PREFIX "%s:%d:%d: warning: " message "\n", \ 150 SK_FONTCONFIGPARSER_PREFIX "%s:%d:%d: warning: " message "\n", \
122 self->fFilename, \ 151 self->fFilename, \
123 XML_GetCurrentLineNumber(self->fParser), \ 152 XML_GetCurrentLineNumber(self->fParser), \
124 XML_GetCurrentColumnNumber(self->fParser), \ 153 XML_GetCurrentColumnNumber(self->fParser), \
125 ##__VA_ARGS__); 154 ##__VA_ARGS__);
126 155
156 static bool is_whitespace(char c) {
157 return c == ' ' || c == '\n'|| c == '\r' || c == '\t';
158 }
159
160 static void trim_string(SkString* s) {
161 char* str = s->writable_str();
162 const char* start = str; // start is inclusive
163 const char* end = start + s->size(); // end is exclusive
164 while (is_whitespace(*start)) { ++start; }
165 if (start != end) {
166 --end; // make end inclusive
167 while (is_whitespace(*end)) { --end; }
168 ++end; // make end exclusive
169 }
170 size_t len = end - start;
171 memmove(str, start, len);
172 s->resize(len);
173 }
174
127 namespace lmpParser { 175 namespace lmpParser {
128 176
129 static void family_element_handler(FamilyData* self, const char** attributes) { 177 static const TagHandler fontHandler = {
130 // A <family> element without a 'name' (string) attribute is a fallback font . 178 /*start*/[](FamilyData* self, const char* tag, const char** attributes) {
131 // The element may have 'lang' (string) and 'variant' ("elegant", "compact") attributes. 179 // 'weight' (non-negative integer) [default 0]
132 // The element should contain <font> elements. 180 // 'style' ("normal", "italic") [default "auto"]
133 FontFamily* family = new FontFamily(self->fBasePath, true); 181 // 'index' (non-negative integer) [default 0]
134 self->fCurrentFamily.reset(family); 182 // The character data should be a filename.
135 for (size_t i = 0; ATTS_NON_NULL(attributes, i); i += 2) { 183 FontFileInfo& file = self->fCurrentFamily->fFonts.push_back();
136 const char* name = attributes[i]; 184 self->fCurrentFontInfo = &file;
137 const char* value = attributes[i+1]; 185 for (size_t i = 0; ATTS_NON_NULL(attributes, i); i += 2) {
138 size_t nameLen = strlen(name); 186 const char* name = attributes[i];
139 size_t valueLen = strlen(value); 187 const char* value = attributes[i+1];
140 if (MEMEQ("name", name, nameLen)) { 188 size_t nameLen = strlen(name);
141 SkAutoAsciiToLC tolc(value); 189 if (MEMEQ("weight", name, nameLen)) {
142 family->fNames.push_back().set(tolc.lc()); 190 if (!parse_non_negative_integer(value, &file.fWeight)) {
143 family->fIsFallbackFont = false; 191 SK_FONTCONFIGPARSER_WARNING("'%s' is an invalid weight", val ue);
144 } else if (MEMEQ("lang", name, nameLen)) { 192 }
145 family->fLanguage = SkLanguage(value, valueLen); 193 } else if (MEMEQ("style", name, nameLen)) {
146 } else if (MEMEQ("variant", name, nameLen)) { 194 size_t valueLen = strlen(value);
147 if (MEMEQ("elegant", value, valueLen)) { 195 if (MEMEQ("normal", value, valueLen)) {
148 family->fVariant = kElegant_FontVariant; 196 file.fStyle = FontFileInfo::Style::kNormal;
149 } else if (MEMEQ("compact", value, valueLen)) { 197 } else if (MEMEQ("italic", value, valueLen)) {
150 family->fVariant = kCompact_FontVariant; 198 file.fStyle = FontFileInfo::Style::kItalic;
199 }
200 } else if (MEMEQ("index", name, nameLen)) {
201 if (!parse_non_negative_integer(value, &file.fIndex)) {
202 SK_FONTCONFIGPARSER_WARNING("'%s' is an invalid index", valu e);
203 }
151 } 204 }
152 } 205 }
206 },
207 /*end*/[](FamilyData* self, const char* tag) {
208 trim_string(&self->fCurrentFontInfo->fFileName);
209 },
210 /*tag*/NULL,
211 /*chars*/[](void* data, const char* s, int len) {
212 FamilyData* self = static_cast<FamilyData*>(data);
213 self->fCurrentFontInfo->fFileName.append(s, len);
153 } 214 }
154 } 215 };
155 216
156 static void XMLCALL font_file_name_handler(void* data, const char* s, int len) { 217 static const TagHandler familyHandler = {
157 FamilyData* self = static_cast<FamilyData*>(data); 218 /*start*/[](FamilyData* self, const char* tag, const char** attributes) {
158 self->fCurrentFontInfo->fFileName.append(s, len); 219 // 'name' (string) [optional]
159 } 220 // 'lang' (string) [default ""]
160 221 // 'variant' ("elegant", "compact") [default "default"]
161 static void font_element_handler(FamilyData* self, const char** attributes) { 222 // If there is no name, this is a fallback only font.
162 // A <font> element should be contained in a <family> element. 223 FontFamily* family = new FontFamily(self->fBasePath, true);
163 // The element may have 'weight' (non-negative integer), 'style' ("normal", "italic"), 224 self->fCurrentFamily.reset(family);
164 // and 'index' (non-negative integer) attributes. 225 for (size_t i = 0; ATTS_NON_NULL(attributes, i); i += 2) {
165 // The element should contain a filename. 226 const char* name = attributes[i];
166 FontFileInfo& file = self->fCurrentFamily->fFonts.push_back(); 227 const char* value = attributes[i+1];
167 self->fCurrentFontInfo = &file; 228 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); 229 size_t valueLen = strlen(value);
178 if (MEMEQ("normal", value, valueLen)) { 230 if (MEMEQ("name", name, nameLen)) {
179 file.fStyle = FontFileInfo::Style::kNormal; 231 SkAutoAsciiToLC tolc(value);
180 } else if (MEMEQ("italic", value, valueLen)) { 232 family->fNames.push_back().set(tolc.lc());
181 file.fStyle = FontFileInfo::Style::kItalic; 233 family->fIsFallbackFont = false;
182 } 234 } else if (MEMEQ("lang", name, nameLen)) {
183 } else if (MEMEQ("index", name, nameLen)) { 235 family->fLanguage = SkLanguage(value, valueLen);
184 if (!parse_non_negative_integer(value, &file.fIndex)) { 236 } else if (MEMEQ("variant", name, nameLen)) {
185 SK_FONTCONFIGPARSER_WARNING("'%s' is an invalid index", value); 237 if (MEMEQ("elegant", value, valueLen)) {
238 family->fVariant = kElegant_FontVariant;
239 } else if (MEMEQ("compact", value, valueLen)) {
240 family->fVariant = kCompact_FontVariant;
241 }
186 } 242 }
187 } 243 }
188 } 244 },
189 XML_SetCharacterDataHandler(self->fParser, font_file_name_handler); 245 /*end*/[](FamilyData* self, const char* tag) {
190 } 246 *self->fFamilies.append() = self->fCurrentFamily.detach();
247 },
248 /*tag*/[](FamilyData* self, const char* tag, const char** attributes) -> con st TagHandler* {
249 size_t len = strlen(tag);
250 if (MEMEQ("font", tag, len)) {
251 return &fontHandler;
252 }
253 return NULL;
254 },
255 /*chars*/NULL,
256 };
191 257
192 static FontFamily* find_family(FamilyData* self, const SkString& familyName) { 258 static FontFamily* find_family(FamilyData* self, const SkString& familyName) {
193 for (int i = 0; i < self->fFamilies.count(); i++) { 259 for (int i = 0; i < self->fFamilies.count(); i++) {
194 FontFamily* candidate = self->fFamilies[i]; 260 FontFamily* candidate = self->fFamilies[i];
195 for (int j = 0; j < candidate->fNames.count(); j++) { 261 for (int j = 0; j < candidate->fNames.count(); j++) {
196 if (candidate->fNames[j] == familyName) { 262 if (candidate->fNames[j] == familyName) {
197 return candidate; 263 return candidate;
198 } 264 }
199 } 265 }
200 } 266 }
201 return NULL; 267 return NULL;
202 } 268 }
203 269
204 static void alias_element_handler(FamilyData* self, const char** attributes) { 270 static const TagHandler aliasHandler = {
205 // A <alias> element must have 'name' (string) and 'to' (string) attributes. 271 /*start*/[](FamilyData* self, const char* tag, const char** attributes) {
206 // The element may also have a 'weight' (non-negative integer) attribute. 272 // 'name' (string) introduces a new family name.
207 // The 'name' introduces a new family name. 273 // 'to' (string) specifies which (previous) family to alias
208 // The 'to' specifies which (previous) <family> to alias. 274 // 'weight' (non-negative integer) [optional]
209 // If it *does not* have a weight, 'name' is an alias for the entire 'to' <f amily>. 275 // If it *does not* have a weight, 'name' is an alias for the entire 'to ' family.
210 // If it *does* have a weight, 'name' is a new family consisting of the <fon t>(s) with 'weight' 276 // If it *does* have a weight, 'name' is a new family consisting of
211 // from the 'to' <family>. 277 // the font(s) with 'weight' from the 'to' family.
212 278
213 SkString aliasName; 279 SkString aliasName;
214 SkString to; 280 SkString to;
215 int weight = 0; 281 int weight = 0;
216 for (size_t i = 0; ATTS_NON_NULL(attributes, i); i += 2) {
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) { 282 for (size_t i = 0; ATTS_NON_NULL(attributes, i); i += 2) {
335 const char* name = attributes[i]; 283 const char* name = attributes[i];
336 const char* value = attributes[i+1]; 284 const char* value = attributes[i+1];
337 size_t nameLen = strlen(name); 285 size_t nameLen = strlen(name);
338 size_t valueLen = strlen(value); 286 if (MEMEQ("name", name, nameLen)) {
339 if (MEMEQ("variant", name, nameLen)) { 287 SkAutoAsciiToLC tolc(value);
340 const FontVariant prevVariant = currentFamily.fVariant; 288 aliasName.set(tolc.lc());
341 if (MEMEQ("elegant", value, valueLen)) { 289 } else if (MEMEQ("to", name, nameLen)) {
342 currentFamily.fVariant = kElegant_FontVariant; 290 to.set(value);
343 } else if (MEMEQ("compact", value, valueLen)) { 291 } else if (MEMEQ("weight", name, nameLen)) {
344 currentFamily.fVariant = kCompact_FontVariant; 292 if (!parse_non_negative_integer(value, &weight)) {
345 } 293 SK_FONTCONFIGPARSER_WARNING("'%s' is an invalid weight", val ue);
346 if (currentFamily.fFonts.count() > 1 && currentFamily.fVariant ! = prevVariant) { 294 }
347 SK_FONTCONFIGPARSER_WARNING("'%s' unexpected variant found\n " 295 }
348 "Note: Every font file within a family must have identic al variants.", 296 }
349 value); 297
350 } 298 // Assumes that the named family is already declared
351 299 FontFamily* targetFamily = find_family(self, to);
352 } else if (MEMEQ("lang", name, nameLen)) { 300 if (!targetFamily) {
353 SkLanguage prevLang = currentFamily.fLanguage; 301 SK_FONTCONFIGPARSER_WARNING("'%s' alias target not found", to.c_str( ));
354 currentFamily.fLanguage = SkLanguage(value, valueLen); 302 return;
355 if (currentFamily.fFonts.count() > 1 && currentFamily.fLanguage != prevLang) { 303 }
356 SK_FONTCONFIGPARSER_WARNING("'%s' unexpected language found\ n" 304
357 "Note: Every font file within a family must have identic al languages.", 305 if (weight) {
358 value); 306 FontFamily* family = new FontFamily(targetFamily->fBasePath, self->f IsFallback);
359 } 307 family->fNames.push_back().set(aliasName);
360 308
361 } else if (MEMEQ("index", name, nameLen)) { 309 for (int i = 0; i < targetFamily->fFonts.count(); i++) {
362 if (!parse_non_negative_integer(value, &newFileInfo.fIndex)) { 310 if (targetFamily->fFonts[i].fWeight == weight) {
363 SK_FONTCONFIGPARSER_WARNING("'%s' is an invalid index", valu e); 311 family->fFonts.push_back(targetFamily->fFonts[i]);
364 } 312 }
365 } 313 }
366 } 314 *self->fFamilies.append() = family;
367 } 315 } else {
368 self->fCurrentFontInfo = &newFileInfo; 316 targetFamily->fNames.push_back().set(aliasName);
369 XML_SetCharacterDataHandler(self->fParser, text_handler); 317 }
318 },
319 /*end*/NULL,
320 /*tag*/NULL,
321 /*chars*/NULL,
322 };
323
324 static const TagHandler familySetHandler = {
325 /*start*/[](FamilyData* self, const char* tag, const char** attributes) { },
326 /*end*/NULL,
327 /*tag*/[](FamilyData* self, const char* tag, const char** attributes) -> con st TagHandler* {
328 size_t len = strlen(tag);
329 if (MEMEQ("family", tag, len)) {
330 return &familyHandler;
331 } else if (MEMEQ("alias", tag, len)) {
332 return &aliasHandler;
333 }
334 return NULL;
335 },
336 /*chars*/NULL,
337 };
338
339 } // lmpParser
340
341 namespace jbParser {
342
343 static const TagHandler fileHandler = {
344 /*start*/[](FamilyData* self, const char* tag, const char** attributes) {
345 // 'variant' ("elegant", "compact") [default "default"]
346 // 'lang' (string) [default ""]
347 // 'index' (non-negative integer) [default 0]
348 // The character data should be a filename.
349 FontFamily& currentFamily = *self->fCurrentFamily.get();
350 FontFileInfo& newFileInfo = currentFamily.fFonts.push_back();
351 if (attributes) {
352 for (size_t i = 0; ATTS_NON_NULL(attributes, i); i += 2) {
353 const char* name = attributes[i];
354 const char* value = attributes[i+1];
355 size_t nameLen = strlen(name);
356 size_t valueLen = strlen(value);
357 if (MEMEQ("variant", name, nameLen)) {
358 const FontVariant prevVariant = currentFamily.fVariant;
359 if (MEMEQ("elegant", value, valueLen)) {
360 currentFamily.fVariant = kElegant_FontVariant;
361 } else if (MEMEQ("compact", value, valueLen)) {
362 currentFamily.fVariant = kCompact_FontVariant;
363 }
364 if (currentFamily.fFonts.count() > 1 && currentFamily.fVaria nt != prevVariant) {
365 SK_FONTCONFIGPARSER_WARNING("'%s' unexpected variant fou nd\n"
366 "Note: Every font file within a family must have ide ntical variants.",
367 value);
368 }
369
370 } else if (MEMEQ("lang", name, nameLen)) {
371 SkLanguage prevLang = currentFamily.fLanguage;
372 currentFamily.fLanguage = SkLanguage(value, valueLen);
373 if (currentFamily.fFonts.count() > 1 && currentFamily.fLangu age != prevLang) {
374 SK_FONTCONFIGPARSER_WARNING("'%s' unexpected language fo und\n"
375 "Note: Every font file within a family must have ide ntical languages.",
376 value);
377 }
378
379 } else if (MEMEQ("index", name, nameLen)) {
380 if (!parse_non_negative_integer(value, &newFileInfo.fIndex)) {
381 SK_FONTCONFIGPARSER_WARNING("'%s' is an invalid index", value);
382 }
383 }
384 }
385 }
386 self->fCurrentFontInfo = &newFileInfo;
387 },
388 /*end*/NULL,
389 /*tag*/NULL,
390 /*chars*/[](void* data, const char* s, int len) {
391 FamilyData* self = static_cast<FamilyData*>(data);
392 self->fCurrentFontInfo->fFileName.append(s, len);
393 }
394 };
395
396 static const TagHandler fileSetHandler = {
397 /*start*/NULL,
398 /*end*/NULL,
399 /*tag*/[](FamilyData* self, const char* tag, const char** attributes) -> con st TagHandler* {
400 size_t len = strlen(tag);
401 if (MEMEQ("file", tag, len)) {
402 return &fileHandler;
403 }
404 return NULL;
405 },
406 /*chars*/NULL,
407 };
408
409 static const TagHandler nameHandler = {
410 /*start*/[](FamilyData* self, const char* tag, const char** attributes) {
411 // The character data should be a name for the font.
412 self->fCurrentFamily->fNames.push_back();
413 },
414 /*end*/NULL,
415 /*tag*/NULL,
416 /*chars*/[](void* data, const char* s, int len) {
417 FamilyData* self = static_cast<FamilyData*>(data);
418 SkAutoAsciiToLC tolc(s, len);
419 self->fCurrentFamily->fNames.back().append(tolc.lc(), len);
420 }
421 };
422
423 static const TagHandler nameSetHandler = {
424 /*start*/NULL,
425 /*end*/NULL,
426 /*tag*/[](FamilyData* self, const char* tag, const char** attributes) -> con st TagHandler* {
427 size_t len = strlen(tag);
428 if (MEMEQ("name", tag, len)) {
429 return &nameHandler;
430 }
431 return NULL;
432 },
433 /*chars*/NULL,
434 };
435
436 static const TagHandler familyHandler = {
437 /*start*/[](FamilyData* self, const char* tag, const char** attributes) {
438 self->fCurrentFamily.reset(new FontFamily(self->fBasePath, self->fIsFall back));
439 // 'order' (non-negative integer) [default -1]
440 for (size_t i = 0; ATTS_NON_NULL(attributes, i); i += 2) {
441 const char* value = attributes[i+1];
442 parse_non_negative_integer(value, &self->fCurrentFamily->fOrder);
443 }
444 },
445 /*end*/[](FamilyData* self, const char* tag) {
446 *self->fFamilies.append() = self->fCurrentFamily.detach();
447 },
448 /*tag*/[](FamilyData* self, const char* tag, const char** attributes) -> con st TagHandler* {
449 size_t len = strlen(tag);
450 if (MEMEQ("nameset", tag, len)) {
451 return &nameSetHandler;
452 } else if (MEMEQ("fileset", tag, len)) {
453 return &fileSetHandler;
454 }
455 return NULL;
456 },
457 /*chars*/NULL,
458 };
459
460 static const TagHandler familySetHandler = {
461 /*start*/NULL,
462 /*end*/NULL,
463 /*tag*/[](FamilyData* self, const char* tag, const char** attributes) -> con st TagHandler* {
464 size_t len = strlen(tag);
465 if (MEMEQ("family", tag, len)) {
466 return &familyHandler;
467 }
468 return NULL;
469 },
470 /*chars*/NULL,
471 };
472
473 } // namespace jbParser
474
475 static const TagHandler topLevelHandler = {
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("familyset", tag, len)) {
481 // 'version' (non-negative integer) [default 0]
482 for (size_t i = 0; ATTS_NON_NULL(attributes, i); i += 2) {
483 const char* name = attributes[i];
484 size_t nameLen = strlen(name);
485 if (MEMEQ("version", name, nameLen)) {
486 const char* value = attributes[i+1];
487 if (parse_non_negative_integer(value, &self->fVersion)) {
488 if (self->fVersion >= 21) {
489 return &lmpParser::familySetHandler;
490 }
491 }
492 }
493 }
494 return &jbParser::familySetHandler;
495 }
496 return NULL;
497 },
498 /*chars*/NULL,
499 };
500
501 static void XMLCALL start_element_handler(void *data, const char *tag, const cha r **attributes) {
502 FamilyData* self = static_cast<FamilyData*>(data);
503
504 if (!self->fSkip) {
505 const TagHandler* parent = self->fHandler.top();
506 const TagHandler* child = parent->tag ? parent->tag(self, tag, attribute s) : NULL;
507 if (child) {
508 if (child->start) {
509 child->start(self, tag, attributes);
510 }
511 self->fHandler.push(child);
512 XML_SetCharacterDataHandler(self->fParser, child->chars);
513 } else {
514 SK_FONTCONFIGPARSER_WARNING("'%s' tag not recognized, skipping", tag );
515 XML_SetCharacterDataHandler(self->fParser, NULL);
516 self->fSkip = self->fDepth;
517 }
518 }
519
520 ++self->fDepth;
370 } 521 }
371 522
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) { 523 static void XMLCALL end_element_handler(void* data, const char* tag) {
423 FamilyData* self = static_cast<FamilyData*>(data); 524 FamilyData* self = static_cast<FamilyData*>(data);
424 size_t len = strlen(tag); 525 --self->fDepth;
425 if (MEMEQ("family", tag, len)) { 526
426 // Done parsing a Family - store the created currentFamily in the famili es array 527 if (!self->fSkip) {
427 *self->fFamilies.append() = self->fCurrentFamily.detach(); 528 const TagHandler* child = self->fHandler.top();
428 } else if (MEMEQ("nameset", tag, len)) { 529 if (child->end) {
429 self->fCurrentTag = kNo_CurrentTag; 530 child->end(self, tag);
430 } else if (MEMEQ("fileset", tag, len)) { 531 }
431 self->fCurrentTag = kNo_CurrentTag; 532 self->fHandler.pop();
432 } else if ((MEMEQ("name", tag, len) && self->fCurrentTag == kNameSet_Current Tag) || 533 const TagHandler* parent = self->fHandler.top();
433 (MEMEQ("file", tag, len) && self->fCurrentTag == kFileSet_Current Tag)) { 534 XML_SetCharacterDataHandler(self->fParser, parent->chars);
434 // Disable the arbitrary text handler installed to load Name data 535 }
435 XML_SetCharacterDataHandler(self->fParser, NULL); 536
537 if (self->fSkip == self->fDepth) {
538 self->fSkip = 0;
539 const TagHandler* parent = self->fHandler.top();
540 XML_SetCharacterDataHandler(self->fParser, parent->chars);
436 } 541 }
437 } 542 }
438 543
439 } // namespace jbParser
440
441 static void XMLCALL xml_entity_decl_handler(void *data, 544 static void XMLCALL xml_entity_decl_handler(void *data,
442 const XML_Char *entityName, 545 const XML_Char *entityName,
443 int is_parameter_entity, 546 int is_parameter_entity,
444 const XML_Char *value, 547 const XML_Char *value,
445 int value_length, 548 int value_length,
446 const XML_Char *base, 549 const XML_Char *base,
447 const XML_Char *systemId, 550 const XML_Char *systemId,
448 const XML_Char *publicId, 551 const XML_Char *publicId,
449 const XML_Char *notationName) 552 const XML_Char *notationName)
450 { 553 {
(...skipping 27 matching lines...) Expand all
478 return -1; 581 return -1;
479 } 582 }
480 583
481 SkAutoTCallVProc<remove_ptr<XML_Parser>::type, XML_ParserFree> parser( 584 SkAutoTCallVProc<remove_ptr<XML_Parser>::type, XML_ParserFree> parser(
482 XML_ParserCreate_MM(NULL, &sk_XML_alloc, NULL)); 585 XML_ParserCreate_MM(NULL, &sk_XML_alloc, NULL));
483 if (!parser) { 586 if (!parser) {
484 SkDebugf(SK_FONTCONFIGPARSER_PREFIX "could not create XML parser\n"); 587 SkDebugf(SK_FONTCONFIGPARSER_PREFIX "could not create XML parser\n");
485 return -1; 588 return -1;
486 } 589 }
487 590
488 FamilyData self(parser, families, basePath, isFallback, filename); 591 FamilyData self(parser, families, basePath, isFallback, filename, &topLevelH andler);
489 XML_SetUserData(parser, &self); 592 XML_SetUserData(parser, &self);
490 593
491 // Disable entity processing, to inhibit internal entity expansion. See expa t CVE-2013-0340 594 // Disable entity processing, to inhibit internal entity expansion. See expa t CVE-2013-0340
492 XML_SetEntityDeclHandler(parser, xml_entity_decl_handler); 595 XML_SetEntityDeclHandler(parser, xml_entity_decl_handler);
493 596
494 // Start parsing oldschool; switch these in flight if we detect a newer vers ion of the file. 597 // 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); 598 XML_SetElementHandler(parser, start_element_handler, end_element_handler);
496 599
497 // One would assume it would be faster to have a buffer on the stack and cal l XML_Parse. 600 // 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. 601 // 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.) 602 // (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. 603 // In debug, buffer a small odd number of bytes to detect slicing in XML_Cha racterDataHandler.
501 static const int bufferSize = 512 SkDEBUGCODE( - 507); 604 static const int bufferSize = 512 SkDEBUGCODE( - 507);
502 bool done = false; 605 bool done = false;
503 while (!done) { 606 while (!done) {
504 void* buffer = XML_GetBuffer(parser, bufferSize); 607 void* buffer = XML_GetBuffer(parser, bufferSize);
505 if (!buffer) { 608 if (!buffer) {
(...skipping 164 matching lines...) Expand 10 before | Expand all | Expand 10 after
670 const char* tag = fTag.c_str(); 773 const char* tag = fTag.c_str();
671 774
672 // strip off the rightmost "-.*" 775 // strip off the rightmost "-.*"
673 const char* parentTagEnd = strrchr(tag, '-'); 776 const char* parentTagEnd = strrchr(tag, '-');
674 if (parentTagEnd == NULL) { 777 if (parentTagEnd == NULL) {
675 return SkLanguage(); 778 return SkLanguage();
676 } 779 }
677 size_t parentTagLen = parentTagEnd - tag; 780 size_t parentTagLen = parentTagEnd - tag;
678 return SkLanguage(tag, parentTagLen); 781 return SkLanguage(tag, parentTagLen);
679 } 782 }
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