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 */ 37struct FamilyData { 38 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 */ 51void 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 */ 79void 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 */ 110void 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 */ 131void 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 */ 158void 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