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 "SkFontConfigParser_android.h" 9 #include "SkTDArray.h" 10 #include "SkTypeface.h" 11 12 #include <expat.h> 13 #include <sys/system_properties.h> 14 15 #define SYSTEM_FONTS_FILE "/system/etc/system_fonts.xml" 16 #define FALLBACK_FONTS_FILE "/system/etc/fallback_fonts.xml" 17 #define VENDOR_FONTS_FILE "/vendor/etc/fallback_fonts.xml" 18 19 // These defines are used to determine the kind of tag that we're currently 20 // populating with data. We only care about the sibling tags nameset and fileset 21 // for now. 22 #define NO_TAG 0 23 #define NAMESET_TAG 1 24 #define FILESET_TAG 2 25 26 /** 27 * The FamilyData structure is passed around by the parser so that each handler 28 * can read these variables that are relevant to the current parsing. 29 */ 30 struct FamilyData { 31 FamilyData(XML_Parser *parserRef, SkTDArray<FontFamily*> &familiesRef) : 32 parser(parserRef), 33 families(familiesRef), 34 currentFamily(NULL), 35 currentFontInfo(NULL), 36 currentTag(NO_TAG) {}; 37 38 XML_Parser *parser; // The expat parser doing the work 39 SkTDArray<FontFamily*> &families; // The array that each family is put into as it is parsed 40 FontFamily *currentFamily; // The current family being created 41 FontFileInfo *currentFontInfo; // The current fontInfo being created 42 int currentTag; // A flag to indicate whether we're in nameset/fileset tags 43 }; 44 45 /** 46 * Handler for arbitrary text. This is used to parse the text inside each name 47 * or file tag. The resulting strings are put into the fNames or FontFileInfo arrays. 48 */ 49 static void textHandler(void *data, const char *s, int len) { 50 FamilyData *familyData = (FamilyData*) data; 51 // Make sure we're in the right state to store this name information 52 if (familyData->currentFamily && 53 (familyData->currentTag == NAMESET_TAG || familyData->currentTag == FILESET_TAG)) { 54 // Malloc new buffer to store the string 55 char *buff; 56 buff = (char*) malloc((len + 1) * sizeof(char)); 57 strncpy(buff, s, len); 58 buff[len] = '\0'; 59 switch (familyData->currentTag) { 60 case NAMESET_TAG: 61 *(familyData->currentFamily->fNames.append()) = buff; 62 break; 63 case FILESET_TAG: 64 if (familyData->currentFontInfo) { 65 familyData->currentFontInfo->fFileName = buff; 66 } 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 font files. This processes the attributes for language and 77 * variants then lets textHandler handle the actual file name 78 */ 79 static void fontFileElementHandler(FamilyData *familyData, const char **attributes) { 80 FontFileInfo* newFileInfo = new FontFileInfo(); 81 if (attributes) { 82 int currentAttributeIndex = 0; 83 while (attributes[currentAttributeIndex]) { 84 const char* attributeName = attributes[currentAttributeIndex]; 85 const char* attributeValue = attributes[currentAttributeIndex+1]; 86 int nameLength = strlen(attributeName); 87 int valueLength = strlen(attributeValue); 88 if (strncmp(attributeName, "variant", nameLength) == 0) { 89 if (strncmp(attributeValue, "elegant", valueLength) == 0) { 90 newFileInfo->fPaintOptions.setFontVariant(SkPaintOptionsAndroid::kElegant_Variant); 91 } else if (strncmp(attributeValue, "compact", valueLength) == 0) { 92 newFileInfo->fPaintOptions.setFontVariant(SkPaintOptionsAndroid::kCompact_Variant); 93 } 94 } else if (strncmp(attributeName, "lang", nameLength) == 0) { 95 newFileInfo->fPaintOptions.setLanguage(attributeValue); 96 } 97 //each element is a pair of attributeName/attributeValue string pairs 98 currentAttributeIndex += 2; 99 } 100 } 101 *(familyData->currentFamily->fFontFiles.append()) = newFileInfo; 102 familyData->currentFontInfo = newFileInfo; 103 XML_SetCharacterDataHandler(*familyData->parser, textHandler); 104 } 105 106 /** 107 * Handler for the start of a tag. The only tags we expect are family, nameset, 108 * fileset, name, and file. 109 */ 110 static void startElementHandler(void *data, const char *tag, const char **atts) { 111 FamilyData *familyData = (FamilyData*) data; 112 int len = strlen(tag); 113 if (strncmp(tag, "family", len)== 0) { 114 familyData->currentFamily = new FontFamily(); 115 familyData->currentFamily->order = -1; 116 // The Family tag has an optional "order" attribute with an integer value >= 0 117 // If this attribute does not exist, the default value is -1 118 for (int i = 0; atts[i] != NULL; i += 2) { 119 const char* valueString = atts[i+1]; 120 int value; 121 int len = sscanf(valueString, "%d", &value); 122 if (len > 0) { 123 familyData->currentFamily->order = value; 124 } 125 } 126 } else if (len == 7 && strncmp(tag, "nameset", len) == 0) { 127 familyData->currentTag = NAMESET_TAG; 128 } else if (len == 7 && strncmp(tag, "fileset", len) == 0) { 129 familyData->currentTag = FILESET_TAG; 130 } else if (strncmp(tag, "name", len) == 0 && familyData->currentTag == NAMESET_TAG) { 131 // If it's a Name, parse the text inside 132 XML_SetCharacterDataHandler(*familyData->parser, textHandler); 133 } else if (strncmp(tag, "file", len) == 0 && familyData->currentTag == FILESET_TAG) { 134 // If it's a file, parse the attributes, then parse the text inside 135 fontFileElementHandler(familyData, atts); 136 } 137 } 138 139 /** 140 * Handler for the end of tags. We only care about family, nameset, fileset, 141 * name, and file. 142 */ 143 static void endElementHandler(void *data, const char *tag) { 144 FamilyData *familyData = (FamilyData*) data; 145 int len = strlen(tag); 146 if (strncmp(tag, "family", len)== 0) { 147 // Done parsing a Family - store the created currentFamily in the families array 148 *familyData->families.append() = familyData->currentFamily; 149 familyData->currentFamily = NULL; 150 } else if (len == 7 && strncmp(tag, "nameset", len) == 0) { 151 familyData->currentTag = NO_TAG; 152 } else if (len == 7 && strncmp(tag, "fileset", len) == 0) { 153 familyData->currentTag = NO_TAG; 154 } else if ((strncmp(tag, "name", len) == 0 && familyData->currentTag == NAMESET_TAG) || 155 (strncmp(tag, "file", len) == 0 && familyData->currentTag == FILESET_TAG)) { 156 // Disable the arbitrary text handler installed to load Name data 157 XML_SetCharacterDataHandler(*familyData->parser, NULL); 158 } 159 } 160 161 /** 162 * This function parses the given filename and stores the results in the given 163 * families array. 164 */ 165 static void parseConfigFile(const char *filename, SkTDArray<FontFamily*> &families) { 166 167 FILE* file = NULL; 168 169 #if !defined(SK_BUILD_FOR_ANDROID_FRAMEWORK) 170 // if we are using a version of Android prior to Android 4.2 (JellyBean MR1 171 // at API Level 17) then we need to look for files with a different suffix. 172 char sdkVersion[PROP_VALUE_MAX]; 173 __system_property_get("ro.build.version.sdk", sdkVersion); 174 const int sdkVersionInt = atoi(sdkVersion); 175 176 if (0 != *sdkVersion && sdkVersionInt < 17) { 177 SkString basename; 178 SkString updatedFilename; 179 SkString locale = SkFontConfigParser::GetLocale(); 180 181 basename.set(filename); 182 // Remove the .xml suffix. We'll add it back in a moment. 183 if (basename.endsWith(".xml")) { 184 basename.resize(basename.size()-4); 185 } 186 // Try first with language and region 187 updatedFilename.printf("%s-%s.xml", basename.c_str(), locale.c_str()); 188 file = fopen(updatedFilename.c_str(), "r"); 189 if (!file) { 190 // If not found, try next with just language 191 updatedFilename.printf("%s-%.2s.xml", basename.c_str(), locale.c_str()); 192 file = fopen(updatedFilename.c_str(), "r"); 193 } 194 } 195 #endif 196 197 if (NULL == file) { 198 file = fopen(filename, "r"); 199 } 200 201 // Some of the files we attempt to parse (in particular, /vendor/etc/fallback_fonts.xml) 202 // are optional - failure here is okay because one of these optional files may not exist. 203 if (NULL == file) { 204 return; 205 } 206 207 XML_Parser parser = XML_ParserCreate(NULL); 208 FamilyData *familyData = new FamilyData(&parser, families); 209 XML_SetUserData(parser, familyData); 210 XML_SetElementHandler(parser, startElementHandler, endElementHandler); 211 212 char buffer[512]; 213 bool done = false; 214 while (!done) { 215 fgets(buffer, sizeof(buffer), file); 216 int len = strlen(buffer); 217 if (feof(file) != 0) { 218 done = true; 219 } 220 XML_Parse(parser, buffer, len, done); 221 } 222 fclose(file); 223 } 224 225 static void getSystemFontFamilies(SkTDArray<FontFamily*> &fontFamilies) { 226 parseConfigFile(SYSTEM_FONTS_FILE, fontFamilies); 227 } 228 229 static void getFallbackFontFamilies(SkTDArray<FontFamily*> &fallbackFonts) { 230 SkTDArray<FontFamily*> vendorFonts; 231 parseConfigFile(FALLBACK_FONTS_FILE, fallbackFonts); 232 parseConfigFile(VENDOR_FONTS_FILE, vendorFonts); 233 234 // This loop inserts the vendor fallback fonts in the correct order in the 235 // overall fallbacks list. 236 int currentOrder = -1; 237 for (int i = 0; i < vendorFonts.count(); ++i) { 238 FontFamily* family = vendorFonts[i]; 239 int order = family->order; 240 if (order < 0) { 241 if (currentOrder < 0) { 242 // Default case - just add it to the end of the fallback list 243 *fallbackFonts.append() = family; 244 } else { 245 // no order specified on this font, but we're incrementing the order 246 // based on an earlier order insertion request 247 *fallbackFonts.insert(currentOrder++) = family; 248 } 249 } else { 250 // Add the font into the fallback list in the specified order. Set 251 // currentOrder for correct placement of other fonts in the vendor list. 252 *fallbackFonts.insert(order) = family; 253 currentOrder = order + 1; 254 } 255 } 256 } 257 258 /** 259 * Loads data on font families from various expected configuration files. The 260 * resulting data is returned in the given fontFamilies array. 261 */ 262 void SkFontConfigParser::GetFontFamilies(SkTDArray<FontFamily*> &fontFamilies) { 263 264 getSystemFontFamilies(fontFamilies); 265 266 // Append all the fallback fonts to system fonts 267 SkTDArray<FontFamily*> fallbackFonts; 268 getFallbackFontFamilies(fallbackFonts); 269 for (int i = 0; i < fallbackFonts.count(); ++i) { 270 fallbackFonts[i]->fIsFallbackFont = true; 271 *fontFamilies.append() = fallbackFonts[i]; 272 } 273 } 274 275 void SkFontConfigParser::GetTestFontFamilies(SkTDArray<FontFamily*> &fontFamilies, 276 const char* testMainConfigFile, 277 const char* testFallbackConfigFile) { 278 parseConfigFile(testMainConfigFile, fontFamilies); 279 280 SkTDArray<FontFamily*> fallbackFonts; 281 parseConfigFile(testFallbackConfigFile, fallbackFonts); 282 283 // Append all fallback fonts to system fonts 284 for (int i = 0; i < fallbackFonts.count(); ++i) { 285 fallbackFonts[i]->fIsFallbackFont = true; 286 *fontFamilies.append() = fallbackFonts[i]; 287 } 288 } 289 290 /** 291 * Read the persistent locale. 292 */ 293 SkString SkFontConfigParser::GetLocale() 294 { 295 char propLang[PROP_VALUE_MAX], propRegn[PROP_VALUE_MAX]; 296 __system_property_get("persist.sys.language", propLang); 297 __system_property_get("persist.sys.country", propRegn); 298 299 if (*propLang == 0 && *propRegn == 0) { 300 /* Set to ro properties, default is en_US */ 301 __system_property_get("ro.product.locale.language", propLang); 302 __system_property_get("ro.product.locale.region", propRegn); 303 if (*propLang == 0 && *propRegn == 0) { 304 strcpy(propLang, "en"); 305 strcpy(propRegn, "US"); 306 } 307 } 308 309 SkString locale(6); 310 char* localeCStr = locale.writable_str(); 311 312 strncpy(localeCStr, propLang, 2); 313 localeCStr[2] = '-'; 314 strncpy(&localeCStr[3], propRegn, 2); 315 localeCStr[5] = '\0'; 316 317 return locale; 318 } 319