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