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