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 { 33 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 */ 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 */ 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 */ 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 */ 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 */ 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 188 void getSystemFontFamilies(SkTDArray<FontFamily*> &fontFamilies) { 189 parseConfigFile(SYSTEM_FONTS_FILE, fontFamilies); 190 } 191 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 */ 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