• 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 <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