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