1 /*
2 * Copyright 2011 The Android Open Source Project
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
8 #include "SkFontConfigParser_android.h"
9 #include "SkTDArray.h"
10 #include "SkTypeface.h"
11
12 #include <expat.h>
13 #include <stdio.h>
14 #include <sys/system_properties.h>
15
16 #define SYSTEM_FONTS_FILE "/system/etc/system_fonts.xml"
17 #define FALLBACK_FONTS_FILE "/system/etc/fallback_fonts.xml"
18 #define VENDOR_FONTS_FILE "/vendor/etc/fallback_fonts.xml"
19
20 // These defines are used to determine the kind of tag that we're currently
21 // populating with data. We only care about the sibling tags nameset and fileset
22 // for now.
23 #define NO_TAG 0
24 #define NAMESET_TAG 1
25 #define FILESET_TAG 2
26
27 /**
28 * The FamilyData structure is passed around by the parser so that each handler
29 * can read these variables that are relevant to the current parsing.
30 */
31 struct FamilyData {
FamilyDataFamilyData32 FamilyData(XML_Parser *parserRef, SkTDArray<FontFamily*> &familiesRef) :
33 parser(parserRef),
34 families(familiesRef),
35 currentFamily(NULL),
36 currentFontInfo(NULL),
37 currentTag(NO_TAG) {};
38
39 XML_Parser *parser; // The expat parser doing the work
40 SkTDArray<FontFamily*> &families; // The array that each family is put into as it is parsed
41 FontFamily *currentFamily; // The current family being created
42 FontFileInfo *currentFontInfo; // The current fontInfo being created
43 int currentTag; // A flag to indicate whether we're in nameset/fileset tags
44 };
45
46 /**
47 * Handler for arbitrary text. This is used to parse the text inside each name
48 * or file tag. The resulting strings are put into the fNames or FontFileInfo arrays.
49 */
textHandler(void * data,const char * s,int len)50 static void textHandler(void *data, const char *s, int len) {
51 FamilyData *familyData = (FamilyData*) data;
52 // Make sure we're in the right state to store this name information
53 if (familyData->currentFamily &&
54 (familyData->currentTag == NAMESET_TAG || familyData->currentTag == FILESET_TAG)) {
55 switch (familyData->currentTag) {
56 case NAMESET_TAG:
57 familyData->currentFamily->fNames.push_back().set(s, len);
58 break;
59 case FILESET_TAG:
60 if (familyData->currentFontInfo) {
61 familyData->currentFontInfo->fFileName.set(s, len);
62 }
63 break;
64 default:
65 // Noop - don't care about any text that's not in the Fonts or Names list
66 break;
67 }
68 }
69 }
70
71 /**
72 * Handler for font files. This processes the attributes for language and
73 * variants then lets textHandler handle the actual file name
74 */
fontFileElementHandler(FamilyData * familyData,const char ** attributes)75 static void fontFileElementHandler(FamilyData *familyData, const char **attributes) {
76
77 FontFileInfo& newFileInfo = familyData->currentFamily->fFontFiles.push_back();
78 if (attributes) {
79 int currentAttributeIndex = 0;
80 while (attributes[currentAttributeIndex]) {
81 const char* attributeName = attributes[currentAttributeIndex];
82 const char* attributeValue = attributes[currentAttributeIndex+1];
83 int nameLength = strlen(attributeName);
84 int valueLength = strlen(attributeValue);
85 if (strncmp(attributeName, "variant", nameLength) == 0) {
86 if (strncmp(attributeValue, "elegant", valueLength) == 0) {
87 newFileInfo.fPaintOptions.setFontVariant(SkPaintOptionsAndroid::kElegant_Variant);
88 } else if (strncmp(attributeValue, "compact", valueLength) == 0) {
89 newFileInfo.fPaintOptions.setFontVariant(SkPaintOptionsAndroid::kCompact_Variant);
90 }
91 } else if (strncmp(attributeName, "lang", nameLength) == 0) {
92 newFileInfo.fPaintOptions.setLanguage(attributeValue);
93 }
94 //each element is a pair of attributeName/attributeValue string pairs
95 currentAttributeIndex += 2;
96 }
97 }
98 familyData->currentFontInfo = &newFileInfo;
99 XML_SetCharacterDataHandler(*familyData->parser, textHandler);
100 }
101
102 /**
103 * Handler for the start of a tag. The only tags we expect are family, nameset,
104 * fileset, name, and file.
105 */
startElementHandler(void * data,const char * tag,const char ** atts)106 static void startElementHandler(void *data, const char *tag, const char **atts) {
107 FamilyData *familyData = (FamilyData*) data;
108 int len = strlen(tag);
109 if (strncmp(tag, "family", len)== 0) {
110 familyData->currentFamily = new FontFamily();
111 familyData->currentFamily->order = -1;
112 // The Family tag has an optional "order" attribute with an integer value >= 0
113 // If this attribute does not exist, the default value is -1
114 for (int i = 0; atts[i] != NULL; i += 2) {
115 const char* valueString = atts[i+1];
116 int value;
117 int len = sscanf(valueString, "%d", &value);
118 if (len > 0) {
119 familyData->currentFamily->order = value;
120 }
121 }
122 } else if (len == 7 && strncmp(tag, "nameset", len) == 0) {
123 familyData->currentTag = NAMESET_TAG;
124 } else if (len == 7 && strncmp(tag, "fileset", len) == 0) {
125 familyData->currentTag = FILESET_TAG;
126 } else if (strncmp(tag, "name", len) == 0 && familyData->currentTag == NAMESET_TAG) {
127 // If it's a Name, parse the text inside
128 XML_SetCharacterDataHandler(*familyData->parser, textHandler);
129 } else if (strncmp(tag, "file", len) == 0 && familyData->currentTag == FILESET_TAG) {
130 // If it's a file, parse the attributes, then parse the text inside
131 fontFileElementHandler(familyData, atts);
132 }
133 }
134
135 /**
136 * Handler for the end of tags. We only care about family, nameset, fileset,
137 * name, and file.
138 */
endElementHandler(void * data,const char * tag)139 static void endElementHandler(void *data, const char *tag) {
140 FamilyData *familyData = (FamilyData*) data;
141 int len = strlen(tag);
142 if (strncmp(tag, "family", len)== 0) {
143 // Done parsing a Family - store the created currentFamily in the families array
144 *familyData->families.append() = familyData->currentFamily;
145 familyData->currentFamily = NULL;
146 } else if (len == 7 && strncmp(tag, "nameset", len) == 0) {
147 familyData->currentTag = NO_TAG;
148 } else if (len == 7 && strncmp(tag, "fileset", len) == 0) {
149 familyData->currentTag = NO_TAG;
150 } else if ((strncmp(tag, "name", len) == 0 && familyData->currentTag == NAMESET_TAG) ||
151 (strncmp(tag, "file", len) == 0 && familyData->currentTag == FILESET_TAG)) {
152 // Disable the arbitrary text handler installed to load Name data
153 XML_SetCharacterDataHandler(*familyData->parser, NULL);
154 }
155 }
156
157 /**
158 * This function parses the given filename and stores the results in the given
159 * families array.
160 */
parseConfigFile(const char * filename,SkTDArray<FontFamily * > & families)161 static void parseConfigFile(const char *filename, SkTDArray<FontFamily*> &families) {
162
163 FILE* file = NULL;
164
165 #if !defined(SK_BUILD_FOR_ANDROID_FRAMEWORK)
166 // if we are using a version of Android prior to Android 4.2 (JellyBean MR1
167 // at API Level 17) then we need to look for files with a different suffix.
168 char sdkVersion[PROP_VALUE_MAX];
169 __system_property_get("ro.build.version.sdk", sdkVersion);
170 const int sdkVersionInt = atoi(sdkVersion);
171
172 if (0 != *sdkVersion && sdkVersionInt < 17) {
173 SkString basename;
174 SkString updatedFilename;
175 SkString locale = SkFontConfigParser::GetLocale();
176
177 basename.set(filename);
178 // Remove the .xml suffix. We'll add it back in a moment.
179 if (basename.endsWith(".xml")) {
180 basename.resize(basename.size()-4);
181 }
182 // Try first with language and region
183 updatedFilename.printf("%s-%s.xml", basename.c_str(), locale.c_str());
184 file = fopen(updatedFilename.c_str(), "r");
185 if (!file) {
186 // If not found, try next with just language
187 updatedFilename.printf("%s-%.2s.xml", basename.c_str(), locale.c_str());
188 file = fopen(updatedFilename.c_str(), "r");
189 }
190 }
191 #endif
192
193 if (NULL == file) {
194 file = fopen(filename, "r");
195 }
196
197 // Some of the files we attempt to parse (in particular, /vendor/etc/fallback_fonts.xml)
198 // are optional - failure here is okay because one of these optional files may not exist.
199 if (NULL == file) {
200 return;
201 }
202
203 XML_Parser parser = XML_ParserCreate(NULL);
204 FamilyData *familyData = new FamilyData(&parser, families);
205 XML_SetUserData(parser, familyData);
206 XML_SetElementHandler(parser, startElementHandler, endElementHandler);
207
208 char buffer[512];
209 bool done = false;
210 while (!done) {
211 fgets(buffer, sizeof(buffer), file);
212 int len = strlen(buffer);
213 if (feof(file) != 0) {
214 done = true;
215 }
216 XML_Parse(parser, buffer, len, done);
217 }
218 XML_ParserFree(parser);
219 fclose(file);
220 }
221
getSystemFontFamilies(SkTDArray<FontFamily * > & fontFamilies)222 static void getSystemFontFamilies(SkTDArray<FontFamily*> &fontFamilies) {
223 parseConfigFile(SYSTEM_FONTS_FILE, fontFamilies);
224 }
225
getFallbackFontFamilies(SkTDArray<FontFamily * > & fallbackFonts)226 static void getFallbackFontFamilies(SkTDArray<FontFamily*> &fallbackFonts) {
227 SkTDArray<FontFamily*> vendorFonts;
228 parseConfigFile(FALLBACK_FONTS_FILE, fallbackFonts);
229 parseConfigFile(VENDOR_FONTS_FILE, vendorFonts);
230
231 // This loop inserts the vendor fallback fonts in the correct order in the
232 // overall fallbacks list.
233 int currentOrder = -1;
234 for (int i = 0; i < vendorFonts.count(); ++i) {
235 FontFamily* family = vendorFonts[i];
236 int order = family->order;
237 if (order < 0) {
238 if (currentOrder < 0) {
239 // Default case - just add it to the end of the fallback list
240 *fallbackFonts.append() = family;
241 } else {
242 // no order specified on this font, but we're incrementing the order
243 // based on an earlier order insertion request
244 *fallbackFonts.insert(currentOrder++) = family;
245 }
246 } else {
247 // Add the font into the fallback list in the specified order. Set
248 // currentOrder for correct placement of other fonts in the vendor list.
249 *fallbackFonts.insert(order) = family;
250 currentOrder = order + 1;
251 }
252 }
253 }
254
255 /**
256 * Loads data on font families from various expected configuration files. The
257 * resulting data is returned in the given fontFamilies array.
258 */
GetFontFamilies(SkTDArray<FontFamily * > & fontFamilies)259 void SkFontConfigParser::GetFontFamilies(SkTDArray<FontFamily*> &fontFamilies) {
260
261 getSystemFontFamilies(fontFamilies);
262
263 // Append all the fallback fonts to system fonts
264 SkTDArray<FontFamily*> fallbackFonts;
265 getFallbackFontFamilies(fallbackFonts);
266 for (int i = 0; i < fallbackFonts.count(); ++i) {
267 fallbackFonts[i]->fIsFallbackFont = true;
268 *fontFamilies.append() = fallbackFonts[i];
269 }
270 }
271
GetTestFontFamilies(SkTDArray<FontFamily * > & fontFamilies,const char * testMainConfigFile,const char * testFallbackConfigFile)272 void SkFontConfigParser::GetTestFontFamilies(SkTDArray<FontFamily*> &fontFamilies,
273 const char* testMainConfigFile,
274 const char* testFallbackConfigFile) {
275 parseConfigFile(testMainConfigFile, fontFamilies);
276
277 SkTDArray<FontFamily*> fallbackFonts;
278 parseConfigFile(testFallbackConfigFile, fallbackFonts);
279
280 // Append all fallback fonts to system fonts
281 for (int i = 0; i < fallbackFonts.count(); ++i) {
282 fallbackFonts[i]->fIsFallbackFont = true;
283 *fontFamilies.append() = fallbackFonts[i];
284 }
285 }
286
287 /**
288 * Read the persistent locale.
289 */
GetLocale()290 SkString SkFontConfigParser::GetLocale()
291 {
292 char propLang[PROP_VALUE_MAX], propRegn[PROP_VALUE_MAX];
293 __system_property_get("persist.sys.language", propLang);
294 __system_property_get("persist.sys.country", propRegn);
295
296 if (*propLang == 0 && *propRegn == 0) {
297 /* Set to ro properties, default is en_US */
298 __system_property_get("ro.product.locale.language", propLang);
299 __system_property_get("ro.product.locale.region", propRegn);
300 if (*propLang == 0 && *propRegn == 0) {
301 strcpy(propLang, "en");
302 strcpy(propRegn, "US");
303 }
304 }
305
306 SkString locale(6);
307 char* localeCStr = locale.writable_str();
308
309 strncpy(localeCStr, propLang, 2);
310 localeCStr[2] = '-';
311 strncpy(&localeCStr[3], propRegn, 2);
312 localeCStr[5] = '\0';
313
314 return locale;
315 }
316