• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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