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

Side by Side Diff: src/ports/SkFontConfigParser_android.cpp

Issue 887113002: Add factory for Android font manager. (Closed) Base URL: https://skia.googlesource.com/skia.git@master
Patch Set: Add 'f' and const. Created 5 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 /* 1 /*
2 * Copyright 2011 The Android Open Source Project 2 * Copyright 2011 The Android Open Source Project
3 * 3 *
4 * Use of this source code is governed by a BSD-style license that can be 4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file. 5 * found in the LICENSE file.
6 */ 6 */
7 7
8 #include "SkFontConfigParser_android.h" 8 #include "SkFontConfigParser_android.h"
9 #include "SkFontMgr_android.h"
10 #include "SkStream.h"
9 #include "SkTDArray.h" 11 #include "SkTDArray.h"
10 #include "SkTSearch.h" 12 #include "SkTSearch.h"
11 #include "SkTypeface.h" 13 #include "SkTypeface.h"
12 14
13 #include <expat.h> 15 #include <expat.h>
14 #include <dirent.h> 16 #include <dirent.h>
15 #include <stdio.h> 17 #include <stdio.h>
16 18
17 #include <limits> 19 #include <limits>
20 #include <stdlib.h>
18 21
19 // From Android version LMP onwards, all font files collapse into 22 // From Android version LMP onwards, all font files collapse into
20 // /system/etc/fonts.xml. Instead of trying to detect which version 23 // /system/etc/fonts.xml. Instead of trying to detect which version
21 // we're on, try to open fonts.xml; if that fails, fall back to the 24 // we're on, try to open fonts.xml; if that fails, fall back to the
22 // older filename. 25 // older filename.
23 #define LMP_SYSTEM_FONTS_FILE "/system/etc/fonts.xml" 26 #define LMP_SYSTEM_FONTS_FILE "/system/etc/fonts.xml"
24 #define OLD_SYSTEM_FONTS_FILE "/system/etc/system_fonts.xml" 27 #define OLD_SYSTEM_FONTS_FILE "/system/etc/system_fonts.xml"
25 #define FALLBACK_FONTS_FILE "/system/etc/fallback_fonts.xml" 28 #define FALLBACK_FONTS_FILE "/system/etc/fallback_fonts.xml"
26 #define VENDOR_FONTS_FILE "/vendor/etc/fallback_fonts.xml" 29 #define VENDOR_FONTS_FILE "/vendor/etc/fallback_fonts.xml"
27 30
28 #define LOCALE_FALLBACK_FONTS_SYSTEM_DIR "/system/etc" 31 #define LOCALE_FALLBACK_FONTS_SYSTEM_DIR "/system/etc"
29 #define LOCALE_FALLBACK_FONTS_VENDOR_DIR "/vendor/etc" 32 #define LOCALE_FALLBACK_FONTS_VENDOR_DIR "/vendor/etc"
30 #define LOCALE_FALLBACK_FONTS_PREFIX "fallback_fonts-" 33 #define LOCALE_FALLBACK_FONTS_PREFIX "fallback_fonts-"
31 #define LOCALE_FALLBACK_FONTS_SUFFIX ".xml" 34 #define LOCALE_FALLBACK_FONTS_SUFFIX ".xml"
32 35
36 #ifndef SK_FONT_FILE_PREFIX
37 # define SK_FONT_FILE_PREFIX "/fonts/"
38 #endif
39
33 /** 40 /**
34 * This file contains TWO parsers: one for JB and earlier (system_fonts.xml / 41 * This file contains TWO parsers: one for JB and earlier (system_fonts.xml /
35 * fallback_fonts.xml), one for LMP and later (fonts.xml). 42 * fallback_fonts.xml), one for LMP and later (fonts.xml).
36 * We start with the JB parser, and if we detect a <familyset> tag with 43 * We start with the JB parser, and if we detect a <familyset> tag with
37 * version 21 or higher we switch to the LMP parser. 44 * version 21 or higher we switch to the LMP parser.
38 */ 45 */
39 46
40 // These defines are used to determine the kind of tag that we're currently 47 /** Used to track which tag is currently populating with data.
41 // populating with data. We only care about the sibling tags nameset and fileset 48 * Only nameset and fileset are of interest for now.
42 // for now. 49 */
43 #define NO_TAG 0 50 enum CurrentTag {
44 #define NAMESET_TAG 1 51 kNo_CurrentTag,
45 #define FILESET_TAG 2 52 kNameSet_CurrentTag,
53 kFileSet_CurrentTag
54 };
46 55
47 /** 56 /**
48 * The FamilyData structure is passed around by the parser so that each handler 57 * The FamilyData structure is passed around by the parser so that each handler
49 * can read these variables that are relevant to the current parsing. 58 * can read these variables that are relevant to the current parsing.
50 */ 59 */
51 struct FamilyData { 60 struct FamilyData {
52 FamilyData(XML_Parser parser, SkTDArray<FontFamily*>& families) 61 FamilyData(XML_Parser parser, SkTDArray<FontFamily*>& families,
62 const SkString& basePath, bool isFallback)
53 : fParser(parser) 63 : fParser(parser)
54 , fFamilies(families) 64 , fFamilies(families)
55 , fCurrentFamily(NULL) 65 , fCurrentFamily(NULL)
56 , fCurrentFontInfo(NULL) 66 , fCurrentFontInfo(NULL)
57 , fCurrentTag(NO_TAG) 67 , fCurrentTag(kNo_CurrentTag)
58 , fVersion(0) 68 , fVersion(0)
59 { } 69 , fBasePath(basePath)
70 , fIsFallback(isFallback)
71 { };
60 72
61 XML_Parser fParser; // The expat parser doing the work , owned by caller 73 XML_Parser fParser; // The expat parser doing the work , owned by caller
62 SkTDArray<FontFamily*>& fFamilies; // The array to append families, o wned by caller 74 SkTDArray<FontFamily*>& fFamilies; // The array to append families, o wned by caller
63 SkAutoTDelete<FontFamily> fCurrentFamily; // The family being created, owned by this 75 SkAutoTDelete<FontFamily> fCurrentFamily; // The family being created, owned by this
64 FontFileInfo* fCurrentFontInfo; // The fontInfo being created, own ed by currentFamily 76 FontFileInfo* fCurrentFontInfo; // The fontInfo being created, own ed by fCurrentFamily
65 int fCurrentTag; // Flag to indicate when we're in nameset/fileset tags 77 CurrentTag fCurrentTag; // The kind of tag currently being parsed.
66 int fVersion; // The version of the file parsed. 78 int fVersion; // The version of the file parsed.
79 const SkString& fBasePath; // The current base path.
80 const bool fIsFallback; // Indicates the file being parsed is a fallback file
67 }; 81 };
68 82
69 /** http://www.w3.org/TR/html-markup/datatypes.html#common.data.integer.non-nega tive-def */ 83 /** Parses a null terminated string into an integer type, checking for overflow.
84 * http://www.w3.org/TR/html-markup/datatypes.html#common.data.integer.non-nega tive-def
85 *
86 * If the string cannot be parsed into 'value', returns false and does not chan ge 'value'.
87 */
70 template <typename T> static bool parse_non_negative_integer(const char* s, T* v alue) { 88 template <typename T> static bool parse_non_negative_integer(const char* s, T* v alue) {
71 SK_COMPILE_ASSERT(std::numeric_limits<T>::is_integer, T_must_be_integer); 89 SK_COMPILE_ASSERT(std::numeric_limits<T>::is_integer, T_must_be_integer);
72 const T nMax = std::numeric_limits<T>::max() / 10; 90 const T nMax = std::numeric_limits<T>::max() / 10;
73 const T dMax = std::numeric_limits<T>::max() - (nMax * 10); 91 const T dMax = std::numeric_limits<T>::max() - (nMax * 10);
74 T n = 0; 92 T n = 0;
75 for (; *s; ++s) { 93 for (; *s; ++s) {
76 // Check if digit 94 // Check if digit
77 if (*s < '0' || '9' < *s) { 95 if (*s < '0' || '9' < *s) {
78 return false; 96 return false;
79 } 97 }
80 int d = *s - '0'; 98 int d = *s - '0';
81 // Check for overflow 99 // Check for overflow
82 if (n > nMax || (n == nMax && d > dMax)) { 100 if (n > nMax || (n == nMax && d > dMax)) {
83 return false; 101 return false;
84 } 102 }
85 n = (n * 10) + d; 103 n = (n * 10) + d;
86 } 104 }
87 *value = n; 105 *value = n;
88 return true; 106 return true;
89 } 107 }
90 108
109 static bool memeq(const char* s1, const char* s2, size_t n1, size_t n2) {
110 return n1 == n2 && 0 == memcmp(s1, s2, n1);
111 }
112 #define MEMEQ(c, s, n) memeq(c, s, sizeof(c) - 1, n)
113
114 #define ATTS_NON_NULL(a, i) (a[i] != NULL && a[i+1] != NULL)
115
91 namespace lmpParser { 116 namespace lmpParser {
92 117
93 void familyElementHandler(FontFamily* family, const char** attributes) { 118 void familyElementHandler(FontFamily* family, const char** attributes) {
94 // A non-fallback <family> tag must have a canonical name attribute. 119 // A non-fallback <family> tag must have a canonical name attribute.
95 // A fallback <family> tag has no name, and may have lang and variant 120 // A fallback <family> tag has no name, and may have lang and variant
96 // attributes. 121 // attributes.
97 family->fIsFallbackFont = true; 122 family->fIsFallbackFont = true;
98 for (size_t i = 0; attributes[i] != NULL && 123 for (size_t i = 0; ATTS_NON_NULL(attributes, i); i += 2) {
99 attributes[i+1] != NULL; i += 2) {
100 const char* name = attributes[i]; 124 const char* name = attributes[i];
101 const char* value = attributes[i+1]; 125 const char* value = attributes[i+1];
102 size_t nameLen = strlen(name); 126 size_t nameLen = strlen(name);
103 size_t valueLen = strlen(value); 127 size_t valueLen = strlen(value);
104 if (nameLen == 4 && !strncmp("name", name, nameLen)) { 128 if (MEMEQ("name", name, nameLen)) {
105 SkAutoAsciiToLC tolc(value); 129 SkAutoAsciiToLC tolc(value);
106 family->fNames.push_back().set(tolc.lc()); 130 family->fNames.push_back().set(tolc.lc());
107 family->fIsFallbackFont = false; 131 family->fIsFallbackFont = false;
108 } else if (nameLen == 4 && !strncmp("lang", name, nameLen)) { 132 } else if (MEMEQ("lang", name, nameLen)) {
109 family->fLanguage = SkLanguage (value); 133 family->fLanguage = SkLanguage(value, valueLen);
110 } else if (nameLen == 7 && !strncmp("variant", name, nameLen)) { 134 } else if (MEMEQ("variant", name, nameLen)) {
111 // Value should be either elegant or compact. 135 // Value should be either elegant or compact.
112 if (valueLen == 7 && !strncmp("elegant", value, valueLen)) { 136 if (MEMEQ("elegant", value, valueLen)) {
113 family->fVariant = kElegant_FontVariant; 137 family->fVariant = kElegant_FontVariant;
114 } else if (valueLen == 7 && !strncmp("compact", value, valueLen)) { 138 } else if (MEMEQ("compact", value, valueLen)) {
115 family->fVariant = kCompact_FontVariant; 139 family->fVariant = kCompact_FontVariant;
116 } 140 }
117 } 141 }
118 } 142 }
119 } 143 }
120 144
121 void fontFileNameHandler(void* data, const char* s, int len) { 145 void XMLCALL fontFileNameHandler(void* data, const char* s, int len) {
122 FamilyData* familyData = (FamilyData*) data; 146 FamilyData* self = static_cast<FamilyData*>(data);
123 familyData->fCurrentFontInfo->fFileName.set(s, len); 147 self->fCurrentFontInfo->fFileName.set(s, len);
124 } 148 }
125 149
126 void fontElementHandler(XML_Parser parser, FontFileInfo* file, const char** attr ibutes) { 150 void fontElementHandler(FamilyData* self, FontFileInfo* file, const char** attri butes) {
127 // A <font> should have weight (integer) and style (normal, italic) attribut es. 151 // A <font> should have weight (integer) and style (normal, italic) attribut es.
128 // NOTE: we ignore the style. 152 // NOTE: we ignore the style.
129 // The element should contain a filename. 153 // The element should contain a filename.
130 for (size_t i = 0; attributes[i] != NULL && 154 for (size_t i = 0; ATTS_NON_NULL(attributes, i); i += 2) {
131 attributes[i+1] != NULL; i += 2) {
132 const char* name = attributes[i]; 155 const char* name = attributes[i];
133 const char* value = attributes[i+1]; 156 const char* value = attributes[i+1];
134 size_t nameLen = strlen(name); 157 size_t nameLen = strlen(name);
135 if (nameLen == 6 && !strncmp("weight", name, nameLen)) { 158 if (MEMEQ("weight", name, nameLen)) {
136 if (!parse_non_negative_integer(value, &file->fWeight)) { 159 if (!parse_non_negative_integer(value, &file->fWeight)) {
137 SkDebugf("---- Font weight %s (INVALID)", value); 160 SkDebugf("---- Font weight %s (INVALID)", value);
138 file->fWeight = 0;
139 } 161 }
140 } 162 }
141 } 163 }
142 XML_SetCharacterDataHandler(parser, fontFileNameHandler); 164 XML_SetCharacterDataHandler(self->fParser, fontFileNameHandler);
143 } 165 }
144 166
145 FontFamily* findFamily(FamilyData* familyData, const char* familyName) { 167 FontFamily* findFamily(FamilyData* self, const SkString& familyName) {
146 size_t nameLen = strlen(familyName); 168 for (int i = 0; i < self->fFamilies.count(); i++) {
147 for (int i = 0; i < familyData->fFamilies.count(); i++) { 169 FontFamily* candidate = self->fFamilies[i];
148 FontFamily* candidate = familyData->fFamilies[i];
149 for (int j = 0; j < candidate->fNames.count(); j++) { 170 for (int j = 0; j < candidate->fNames.count(); j++) {
150 if (!strncmp(candidate->fNames[j].c_str(), familyName, nameLen) && 171 if (candidate->fNames[j] == familyName) {
151 nameLen == strlen(candidate->fNames[j].c_str())) {
152 return candidate; 172 return candidate;
153 } 173 }
154 } 174 }
155 } 175 }
156
157 return NULL; 176 return NULL;
158 } 177 }
159 178
160 void aliasElementHandler(FamilyData* familyData, const char** attributes) { 179 void aliasElementHandler(FamilyData* self, const char** attributes) {
161 // An <alias> must have name and to attributes. 180 // An <alias> must have name and to attributes.
162 // It may have weight (integer). 181 // It may have weight (integer).
163 // If it *does not* have a weight, it is a variant name for a <family>. 182 // If it *does not* have a weight, it is a variant name for a <family>.
164 // If it *does* have a weight, it names the <font>(s) of a specific weight 183 // If it *does* have a weight, it names the <font>(s) of a specific weight
165 // from a <family>. 184 // from a <family>.
166 185
167 SkString aliasName; 186 SkString aliasName;
168 SkString to; 187 SkString to;
169 int weight = 0; 188 int weight = 0;
170 for (size_t i = 0; attributes[i] != NULL && 189 for (size_t i = 0; ATTS_NON_NULL(attributes, i); i += 2) {
171 attributes[i+1] != NULL; i += 2) {
172 const char* name = attributes[i]; 190 const char* name = attributes[i];
173 const char* value = attributes[i+1]; 191 const char* value = attributes[i+1];
174 size_t nameLen = strlen(name); 192 size_t nameLen = strlen(name);
175 if (nameLen == 4 && !strncmp("name", name, nameLen)) { 193 if (MEMEQ("name", name, nameLen)) {
176 SkAutoAsciiToLC tolc(value); 194 SkAutoAsciiToLC tolc(value);
177 aliasName.set(tolc.lc()); 195 aliasName.set(tolc.lc());
178 } else if (nameLen == 2 && !strncmp("to", name, nameLen)) { 196 } else if (MEMEQ("to", name, nameLen)) {
179 to.set(value); 197 to.set(value);
180 } else if (nameLen == 6 && !strncmp("weight", name, nameLen)) { 198 } else if (MEMEQ("weight", name, nameLen)) {
181 parse_non_negative_integer(value, &weight); 199 if (!parse_non_negative_integer(value, &weight)) {
200 SkDebugf("---- Font weight %s (INVALID)", value);
201 }
182 } 202 }
183 } 203 }
184 204
185 // Assumes that the named family is already declared 205 // Assumes that the named family is already declared
186 FontFamily* targetFamily = findFamily(familyData, to.c_str()); 206 FontFamily* targetFamily = findFamily(self, to);
187 if (!targetFamily) { 207 if (!targetFamily) {
188 SkDebugf("---- Font alias target %s (NOT FOUND)", to.c_str()); 208 SkDebugf("---- Font alias target %s (NOT FOUND)", to.c_str());
189 return; 209 return;
190 } 210 }
191 211
192 if (weight) { 212 if (weight) {
193 FontFamily* family = new FontFamily(); 213 FontFamily* family = new FontFamily(targetFamily->fBasePath, self->fIsFa llback);
194 family->fNames.push_back().set(aliasName); 214 family->fNames.push_back().set(aliasName);
195 215
196 for (int i = 0; i < targetFamily->fFonts.count(); i++) { 216 for (int i = 0; i < targetFamily->fFonts.count(); i++) {
197 if (targetFamily->fFonts[i].fWeight == weight) { 217 if (targetFamily->fFonts[i].fWeight == weight) {
198 family->fFonts.push_back(targetFamily->fFonts[i]); 218 family->fFonts.push_back(targetFamily->fFonts[i]);
199 } 219 }
200 } 220 }
201 *familyData->fFamilies.append() = family; 221 *self->fFamilies.append() = family;
202 } else { 222 } else {
203 targetFamily->fNames.push_back().set(aliasName); 223 targetFamily->fNames.push_back().set(aliasName);
204 } 224 }
205 } 225 }
206 226
207 void startElementHandler(void* data, const char* tag, const char** attributes) { 227 void XMLCALL startElementHandler(void* data, const char* tag, const char** attri butes) {
208 FamilyData* familyData = (FamilyData*) data; 228 FamilyData* self = static_cast<FamilyData*>(data);
209 size_t len = strlen(tag); 229 size_t len = strlen(tag);
210 if (len == 6 && !strncmp(tag, "family", len)) { 230 if (MEMEQ("family", tag, len)) {
211 familyData->fCurrentFamily.reset(new FontFamily()); 231 self->fCurrentFamily.reset(new FontFamily(self->fBasePath, self->fIsFall back));
212 familyElementHandler(familyData->fCurrentFamily, attributes); 232 familyElementHandler(self->fCurrentFamily, attributes);
213 } else if (len == 4 && !strncmp(tag, "font", len)) { 233 } else if (MEMEQ("font", tag, len)) {
214 FontFileInfo* file = &familyData->fCurrentFamily->fFonts.push_back(); 234 FontFileInfo* file = &self->fCurrentFamily->fFonts.push_back();
215 familyData->fCurrentFontInfo = file; 235 self->fCurrentFontInfo = file;
216 fontElementHandler(familyData->fParser, file, attributes); 236 fontElementHandler(self, file, attributes);
217 } else if (len == 5 && !strncmp(tag, "alias", len)) { 237 } else if (MEMEQ("alias", tag, len)) {
218 aliasElementHandler(familyData, attributes); 238 aliasElementHandler(self, attributes);
219 } 239 }
220 } 240 }
221 241
222 void endElementHandler(void* data, const char* tag) { 242 void XMLCALL endElementHandler(void* data, const char* tag) {
223 FamilyData* familyData = (FamilyData*) data; 243 FamilyData* self = static_cast<FamilyData*>(data);
224 size_t len = strlen(tag); 244 size_t len = strlen(tag);
225 if (len == 6 && strncmp(tag, "family", len) == 0) { 245 if (MEMEQ("family", tag, len)) {
226 *familyData->fFamilies.append() = familyData->fCurrentFamily.detach(); 246 *self->fFamilies.append() = self->fCurrentFamily.detach();
227 } else if (len == 4 && !strncmp(tag, "font", len)) { 247 } else if (MEMEQ("font", tag, len)) {
228 XML_SetCharacterDataHandler(familyData->fParser, NULL); 248 XML_SetCharacterDataHandler(self->fParser, NULL);
229 } 249 }
230 } 250 }
231 251
232 } // lmpParser 252 } // lmpParser
233 253
234 namespace jbParser { 254 namespace jbParser {
235 255
236 /** 256 /**
237 * Handler for arbitrary text. This is used to parse the text inside each name 257 * Handler for arbitrary text. This is used to parse the text inside each name
238 * or file tag. The resulting strings are put into the fNames or FontFileInfo ar rays. 258 * or file tag. The resulting strings are put into the fNames or FontFileInfo ar rays.
239 */ 259 */
240 static void text_handler(void* data, const char* s, int len) { 260 static void XMLCALL text_handler(void* data, const char* s, int len) {
241 FamilyData* familyData = (FamilyData*) data; 261 FamilyData* self = static_cast<FamilyData*>(data);
242 // Make sure we're in the right state to store this name information 262 // Make sure we're in the right state to store this name information
243 if (familyData->fCurrentFamily.get() && 263 if (self->fCurrentFamily.get()) {
244 (familyData->fCurrentTag == NAMESET_TAG || familyData->fCurrentTag = = FILESET_TAG)) { 264 switch (self->fCurrentTag) {
245 switch (familyData->fCurrentTag) { 265 case kNameSet_CurrentTag: {
246 case NAMESET_TAG: {
247 SkAutoAsciiToLC tolc(s, len); 266 SkAutoAsciiToLC tolc(s, len);
248 familyData->fCurrentFamily->fNames.push_back().set(tolc.lc(), len); 267 self->fCurrentFamily->fNames.push_back().set(tolc.lc(), len);
249 break; 268 break;
250 } 269 }
251 case FILESET_TAG: 270 case kFileSet_CurrentTag:
252 if (familyData->fCurrentFontInfo) { 271 if (self->fCurrentFontInfo) {
253 familyData->fCurrentFontInfo->fFileName.set(s, len); 272 self->fCurrentFontInfo->fFileName.set(s, len);
254 } 273 }
255 break; 274 break;
256 default: 275 default:
257 // Noop - don't care about any text that's not in the Fonts or Names list 276 // Noop - don't care about any text that's not in the Fonts or Names list
258 break; 277 break;
259 } 278 }
260 } 279 }
261 } 280 }
262 281
263 /** 282 /**
264 * Handler for font files. This processes the attributes for language and 283 * Handler for font files. This processes the attributes for language and
265 * variants then lets textHandler handle the actual file name 284 * variants then lets textHandler handle the actual file name
266 */ 285 */
267 static void font_file_element_handler(FamilyData* familyData, const char** attri butes) { 286 static void font_file_element_handler(FamilyData* self, const char** attributes) {
268 FontFileInfo& newFileInfo = familyData->fCurrentFamily->fFonts.push_back(); 287 FontFamily& currentFamily = *self->fCurrentFamily.get();
288 FontFileInfo& newFileInfo = currentFamily.fFonts.push_back();
269 if (attributes) { 289 if (attributes) {
270 size_t currentAttributeIndex = 0; 290 for (size_t i = 0; ATTS_NON_NULL(attributes, i); i += 2) {
271 while (attributes[currentAttributeIndex] && 291 const char* attributeName = attributes[i];
272 attributes[currentAttributeIndex + 1]) { 292 const char* attributeValue = attributes[i+1];
273 const char* attributeName = attributes[currentAttributeIndex];
274 const char* attributeValue = attributes[currentAttributeIndex+1];
275 size_t nameLength = strlen(attributeName); 293 size_t nameLength = strlen(attributeName);
276 size_t valueLength = strlen(attributeValue); 294 size_t valueLength = strlen(attributeValue);
277 if (nameLength == 7 && strncmp(attributeName, "variant", nameLength) == 0) { 295 if (MEMEQ("variant", attributeName, nameLength)) {
278 const FontVariant prevVariant = familyData->fCurrentFamily->fVar iant; 296 const FontVariant prevVariant = currentFamily.fVariant;
279 if (valueLength == 7 && strncmp(attributeValue, "elegant", value Length) == 0) { 297 if (MEMEQ("elegant", attributeValue, valueLength)) {
280 familyData->fCurrentFamily->fVariant = kElegant_FontVariant; 298 currentFamily.fVariant = kElegant_FontVariant;
281 } else if (valueLength == 7 && 299 } else if (MEMEQ("compact", attributeValue, valueLength)) {
282 strncmp(attributeValue, "compact", valueLength) == 0) { 300 currentFamily.fVariant = kCompact_FontVariant;
283 familyData->fCurrentFamily->fVariant = kCompact_FontVariant;
284 } 301 }
285 if (familyData->fCurrentFamily->fFonts.count() > 1 && 302 if (currentFamily.fFonts.count() > 1 && currentFamily.fVariant ! = prevVariant) {
286 familyData->fCurrentFamily->fVariant != prevVariant) {
287 SkDebugf("Every font file within a family must have identica l variants"); 303 SkDebugf("Every font file within a family must have identica l variants");
288 sk_throw();
289 } 304 }
290 305
291 } else if (nameLength == 4 && strncmp(attributeName, "lang", nameLen gth) == 0) { 306 } else if (MEMEQ("lang", attributeName, nameLength)) {
292 SkLanguage prevLang = familyData->fCurrentFamily->fLanguage; 307 SkLanguage prevLang = currentFamily.fLanguage;
293 familyData->fCurrentFamily->fLanguage = SkLanguage(attributeValu e); 308 currentFamily.fLanguage = SkLanguage(attributeValue, valueLength );
294 if (familyData->fCurrentFamily->fFonts.count() > 1 && 309 if (currentFamily.fFonts.count() > 1 && currentFamily.fLanguage != prevLang) {
295 familyData->fCurrentFamily->fLanguage != prevLang) {
296 SkDebugf("Every font file within a family must have identica l languages"); 310 SkDebugf("Every font file within a family must have identica l languages");
297 sk_throw();
298 } 311 }
299 } else if (nameLength == 5 && strncmp(attributeName, "index", nameLe ngth) == 0) { 312
300 int value; 313 } else if (MEMEQ("index", attributeName, nameLength)) {
301 if (parse_non_negative_integer(attributeValue, &value)) { 314 if (!parse_non_negative_integer(attributeValue, &newFileInfo.fIn dex)) {
302 newFileInfo.fIndex = value;
303 } else {
304 SkDebugf("---- SystemFonts index=%s (INVALID)", attributeVal ue); 315 SkDebugf("---- SystemFonts index=%s (INVALID)", attributeVal ue);
305 } 316 }
306 } 317 }
307 //each element is a pair of attributeName/attributeValue string pair s
308 currentAttributeIndex += 2;
309 } 318 }
310 } 319 }
311 familyData->fCurrentFontInfo = &newFileInfo; 320 self->fCurrentFontInfo = &newFileInfo;
312 XML_SetCharacterDataHandler(familyData->fParser, text_handler); 321 XML_SetCharacterDataHandler(self->fParser, text_handler);
313 } 322 }
314 323
315 /** 324 /**
316 * Handler for the start of a tag. The only tags we expect are familyset, family , 325 * Handler for the start of a tag. The only tags we expect are familyset, family ,
317 * nameset, fileset, name, and file. 326 * nameset, fileset, name, and file.
318 */ 327 */
319 static void start_element_handler(void* data, const char* tag, const char** atts ) { 328 static void XMLCALL start_element_handler(void* data, const char* tag, const cha r** attributes) {
320 FamilyData* familyData = (FamilyData*) data; 329 FamilyData* self = static_cast<FamilyData*>(data);
321 size_t len = strlen(tag); 330 size_t len = strlen(tag);
322 if (len == 9 && strncmp(tag, "familyset", len) == 0) { 331 if (MEMEQ("familyset", tag, len)) {
323 // The familyset tag has an optional "version" attribute with an integer value >= 0 332 // The familyset tag has an optional "version" attribute with an integer value >= 0
324 for (size_t i = 0; atts[i] != NULL && 333 for (size_t i = 0; ATTS_NON_NULL(attributes, i); i += 2) {
325 atts[i+1] != NULL; i += 2) { 334 size_t nameLen = strlen(attributes[i]);
326 size_t nameLen = strlen(atts[i]); 335 if (!MEMEQ("version", attributes[i], nameLen)) continue;
327 if (nameLen == 7 && strncmp(atts[i], "version", nameLen)) continue; 336 const char* valueString = attributes[i+1];
328 const char* valueString = atts[i+1];
329 int version; 337 int version;
330 if (parse_non_negative_integer(valueString, &version) && (version >= 21)) { 338 if (parse_non_negative_integer(valueString, &version) && (version >= 21)) {
331 XML_SetElementHandler(familyData->fParser, 339 XML_SetElementHandler(self->fParser,
332 lmpParser::startElementHandler, 340 lmpParser::startElementHandler,
333 lmpParser::endElementHandler); 341 lmpParser::endElementHandler);
334 familyData->fVersion = version; 342 self->fVersion = version;
335 } 343 }
336 } 344 }
337 } else if (len == 6 && strncmp(tag, "family", len) == 0) { 345 } else if (MEMEQ("family", tag, len)) {
338 familyData->fCurrentFamily.reset(new FontFamily()); 346 self->fCurrentFamily.reset(new FontFamily(self->fBasePath, self->fIsFall back));
339 // The Family tag has an optional "order" attribute with an integer valu e >= 0 347 // The Family tag has an optional "order" attribute with an integer valu e >= 0
340 // If this attribute does not exist, the default value is -1 348 // If this attribute does not exist, the default value is -1
341 for (size_t i = 0; atts[i] != NULL && 349 for (size_t i = 0; ATTS_NON_NULL(attributes, i); i += 2) {
342 atts[i+1] != NULL; i += 2) { 350 const char* valueString = attributes[i+1];
343 const char* valueString = atts[i+1];
344 int value; 351 int value;
345 if (parse_non_negative_integer(valueString, &value)) { 352 if (parse_non_negative_integer(valueString, &value)) {
346 familyData->fCurrentFamily->fOrder = value; 353 self->fCurrentFamily->fOrder = value;
347 } 354 }
348 } 355 }
349 } else if (len == 7 && strncmp(tag, "nameset", len) == 0) { 356 } else if (MEMEQ("nameset", tag, len)) {
350 familyData->fCurrentTag = NAMESET_TAG; 357 self->fCurrentTag = kNameSet_CurrentTag;
351 } else if (len == 7 && strncmp(tag, "fileset", len) == 0) { 358 } else if (MEMEQ("fileset", tag, len)) {
352 familyData->fCurrentTag = FILESET_TAG; 359 self->fCurrentTag = kFileSet_CurrentTag;
353 } else if (len == 4 && strncmp(tag, "name", len) == 0 && familyData->fCurren tTag == NAMESET_TAG) { 360 } else if (MEMEQ("name", tag, len) && self->fCurrentTag == kNameSet_CurrentT ag) {
354 // If it's a Name, parse the text inside 361 // If it's a Name, parse the text inside
355 XML_SetCharacterDataHandler(familyData->fParser, text_handler); 362 XML_SetCharacterDataHandler(self->fParser, text_handler);
356 } else if (len == 4 && strncmp(tag, "file", len) == 0 && familyData->fCurren tTag == FILESET_TAG) { 363 } else if (MEMEQ("file", tag, len) && self->fCurrentTag == kFileSet_CurrentT ag) {
357 // If it's a file, parse the attributes, then parse the text inside 364 // If it's a file, parse the attributes, then parse the text inside
358 font_file_element_handler(familyData, atts); 365 font_file_element_handler(self, attributes);
359 } 366 }
360 } 367 }
361 368
362 /** 369 /**
363 * Handler for the end of tags. We only care about family, nameset, fileset, 370 * Handler for the end of tags. We only care about family, nameset, fileset,
364 * name, and file. 371 * name, and file.
365 */ 372 */
366 static void end_element_handler(void* data, const char* tag) { 373 static void XMLCALL end_element_handler(void* data, const char* tag) {
367 FamilyData* familyData = (FamilyData*) data; 374 FamilyData* self = static_cast<FamilyData*>(data);
368 size_t len = strlen(tag); 375 size_t len = strlen(tag);
369 if (len == 6 && strncmp(tag, "family", len)== 0) { 376 if (MEMEQ("family", tag, len)) {
370 // Done parsing a Family - store the created currentFamily in the famili es array 377 // Done parsing a Family - store the created currentFamily in the famili es array
371 *familyData->fFamilies.append() = familyData->fCurrentFamily.detach(); 378 *self->fFamilies.append() = self->fCurrentFamily.detach();
372 } else if (len == 7 && strncmp(tag, "nameset", len) == 0) { 379 } else if (MEMEQ("nameset", tag, len)) {
373 familyData->fCurrentTag = NO_TAG; 380 self->fCurrentTag = kNo_CurrentTag;
374 } else if (len == 7 && strncmp(tag, "fileset", len) == 0) { 381 } else if (MEMEQ("fileset", tag, len)) {
375 familyData->fCurrentTag = NO_TAG; 382 self->fCurrentTag = kNo_CurrentTag;
376 } else if ((len == 4 && 383 } else if ((MEMEQ("name", tag, len) && self->fCurrentTag == kNameSet_Current Tag) ||
377 strncmp(tag, "name", len) == 0 && 384 (MEMEQ("file", tag, len) && self->fCurrentTag == kFileSet_Current Tag)) {
378 familyData->fCurrentTag == NAMESET_TAG) ||
379 (len == 4 &&
380 strncmp(tag, "file", len) == 0 &&
381 familyData->fCurrentTag == FILESET_TAG)) {
382 // Disable the arbitrary text handler installed to load Name data 385 // Disable the arbitrary text handler installed to load Name data
383 XML_SetCharacterDataHandler(familyData->fParser, NULL); 386 XML_SetCharacterDataHandler(self->fParser, NULL);
384 } 387 }
385 } 388 }
386 389
387 } // namespace jbParser 390 } // namespace jbParser
388 391
392 static void XMLCALL xml_entity_decl_handler(void *data,
393 const XML_Char *entityName,
394 int is_parameter_entity,
395 const XML_Char *value,
396 int value_length,
397 const XML_Char *base,
398 const XML_Char *systemId,
399 const XML_Char *publicId,
400 const XML_Char *notationName)
401 {
402 FamilyData* self = static_cast<FamilyData*>(data);
403 SkDebugf("Entity declaration %s found, stopping processing.", entityName);
404 XML_StopParser(self->fParser, XML_FALSE);
405 }
406
407 template<typename T> struct remove_ptr {typedef T type;};
408 template<typename T> struct remove_ptr<T*> {typedef T type;};
409
389 /** 410 /**
390 * This function parses the given filename and stores the results in the given 411 * This function parses the given filename and stores the results in the given
391 * families array. Returns the version of the file, negative if the file does no t exist. 412 * families array. Returns the version of the file, negative if the file does no t exist.
392 */ 413 */
393 static int parse_config_file(const char* filename, SkTDArray<FontFamily*>& famil ies) { 414 static int parse_config_file(const char* filename, SkTDArray<FontFamily*>& famil ies,
394 415 const SkString& basePath, bool isFallback)
395 FILE* file = fopen(filename, "r"); 416 {
417 SkFILEStream file(filename);
396 418
397 // Some of the files we attempt to parse (in particular, /vendor/etc/fallbac k_fonts.xml) 419 // Some of the files we attempt to parse (in particular, /vendor/etc/fallbac k_fonts.xml)
398 // are optional - failure here is okay because one of these optional files m ay not exist. 420 // are optional - failure here is okay because one of these optional files m ay not exist.
399 if (NULL == file) { 421 if (!file.isValid()) {
400 return -1; 422 return -1;
401 } 423 }
402 424
403 XML_Parser parser = XML_ParserCreate(NULL); 425 SkAutoTCallVProc<remove_ptr<XML_Parser>::type, XML_ParserFree> parser(XML_Pa rserCreate(NULL));
404 FamilyData familyData(parser, families); 426 if (!parser) {
405 XML_SetUserData(parser, &familyData); 427 return -1;
428 }
429
430 FamilyData self(parser, families, basePath, isFallback);
431 XML_SetUserData(parser, &self);
432
433 // Disable entity processing, to inhibit internal entity expansion. See expa t CVE-2013-0340
434 XML_SetEntityDeclHandler(parser, xml_entity_decl_handler);
435
406 // Start parsing oldschool; switch these in flight if we detect a newer vers ion of the file. 436 // Start parsing oldschool; switch these in flight if we detect a newer vers ion of the file.
407 XML_SetElementHandler(parser, jbParser::start_element_handler, jbParser::end _element_handler); 437 XML_SetElementHandler(parser, jbParser::start_element_handler, jbParser::end _element_handler);
408 438
409 char buffer[512]; 439 char buffer[512];
410 bool done = false; 440 bool done = false;
411 while (!done) { 441 while (!done) {
412 fgets(buffer, sizeof(buffer), file); 442 size_t len = file.read(buffer, 512);
413 size_t len = strlen(buffer); 443 done = file.isAtEnd();
414 if (feof(file) != 0) { 444 XML_Status status = XML_Parse(parser, buffer, len, done);
415 done = true; 445 if (XML_STATUS_ERROR == status) {
446 XML_Error error = XML_GetErrorCode(parser);
447 int line = XML_GetCurrentLineNumber(parser);
448 int column = XML_GetCurrentColumnNumber(parser);
449 int index = XML_GetCurrentByteIndex(parser);
450 const XML_LChar* errorString = XML_ErrorString(error);
451 SkDebugf("Line: %d Column: %d (Offset: %d) Error %d: %s.\n",
452 line, column, index, error, errorString);
453 return -1;
416 } 454 }
417 XML_Parse(parser, buffer, len, done);
418 } 455 }
419 XML_ParserFree(parser); 456 return self.fVersion;
420 fclose(file);
421 return familyData.fVersion;
422 } 457 }
423 458
424 /** Returns the version of the system font file actually found, negative if none . */ 459 /** Returns the version of the system font file actually found, negative if none . */
425 static int append_system_font_families(SkTDArray<FontFamily*>& fontFamilies) { 460 static int append_system_font_families(SkTDArray<FontFamily*>& fontFamilies,
461 const SkString& basePath)
462 {
426 int initialCount = fontFamilies.count(); 463 int initialCount = fontFamilies.count();
427 int version = parse_config_file(LMP_SYSTEM_FONTS_FILE, fontFamilies); 464 int version = parse_config_file(LMP_SYSTEM_FONTS_FILE, fontFamilies, basePat h, false);
428 if (version < 0 || fontFamilies.count() == initialCount) { 465 if (version < 0 || fontFamilies.count() == initialCount) {
429 version = parse_config_file(OLD_SYSTEM_FONTS_FILE, fontFamilies); 466 version = parse_config_file(OLD_SYSTEM_FONTS_FILE, fontFamilies, basePat h, false);
430 } 467 }
431 return version; 468 return version;
432 } 469 }
433 470
434 /** 471 /**
435 * In some versions of Android prior to Android 4.2 (JellyBean MR1 at API 472 * In some versions of Android prior to Android 4.2 (JellyBean MR1 at API
436 * Level 17) the fallback fonts for certain locales were encoded in their own 473 * Level 17) the fallback fonts for certain locales were encoded in their own
437 * XML files with a suffix that identified the locale. We search the provided 474 * XML files with a suffix that identified the locale. We search the provided
438 * directory for those files,add all of their entries to the fallback chain, and 475 * directory for those files,add all of their entries to the fallback chain, and
439 * include the locale as part of each entry. 476 * include the locale as part of each entry.
440 */ 477 */
441 static void append_fallback_font_families_for_locale(SkTDArray<FontFamily*>& fal lbackFonts, 478 static void append_fallback_font_families_for_locale(SkTDArray<FontFamily*>& fal lbackFonts,
442 const char* dir) 479 const char* dir,
480 const SkString& basePath)
443 { 481 {
444 #if defined(SK_BUILD_FOR_ANDROID_FRAMEWORK) 482 #if defined(SK_BUILD_FOR_ANDROID_FRAMEWORK)
445 // The framework is beyond Android 4.2 and can therefore skip this function 483 // The framework is beyond Android 4.2 and can therefore skip this function
446 return; 484 return;
447 #endif 485 #endif
448 486
449 DIR* fontDirectory = opendir(dir); 487 DIR* fontDirectory = opendir(dir);
450 if (fontDirectory != NULL){ 488 if (fontDirectory != NULL){
451 struct dirent* dirEntry = readdir(fontDirectory); 489 struct dirent* dirEntry = readdir(fontDirectory);
452 while (dirEntry) { 490 while (dirEntry) {
(...skipping 10 matching lines...) Expand all
463 static const size_t fixedLen = strlen(LOCALE_FALLBACK_FONTS_PREF IX) - 501 static const size_t fixedLen = strlen(LOCALE_FALLBACK_FONTS_PREF IX) -
464 strlen(LOCALE_FALLBACK_FONTS_SUFF IX); 502 strlen(LOCALE_FALLBACK_FONTS_SUFF IX);
465 503
466 SkString locale(fileName.c_str() - strlen(LOCALE_FALLBACK_FONTS_ PREFIX), 504 SkString locale(fileName.c_str() - strlen(LOCALE_FALLBACK_FONTS_ PREFIX),
467 fileName.size() - fixedLen); 505 fileName.size() - fixedLen);
468 506
469 SkString absoluteFilename; 507 SkString absoluteFilename;
470 absoluteFilename.printf("%s/%s", dir, fileName.c_str()); 508 absoluteFilename.printf("%s/%s", dir, fileName.c_str());
471 509
472 SkTDArray<FontFamily*> langSpecificFonts; 510 SkTDArray<FontFamily*> langSpecificFonts;
473 parse_config_file(absoluteFilename.c_str(), langSpecificFonts); 511 parse_config_file(absoluteFilename.c_str(), langSpecificFonts, b asePath, true);
474 512
475 for (int i = 0; i < langSpecificFonts.count(); ++i) { 513 for (int i = 0; i < langSpecificFonts.count(); ++i) {
476 FontFamily* family = langSpecificFonts[i]; 514 FontFamily* family = langSpecificFonts[i];
477 family->fLanguage = SkLanguage(locale); 515 family->fLanguage = SkLanguage(locale);
478 *fallbackFonts.append() = family; 516 *fallbackFonts.append() = family;
479 } 517 }
480 } 518 }
481 519
482 // proceed to the next entry in the directory 520 // proceed to the next entry in the directory
483 dirEntry = readdir(fontDirectory); 521 dirEntry = readdir(fontDirectory);
484 } 522 }
485 // cleanup the directory reference 523 // cleanup the directory reference
486 closedir(fontDirectory); 524 closedir(fontDirectory);
487 } 525 }
488 } 526 }
489 527
490 static void append_system_fallback_font_families(SkTDArray<FontFamily*>& fallbac kFonts) { 528 static void append_system_fallback_font_families(SkTDArray<FontFamily*>& fallbac kFonts,
491 parse_config_file(FALLBACK_FONTS_FILE, fallbackFonts); 529 const SkString& basePath)
492 append_fallback_font_families_for_locale(fallbackFonts, LOCALE_FALLBACK_FONT S_SYSTEM_DIR); 530 {
531 parse_config_file(FALLBACK_FONTS_FILE, fallbackFonts, basePath, true);
532 append_fallback_font_families_for_locale(fallbackFonts,
533 LOCALE_FALLBACK_FONTS_SYSTEM_DIR,
534 basePath);
493 } 535 }
494 536
495 static void mixin_vendor_fallback_font_families(SkTDArray<FontFamily*>& fallback Fonts) { 537 static void mixin_vendor_fallback_font_families(SkTDArray<FontFamily*>& fallback Fonts,
538 const SkString& basePath)
539 {
496 SkTDArray<FontFamily*> vendorFonts; 540 SkTDArray<FontFamily*> vendorFonts;
497 parse_config_file(VENDOR_FONTS_FILE, vendorFonts); 541 parse_config_file(VENDOR_FONTS_FILE, vendorFonts, basePath, true);
498 append_fallback_font_families_for_locale(vendorFonts, LOCALE_FALLBACK_FONTS_ VENDOR_DIR); 542 append_fallback_font_families_for_locale(vendorFonts,
543 LOCALE_FALLBACK_FONTS_VENDOR_DIR,
544 basePath);
499 545
500 // This loop inserts the vendor fallback fonts in the correct order in the 546 // This loop inserts the vendor fallback fonts in the correct order in the
501 // overall fallbacks list. 547 // overall fallbacks list.
502 int currentOrder = -1; 548 int currentOrder = -1;
503 for (int i = 0; i < vendorFonts.count(); ++i) { 549 for (int i = 0; i < vendorFonts.count(); ++i) {
504 FontFamily* family = vendorFonts[i]; 550 FontFamily* family = vendorFonts[i];
505 int order = family->fOrder; 551 int order = family->fOrder;
506 if (order < 0) { 552 if (order < 0) {
507 if (currentOrder < 0) { 553 if (currentOrder < 0) {
508 // Default case - just add it to the end of the fallback list 554 // Default case - just add it to the end of the fallback list
509 *fallbackFonts.append() = family; 555 *fallbackFonts.append() = family;
510 } else { 556 } else {
511 // no order specified on this font, but we're incrementing the o rder 557 // no order specified on this font, but we're incrementing the o rder
512 // based on an earlier order insertion request 558 // based on an earlier order insertion request
513 *fallbackFonts.insert(currentOrder++) = family; 559 *fallbackFonts.insert(currentOrder++) = family;
514 } 560 }
515 } else { 561 } else {
516 // Add the font into the fallback list in the specified order. Set 562 // Add the font into the fallback list in the specified order. Set
517 // currentOrder for correct placement of other fonts in the vendor l ist. 563 // currentOrder for correct placement of other fonts in the vendor l ist.
518 *fallbackFonts.insert(order) = family; 564 *fallbackFonts.insert(order) = family;
519 currentOrder = order + 1; 565 currentOrder = order + 1;
520 } 566 }
521 } 567 }
522 } 568 }
523 569
524 /** 570 void SkFontConfigParser::GetSystemFontFamilies(SkTDArray<FontFamily*>& fontFamil ies) {
525 * Loads data on font families from various expected configuration files. The
526 * resulting data is returned in the given fontFamilies array.
527 */
528 void SkFontConfigParser::GetFontFamilies(SkTDArray<FontFamily*>& fontFamilies) {
529 // Version 21 of the system font configuration does not need any fallback co nfiguration files. 571 // Version 21 of the system font configuration does not need any fallback co nfiguration files.
530 if (append_system_font_families(fontFamilies) >= 21) { 572 SkString basePath(getenv("ANDROID_ROOT"));
573 basePath.append(SK_FONT_FILE_PREFIX, sizeof(SK_FONT_FILE_PREFIX) - 1);
574
575 if (append_system_font_families(fontFamilies, basePath) >= 21) {
531 return; 576 return;
532 } 577 }
533 578
534 // Append all the fallback fonts to system fonts 579 // Append all the fallback fonts to system fonts
535 SkTDArray<FontFamily*> fallbackFonts; 580 SkTDArray<FontFamily*> fallbackFonts;
536 append_system_fallback_font_families(fallbackFonts); 581 append_system_fallback_font_families(fallbackFonts, basePath);
537 mixin_vendor_fallback_font_families(fallbackFonts); 582 mixin_vendor_fallback_font_families(fallbackFonts, basePath);
538 for (int i = 0; i < fallbackFonts.count(); ++i) { 583 fontFamilies.append(fallbackFonts.count(), fallbackFonts.begin());
539 fallbackFonts[i]->fIsFallbackFont = true; 584 }
540 *fontFamilies.append() = fallbackFonts[i]; 585
586 void SkFontConfigParser::GetCustomFontFamilies(SkTDArray<FontFamily*>& fontFamil ies,
587 const SkString& basePath,
588 const char* fontsXml,
589 const char* fallbackFontsXml)
590 {
591 if (fontsXml) {
592 parse_config_file(fontsXml, fontFamilies, basePath, false);
593 }
594 if (fallbackFontsXml) {
595 parse_config_file(fallbackFontsXml, fontFamilies, basePath, true);
541 } 596 }
542 } 597 }
543 598
544 void SkFontConfigParser::GetTestFontFamilies(SkTDArray<FontFamily*>& fontFamilie s,
545 const char* testMainConfigFile,
546 const char* testFallbackConfigFile) {
547 parse_config_file(testMainConfigFile, fontFamilies);
548
549 SkTDArray<FontFamily*> fallbackFonts;
550 if (testFallbackConfigFile) {
551 parse_config_file(testFallbackConfigFile, fallbackFonts);
552 }
553
554 // Append all fallback fonts to system fonts
555 for (int i = 0; i < fallbackFonts.count(); ++i) {
556 fallbackFonts[i]->fIsFallbackFont = true;
557 *fontFamilies.append() = fallbackFonts[i];
558 }
559 }
560
561 SkLanguage SkLanguage::getParent() const { 599 SkLanguage SkLanguage::getParent() const {
562 SkASSERT(!fTag.isEmpty()); 600 SkASSERT(!fTag.isEmpty());
563 const char* tag = fTag.c_str(); 601 const char* tag = fTag.c_str();
564 602
565 // strip off the rightmost "-.*" 603 // strip off the rightmost "-.*"
566 const char* parentTagEnd = strrchr(tag, '-'); 604 const char* parentTagEnd = strrchr(tag, '-');
567 if (parentTagEnd == NULL) { 605 if (parentTagEnd == NULL) {
568 return SkLanguage(); 606 return SkLanguage();
569 } 607 }
570 size_t parentTagLen = parentTagEnd - tag; 608 size_t parentTagLen = parentTagEnd - tag;
571 return SkLanguage(tag, parentTagLen); 609 return SkLanguage(tag, parentTagLen);
572 } 610 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698