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