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