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 "FontHostConfiguration_android.h"
9 #include "SkLanguage.h"
10 #include "SkTDArray.h"
11 #include "SkTypeface.h"
12 #include <expat.h>
13 #if !defined(SK_BUILD_FOR_ANDROID_NDK)
14 #include <cutils/properties.h>
15 #endif
16
17 #define SYSTEM_FONTS_FILE "/system/etc/system_fonts.xml"
18 #define FALLBACK_FONTS_FILE "/system/etc/fallback_fonts.xml"
19 #define VENDOR_FONTS_FILE "/vendor/etc/fallback_fonts.xml"
20
21 // These defines are used to determine the kind of tag that we're currently
22 // populating with data. We only care about the sibling tags nameset and fileset
23 // for now.
24 #define NO_TAG 0
25 #define NAMESET_TAG 1
26 #define FILESET_TAG 2
27
28 /**
29 * The FamilyData structure is passed around by the parser so that each handler
30 * can read these variables that are relevant to the current parsing.
31 */
32 struct FamilyData {
FamilyDataFamilyData33 FamilyData(XML_Parser *parserRef, SkTDArray<FontFamily*> &familiesRef) :
34 parser(parserRef), families(familiesRef), currentTag(NO_TAG) {};
35
36 XML_Parser *parser; // The expat parser doing the work
37 SkTDArray<FontFamily*> &families; // The array that each family is put into as it is parsed
38 FontFamily *currentFamily; // The current family being created
39 FontFileInfo *currentFontInfo; // The current fontInfo being created
40 int currentTag; // A flag to indicate whether we're in nameset/fileset tags
41 };
42
43 /**
44 * Handler for arbitrary text. This is used to parse the text inside each name
45 * or file tag. The resulting strings are put into the fNames or FontFileInfo arrays.
46 */
textHandler(void * data,const char * s,int len)47 void textHandler(void *data, const char *s, int len) {
48 FamilyData *familyData = (FamilyData*) data;
49 // Make sure we're in the right state to store this name information
50 if (familyData->currentFamily &&
51 (familyData->currentTag == NAMESET_TAG || familyData->currentTag == FILESET_TAG)) {
52 // Malloc new buffer to store the string
53 char *buff;
54 buff = (char*) malloc((len + 1) * sizeof(char));
55 strncpy(buff, s, len);
56 buff[len] = '\0';
57 switch (familyData->currentTag) {
58 case NAMESET_TAG:
59 *(familyData->currentFamily->fNames.append()) = buff;
60 break;
61 case FILESET_TAG:
62 if (familyData->currentFontInfo) {
63 familyData->currentFontInfo->fFileName = buff;
64 }
65 break;
66 default:
67 // Noop - don't care about any text that's not in the Fonts or Names list
68 break;
69 }
70 }
71 }
72
73 /**
74 * Handler for font files. This processes the attributes for language and
75 * variants then lets textHandler handle the actual file name
76 */
fontFileElementHandler(FamilyData * familyData,const char ** attributes)77 void fontFileElementHandler(FamilyData *familyData, const char **attributes) {
78 FontFileInfo* newFileInfo = new FontFileInfo();
79 if (attributes) {
80 int currentAttributeIndex = 0;
81 while (attributes[currentAttributeIndex]) {
82 const char* attributeName = attributes[currentAttributeIndex];
83 const char* attributeValue = attributes[currentAttributeIndex+1];
84 int nameLength = strlen(attributeName);
85 int valueLength = strlen(attributeValue);
86 if (strncmp(attributeName, "variant", nameLength) == 0) {
87 if (strncmp(attributeValue, "elegant", valueLength) == 0) {
88 newFileInfo->fVariant = SkPaint::kElegant_Variant;
89 } else if (strncmp(attributeValue, "compact", valueLength) == 0) {
90 newFileInfo->fVariant = SkPaint::kCompact_Variant;
91 }
92 } else if (strncmp(attributeName, "lang", nameLength) == 0) {
93 newFileInfo->fLanguage = SkLanguage(attributeValue);
94 }
95 //each element is a pair of attributeName/attributeValue string pairs
96 currentAttributeIndex += 2;
97 }
98 }
99 *(familyData->currentFamily->fFontFileArray.append()) = newFileInfo;
100 familyData->currentFontInfo = newFileInfo;
101 XML_SetCharacterDataHandler(*familyData->parser, textHandler);
102 }
103
104 /**
105 * Handler for the start of a tag. The only tags we expect are family, nameset,
106 * fileset, name, and file.
107 */
startElementHandler(void * data,const char * tag,const char ** atts)108 void startElementHandler(void *data, const char *tag, const char **atts) {
109 FamilyData *familyData = (FamilyData*) data;
110 int len = strlen(tag);
111 if (strncmp(tag, "family", len)== 0) {
112 familyData->currentFamily = new FontFamily();
113 familyData->currentFamily->order = -1;
114 // The Family tag has an optional "order" attribute with an integer value >= 0
115 // If this attribute does not exist, the default value is -1
116 for (int i = 0; atts[i] != NULL; i += 2) {
117 const char* attribute = atts[i];
118 const char* valueString = atts[i+1];
119 int value;
120 int len = sscanf(valueString, "%d", &value);
121 if (len > 0) {
122 familyData->currentFamily->order = value;
123 }
124 }
125 } else if (len == 7 && strncmp(tag, "nameset", len) == 0) {
126 familyData->currentTag = NAMESET_TAG;
127 } else if (len == 7 && strncmp(tag, "fileset", len) == 0) {
128 familyData->currentTag = FILESET_TAG;
129 } else if (strncmp(tag, "name", len) == 0 && familyData->currentTag == NAMESET_TAG) {
130 // If it's a Name, parse the text inside
131 XML_SetCharacterDataHandler(*familyData->parser, textHandler);
132 } else if (strncmp(tag, "file", len) == 0 && familyData->currentTag == FILESET_TAG) {
133 // If it's a file, parse the attributes, then parse the text inside
134 fontFileElementHandler(familyData, atts);
135 }
136 }
137
138 /**
139 * Handler for the end of tags. We only care about family, nameset, fileset,
140 * name, and file.
141 */
endElementHandler(void * data,const char * tag)142 void endElementHandler(void *data, const char *tag) {
143 FamilyData *familyData = (FamilyData*) data;
144 int len = strlen(tag);
145 if (strncmp(tag, "family", len)== 0) {
146 // Done parsing a Family - store the created currentFamily in the families array
147 *familyData->families.append() = familyData->currentFamily;
148 familyData->currentFamily = NULL;
149 } else if (len == 7 && strncmp(tag, "nameset", len) == 0) {
150 familyData->currentTag = NO_TAG;
151 } else if (len == 7 && strncmp(tag, "fileset", len) == 0) {
152 familyData->currentTag = NO_TAG;
153 } else if ((strncmp(tag, "name", len) == 0 && familyData->currentTag == NAMESET_TAG) ||
154 (strncmp(tag, "file", len) == 0 && familyData->currentTag == FILESET_TAG)) {
155 // Disable the arbitrary text handler installed to load Name data
156 XML_SetCharacterDataHandler(*familyData->parser, NULL);
157 }
158 }
159
160 /**
161 * This function parses the given filename and stores the results in the given
162 * families array.
163 */
parseConfigFile(const char * filename,SkTDArray<FontFamily * > & families)164 void parseConfigFile(const char *filename, SkTDArray<FontFamily*> &families) {
165 XML_Parser parser = XML_ParserCreate(NULL);
166 FamilyData *familyData = new FamilyData(&parser, families);
167 XML_SetUserData(parser, familyData);
168 XML_SetElementHandler(parser, startElementHandler, endElementHandler);
169 FILE *file = fopen(filename, "r");
170 // Some of the files we attempt to parse (in particular, /vendor/etc/fallback_fonts.xml)
171 // are optional - failure here is okay because one of these optional files may not exist.
172 if (file == NULL) {
173 return;
174 }
175 char buffer[512];
176 bool done = false;
177 while (!done) {
178 fgets(buffer, sizeof(buffer), file);
179 int len = strlen(buffer);
180 if (feof(file) != 0) {
181 done = true;
182 }
183 XML_Parse(parser, buffer, len, done);
184 }
185 fclose(file);
186 }
187
getSystemFontFamilies(SkTDArray<FontFamily * > & fontFamilies)188 void getSystemFontFamilies(SkTDArray<FontFamily*> &fontFamilies) {
189 parseConfigFile(SYSTEM_FONTS_FILE, fontFamilies);
190 }
191
getFallbackFontFamilies(SkTDArray<FontFamily * > & fallbackFonts)192 void getFallbackFontFamilies(SkTDArray<FontFamily*> &fallbackFonts) {
193 SkTDArray<FontFamily*> vendorFonts;
194 parseConfigFile(FALLBACK_FONTS_FILE, fallbackFonts);
195 parseConfigFile(VENDOR_FONTS_FILE, vendorFonts);
196
197 // This loop inserts the vendor fallback fonts in the correct order in the
198 // overall fallbacks list.
199 int currentOrder = -1;
200 for (int i = 0; i < vendorFonts.count(); ++i) {
201 FontFamily* family = vendorFonts[i];
202 int order = family->order;
203 if (order < 0) {
204 if (currentOrder < 0) {
205 // Default case - just add it to the end of the fallback list
206 *fallbackFonts.append() = family;
207 } else {
208 // no order specified on this font, but we're incrementing the order
209 // based on an earlier order insertion request
210 *fallbackFonts.insert(currentOrder++) = family;
211 }
212 } else {
213 // Add the font into the fallback list in the specified order. Set
214 // currentOrder for correct placement of other fonts in the vendor list.
215 *fallbackFonts.insert(order) = family;
216 currentOrder = order + 1;
217 }
218 }
219 }
220
221 /**
222 * Loads data on font families from various expected configuration files. The
223 * resulting data is returned in the given fontFamilies array.
224 */
getFontFamilies(SkTDArray<FontFamily * > & fontFamilies)225 void getFontFamilies(SkTDArray<FontFamily*> &fontFamilies) {
226 SkTDArray<FontFamily*> fallbackFonts;
227
228 getSystemFontFamilies(fontFamilies);
229 getFallbackFontFamilies(fallbackFonts);
230
231 // Append all fallback fonts to system fonts
232 for (int i = 0; i < fallbackFonts.count(); ++i) {
233 *fontFamilies.append() = fallbackFonts[i];
234 }
235 }
236