OLD | NEW |
---|---|
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 Loading... | |
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 Loading... | |
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 Loading... | |
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 } |
OLD | NEW |