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