Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 /* | 1 /* |
| 2 * Copyright 2011 The Android Open Source Project | 2 * Copyright 2011 The Android Open Source Project |
| 3 * | 3 * |
| 4 * Use of this source code is governed by a BSD-style license that can be | 4 * Use of this source code is governed by a BSD-style license that can be |
| 5 * found in the LICENSE file. | 5 * found in the LICENSE file. |
| 6 */ | 6 */ |
| 7 | 7 |
| 8 #include "SkFontConfigParser_android.h" | 8 #include "SkFontConfigParser_android.h" |
| 9 #include "SkFontMgr_android.h" | 9 #include "SkFontMgr_android.h" |
| 10 #include "SkStream.h" | 10 #include "SkStream.h" |
| 11 #include "SkTDArray.h" | 11 #include "SkTDArray.h" |
| 12 #include "SkTSearch.h" | 12 #include "SkTSearch.h" |
| 13 #include "SkTypeface.h" | 13 #include "SkTypeface.h" |
| 14 | 14 |
| 15 #include <expat.h> | 15 #include <expat.h> |
| 16 #include <dirent.h> | 16 #include <dirent.h> |
| 17 #include <stdio.h> | 17 #include <stdio.h> |
| 18 | 18 |
| 19 #include <limits> | 19 #include <limits> |
| 20 #include <stdlib.h> | 20 #include <stdlib.h> |
| 21 | 21 |
| 22 // From Android version LMP onwards, all font files collapse into | |
| 23 // /system/etc/fonts.xml. Instead of trying to detect which version | |
| 24 // we're on, try to open fonts.xml; if that fails, fall back to the | |
| 25 // older filename. | |
| 26 #define LMP_SYSTEM_FONTS_FILE "/system/etc/fonts.xml" | 22 #define LMP_SYSTEM_FONTS_FILE "/system/etc/fonts.xml" |
| 27 #define OLD_SYSTEM_FONTS_FILE "/system/etc/system_fonts.xml" | 23 #define OLD_SYSTEM_FONTS_FILE "/system/etc/system_fonts.xml" |
| 28 #define FALLBACK_FONTS_FILE "/system/etc/fallback_fonts.xml" | 24 #define FALLBACK_FONTS_FILE "/system/etc/fallback_fonts.xml" |
| 29 #define VENDOR_FONTS_FILE "/vendor/etc/fallback_fonts.xml" | 25 #define VENDOR_FONTS_FILE "/vendor/etc/fallback_fonts.xml" |
| 30 | 26 |
| 31 #define LOCALE_FALLBACK_FONTS_SYSTEM_DIR "/system/etc" | 27 #define LOCALE_FALLBACK_FONTS_SYSTEM_DIR "/system/etc" |
| 32 #define LOCALE_FALLBACK_FONTS_VENDOR_DIR "/vendor/etc" | 28 #define LOCALE_FALLBACK_FONTS_VENDOR_DIR "/vendor/etc" |
| 33 #define LOCALE_FALLBACK_FONTS_PREFIX "fallback_fonts-" | 29 #define LOCALE_FALLBACK_FONTS_PREFIX "fallback_fonts-" |
| 34 #define LOCALE_FALLBACK_FONTS_SUFFIX ".xml" | 30 #define LOCALE_FALLBACK_FONTS_SUFFIX ".xml" |
| 35 | 31 |
| 36 #ifndef SK_FONT_FILE_PREFIX | 32 #ifndef SK_FONT_FILE_PREFIX |
| 37 # define SK_FONT_FILE_PREFIX "/fonts/" | 33 # define SK_FONT_FILE_PREFIX "/fonts/" |
| 38 #endif | 34 #endif |
| 39 | 35 |
| 40 /** | 36 /** |
| 41 * This file contains TWO parsers: one for JB and earlier (system_fonts.xml / | 37 * This file contains TWO 'familyset' handlers: |
| 42 * fallback_fonts.xml), one for LMP and later (fonts.xml). | 38 * One for JB and earlier which works with |
| 43 * We start with the JB parser, and if we detect a <familyset> tag with | 39 * /system/etc/system_fonts.xml |
| 44 * version 21 or higher we switch to the LMP parser. | 40 * /system/etc/fallback_fonts.xml |
| 41 * /vendor/etc/fallback_fonts.xml | |
| 42 * /system/etc/fallback_fonts-XX.xml | |
| 43 * /vendor/etc/fallback_fonts-XX.xml | |
| 44 * and the other for LMP and later which works with | |
| 45 * /system/etc/fonts.xml | |
| 46 * | |
| 47 * If the 'familyset' 'version' attribute is 21 or higher the LMP parser is used , otherwise the JB. | |
| 45 */ | 48 */ |
| 46 | 49 |
| 47 /** Used to track which tag is currently populating with data. | 50 struct FamilyData; |
| 48 * Only nameset and fileset are of interest for now. | 51 |
| 49 */ | 52 struct TagHandler { |
|
djsollen
2015/05/13 14:27:33
comments for the purpose of each of these methods.
bungeman-skia
2015/05/13 15:41:50
Done.
| |
| 50 enum CurrentTag { | 53 void (*start)(FamilyData* data, const char* tag, const char** attributes); |
| 51 kNo_CurrentTag, | 54 void (*end)(FamilyData* data, const char* tag); |
| 52 kNameSet_CurrentTag, | 55 const TagHandler* (*tag)(FamilyData* data, const char* tag, const char** att ributes); |
| 53 kFileSet_CurrentTag | 56 XML_CharacterDataHandler chars; |
| 54 }; | 57 }; |
| 55 | 58 |
| 56 /** | 59 /** Represents the current parsing state. */ |
| 57 * The FamilyData structure is passed around by the parser so that each handler | |
| 58 * can read these variables that are relevant to the current parsing. | |
| 59 */ | |
| 60 struct FamilyData { | 60 struct FamilyData { |
| 61 FamilyData(XML_Parser parser, SkTDArray<FontFamily*>& families, | 61 FamilyData(XML_Parser parser, SkTDArray<FontFamily*>& families, |
| 62 const SkString& basePath, bool isFallback, const char* filename) | 62 const SkString& basePath, bool isFallback, const char* filename, |
| 63 const TagHandler* topLevelHandler) | |
| 63 : fParser(parser) | 64 : fParser(parser) |
| 64 , fFamilies(families) | 65 , fFamilies(families) |
| 65 , fCurrentFamily(NULL) | 66 , fCurrentFamily(NULL) |
| 66 , fCurrentFontInfo(NULL) | 67 , fCurrentFontInfo(NULL) |
| 67 , fCurrentTag(kNo_CurrentTag) | |
| 68 , fVersion(0) | 68 , fVersion(0) |
| 69 , fBasePath(basePath) | 69 , fBasePath(basePath) |
| 70 , fIsFallback(isFallback) | 70 , fIsFallback(isFallback) |
| 71 , fFilename(filename) | 71 , fFilename(filename) |
| 72 , fDepth(1) | |
| 73 , fSkip(0) | |
| 74 , fHandler(&topLevelHandler, 1) | |
| 72 { }; | 75 { }; |
| 73 | 76 |
| 74 XML_Parser fParser; // The expat parser doing the work , owned by caller | 77 XML_Parser fParser; // The expat parser doing the work , owned by caller |
| 75 SkTDArray<FontFamily*>& fFamilies; // The array to append families, o wned by caller | 78 SkTDArray<FontFamily*>& fFamilies; // The array to append families, o wned by caller |
| 76 SkAutoTDelete<FontFamily> fCurrentFamily; // The family being created, owned by this | 79 SkAutoTDelete<FontFamily> fCurrentFamily; // The family being created, owned by this |
| 77 FontFileInfo* fCurrentFontInfo; // The fontInfo being created, own ed by fCurrentFamily | 80 FontFileInfo* fCurrentFontInfo; // The fontInfo being created, own ed by fCurrentFamily |
| 78 CurrentTag fCurrentTag; // The kind of tag currently being parsed. | |
| 79 int fVersion; // The version of the file parsed. | 81 int fVersion; // The version of the file parsed. |
| 80 const SkString& fBasePath; // The current base path. | 82 const SkString& fBasePath; // The current base path. |
| 81 const bool fIsFallback; // Indicates the file being parsed is a fallback file | 83 const bool fIsFallback; // Indicates the file being parsed is a fallback file |
| 82 const char* fFilename; // The name of the file currently being parsed. | 84 const char* fFilename; // The name of the file currently being parsed. |
| 85 | |
| 86 int fDepth; // The current element depth of th e parse. | |
| 87 int fSkip; // The depth to stop skipping, 0 i f not skipping. | |
| 88 SkTDArray<const TagHandler*> fHandler; // The stack of current tag handle rs. | |
| 83 }; | 89 }; |
| 84 | 90 |
| 85 /** Parses a null terminated string into an integer type, checking for overflow. | 91 /** Parses a null terminated string into an integer type, checking for overflow. |
| 86 * http://www.w3.org/TR/html-markup/datatypes.html#common.data.integer.non-nega tive-def | 92 * http://www.w3.org/TR/html-markup/datatypes.html#common.data.integer.non-nega tive-def |
| 87 * | 93 * |
| 88 * If the string cannot be parsed into 'value', returns false and does not chan ge 'value'. | 94 * If the string cannot be parsed into 'value', returns false and does not chan ge 'value'. |
| 89 */ | 95 */ |
| 90 template <typename T> static bool parse_non_negative_integer(const char* s, T* v alue) { | 96 template <typename T> static bool parse_non_negative_integer(const char* s, T* v alue) { |
| 91 SK_COMPILE_ASSERT(std::numeric_limits<T>::is_integer, T_must_be_integer); | 97 SK_COMPILE_ASSERT(std::numeric_limits<T>::is_integer, T_must_be_integer); |
| 92 const T nMax = std::numeric_limits<T>::max() / 10; | 98 const T nMax = std::numeric_limits<T>::max() / 10; |
| (...skipping 24 matching lines...) Expand all Loading... | |
| 117 | 123 |
| 118 #define SK_FONTCONFIGPARSER_PREFIX "[SkFontConfigParser] " | 124 #define SK_FONTCONFIGPARSER_PREFIX "[SkFontConfigParser] " |
| 119 | 125 |
| 120 #define SK_FONTCONFIGPARSER_WARNING(message, ...) SkDebugf( \ | 126 #define SK_FONTCONFIGPARSER_WARNING(message, ...) SkDebugf( \ |
| 121 SK_FONTCONFIGPARSER_PREFIX "%s:%d:%d: warning: " message "\n", \ | 127 SK_FONTCONFIGPARSER_PREFIX "%s:%d:%d: warning: " message "\n", \ |
| 122 self->fFilename, \ | 128 self->fFilename, \ |
| 123 XML_GetCurrentLineNumber(self->fParser), \ | 129 XML_GetCurrentLineNumber(self->fParser), \ |
| 124 XML_GetCurrentColumnNumber(self->fParser), \ | 130 XML_GetCurrentColumnNumber(self->fParser), \ |
| 125 ##__VA_ARGS__); | 131 ##__VA_ARGS__); |
| 126 | 132 |
| 133 static bool is_whitespace(char c) { | |
| 134 return c == ' ' || c == '\n'|| c == '\r' || c == '\t'; | |
| 135 } | |
| 136 | |
| 137 static void trim_string(SkString* s) { | |
| 138 char* str = s->writable_str(); | |
| 139 const char* start = str; // start is inclusive | |
| 140 const char* end = start + s->size(); // end is exclusive | |
| 141 while (is_whitespace(*start)) { ++start; } | |
| 142 if (start != end) { | |
| 143 --end; // make end inclusive | |
| 144 while (is_whitespace(*end)) { --end; } | |
| 145 ++end; // make end exclusive | |
| 146 } | |
| 147 size_t len = end - start; | |
| 148 memmove(str, start, len); | |
| 149 s->resize(len); | |
| 150 } | |
| 151 | |
| 127 namespace lmpParser { | 152 namespace lmpParser { |
| 128 | 153 |
| 129 static void family_element_handler(FamilyData* self, const char** attributes) { | 154 static const TagHandler fontHandler = { |
| 130 // A <family> element without a 'name' (string) attribute is a fallback font . | 155 /*start*/[](FamilyData* self, const char* tag, const char** attributes) { |
| 131 // The element may have 'lang' (string) and 'variant' ("elegant", "compact") attributes. | 156 // 'weight' (non-negative integer) [default 0] |
| 132 // The element should contain <font> elements. | 157 // 'style' ("normal", "italic") [default "auto"] |
| 133 FontFamily* family = new FontFamily(self->fBasePath, true); | 158 // 'index' (non-negative integer) [default 0] |
| 134 self->fCurrentFamily.reset(family); | 159 // The character data should be a filename. |
| 135 for (size_t i = 0; ATTS_NON_NULL(attributes, i); i += 2) { | 160 FontFileInfo& file = self->fCurrentFamily->fFonts.push_back(); |
| 136 const char* name = attributes[i]; | 161 self->fCurrentFontInfo = &file; |
| 137 const char* value = attributes[i+1]; | 162 for (size_t i = 0; ATTS_NON_NULL(attributes, i); i += 2) { |
| 138 size_t nameLen = strlen(name); | 163 const char* name = attributes[i]; |
| 139 size_t valueLen = strlen(value); | 164 const char* value = attributes[i+1]; |
| 140 if (MEMEQ("name", name, nameLen)) { | 165 size_t nameLen = strlen(name); |
| 141 SkAutoAsciiToLC tolc(value); | 166 if (MEMEQ("weight", name, nameLen)) { |
| 142 family->fNames.push_back().set(tolc.lc()); | 167 if (!parse_non_negative_integer(value, &file.fWeight)) { |
| 143 family->fIsFallbackFont = false; | 168 SK_FONTCONFIGPARSER_WARNING("'%s' is an invalid weight", val ue); |
| 144 } else if (MEMEQ("lang", name, nameLen)) { | 169 } |
| 145 family->fLanguage = SkLanguage(value, valueLen); | 170 } else if (MEMEQ("style", name, nameLen)) { |
| 146 } else if (MEMEQ("variant", name, nameLen)) { | 171 size_t valueLen = strlen(value); |
| 147 if (MEMEQ("elegant", value, valueLen)) { | 172 if (MEMEQ("normal", value, valueLen)) { |
| 148 family->fVariant = kElegant_FontVariant; | 173 file.fStyle = FontFileInfo::Style::kNormal; |
| 149 } else if (MEMEQ("compact", value, valueLen)) { | 174 } else if (MEMEQ("italic", value, valueLen)) { |
| 150 family->fVariant = kCompact_FontVariant; | 175 file.fStyle = FontFileInfo::Style::kItalic; |
| 176 } | |
| 177 } else if (MEMEQ("index", name, nameLen)) { | |
| 178 if (!parse_non_negative_integer(value, &file.fIndex)) { | |
| 179 SK_FONTCONFIGPARSER_WARNING("'%s' is an invalid index", valu e); | |
| 180 } | |
| 151 } | 181 } |
| 152 } | 182 } |
| 183 }, | |
| 184 /*end*/[](FamilyData* self, const char* tag) { | |
| 185 trim_string(&self->fCurrentFontInfo->fFileName); | |
| 186 }, | |
| 187 /*tag*/[](FamilyData*, const char*, const char**) -> const TagHandler* { ret urn NULL; }, | |
| 188 /*chars*/[](void* data, const char* s, int len) { | |
| 189 FamilyData* self = static_cast<FamilyData*>(data); | |
| 190 self->fCurrentFontInfo->fFileName.append(s, len); | |
| 153 } | 191 } |
| 154 } | 192 }; |
| 155 | 193 |
| 156 static void XMLCALL font_file_name_handler(void* data, const char* s, int len) { | 194 static const TagHandler familyHandler = { |
| 157 FamilyData* self = static_cast<FamilyData*>(data); | 195 /*start*/[](FamilyData* self, const char* tag, const char** attributes) { |
| 158 self->fCurrentFontInfo->fFileName.append(s, len); | 196 // 'name' (string) [optional] |
| 159 } | 197 // 'lang' (string) [default ""] |
| 160 | 198 // 'variant' ("elegant", "compact") [default "default"] |
| 161 static void font_element_handler(FamilyData* self, const char** attributes) { | 199 // If there is no name, this is a fallback only font. |
| 162 // A <font> element should be contained in a <family> element. | 200 FontFamily* family = new FontFamily(self->fBasePath, true); |
| 163 // The element may have 'weight' (non-negative integer), 'style' ("normal", "italic"), | 201 self->fCurrentFamily.reset(family); |
| 164 // and 'index' (non-negative integer) attributes. | 202 for (size_t i = 0; ATTS_NON_NULL(attributes, i); i += 2) { |
| 165 // The element should contain a filename. | 203 const char* name = attributes[i]; |
| 166 FontFileInfo& file = self->fCurrentFamily->fFonts.push_back(); | 204 const char* value = attributes[i+1]; |
| 167 self->fCurrentFontInfo = &file; | 205 size_t nameLen = strlen(name); |
| 168 for (size_t i = 0; ATTS_NON_NULL(attributes, i); i += 2) { | |
| 169 const char* name = attributes[i]; | |
| 170 const char* value = attributes[i+1]; | |
| 171 size_t nameLen = strlen(name); | |
| 172 if (MEMEQ("weight", name, nameLen)) { | |
| 173 if (!parse_non_negative_integer(value, &file.fWeight)) { | |
| 174 SK_FONTCONFIGPARSER_WARNING("'%s' is an invalid weight", value); | |
| 175 } | |
| 176 } else if (MEMEQ("style", name, nameLen)) { | |
| 177 size_t valueLen = strlen(value); | 206 size_t valueLen = strlen(value); |
| 178 if (MEMEQ("normal", value, valueLen)) { | 207 if (MEMEQ("name", name, nameLen)) { |
| 179 file.fStyle = FontFileInfo::Style::kNormal; | 208 SkAutoAsciiToLC tolc(value); |
| 180 } else if (MEMEQ("italic", value, valueLen)) { | 209 family->fNames.push_back().set(tolc.lc()); |
| 181 file.fStyle = FontFileInfo::Style::kItalic; | 210 family->fIsFallbackFont = false; |
| 182 } | 211 } else if (MEMEQ("lang", name, nameLen)) { |
| 183 } else if (MEMEQ("index", name, nameLen)) { | 212 family->fLanguage = SkLanguage(value, valueLen); |
| 184 if (!parse_non_negative_integer(value, &file.fIndex)) { | 213 } else if (MEMEQ("variant", name, nameLen)) { |
| 185 SK_FONTCONFIGPARSER_WARNING("'%s' is an invalid index", value); | 214 if (MEMEQ("elegant", value, valueLen)) { |
| 215 family->fVariant = kElegant_FontVariant; | |
| 216 } else if (MEMEQ("compact", value, valueLen)) { | |
| 217 family->fVariant = kCompact_FontVariant; | |
| 218 } | |
| 186 } | 219 } |
| 187 } | 220 } |
| 188 } | 221 }, |
| 189 XML_SetCharacterDataHandler(self->fParser, font_file_name_handler); | 222 /*end*/[](FamilyData* self, const char* tag) { |
| 190 } | 223 *self->fFamilies.append() = self->fCurrentFamily.detach(); |
| 224 }, | |
| 225 /*tag*/[](FamilyData* self, const char* tag, const char** attributes) -> con st TagHandler* { | |
| 226 size_t len = strlen(tag); | |
| 227 if (MEMEQ("font", tag, len)) { | |
| 228 return &fontHandler; | |
| 229 } | |
| 230 return NULL; | |
| 231 }, | |
| 232 /*chars*/NULL, | |
| 233 }; | |
| 191 | 234 |
| 192 static FontFamily* find_family(FamilyData* self, const SkString& familyName) { | 235 static FontFamily* find_family(FamilyData* self, const SkString& familyName) { |
| 193 for (int i = 0; i < self->fFamilies.count(); i++) { | 236 for (int i = 0; i < self->fFamilies.count(); i++) { |
| 194 FontFamily* candidate = self->fFamilies[i]; | 237 FontFamily* candidate = self->fFamilies[i]; |
| 195 for (int j = 0; j < candidate->fNames.count(); j++) { | 238 for (int j = 0; j < candidate->fNames.count(); j++) { |
| 196 if (candidate->fNames[j] == familyName) { | 239 if (candidate->fNames[j] == familyName) { |
| 197 return candidate; | 240 return candidate; |
| 198 } | 241 } |
| 199 } | 242 } |
| 200 } | 243 } |
| 201 return NULL; | 244 return NULL; |
| 202 } | 245 } |
| 203 | 246 |
| 204 static void alias_element_handler(FamilyData* self, const char** attributes) { | 247 static const TagHandler aliasHandler = { |
| 205 // A <alias> element must have 'name' (string) and 'to' (string) attributes. | 248 /*start*/[](FamilyData* self, const char* tag, const char** attributes) { |
| 206 // The element may also have a 'weight' (non-negative integer) attribute. | 249 // 'name' (string) introduces a new family name. |
| 207 // The 'name' introduces a new family name. | 250 // 'to' (string) specifies which (previous) family to alias |
| 208 // The 'to' specifies which (previous) <family> to alias. | 251 // 'weight' (non-negative integer) [optional] |
| 209 // If it *does not* have a weight, 'name' is an alias for the entire 'to' <f amily>. | 252 // If it *does not* have a weight, 'name' is an alias for the entire 'to ' family. |
| 210 // If it *does* have a weight, 'name' is a new family consisting of the <fon t>(s) with 'weight' | 253 // If it *does* have a weight, 'name' is a new family consisting of |
| 211 // from the 'to' <family>. | 254 // the font(s) with 'weight' from the 'to' family. |
| 212 | 255 |
| 213 SkString aliasName; | 256 SkString aliasName; |
| 214 SkString to; | 257 SkString to; |
| 215 int weight = 0; | 258 int weight = 0; |
| 216 for (size_t i = 0; ATTS_NON_NULL(attributes, i); i += 2) { | |
| 217 const char* name = attributes[i]; | |
| 218 const char* value = attributes[i+1]; | |
| 219 size_t nameLen = strlen(name); | |
| 220 if (MEMEQ("name", name, nameLen)) { | |
| 221 SkAutoAsciiToLC tolc(value); | |
| 222 aliasName.set(tolc.lc()); | |
| 223 } else if (MEMEQ("to", name, nameLen)) { | |
| 224 to.set(value); | |
| 225 } else if (MEMEQ("weight", name, nameLen)) { | |
| 226 if (!parse_non_negative_integer(value, &weight)) { | |
| 227 SK_FONTCONFIGPARSER_WARNING("'%s' is an invalid weight", value); | |
| 228 } | |
| 229 } | |
| 230 } | |
| 231 | |
| 232 // Assumes that the named family is already declared | |
| 233 FontFamily* targetFamily = find_family(self, to); | |
| 234 if (!targetFamily) { | |
| 235 SK_FONTCONFIGPARSER_WARNING("'%s' alias target not found", to.c_str()); | |
| 236 return; | |
| 237 } | |
| 238 | |
| 239 if (weight) { | |
| 240 FontFamily* family = new FontFamily(targetFamily->fBasePath, self->fIsFa llback); | |
| 241 family->fNames.push_back().set(aliasName); | |
| 242 | |
| 243 for (int i = 0; i < targetFamily->fFonts.count(); i++) { | |
| 244 if (targetFamily->fFonts[i].fWeight == weight) { | |
| 245 family->fFonts.push_back(targetFamily->fFonts[i]); | |
| 246 } | |
| 247 } | |
| 248 *self->fFamilies.append() = family; | |
| 249 } else { | |
| 250 targetFamily->fNames.push_back().set(aliasName); | |
| 251 } | |
| 252 } | |
| 253 | |
| 254 static void XMLCALL start_element_handler(void* data, const char* tag, const cha r** attributes) { | |
| 255 FamilyData* self = static_cast<FamilyData*>(data); | |
| 256 size_t len = strlen(tag); | |
| 257 if (MEMEQ("family", tag, len)) { | |
| 258 family_element_handler(self, attributes); | |
| 259 } else if (MEMEQ("font", tag, len)) { | |
| 260 font_element_handler(self, attributes); | |
| 261 } else if (MEMEQ("alias", tag, len)) { | |
| 262 alias_element_handler(self, attributes); | |
| 263 } | |
| 264 } | |
| 265 | |
| 266 static bool is_whitespace(char c) { | |
| 267 return c == ' ' || c == '\n'|| c == '\r' || c == '\t'; | |
| 268 } | |
| 269 | |
| 270 static void trim_string(SkString* s) { | |
| 271 char* str = s->writable_str(); | |
| 272 const char* start = str; // start is inclusive | |
| 273 const char* end = start + s->size(); // end is exclusive | |
| 274 while (is_whitespace(*start)) { ++start; } | |
| 275 if (start != end) { | |
| 276 --end; // make end inclusive | |
| 277 while (is_whitespace(*end)) { --end; } | |
| 278 ++end; // make end exclusive | |
| 279 } | |
| 280 size_t len = end - start; | |
| 281 memmove(str, start, len); | |
| 282 s->resize(len); | |
| 283 } | |
| 284 | |
| 285 static void XMLCALL end_element_handler(void* data, const char* tag) { | |
| 286 FamilyData* self = static_cast<FamilyData*>(data); | |
| 287 size_t len = strlen(tag); | |
| 288 if (MEMEQ("family", tag, len)) { | |
| 289 *self->fFamilies.append() = self->fCurrentFamily.detach(); | |
| 290 } else if (MEMEQ("font", tag, len)) { | |
| 291 XML_SetCharacterDataHandler(self->fParser, NULL); | |
| 292 trim_string(&self->fCurrentFontInfo->fFileName); | |
| 293 } | |
| 294 } | |
| 295 | |
| 296 } // lmpParser | |
| 297 | |
| 298 namespace jbParser { | |
| 299 | |
| 300 /** | |
| 301 * Handler for arbitrary text. This is used to parse the text inside each name | |
| 302 * or file tag. The resulting strings are put into the fNames or FontFileInfo ar rays. | |
| 303 */ | |
| 304 static void XMLCALL text_handler(void* data, const char* s, int len) { | |
| 305 FamilyData* self = static_cast<FamilyData*>(data); | |
| 306 // Make sure we're in the right state to store this name information | |
| 307 if (self->fCurrentFamily.get()) { | |
| 308 switch (self->fCurrentTag) { | |
| 309 case kNameSet_CurrentTag: { | |
| 310 SkAutoAsciiToLC tolc(s, len); | |
| 311 self->fCurrentFamily->fNames.back().append(tolc.lc(), len); | |
| 312 break; | |
| 313 } | |
| 314 case kFileSet_CurrentTag: | |
| 315 if (self->fCurrentFontInfo) { | |
| 316 self->fCurrentFontInfo->fFileName.append(s, len); | |
| 317 } | |
| 318 break; | |
| 319 default: | |
| 320 // Noop - don't care about any text that's not in the Fonts or Names list | |
| 321 break; | |
| 322 } | |
| 323 } | |
| 324 } | |
| 325 | |
| 326 /** | |
| 327 * Handler for font files. This processes the attributes for language and | |
| 328 * variants then lets textHandler handle the actual file name | |
| 329 */ | |
| 330 static void font_file_element_handler(FamilyData* self, const char** attributes) { | |
| 331 FontFamily& currentFamily = *self->fCurrentFamily.get(); | |
| 332 FontFileInfo& newFileInfo = currentFamily.fFonts.push_back(); | |
| 333 if (attributes) { | |
| 334 for (size_t i = 0; ATTS_NON_NULL(attributes, i); i += 2) { | 259 for (size_t i = 0; ATTS_NON_NULL(attributes, i); i += 2) { |
| 335 const char* name = attributes[i]; | 260 const char* name = attributes[i]; |
| 336 const char* value = attributes[i+1]; | 261 const char* value = attributes[i+1]; |
| 337 size_t nameLen = strlen(name); | 262 size_t nameLen = strlen(name); |
| 338 size_t valueLen = strlen(value); | 263 if (MEMEQ("name", name, nameLen)) { |
| 339 if (MEMEQ("variant", name, nameLen)) { | 264 SkAutoAsciiToLC tolc(value); |
| 340 const FontVariant prevVariant = currentFamily.fVariant; | 265 aliasName.set(tolc.lc()); |
| 341 if (MEMEQ("elegant", value, valueLen)) { | 266 } else if (MEMEQ("to", name, nameLen)) { |
| 342 currentFamily.fVariant = kElegant_FontVariant; | 267 to.set(value); |
| 343 } else if (MEMEQ("compact", value, valueLen)) { | 268 } else if (MEMEQ("weight", name, nameLen)) { |
| 344 currentFamily.fVariant = kCompact_FontVariant; | 269 if (!parse_non_negative_integer(value, &weight)) { |
| 345 } | 270 SK_FONTCONFIGPARSER_WARNING("'%s' is an invalid weight", val ue); |
| 346 if (currentFamily.fFonts.count() > 1 && currentFamily.fVariant ! = prevVariant) { | 271 } |
| 347 SK_FONTCONFIGPARSER_WARNING("'%s' unexpected variant found\n " | 272 } |
| 348 "Note: Every font file within a family must have identic al variants.", | 273 } |
| 349 value); | 274 |
| 350 } | 275 // Assumes that the named family is already declared |
| 351 | 276 FontFamily* targetFamily = find_family(self, to); |
| 352 } else if (MEMEQ("lang", name, nameLen)) { | 277 if (!targetFamily) { |
| 353 SkLanguage prevLang = currentFamily.fLanguage; | 278 SK_FONTCONFIGPARSER_WARNING("'%s' alias target not found", to.c_str( )); |
| 354 currentFamily.fLanguage = SkLanguage(value, valueLen); | 279 return; |
| 355 if (currentFamily.fFonts.count() > 1 && currentFamily.fLanguage != prevLang) { | 280 } |
| 356 SK_FONTCONFIGPARSER_WARNING("'%s' unexpected language found\ n" | 281 |
| 357 "Note: Every font file within a family must have identic al languages.", | 282 if (weight) { |
| 358 value); | 283 FontFamily* family = new FontFamily(targetFamily->fBasePath, self->f IsFallback); |
| 359 } | 284 family->fNames.push_back().set(aliasName); |
| 360 | 285 |
| 361 } else if (MEMEQ("index", name, nameLen)) { | 286 for (int i = 0; i < targetFamily->fFonts.count(); i++) { |
| 362 if (!parse_non_negative_integer(value, &newFileInfo.fIndex)) { | 287 if (targetFamily->fFonts[i].fWeight == weight) { |
| 363 SK_FONTCONFIGPARSER_WARNING("'%s' is an invalid index", valu e); | 288 family->fFonts.push_back(targetFamily->fFonts[i]); |
| 364 } | 289 } |
| 365 } | 290 } |
| 366 } | 291 *self->fFamilies.append() = family; |
| 367 } | 292 } else { |
| 368 self->fCurrentFontInfo = &newFileInfo; | 293 targetFamily->fNames.push_back().set(aliasName); |
| 369 XML_SetCharacterDataHandler(self->fParser, text_handler); | 294 } |
| 295 }, | |
| 296 /*end*/[](FamilyData* self, const char* tag) { }, | |
|
djsollen
2015/05/13 14:27:33
why not a null function ptr?
bungeman-skia
2015/05/13 15:41:50
Tags which are just containers without attributes
| |
| 297 /*tag*/[](FamilyData*, const char*, const char**) -> const TagHandler* { ret urn NULL; }, | |
| 298 /*chars*/NULL, | |
| 299 }; | |
| 300 | |
| 301 static const TagHandler familySetHandler = { | |
| 302 /*start*/[](FamilyData* self, const char* tag, const char** attributes) { }, | |
| 303 /*end*/[](FamilyData* self, const char* tag) { }, | |
| 304 /*tag*/[](FamilyData* self, const char* tag, const char** attributes) -> con st TagHandler* { | |
| 305 size_t len = strlen(tag); | |
| 306 if (MEMEQ("family", tag, len)) { | |
| 307 return &familyHandler; | |
| 308 } else if (MEMEQ("alias", tag, len)) { | |
| 309 return &aliasHandler; | |
| 310 } | |
| 311 return NULL; | |
| 312 }, | |
| 313 /*chars*/NULL, | |
| 314 }; | |
| 315 | |
| 316 } // lmpParser | |
| 317 | |
| 318 namespace jbParser { | |
| 319 | |
| 320 static const TagHandler fileHandler = { | |
| 321 /*start*/[](FamilyData* self, const char* tag, const char** attributes) { | |
| 322 // 'variant' ("elegant", "compact") [default "default"] | |
| 323 // 'lang' (string) [default ""] | |
| 324 // 'index' (non-negative integer) [default 0] | |
| 325 // The character data should be a filename. | |
| 326 FontFamily& currentFamily = *self->fCurrentFamily.get(); | |
| 327 FontFileInfo& newFileInfo = currentFamily.fFonts.push_back(); | |
| 328 if (attributes) { | |
| 329 for (size_t i = 0; ATTS_NON_NULL(attributes, i); i += 2) { | |
| 330 const char* name = attributes[i]; | |
| 331 const char* value = attributes[i+1]; | |
| 332 size_t nameLen = strlen(name); | |
| 333 size_t valueLen = strlen(value); | |
| 334 if (MEMEQ("variant", name, nameLen)) { | |
| 335 const FontVariant prevVariant = currentFamily.fVariant; | |
| 336 if (MEMEQ("elegant", value, valueLen)) { | |
| 337 currentFamily.fVariant = kElegant_FontVariant; | |
| 338 } else if (MEMEQ("compact", value, valueLen)) { | |
| 339 currentFamily.fVariant = kCompact_FontVariant; | |
| 340 } | |
| 341 if (currentFamily.fFonts.count() > 1 && currentFamily.fVaria nt != prevVariant) { | |
| 342 SK_FONTCONFIGPARSER_WARNING("'%s' unexpected variant fou nd\n" | |
| 343 "Note: Every font file within a family must have ide ntical variants.", | |
| 344 value); | |
| 345 } | |
| 346 | |
| 347 } else if (MEMEQ("lang", name, nameLen)) { | |
| 348 SkLanguage prevLang = currentFamily.fLanguage; | |
| 349 currentFamily.fLanguage = SkLanguage(value, valueLen); | |
| 350 if (currentFamily.fFonts.count() > 1 && currentFamily.fLangu age != prevLang) { | |
| 351 SK_FONTCONFIGPARSER_WARNING("'%s' unexpected language fo und\n" | |
| 352 "Note: Every font file within a family must have ide ntical languages.", | |
| 353 value); | |
| 354 } | |
| 355 | |
| 356 } else if (MEMEQ("index", name, nameLen)) { | |
| 357 if (!parse_non_negative_integer(value, &newFileInfo.fIndex)) { | |
| 358 SK_FONTCONFIGPARSER_WARNING("'%s' is an invalid index", value); | |
| 359 } | |
| 360 } | |
| 361 } | |
| 362 } | |
| 363 self->fCurrentFontInfo = &newFileInfo; | |
| 364 }, | |
| 365 /*end*/[](FamilyData* self, const char* tag) { }, | |
| 366 /*tag*/[](FamilyData*, const char*, const char**) -> const TagHandler* { ret urn NULL; }, | |
| 367 /*chars*/[](void* data, const char* s, int len) { | |
| 368 FamilyData* self = static_cast<FamilyData*>(data); | |
| 369 self->fCurrentFontInfo->fFileName.append(s, len); | |
| 370 } | |
| 371 }; | |
| 372 | |
| 373 static const TagHandler fileSetHandler = { | |
| 374 /*start*/[](FamilyData* self, const char* tag, const char** attributes) { }, | |
| 375 /*end*/[](FamilyData* self, const char* tag) { }, | |
| 376 /*tag*/[](FamilyData* self, const char* tag, const char** attributes) -> con st TagHandler* { | |
| 377 size_t len = strlen(tag); | |
| 378 if (MEMEQ("file", tag, len)) { | |
| 379 return &fileHandler; | |
| 380 } | |
| 381 return NULL; | |
| 382 }, | |
| 383 /*chars*/NULL, | |
| 384 }; | |
| 385 | |
| 386 static const TagHandler nameHandler = { | |
| 387 /*start*/[](FamilyData* self, const char* tag, const char** attributes) { | |
| 388 // The character data should be a name for the font. | |
| 389 self->fCurrentFamily->fNames.push_back(); | |
| 390 }, | |
| 391 /*end*/[](FamilyData* self, const char* tag) { }, | |
| 392 /*tag*/[](FamilyData*, const char*, const char**) -> const TagHandler* { ret urn NULL; }, | |
| 393 /*chars*/[](void* data, const char* s, int len) { | |
| 394 FamilyData* self = static_cast<FamilyData*>(data); | |
| 395 SkAutoAsciiToLC tolc(s, len); | |
| 396 self->fCurrentFamily->fNames.back().append(tolc.lc(), len); | |
| 397 } | |
| 398 }; | |
| 399 | |
| 400 static const TagHandler nameSetHandler = { | |
| 401 /*start*/[](FamilyData* self, const char* tag, const char** attributes) { }, | |
| 402 /*end*/[](FamilyData* self, const char* tag) { }, | |
| 403 /*tag*/[](FamilyData* self, const char* tag, const char** attributes) -> con st TagHandler* { | |
| 404 size_t len = strlen(tag); | |
| 405 if (MEMEQ("name", tag, len)) { | |
| 406 return &nameHandler; | |
| 407 } | |
| 408 return NULL; | |
| 409 }, | |
| 410 /*chars*/NULL, | |
| 411 }; | |
| 412 | |
| 413 static const TagHandler familyHandler = { | |
| 414 /*start*/[](FamilyData* self, const char* tag, const char** attributes) { | |
| 415 self->fCurrentFamily.reset(new FontFamily(self->fBasePath, self->fIsFall back)); | |
| 416 // 'order' (non-negative integer) [default -1] | |
| 417 for (size_t i = 0; ATTS_NON_NULL(attributes, i); i += 2) { | |
| 418 const char* value = attributes[i+1]; | |
| 419 parse_non_negative_integer(value, &self->fCurrentFamily->fOrder); | |
| 420 } | |
| 421 }, | |
| 422 /*end*/[](FamilyData* self, const char* tag) { | |
| 423 *self->fFamilies.append() = self->fCurrentFamily.detach(); | |
| 424 }, | |
| 425 /*tag*/[](FamilyData* self, const char* tag, const char** attributes) -> con st TagHandler* { | |
| 426 size_t len = strlen(tag); | |
| 427 if (MEMEQ("nameset", tag, len)) { | |
| 428 return &nameSetHandler; | |
| 429 } else if (MEMEQ("fileset", tag, len)) { | |
| 430 return &fileSetHandler; | |
| 431 } | |
| 432 return NULL; | |
| 433 }, | |
| 434 /*chars*/NULL, | |
| 435 }; | |
| 436 | |
| 437 static const TagHandler familySetHandler = { | |
| 438 /*start*/[](FamilyData* self, const char* tag, const char** attributes) { }, | |
| 439 /*end*/[](FamilyData* self, const char* tag) { }, | |
| 440 /*tag*/[](FamilyData* self, const char* tag, const char** attributes) -> con st TagHandler* { | |
| 441 size_t len = strlen(tag); | |
| 442 if (MEMEQ("family", tag, len)) { | |
| 443 return &familyHandler; | |
| 444 } | |
| 445 return NULL; | |
| 446 }, | |
| 447 /*chars*/NULL, | |
| 448 }; | |
| 449 | |
| 450 } // namespace jbParser | |
| 451 | |
| 452 static const TagHandler topLevelHandler = { | |
| 453 /*start*/[](FamilyData* self, const char* tag, const char** attributes) { }, | |
| 454 /*end*/[](FamilyData* self, const char* tag) { }, | |
| 455 /*tag*/[](FamilyData* self, const char* tag, const char** attributes) -> con st TagHandler* { | |
| 456 size_t len = strlen(tag); | |
| 457 if (MEMEQ("familyset", tag, len)) { | |
| 458 // 'version' (non-negative integer) [default 0] | |
| 459 for (size_t i = 0; ATTS_NON_NULL(attributes, i); i += 2) { | |
| 460 const char* name = attributes[i]; | |
| 461 size_t nameLen = strlen(name); | |
| 462 if (MEMEQ("version", name, nameLen)) { | |
| 463 const char* value = attributes[i+1]; | |
| 464 if (parse_non_negative_integer(value, &self->fVersion)) { | |
| 465 if (self->fVersion >= 21) { | |
| 466 return &lmpParser::familySetHandler; | |
| 467 } | |
| 468 } | |
| 469 } | |
| 470 } | |
| 471 return &jbParser::familySetHandler; | |
| 472 } | |
| 473 return NULL; | |
| 474 }, | |
| 475 /*chars*/NULL, | |
| 476 }; | |
| 477 | |
| 478 static void XMLCALL start_element_handler(void *data, const char *tag, const cha r **attributes) { | |
| 479 FamilyData* self = static_cast<FamilyData*>(data); | |
| 480 | |
| 481 if (!self->fSkip) { | |
| 482 const TagHandler* subHandler = self->fHandler.top()->tag(self, tag, attr ibutes); | |
| 483 if (subHandler) { | |
| 484 subHandler->start(self, tag, attributes); | |
| 485 self->fHandler.push(subHandler); | |
| 486 XML_SetCharacterDataHandler(self->fParser, subHandler->chars); | |
| 487 } else { | |
| 488 SK_FONTCONFIGPARSER_WARNING("'%s' tag not recognized, skipping", tag ); | |
| 489 XML_SetCharacterDataHandler(self->fParser, NULL); | |
| 490 self->fSkip = self->fDepth; | |
| 491 } | |
| 492 } | |
| 493 | |
| 494 ++self->fDepth; | |
| 370 } | 495 } |
| 371 | 496 |
| 372 /** | |
| 373 * Handler for the start of a tag. The only tags we expect are familyset, family , | |
| 374 * nameset, fileset, name, and file. | |
| 375 */ | |
| 376 static void XMLCALL start_element_handler(void* data, const char* tag, const cha r** attributes) { | |
| 377 FamilyData* self = static_cast<FamilyData*>(data); | |
| 378 size_t len = strlen(tag); | |
| 379 if (MEMEQ("familyset", tag, len)) { | |
| 380 // The <familyset> element has an optional 'version' (non-negative integ er) attribute. | |
| 381 for (size_t i = 0; ATTS_NON_NULL(attributes, i); i += 2) { | |
| 382 size_t nameLen = strlen(attributes[i]); | |
| 383 if (!MEMEQ("version", attributes[i], nameLen)) continue; | |
| 384 const char* valueString = attributes[i+1]; | |
| 385 int version; | |
| 386 if (parse_non_negative_integer(valueString, &version) && (version >= 21)) { | |
| 387 XML_SetElementHandler(self->fParser, | |
| 388 lmpParser::start_element_handler, | |
| 389 lmpParser::end_element_handler); | |
| 390 self->fVersion = version; | |
| 391 } | |
| 392 } | |
| 393 } else if (MEMEQ("family", tag, len)) { | |
| 394 self->fCurrentFamily.reset(new FontFamily(self->fBasePath, self->fIsFall back)); | |
| 395 // The <family> element has an optional 'order' (non-negative integer) a ttribute. | |
| 396 // If this attribute does not exist, the default value is -1. | |
| 397 for (size_t i = 0; ATTS_NON_NULL(attributes, i); i += 2) { | |
| 398 const char* valueString = attributes[i+1]; | |
| 399 int value; | |
| 400 if (parse_non_negative_integer(valueString, &value)) { | |
| 401 self->fCurrentFamily->fOrder = value; | |
| 402 } | |
| 403 } | |
| 404 } else if (MEMEQ("nameset", tag, len)) { | |
| 405 self->fCurrentTag = kNameSet_CurrentTag; | |
| 406 } else if (MEMEQ("fileset", tag, len)) { | |
| 407 self->fCurrentTag = kFileSet_CurrentTag; | |
| 408 } else if (MEMEQ("name", tag, len) && self->fCurrentTag == kNameSet_CurrentT ag) { | |
| 409 // If it's a Name, parse the text inside | |
| 410 self->fCurrentFamily->fNames.push_back(); | |
| 411 XML_SetCharacterDataHandler(self->fParser, text_handler); | |
| 412 } else if (MEMEQ("file", tag, len) && self->fCurrentTag == kFileSet_CurrentT ag) { | |
| 413 // If it's a file, parse the attributes, then parse the text inside | |
| 414 font_file_element_handler(self, attributes); | |
| 415 } | |
| 416 } | |
| 417 | |
| 418 /** | |
| 419 * Handler for the end of tags. We only care about family, nameset, fileset, | |
| 420 * name, and file. | |
| 421 */ | |
| 422 static void XMLCALL end_element_handler(void* data, const char* tag) { | 497 static void XMLCALL end_element_handler(void* data, const char* tag) { |
| 423 FamilyData* self = static_cast<FamilyData*>(data); | 498 FamilyData* self = static_cast<FamilyData*>(data); |
| 424 size_t len = strlen(tag); | 499 --self->fDepth; |
| 425 if (MEMEQ("family", tag, len)) { | 500 |
| 426 // Done parsing a Family - store the created currentFamily in the famili es array | 501 if (!self->fSkip) { |
| 427 *self->fFamilies.append() = self->fCurrentFamily.detach(); | 502 self->fHandler.top()->end(self, tag); |
| 428 } else if (MEMEQ("nameset", tag, len)) { | 503 self->fHandler.pop(); |
| 429 self->fCurrentTag = kNo_CurrentTag; | 504 XML_SetCharacterDataHandler(self->fParser, self->fHandler.top()->chars); |
| 430 } else if (MEMEQ("fileset", tag, len)) { | 505 } |
| 431 self->fCurrentTag = kNo_CurrentTag; | 506 |
| 432 } else if ((MEMEQ("name", tag, len) && self->fCurrentTag == kNameSet_Current Tag) || | 507 if (self->fSkip == self->fDepth) { |
| 433 (MEMEQ("file", tag, len) && self->fCurrentTag == kFileSet_Current Tag)) { | 508 self->fSkip = 0; |
| 434 // Disable the arbitrary text handler installed to load Name data | 509 XML_SetCharacterDataHandler(self->fParser, self->fHandler.top()->chars); |
| 435 XML_SetCharacterDataHandler(self->fParser, NULL); | |
| 436 } | 510 } |
| 437 } | 511 } |
| 438 | 512 |
| 439 } // namespace jbParser | |
| 440 | |
| 441 static void XMLCALL xml_entity_decl_handler(void *data, | 513 static void XMLCALL xml_entity_decl_handler(void *data, |
| 442 const XML_Char *entityName, | 514 const XML_Char *entityName, |
| 443 int is_parameter_entity, | 515 int is_parameter_entity, |
| 444 const XML_Char *value, | 516 const XML_Char *value, |
| 445 int value_length, | 517 int value_length, |
| 446 const XML_Char *base, | 518 const XML_Char *base, |
| 447 const XML_Char *systemId, | 519 const XML_Char *systemId, |
| 448 const XML_Char *publicId, | 520 const XML_Char *publicId, |
| 449 const XML_Char *notationName) | 521 const XML_Char *notationName) |
| 450 { | 522 { |
| (...skipping 27 matching lines...) Expand all Loading... | |
| 478 return -1; | 550 return -1; |
| 479 } | 551 } |
| 480 | 552 |
| 481 SkAutoTCallVProc<remove_ptr<XML_Parser>::type, XML_ParserFree> parser( | 553 SkAutoTCallVProc<remove_ptr<XML_Parser>::type, XML_ParserFree> parser( |
| 482 XML_ParserCreate_MM(NULL, &sk_XML_alloc, NULL)); | 554 XML_ParserCreate_MM(NULL, &sk_XML_alloc, NULL)); |
| 483 if (!parser) { | 555 if (!parser) { |
| 484 SkDebugf(SK_FONTCONFIGPARSER_PREFIX "could not create XML parser\n"); | 556 SkDebugf(SK_FONTCONFIGPARSER_PREFIX "could not create XML parser\n"); |
| 485 return -1; | 557 return -1; |
| 486 } | 558 } |
| 487 | 559 |
| 488 FamilyData self(parser, families, basePath, isFallback, filename); | 560 FamilyData self(parser, families, basePath, isFallback, filename, &topLevelH andler); |
| 489 XML_SetUserData(parser, &self); | 561 XML_SetUserData(parser, &self); |
| 490 | 562 |
| 491 // Disable entity processing, to inhibit internal entity expansion. See expa t CVE-2013-0340 | 563 // Disable entity processing, to inhibit internal entity expansion. See expa t CVE-2013-0340 |
| 492 XML_SetEntityDeclHandler(parser, xml_entity_decl_handler); | 564 XML_SetEntityDeclHandler(parser, xml_entity_decl_handler); |
| 493 | 565 |
| 494 // Start parsing oldschool; switch these in flight if we detect a newer vers ion of the file. | 566 // Start parsing oldschool; switch these in flight if we detect a newer vers ion of the file. |
| 495 XML_SetElementHandler(parser, jbParser::start_element_handler, jbParser::end _element_handler); | 567 XML_SetElementHandler(parser, start_element_handler, end_element_handler); |
| 496 | 568 |
| 497 // One would assume it would be faster to have a buffer on the stack and cal l XML_Parse. | 569 // One would assume it would be faster to have a buffer on the stack and cal l XML_Parse. |
| 498 // But XML_Parse will call XML_GetBuffer anyway and memmove the passed buffe r into it. | 570 // But XML_Parse will call XML_GetBuffer anyway and memmove the passed buffe r into it. |
| 499 // (Unless XML_CONTEXT_BYTES is undefined, but all users define it.) | 571 // (Unless XML_CONTEXT_BYTES is undefined, but all users define it.) |
| 500 // In debug, buffer a small odd number of bytes to detect slicing in XML_Cha racterDataHandler. | 572 // In debug, buffer a small odd number of bytes to detect slicing in XML_Cha racterDataHandler. |
| 501 static const int bufferSize = 512 SkDEBUGCODE( - 507); | 573 static const int bufferSize = 512 SkDEBUGCODE( - 507); |
| 502 bool done = false; | 574 bool done = false; |
| 503 while (!done) { | 575 while (!done) { |
| 504 void* buffer = XML_GetBuffer(parser, bufferSize); | 576 void* buffer = XML_GetBuffer(parser, bufferSize); |
| 505 if (!buffer) { | 577 if (!buffer) { |
| (...skipping 164 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 670 const char* tag = fTag.c_str(); | 742 const char* tag = fTag.c_str(); |
| 671 | 743 |
| 672 // strip off the rightmost "-.*" | 744 // strip off the rightmost "-.*" |
| 673 const char* parentTagEnd = strrchr(tag, '-'); | 745 const char* parentTagEnd = strrchr(tag, '-'); |
| 674 if (parentTagEnd == NULL) { | 746 if (parentTagEnd == NULL) { |
| 675 return SkLanguage(); | 747 return SkLanguage(); |
| 676 } | 748 } |
| 677 size_t parentTagLen = parentTagEnd - tag; | 749 size_t parentTagLen = parentTagEnd - tag; |
| 678 return SkLanguage(tag, parentTagLen); | 750 return SkLanguage(tag, parentTagLen); |
| 679 } | 751 } |
| OLD | NEW |