1 2/* 3 * Copyright 2008 Google Inc. 4 * 5 * Use of this source code is governed by a BSD-style license that can be 6 * found in the LICENSE file. 7 */ 8 9 10// ----------------------------------------------------------------------------- 11// This file provides implementations of the font resolution members of 12// SkFontHost by using the fontconfig[1] library. Fontconfig is usually found 13// on Linux systems and handles configuration, parsing and caching issues 14// involved with enumerating and matching fonts. 15// 16// [1] http://fontconfig.org 17// ----------------------------------------------------------------------------- 18 19#include <map> 20#include <string> 21 22#include <fontconfig/fontconfig.h> 23 24#include "SkFontHost.h" 25#include "SkStream.h" 26 27// This is an extern from SkFontHost_FreeType 28SkTypeface::Style find_name_and_style(SkStream* stream, SkString* name); 29 30// ----------------------------------------------------------------------------- 31// The rest of Skia requires that fonts be identified by a unique unsigned id 32// and that we be able to load them given the id. What we actually get from 33// fontconfig is the filename of the font so we keep a locked map from 34// filenames to fileid numbers and back. 35// 36// Note that there's also a unique id in the SkTypeface. This is unique over 37// both filename and style. Thus we encode that id as (fileid << 8) | style. 38// Although truetype fonts can support multiple faces in a single file, at the 39// moment Skia doesn't. 40// ----------------------------------------------------------------------------- 41SK_DECLARE_STATIC_MUTEX(global_fc_map_lock); 42static std::map<std::string, unsigned> global_fc_map; 43static std::map<unsigned, std::string> global_fc_map_inverted; 44static std::map<uint32_t, SkTypeface *> global_fc_typefaces; 45static unsigned global_fc_map_next_id = 0; 46 47static unsigned UniqueIdToFileId(unsigned uniqueid) 48{ 49 return uniqueid >> 8; 50} 51 52static SkTypeface::Style UniqueIdToStyle(unsigned uniqueid) 53{ 54 return static_cast<SkTypeface::Style>(uniqueid & 0xff); 55} 56 57static unsigned FileIdAndStyleToUniqueId(unsigned fileid, 58 SkTypeface::Style style) 59{ 60 SkASSERT((style & 0xff) == style); 61 return (fileid << 8) | static_cast<int>(style); 62} 63 64// ----------------------------------------------------------------------------- 65// Normally we only return exactly the font asked for. In last-resort cases, 66// the request is for one of the basic font names "Sans", "Serif" or 67// "Monospace". This function tells you whether a given request is for such a 68// fallback. 69// ----------------------------------------------------------------------------- 70static bool IsFallbackFontAllowed(const char* request) 71{ 72 return strcmp(request, "Sans") == 0 || 73 strcmp(request, "Serif") == 0 || 74 strcmp(request, "Monospace") == 0; 75} 76 77class FontConfigTypeface : public SkTypeface { 78public: 79 FontConfigTypeface(Style style, uint32_t id) 80 : SkTypeface(style, id) 81 { } 82}; 83 84// ----------------------------------------------------------------------------- 85// Find a matching font where @type (one of FC_*) is equal to @value. For a 86// list of types, see http://fontconfig.org/fontconfig-devel/x19.html#AEN27. 87// The variable arguments are a list of triples, just like the first three 88// arguments, and must be NULL terminated. 89// 90// For example, 91// FontMatchString(FC_FILE, FcTypeString, "/usr/share/fonts/myfont.ttf", 92// NULL); 93// ----------------------------------------------------------------------------- 94static FcPattern* FontMatch(const char* type, FcType vtype, const void* value, 95 ...) 96{ 97 va_list ap; 98 va_start(ap, value); 99 100 FcPattern* pattern = FcPatternCreate(); 101 const char* family_requested = NULL; 102 103 for (;;) { 104 FcValue fcvalue; 105 fcvalue.type = vtype; 106 switch (vtype) { 107 case FcTypeString: 108 fcvalue.u.s = (FcChar8*) value; 109 break; 110 case FcTypeInteger: 111 fcvalue.u.i = (int)(intptr_t)value; 112 break; 113 default: 114 SkDEBUGFAIL("FontMatch unhandled type"); 115 } 116 FcPatternAdd(pattern, type, fcvalue, 0); 117 118 if (vtype == FcTypeString && strcmp(type, FC_FAMILY) == 0) 119 family_requested = (const char*) value; 120 121 type = va_arg(ap, const char *); 122 if (!type) 123 break; 124 // FcType is promoted to int when passed through ... 125 vtype = static_cast<FcType>(va_arg(ap, int)); 126 value = va_arg(ap, const void *); 127 }; 128 va_end(ap); 129 130 FcConfigSubstitute(0, pattern, FcMatchPattern); 131 FcDefaultSubstitute(pattern); 132 133 // Font matching: 134 // CSS often specifies a fallback list of families: 135 // font-family: a, b, c, serif; 136 // However, fontconfig will always do its best to find *a* font when asked 137 // for something so we need a way to tell if the match which it has found is 138 // "good enough" for us. Otherwise, we can return NULL which gets piped up 139 // and lets WebKit know to try the next CSS family name. However, fontconfig 140 // configs allow substitutions (mapping "Arial -> Helvetica" etc) and we 141 // wish to support that. 142 // 143 // Thus, if a specific family is requested we set @family_requested. Then we 144 // record two strings: the family name after config processing and the 145 // family name after resolving. If the two are equal, it's a good match. 146 // 147 // So consider the case where a user has mapped Arial to Helvetica in their 148 // config. 149 // requested family: "Arial" 150 // post_config_family: "Helvetica" 151 // post_match_family: "Helvetica" 152 // -> good match 153 // 154 // and for a missing font: 155 // requested family: "Monaco" 156 // post_config_family: "Monaco" 157 // post_match_family: "Times New Roman" 158 // -> BAD match 159 // 160 // However, we special-case fallback fonts; see IsFallbackFontAllowed(). 161 FcChar8* post_config_family; 162 FcPatternGetString(pattern, FC_FAMILY, 0, &post_config_family); 163 164 FcResult result; 165 FcPattern* match = FcFontMatch(0, pattern, &result); 166 if (!match) { 167 FcPatternDestroy(pattern); 168 return NULL; 169 } 170 171 FcChar8* post_match_family; 172 FcPatternGetString(match, FC_FAMILY, 0, &post_match_family); 173 const bool family_names_match = 174 !family_requested ? 175 true : 176 strcasecmp((char *)post_config_family, (char *)post_match_family) == 0; 177 178 FcPatternDestroy(pattern); 179 180 if (!family_names_match && !IsFallbackFontAllowed(family_requested)) { 181 FcPatternDestroy(match); 182 return NULL; 183 } 184 185 return match; 186} 187 188// ----------------------------------------------------------------------------- 189// Check to see if the filename has already been assigned a fileid and, if so, 190// use it. Otherwise, assign one. Return the resulting fileid. 191// ----------------------------------------------------------------------------- 192static unsigned FileIdFromFilename(const char* filename) 193{ 194 SkAutoMutexAcquire ac(global_fc_map_lock); 195 196 std::map<std::string, unsigned>::const_iterator i = 197 global_fc_map.find(filename); 198 if (i == global_fc_map.end()) { 199 const unsigned fileid = global_fc_map_next_id++; 200 global_fc_map[filename] = fileid; 201 global_fc_map_inverted[fileid] = filename; 202 return fileid; 203 } else { 204 return i->second; 205 } 206} 207 208// static 209SkTypeface* SkFontHost::CreateTypeface(const SkTypeface* familyFace, 210 const char familyName[], 211 const void* data, size_t bytelength, 212 SkTypeface::Style style) 213{ 214 const char* resolved_family_name = NULL; 215 FcPattern* face_match = NULL; 216 217 { 218 SkAutoMutexAcquire ac(global_fc_map_lock); 219 FcInit(); 220 } 221 222 if (familyFace) { 223 // Here we use the inverted global id map to find the filename from the 224 // SkTypeface object. Given the filename we can ask fontconfig for the 225 // familyname of the font. 226 SkAutoMutexAcquire ac(global_fc_map_lock); 227 228 const unsigned fileid = UniqueIdToFileId(familyFace->uniqueID()); 229 std::map<unsigned, std::string>::const_iterator i = 230 global_fc_map_inverted.find(fileid); 231 if (i == global_fc_map_inverted.end()) 232 return NULL; 233 234 FcInit(); 235 face_match = FontMatch(FC_FILE, FcTypeString, i->second.c_str(), 236 NULL); 237 238 if (!face_match) 239 return NULL; 240 FcChar8* family; 241 if (FcPatternGetString(face_match, FC_FAMILY, 0, &family)) { 242 FcPatternDestroy(face_match); 243 return NULL; 244 } 245 // At this point, @family is pointing into the @face_match object so we 246 // cannot release it yet. 247 248 resolved_family_name = reinterpret_cast<char*>(family); 249 } else if (familyName) { 250 resolved_family_name = familyName; 251 } else { 252 return NULL; 253 } 254 255 // At this point, we have a resolved_family_name from somewhere 256 SkASSERT(resolved_family_name); 257 258 const int bold = style & SkTypeface::kBold ? 259 FC_WEIGHT_BOLD : FC_WEIGHT_NORMAL; 260 const int italic = style & SkTypeface::kItalic ? 261 FC_SLANT_ITALIC : FC_SLANT_ROMAN; 262 FcPattern* match = FontMatch(FC_FAMILY, FcTypeString, resolved_family_name, 263 FC_WEIGHT, FcTypeInteger, bold, 264 FC_SLANT, FcTypeInteger, italic, 265 NULL); 266 if (face_match) 267 FcPatternDestroy(face_match); 268 269 if (!match) 270 return NULL; 271 272 FcChar8* filename; 273 if (FcPatternGetString(match, FC_FILE, 0, &filename) != FcResultMatch) { 274 FcPatternDestroy(match); 275 return NULL; 276 } 277 // Now @filename is pointing into @match 278 279 const unsigned fileid = FileIdFromFilename(reinterpret_cast<char*>(filename)); 280 const unsigned id = FileIdAndStyleToUniqueId(fileid, style); 281 SkTypeface* typeface = SkNEW_ARGS(FontConfigTypeface, (style, id)); 282 FcPatternDestroy(match); 283 284 { 285 SkAutoMutexAcquire ac(global_fc_map_lock); 286 global_fc_typefaces[id] = typeface; 287 } 288 289 return typeface; 290} 291 292// static 293SkTypeface* SkFontHost::CreateTypefaceFromStream(SkStream* stream) 294{ 295 SkDEBUGFAIL("SkFontHost::CreateTypefaceFromStream unimplemented"); 296 return NULL; 297} 298 299// static 300SkTypeface* SkFontHost::CreateTypefaceFromFile(const char path[]) 301{ 302 SkDEBUGFAIL("SkFontHost::CreateTypefaceFromFile unimplemented"); 303 return NULL; 304} 305 306// static 307SkStream* SkFontHost::OpenStream(uint32_t id) 308{ 309 SkAutoMutexAcquire ac(global_fc_map_lock); 310 const unsigned fileid = UniqueIdToFileId(id); 311 312 std::map<unsigned, std::string>::const_iterator i = 313 global_fc_map_inverted.find(fileid); 314 if (i == global_fc_map_inverted.end()) 315 return NULL; 316 317 return SkNEW_ARGS(SkFILEStream, (i->second.c_str())); 318} 319 320size_t SkFontHost::GetFileName(SkFontID fontID, char path[], size_t length, 321 int32_t* index) { 322 SkAutoMutexAcquire ac(global_fc_map_lock); 323 const unsigned fileid = UniqueIdToFileId(fontID); 324 325 std::map<unsigned, std::string>::const_iterator i = 326 global_fc_map_inverted.find(fileid); 327 if (i == global_fc_map_inverted.end()) { 328 return 0; 329 } 330 331 const std::string& str = i->second; 332 if (path) { 333 memcpy(path, str.c_str(), SkMin32(str.size(), length)); 334 } 335 if (index) { // TODO: check if we're in a TTC 336 *index = 0; 337 } 338 return str.size(); 339} 340 341void SkFontHost::Serialize(const SkTypeface*, SkWStream*) { 342 SkDEBUGFAIL("SkFontHost::Serialize unimplemented"); 343} 344 345SkTypeface* SkFontHost::Deserialize(SkStream* stream) { 346 SkDEBUGFAIL("SkFontHost::Deserialize unimplemented"); 347 return NULL; 348} 349 350SkFontID SkFontHost::NextLogicalFont(SkFontID currFontID, SkFontID origFontID) { 351 // We don't handle font fallback, WebKit does. 352 return 0; 353} 354 355