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