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

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: Const, comments, length. 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 {
djsollen 2015/05/13 14:27:33 comments for the purpose of each of these methods.
bungeman-skia 2015/05/13 15:41:50 Done.
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 const TagHandler* (*tag)(FamilyData* data, const char* tag, const char** att ributes);
53 kFileSet_CurrentTag 56 XML_CharacterDataHandler chars;
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 const 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<const 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 const TagHandler fontHandler = {
130 // A <family> element without a 'name' (string) attribute is a fallback font . 155 /*start*/[](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) [default 0]
132 // The element should contain <font> elements. 157 // 'style' ("normal", "italic") [default "auto"]
133 FontFamily* family = new FontFamily(self->fBasePath, true); 158 // 'index' (non-negative integer) [default 0]
134 self->fCurrentFamily.reset(family); 159 // The character data should be 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 /*end*/[](FamilyData* self, const char* tag) {
185 trim_string(&self->fCurrentFontInfo->fFileName);
186 },
187 /*tag*/[](FamilyData*, const char*, const char**) -> const TagHandler* { ret urn NULL; },
188 /*chars*/[](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 const TagHandler familyHandler = {
157 FamilyData* self = static_cast<FamilyData*>(data); 195 /*start*/[](FamilyData* self, const char* tag, const char** attributes) {
158 self->fCurrentFontInfo->fFileName.append(s, len); 196 // 'name' (string) [optional]
159 } 197 // 'lang' (string) [default ""]
160 198 // 'variant' ("elegant", "compact") [default "default"]
161 static void font_element_handler(FamilyData* self, const char** attributes) { 199 // If there is no name, this is a fallback only font.
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 /*end*/[](FamilyData* self, const char* tag) {
190 } 223 *self->fFamilies.append() = self->fCurrentFamily.detach();
224 },
225 /*tag*/[](FamilyData* self, const char* tag, const char** attributes) -> con st TagHandler* {
226 size_t len = strlen(tag);
227 if (MEMEQ("font", tag, len)) {
228 return &fontHandler;
229 }
230 return NULL;
231 },
232 /*chars*/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 const TagHandler aliasHandler = {
205 // A <alias> element must have 'name' (string) and 'to' (string) attributes. 248 /*start*/[](FamilyData* self, const char* tag, const char** attributes) {
206 // The element may also have a 'weight' (non-negative integer) attribute. 249 // 'name' (string) introduces a new family name.
207 // The 'name' introduces a new family name. 250 // 'to' (string) specifies which (previous) family to alias
208 // The 'to' specifies which (previous) <family> to alias. 251 // 'weight' (non-negative integer) [optional]
209 // If it *does not* have a weight, 'name' is an alias for the entire 'to' <f amily>. 252 // 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' 253 // If it *does* have a weight, 'name' is a new family consisting of
211 // from the 'to' <family>. 254 // the font(s) with 'weight' from the 'to' family.
212 255
213 SkString aliasName; 256 SkString aliasName;
214 SkString to; 257 SkString to;
215 int weight = 0; 258 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) { 259 for (size_t i = 0; ATTS_NON_NULL(attributes, i); i += 2) {
335 const char* name = attributes[i]; 260 const char* name = attributes[i];
336 const char* value = attributes[i+1]; 261 const char* value = attributes[i+1];
337 size_t nameLen = strlen(name); 262 size_t nameLen = strlen(name);
338 size_t valueLen = strlen(value); 263 if (MEMEQ("name", name, nameLen)) {
339 if (MEMEQ("variant", name, nameLen)) { 264 SkAutoAsciiToLC tolc(value);
340 const FontVariant prevVariant = currentFamily.fVariant; 265 aliasName.set(tolc.lc());
341 if (MEMEQ("elegant", value, valueLen)) { 266 } else if (MEMEQ("to", name, nameLen)) {
342 currentFamily.fVariant = kElegant_FontVariant; 267 to.set(value);
343 } else if (MEMEQ("compact", value, valueLen)) { 268 } else if (MEMEQ("weight", name, nameLen)) {
344 currentFamily.fVariant = kCompact_FontVariant; 269 if (!parse_non_negative_integer(value, &weight)) {
345 } 270 SK_FONTCONFIGPARSER_WARNING("'%s' is an invalid weight", val ue);
346 if (currentFamily.fFonts.count() > 1 && currentFamily.fVariant ! = prevVariant) { 271 }
347 SK_FONTCONFIGPARSER_WARNING("'%s' unexpected variant found\n " 272 }
348 "Note: Every font file within a family must have identic al variants.", 273 }
349 value); 274
350 } 275 // Assumes that the named family is already declared
351 276 FontFamily* targetFamily = find_family(self, to);
352 } else if (MEMEQ("lang", name, nameLen)) { 277 if (!targetFamily) {
353 SkLanguage prevLang = currentFamily.fLanguage; 278 SK_FONTCONFIGPARSER_WARNING("'%s' alias target not found", to.c_str( ));
354 currentFamily.fLanguage = SkLanguage(value, valueLen); 279 return;
355 if (currentFamily.fFonts.count() > 1 && currentFamily.fLanguage != prevLang) { 280 }
356 SK_FONTCONFIGPARSER_WARNING("'%s' unexpected language found\ n" 281
357 "Note: Every font file within a family must have identic al languages.", 282 if (weight) {
358 value); 283 FontFamily* family = new FontFamily(targetFamily->fBasePath, self->f IsFallback);
359 } 284 family->fNames.push_back().set(aliasName);
360 285
361 } else if (MEMEQ("index", name, nameLen)) { 286 for (int i = 0; i < targetFamily->fFonts.count(); i++) {
362 if (!parse_non_negative_integer(value, &newFileInfo.fIndex)) { 287 if (targetFamily->fFonts[i].fWeight == weight) {
363 SK_FONTCONFIGPARSER_WARNING("'%s' is an invalid index", valu e); 288 family->fFonts.push_back(targetFamily->fFonts[i]);
364 } 289 }
365 } 290 }
366 } 291 *self->fFamilies.append() = family;
367 } 292 } else {
368 self->fCurrentFontInfo = &newFileInfo; 293 targetFamily->fNames.push_back().set(aliasName);
369 XML_SetCharacterDataHandler(self->fParser, text_handler); 294 }
295 },
296 /*end*/[](FamilyData* self, const char* tag) { },
djsollen 2015/05/13 14:27:33 why not a null function ptr?
bungeman-skia 2015/05/13 15:41:50 Tags which are just containers without attributes
297 /*tag*/[](FamilyData*, const char*, const char**) -> const TagHandler* { ret urn NULL; },
298 /*chars*/NULL,
299 };
300
301 static const TagHandler familySetHandler = {
302 /*start*/[](FamilyData* self, const char* tag, const char** attributes) { },
303 /*end*/[](FamilyData* self, const char* tag) { },
304 /*tag*/[](FamilyData* self, const char* tag, const char** attributes) -> con st TagHandler* {
305 size_t len = strlen(tag);
306 if (MEMEQ("family", tag, len)) {
307 return &familyHandler;
308 } else if (MEMEQ("alias", tag, len)) {
309 return &aliasHandler;
310 }
311 return NULL;
312 },
313 /*chars*/NULL,
314 };
315
316 } // lmpParser
317
318 namespace jbParser {
319
320 static const TagHandler fileHandler = {
321 /*start*/[](FamilyData* self, const char* tag, const char** attributes) {
322 // 'variant' ("elegant", "compact") [default "default"]
323 // 'lang' (string) [default ""]
324 // 'index' (non-negative integer) [default 0]
325 // The character data should be a filename.
326 FontFamily& currentFamily = *self->fCurrentFamily.get();
327 FontFileInfo& newFileInfo = currentFamily.fFonts.push_back();
328 if (attributes) {
329 for (size_t i = 0; ATTS_NON_NULL(attributes, i); i += 2) {
330 const char* name = attributes[i];
331 const char* value = attributes[i+1];
332 size_t nameLen = strlen(name);
333 size_t valueLen = strlen(value);
334 if (MEMEQ("variant", name, nameLen)) {
335 const FontVariant prevVariant = currentFamily.fVariant;
336 if (MEMEQ("elegant", value, valueLen)) {
337 currentFamily.fVariant = kElegant_FontVariant;
338 } else if (MEMEQ("compact", value, valueLen)) {
339 currentFamily.fVariant = kCompact_FontVariant;
340 }
341 if (currentFamily.fFonts.count() > 1 && currentFamily.fVaria nt != prevVariant) {
342 SK_FONTCONFIGPARSER_WARNING("'%s' unexpected variant fou nd\n"
343 "Note: Every font file within a family must have ide ntical variants.",
344 value);
345 }
346
347 } else if (MEMEQ("lang", name, nameLen)) {
348 SkLanguage prevLang = currentFamily.fLanguage;
349 currentFamily.fLanguage = SkLanguage(value, valueLen);
350 if (currentFamily.fFonts.count() > 1 && currentFamily.fLangu age != prevLang) {
351 SK_FONTCONFIGPARSER_WARNING("'%s' unexpected language fo und\n"
352 "Note: Every font file within a family must have ide ntical languages.",
353 value);
354 }
355
356 } else if (MEMEQ("index", name, nameLen)) {
357 if (!parse_non_negative_integer(value, &newFileInfo.fIndex)) {
358 SK_FONTCONFIGPARSER_WARNING("'%s' is an invalid index", value);
359 }
360 }
361 }
362 }
363 self->fCurrentFontInfo = &newFileInfo;
364 },
365 /*end*/[](FamilyData* self, const char* tag) { },
366 /*tag*/[](FamilyData*, const char*, const char**) -> const TagHandler* { ret urn NULL; },
367 /*chars*/[](void* data, const char* s, int len) {
368 FamilyData* self = static_cast<FamilyData*>(data);
369 self->fCurrentFontInfo->fFileName.append(s, len);
370 }
371 };
372
373 static const TagHandler fileSetHandler = {
374 /*start*/[](FamilyData* self, const char* tag, const char** attributes) { },
375 /*end*/[](FamilyData* self, const char* tag) { },
376 /*tag*/[](FamilyData* self, const char* tag, const char** attributes) -> con st TagHandler* {
377 size_t len = strlen(tag);
378 if (MEMEQ("file", tag, len)) {
379 return &fileHandler;
380 }
381 return NULL;
382 },
383 /*chars*/NULL,
384 };
385
386 static const TagHandler nameHandler = {
387 /*start*/[](FamilyData* self, const char* tag, const char** attributes) {
388 // The character data should be a name for the font.
389 self->fCurrentFamily->fNames.push_back();
390 },
391 /*end*/[](FamilyData* self, const char* tag) { },
392 /*tag*/[](FamilyData*, const char*, const char**) -> const TagHandler* { ret urn NULL; },
393 /*chars*/[](void* data, const char* s, int len) {
394 FamilyData* self = static_cast<FamilyData*>(data);
395 SkAutoAsciiToLC tolc(s, len);
396 self->fCurrentFamily->fNames.back().append(tolc.lc(), len);
397 }
398 };
399
400 static const TagHandler nameSetHandler = {
401 /*start*/[](FamilyData* self, const char* tag, const char** attributes) { },
402 /*end*/[](FamilyData* self, const char* tag) { },
403 /*tag*/[](FamilyData* self, const char* tag, const char** attributes) -> con st TagHandler* {
404 size_t len = strlen(tag);
405 if (MEMEQ("name", tag, len)) {
406 return &nameHandler;
407 }
408 return NULL;
409 },
410 /*chars*/NULL,
411 };
412
413 static const TagHandler familyHandler = {
414 /*start*/[](FamilyData* self, const char* tag, const char** attributes) {
415 self->fCurrentFamily.reset(new FontFamily(self->fBasePath, self->fIsFall back));
416 // 'order' (non-negative integer) [default -1]
417 for (size_t i = 0; ATTS_NON_NULL(attributes, i); i += 2) {
418 const char* value = attributes[i+1];
419 parse_non_negative_integer(value, &self->fCurrentFamily->fOrder);
420 }
421 },
422 /*end*/[](FamilyData* self, const char* tag) {
423 *self->fFamilies.append() = self->fCurrentFamily.detach();
424 },
425 /*tag*/[](FamilyData* self, const char* tag, const char** attributes) -> con st TagHandler* {
426 size_t len = strlen(tag);
427 if (MEMEQ("nameset", tag, len)) {
428 return &nameSetHandler;
429 } else if (MEMEQ("fileset", tag, len)) {
430 return &fileSetHandler;
431 }
432 return NULL;
433 },
434 /*chars*/NULL,
435 };
436
437 static const TagHandler familySetHandler = {
438 /*start*/[](FamilyData* self, const char* tag, const char** attributes) { },
439 /*end*/[](FamilyData* self, const char* tag) { },
440 /*tag*/[](FamilyData* self, const char* tag, const char** attributes) -> con st TagHandler* {
441 size_t len = strlen(tag);
442 if (MEMEQ("family", tag, len)) {
443 return &familyHandler;
444 }
445 return NULL;
446 },
447 /*chars*/NULL,
448 };
449
450 } // namespace jbParser
451
452 static const TagHandler topLevelHandler = {
453 /*start*/[](FamilyData* self, const char* tag, const char** attributes) { },
454 /*end*/[](FamilyData* self, const char* tag) { },
455 /*tag*/[](FamilyData* self, const char* tag, const char** attributes) -> con st TagHandler* {
456 size_t len = strlen(tag);
457 if (MEMEQ("familyset", tag, len)) {
458 // 'version' (non-negative integer) [default 0]
459 for (size_t i = 0; ATTS_NON_NULL(attributes, i); i += 2) {
460 const char* name = attributes[i];
461 size_t nameLen = strlen(name);
462 if (MEMEQ("version", name, nameLen)) {
463 const char* value = attributes[i+1];
464 if (parse_non_negative_integer(value, &self->fVersion)) {
465 if (self->fVersion >= 21) {
466 return &lmpParser::familySetHandler;
467 }
468 }
469 }
470 }
471 return &jbParser::familySetHandler;
472 }
473 return NULL;
474 },
475 /*chars*/NULL,
476 };
477
478 static void XMLCALL start_element_handler(void *data, const char *tag, const cha r **attributes) {
479 FamilyData* self = static_cast<FamilyData*>(data);
480
481 if (!self->fSkip) {
482 const TagHandler* subHandler = self->fHandler.top()->tag(self, tag, attr ibutes);
483 if (subHandler) {
484 subHandler->start(self, tag, attributes);
485 self->fHandler.push(subHandler);
486 XML_SetCharacterDataHandler(self->fParser, subHandler->chars);
487 } else {
488 SK_FONTCONFIGPARSER_WARNING("'%s' tag not recognized, skipping", tag );
489 XML_SetCharacterDataHandler(self->fParser, NULL);
490 self->fSkip = self->fDepth;
491 }
492 }
493
494 ++self->fDepth;
370 } 495 }
371 496
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) { 497 static void XMLCALL end_element_handler(void* data, const char* tag) {
423 FamilyData* self = static_cast<FamilyData*>(data); 498 FamilyData* self = static_cast<FamilyData*>(data);
424 size_t len = strlen(tag); 499 --self->fDepth;
425 if (MEMEQ("family", tag, len)) { 500
426 // Done parsing a Family - store the created currentFamily in the famili es array 501 if (!self->fSkip) {
427 *self->fFamilies.append() = self->fCurrentFamily.detach(); 502 self->fHandler.top()->end(self, tag);
428 } else if (MEMEQ("nameset", tag, len)) { 503 self->fHandler.pop();
429 self->fCurrentTag = kNo_CurrentTag; 504 XML_SetCharacterDataHandler(self->fParser, self->fHandler.top()->chars);
430 } else if (MEMEQ("fileset", tag, len)) { 505 }
431 self->fCurrentTag = kNo_CurrentTag; 506
432 } else if ((MEMEQ("name", tag, len) && self->fCurrentTag == kNameSet_Current Tag) || 507 if (self->fSkip == self->fDepth) {
433 (MEMEQ("file", tag, len) && self->fCurrentTag == kFileSet_Current Tag)) { 508 self->fSkip = 0;
434 // Disable the arbitrary text handler installed to load Name data 509 XML_SetCharacterDataHandler(self->fParser, self->fHandler.top()->chars);
435 XML_SetCharacterDataHandler(self->fParser, NULL);
436 } 510 }
437 } 511 }
438 512
439 } // namespace jbParser
440
441 static void XMLCALL xml_entity_decl_handler(void *data, 513 static void XMLCALL xml_entity_decl_handler(void *data,
442 const XML_Char *entityName, 514 const XML_Char *entityName,
443 int is_parameter_entity, 515 int is_parameter_entity,
444 const XML_Char *value, 516 const XML_Char *value,
445 int value_length, 517 int value_length,
446 const XML_Char *base, 518 const XML_Char *base,
447 const XML_Char *systemId, 519 const XML_Char *systemId,
448 const XML_Char *publicId, 520 const XML_Char *publicId,
449 const XML_Char *notationName) 521 const XML_Char *notationName)
450 { 522 {
(...skipping 27 matching lines...) Expand all
478 return -1; 550 return -1;
479 } 551 }
480 552
481 SkAutoTCallVProc<remove_ptr<XML_Parser>::type, XML_ParserFree> parser( 553 SkAutoTCallVProc<remove_ptr<XML_Parser>::type, XML_ParserFree> parser(
482 XML_ParserCreate_MM(NULL, &sk_XML_alloc, NULL)); 554 XML_ParserCreate_MM(NULL, &sk_XML_alloc, NULL));
483 if (!parser) { 555 if (!parser) {
484 SkDebugf(SK_FONTCONFIGPARSER_PREFIX "could not create XML parser\n"); 556 SkDebugf(SK_FONTCONFIGPARSER_PREFIX "could not create XML parser\n");
485 return -1; 557 return -1;
486 } 558 }
487 559
488 FamilyData self(parser, families, basePath, isFallback, filename); 560 FamilyData self(parser, families, basePath, isFallback, filename, &topLevelH andler);
489 XML_SetUserData(parser, &self); 561 XML_SetUserData(parser, &self);
490 562
491 // Disable entity processing, to inhibit internal entity expansion. See expa t CVE-2013-0340 563 // Disable entity processing, to inhibit internal entity expansion. See expa t CVE-2013-0340
492 XML_SetEntityDeclHandler(parser, xml_entity_decl_handler); 564 XML_SetEntityDeclHandler(parser, xml_entity_decl_handler);
493 565
494 // Start parsing oldschool; switch these in flight if we detect a newer vers ion of the file. 566 // 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); 567 XML_SetElementHandler(parser, start_element_handler, end_element_handler);
496 568
497 // One would assume it would be faster to have a buffer on the stack and cal l XML_Parse. 569 // 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. 570 // 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.) 571 // (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. 572 // In debug, buffer a small odd number of bytes to detect slicing in XML_Cha racterDataHandler.
501 static const int bufferSize = 512 SkDEBUGCODE( - 507); 573 static const int bufferSize = 512 SkDEBUGCODE( - 507);
502 bool done = false; 574 bool done = false;
503 while (!done) { 575 while (!done) {
504 void* buffer = XML_GetBuffer(parser, bufferSize); 576 void* buffer = XML_GetBuffer(parser, bufferSize);
505 if (!buffer) { 577 if (!buffer) {
(...skipping 164 matching lines...) Expand 10 before | Expand all | Expand 10 after
670 const char* tag = fTag.c_str(); 742 const char* tag = fTag.c_str();
671 743
672 // strip off the rightmost "-.*" 744 // strip off the rightmost "-.*"
673 const char* parentTagEnd = strrchr(tag, '-'); 745 const char* parentTagEnd = strrchr(tag, '-');
674 if (parentTagEnd == NULL) { 746 if (parentTagEnd == NULL) {
675 return SkLanguage(); 747 return SkLanguage();
676 } 748 }
677 size_t parentTagLen = parentTagEnd - tag; 749 size_t parentTagLen = parentTagEnd - tag;
678 return SkLanguage(tag, parentTagLen); 750 return SkLanguage(tag, parentTagLen);
679 } 751 }
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