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