1 /* 2 * Copyright 2009-2015 Google Inc. 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 /* migrated from chrome/src/skia/ext/SkFontHost_fontconfig_direct.cpp */ 9 10 #include "SkBuffer.h" 11 #include "SkDataTable.h" 12 #include "SkFontConfigInterface_direct.h" 13 #include "SkFontStyle.h" 14 #include "SkMutex.h" 15 #include "SkStream.h" 16 #include "SkString.h" 17 #include "SkTArray.h" 18 #include "SkTDArray.h" 19 #include "SkTemplates.h" 20 #include "SkTypeface.h" 21 #include "SkTypes.h" 22 23 #include <fontconfig/fontconfig.h> 24 #include <unistd.h> 25 26 size_t SkFontConfigInterface::FontIdentity::writeToMemory(void* addr) const { 27 size_t size = sizeof(fID) + sizeof(fTTCIndex); 28 size += sizeof(int32_t) + sizeof(int32_t) + sizeof(uint8_t); // weight, width, italic 29 size += sizeof(int32_t) + fString.size(); // store length+data 30 if (addr) { 31 SkWBuffer buffer(addr, size); 32 33 buffer.write32(fID); 34 buffer.write32(fTTCIndex); 35 buffer.write32(fString.size()); 36 buffer.write32(fStyle.weight()); 37 buffer.write32(fStyle.width()); 38 buffer.write8(fStyle.slant()); 39 buffer.write(fString.c_str(), fString.size()); 40 buffer.padToAlign4(); 41 42 SkASSERT(buffer.pos() == size); 43 } 44 return size; 45 } 46 47 size_t SkFontConfigInterface::FontIdentity::readFromMemory(const void* addr, 48 size_t size) { 49 SkRBuffer buffer(addr, size); 50 51 (void)buffer.readU32(&fID); 52 (void)buffer.readS32(&fTTCIndex); 53 uint32_t strLen, weight, width; 54 (void)buffer.readU32(&strLen); 55 (void)buffer.readU32(&weight); 56 (void)buffer.readU32(&width); 57 uint8_t u8; 58 (void)buffer.readU8(&u8); 59 SkFontStyle::Slant slant = (SkFontStyle::Slant)u8; 60 fStyle = SkFontStyle(weight, width, slant); 61 fString.resize(strLen); 62 (void)buffer.read(fString.writable_str(), strLen); 63 buffer.skipToAlign4(); 64 65 return buffer.pos(); // the actual number of bytes read 66 } 67 68 #ifdef SK_DEBUG 69 static void make_iden(SkFontConfigInterface::FontIdentity* iden) { 70 iden->fID = 10; 71 iden->fTTCIndex = 2; 72 iden->fString.set("Hello world"); 73 iden->fStyle = SkFontStyle(300, 6, SkFontStyle::kItalic_Slant); 74 } 75 76 static void test_writeToMemory(const SkFontConfigInterface::FontIdentity& iden0, 77 int initValue) { 78 SkFontConfigInterface::FontIdentity iden1; 79 80 size_t size0 = iden0.writeToMemory(nullptr); 81 82 SkAutoMalloc storage(size0); 83 memset(storage.get(), initValue, size0); 84 85 size_t size1 = iden0.writeToMemory(storage.get()); 86 SkASSERT(size0 == size1); 87 88 SkASSERT(iden0 != iden1); 89 size_t size2 = iden1.readFromMemory(storage.get(), size1); 90 SkASSERT(size2 == size1); 91 SkASSERT(iden0 == iden1); 92 } 93 94 static void fontconfiginterface_unittest() { 95 SkFontConfigInterface::FontIdentity iden0, iden1; 96 97 SkASSERT(iden0 == iden1); 98 99 make_iden(&iden0); 100 SkASSERT(iden0 != iden1); 101 102 make_iden(&iden1); 103 SkASSERT(iden0 == iden1); 104 105 test_writeToMemory(iden0, 0); 106 test_writeToMemory(iden0, 0); 107 } 108 #endif 109 110 /////////////////////////////////////////////////////////////////////////////// 111 112 // Returns the string from the pattern, or nullptr 113 static const char* get_name(FcPattern* pattern, const char field[], 114 int index = 0) { 115 const char* name; 116 if (FcPatternGetString(pattern, field, index, 117 (FcChar8**)&name) != FcResultMatch) { 118 name = nullptr; 119 } 120 return name; 121 } 122 123 /////////////////////////////////////////////////////////////////////////////// 124 125 namespace { 126 127 // Equivalence classes, used to match the Liberation and other fonts 128 // with their metric-compatible replacements. See the discussion in 129 // GetFontEquivClass(). 130 enum FontEquivClass 131 { 132 OTHER, 133 SANS, 134 SERIF, 135 MONO, 136 SYMBOL, 137 PGOTHIC, 138 GOTHIC, 139 PMINCHO, 140 MINCHO, 141 SIMSUN, 142 NSIMSUN, 143 SIMHEI, 144 PMINGLIU, 145 MINGLIU, 146 PMINGLIUHK, 147 MINGLIUHK, 148 CAMBRIA, 149 CALIBRI, 150 }; 151 152 // Match the font name against a whilelist of fonts, returning the equivalence 153 // class. 154 FontEquivClass GetFontEquivClass(const char* fontname) 155 { 156 // It would be nice for fontconfig to tell us whether a given suggested 157 // replacement is a "strong" match (that is, an equivalent font) or 158 // a "weak" match (that is, fontconfig's next-best attempt at finding a 159 // substitute). However, I played around with the fontconfig API for 160 // a good few hours and could not make it reveal this information. 161 // 162 // So instead, we hardcode. Initially this function emulated 163 // /etc/fonts/conf.d/30-metric-aliases.conf 164 // from my Ubuntu system, but we're better off being very conservative. 165 166 // Arimo, Tinos and Cousine are a set of fonts metric-compatible with 167 // Arial, Times New Roman and Courier New with a character repertoire 168 // much larger than Liberation. Note that Cousine is metrically 169 // compatible with Courier New, but the former is sans-serif while 170 // the latter is serif. 171 172 173 struct FontEquivMap { 174 FontEquivClass clazz; 175 const char name[40]; 176 }; 177 178 static const FontEquivMap kFontEquivMap[] = { 179 { SANS, "Arial" }, 180 { SANS, "Arimo" }, 181 { SANS, "Liberation Sans" }, 182 183 { SERIF, "Times New Roman" }, 184 { SERIF, "Tinos" }, 185 { SERIF, "Liberation Serif" }, 186 187 { MONO, "Courier New" }, 188 { MONO, "Cousine" }, 189 { MONO, "Liberation Mono" }, 190 191 { SYMBOL, "Symbol" }, 192 { SYMBOL, "Symbol Neu" }, 193 194 // 195 { PGOTHIC, "MS PGothic" }, 196 { PGOTHIC, "\xef\xbc\xad\xef\xbc\xb3 \xef\xbc\xb0" 197 "\xe3\x82\xb4\xe3\x82\xb7\xe3\x83\x83\xe3\x82\xaf" }, 198 { PGOTHIC, "Noto Sans CJK JP" }, 199 { PGOTHIC, "IPAPGothic" }, 200 { PGOTHIC, "MotoyaG04Gothic" }, 201 202 // 203 { GOTHIC, "MS Gothic" }, 204 { GOTHIC, "\xef\xbc\xad\xef\xbc\xb3 " 205 "\xe3\x82\xb4\xe3\x82\xb7\xe3\x83\x83\xe3\x82\xaf" }, 206 { GOTHIC, "Noto Sans Mono CJK JP" }, 207 { GOTHIC, "IPAGothic" }, 208 { GOTHIC, "MotoyaG04GothicMono" }, 209 210 // 211 { PMINCHO, "MS PMincho" }, 212 { PMINCHO, "\xef\xbc\xad\xef\xbc\xb3 \xef\xbc\xb0" 213 "\xe6\x98\x8e\xe6\x9c\x9d"}, 214 { PMINCHO, "IPAPMincho" }, 215 { PMINCHO, "MotoyaG04Mincho" }, 216 217 // 218 { MINCHO, "MS Mincho" }, 219 { MINCHO, "\xef\xbc\xad\xef\xbc\xb3 \xe6\x98\x8e\xe6\x9c\x9d" }, 220 { MINCHO, "IPAMincho" }, 221 { MINCHO, "MotoyaG04MinchoMono" }, 222 223 // 224 { SIMSUN, "Simsun" }, 225 { SIMSUN, "\xe5\xae\x8b\xe4\xbd\x93" }, 226 { SIMSUN, "MSung GB18030" }, 227 { SIMSUN, "Song ASC" }, 228 229 // 230 { NSIMSUN, "NSimsun" }, 231 { NSIMSUN, "\xe6\x96\xb0\xe5\xae\x8b\xe4\xbd\x93" }, 232 { NSIMSUN, "MSung GB18030" }, 233 { NSIMSUN, "N Song ASC" }, 234 235 // 236 { SIMHEI, "Simhei" }, 237 { SIMHEI, "\xe9\xbb\x91\xe4\xbd\x93" }, 238 { SIMHEI, "Noto Sans CJK SC" }, 239 { SIMHEI, "MYingHeiGB18030" }, 240 { SIMHEI, "MYingHeiB5HK" }, 241 242 // 243 { PMINGLIU, "PMingLiU"}, 244 { PMINGLIU, "\xe6\x96\xb0\xe7\xb4\xb0\xe6\x98\x8e\xe9\xab\x94" }, 245 { PMINGLIU, "MSung B5HK"}, 246 247 // 248 { MINGLIU, "MingLiU"}, 249 { MINGLIU, "\xe7\xb4\xb0\xe6\x98\x8e\xe9\xab\x94" }, 250 { MINGLIU, "MSung B5HK"}, 251 252 // 253 { PMINGLIUHK, "PMingLiU_HKSCS"}, 254 { PMINGLIUHK, "\xe6\x96\xb0\xe7\xb4\xb0\xe6\x98\x8e\xe9\xab\x94_HKSCS" }, 255 { PMINGLIUHK, "MSung B5HK"}, 256 257 // 258 { MINGLIUHK, "MingLiU_HKSCS"}, 259 { MINGLIUHK, "\xe7\xb4\xb0\xe6\x98\x8e\xe9\xab\x94_HKSCS" }, 260 { MINGLIUHK, "MSung B5HK"}, 261 262 // Cambria 263 { CAMBRIA, "Cambria" }, 264 { CAMBRIA, "Caladea" }, 265 266 // Calibri 267 { CALIBRI, "Calibri" }, 268 { CALIBRI, "Carlito" }, 269 }; 270 271 static const size_t kFontCount = 272 sizeof(kFontEquivMap)/sizeof(kFontEquivMap[0]); 273 274 // TODO(jungshik): If this loop turns out to be hot, turn 275 // the array to a static (hash)map to speed it up. 276 for (size_t i = 0; i < kFontCount; ++i) { 277 if (strcasecmp(kFontEquivMap[i].name, fontname) == 0) 278 return kFontEquivMap[i].clazz; 279 } 280 return OTHER; 281 } 282 283 284 // Return true if |font_a| and |font_b| are visually and at the metrics 285 // level interchangeable. 286 bool IsMetricCompatibleReplacement(const char* font_a, const char* font_b) 287 { 288 FontEquivClass class_a = GetFontEquivClass(font_a); 289 FontEquivClass class_b = GetFontEquivClass(font_b); 290 291 return class_a != OTHER && class_a == class_b; 292 } 293 294 // Normally we only return exactly the font asked for. In last-resort 295 // cases, the request either doesn't specify a font or is one of the 296 // basic font names like "Sans", "Serif" or "Monospace". This function 297 // tells you whether a given request is for such a fallback. 298 bool IsFallbackFontAllowed(const SkString& family) { 299 const char* family_cstr = family.c_str(); 300 return family.isEmpty() || 301 strcasecmp(family_cstr, "sans") == 0 || 302 strcasecmp(family_cstr, "serif") == 0 || 303 strcasecmp(family_cstr, "monospace") == 0; 304 } 305 306 // Retrieves |is_bold|, |is_italic| and |font_family| properties from |font|. 307 SkTypeface::Style GetFontStyle(FcPattern* font) { 308 int resulting_bold; 309 if (FcPatternGetInteger(font, FC_WEIGHT, 0, &resulting_bold)) 310 resulting_bold = FC_WEIGHT_NORMAL; 311 312 int resulting_italic; 313 if (FcPatternGetInteger(font, FC_SLANT, 0, &resulting_italic)) 314 resulting_italic = FC_SLANT_ROMAN; 315 316 // If we ask for an italic font, fontconfig might take a roman font and set 317 // the undocumented property FC_MATRIX to a skew matrix. It'll then say 318 // that the font is italic or oblique. So, if we see a matrix, we don't 319 // believe that it's italic. 320 FcValue matrix; 321 const bool have_matrix = FcPatternGet(font, FC_MATRIX, 0, &matrix) == 0; 322 323 // If we ask for an italic font, fontconfig might take a roman font and set 324 // FC_EMBOLDEN. 325 FcValue embolden; 326 const bool have_embolden = FcPatternGet(font, FC_EMBOLDEN, 0, &embolden) == 0; 327 328 int styleBits = 0; 329 if (resulting_bold > FC_WEIGHT_MEDIUM && !have_embolden) { 330 styleBits |= SkTypeface::kBold; 331 } 332 if (resulting_italic > FC_SLANT_ROMAN && !have_matrix) { 333 styleBits |= SkTypeface::kItalic; 334 } 335 336 return (SkTypeface::Style)styleBits; 337 } 338 339 } // anonymous namespace 340 341 /////////////////////////////////////////////////////////////////////////////// 342 343 #define kMaxFontFamilyLength 2048 344 345 SkFontConfigInterfaceDirect::SkFontConfigInterfaceDirect() { 346 SkAutoMutexAcquire ac(mutex_); 347 348 FcInit(); 349 350 SkDEBUGCODE(fontconfiginterface_unittest();) 351 } 352 353 SkFontConfigInterfaceDirect::~SkFontConfigInterfaceDirect() { 354 } 355 356 bool SkFontConfigInterfaceDirect::isAccessible(const char* filename) { 357 if (access(filename, R_OK) != 0) { 358 return false; 359 } 360 return true; 361 } 362 363 bool SkFontConfigInterfaceDirect::isValidPattern(FcPattern* pattern) { 364 #ifdef SK_FONT_CONFIG_ONLY_ALLOW_SCALABLE_FONTS 365 FcBool is_scalable; 366 if (FcPatternGetBool(pattern, FC_SCALABLE, 0, &is_scalable) != FcResultMatch 367 || !is_scalable) { 368 return false; 369 } 370 #endif 371 372 // fontconfig can also return fonts which are unreadable 373 const char* c_filename = get_name(pattern, FC_FILE); 374 if (!c_filename) { 375 return false; 376 } 377 return this->isAccessible(c_filename); 378 } 379 380 // Find matching font from |font_set| for the given font family. 381 FcPattern* SkFontConfigInterfaceDirect::MatchFont(FcFontSet* font_set, 382 const char* post_config_family, 383 const SkString& family) { 384 // Older versions of fontconfig have a bug where they cannot select 385 // only scalable fonts so we have to manually filter the results. 386 FcPattern* match = nullptr; 387 for (int i = 0; i < font_set->nfont; ++i) { 388 FcPattern* current = font_set->fonts[i]; 389 if (this->isValidPattern(current)) { 390 match = current; 391 break; 392 } 393 } 394 395 if (match && !IsFallbackFontAllowed(family)) { 396 bool acceptable_substitute = false; 397 for (int id = 0; id < 255; ++id) { 398 const char* post_match_family = get_name(match, FC_FAMILY, id); 399 if (!post_match_family) 400 break; 401 acceptable_substitute = 402 (strcasecmp(post_config_family, post_match_family) == 0 || 403 // Workaround for Issue 12530: 404 // requested family: "Bitstream Vera Sans" 405 // post_config_family: "Arial" 406 // post_match_family: "Bitstream Vera Sans" 407 // -> We should treat this case as a good match. 408 strcasecmp(family.c_str(), post_match_family) == 0) || 409 IsMetricCompatibleReplacement(family.c_str(), post_match_family); 410 if (acceptable_substitute) 411 break; 412 } 413 if (!acceptable_substitute) 414 return nullptr; 415 } 416 417 return match; 418 } 419 420 bool SkFontConfigInterfaceDirect::matchFamilyName(const char familyName[], 421 SkTypeface::Style style, 422 FontIdentity* outIdentity, 423 SkString* outFamilyName, 424 SkTypeface::Style* outStyle) { 425 SkString familyStr(familyName ? familyName : ""); 426 if (familyStr.size() > kMaxFontFamilyLength) { 427 return false; 428 } 429 430 SkAutoMutexAcquire ac(mutex_); 431 432 FcPattern* pattern = FcPatternCreate(); 433 434 if (familyName) { 435 FcPatternAddString(pattern, FC_FAMILY, (FcChar8*)familyName); 436 } 437 FcPatternAddInteger(pattern, FC_WEIGHT, 438 (style & SkTypeface::kBold) ? FC_WEIGHT_BOLD 439 : FC_WEIGHT_NORMAL); 440 FcPatternAddInteger(pattern, FC_SLANT, 441 (style & SkTypeface::kItalic) ? FC_SLANT_ITALIC 442 : FC_SLANT_ROMAN); 443 FcPatternAddBool(pattern, FC_SCALABLE, FcTrue); 444 445 FcConfigSubstitute(nullptr, pattern, FcMatchPattern); 446 FcDefaultSubstitute(pattern); 447 448 // Font matching: 449 // CSS often specifies a fallback list of families: 450 // font-family: a, b, c, serif; 451 // However, fontconfig will always do its best to find *a* font when asked 452 // for something so we need a way to tell if the match which it has found is 453 // "good enough" for us. Otherwise, we can return nullptr which gets piped up 454 // and lets WebKit know to try the next CSS family name. However, fontconfig 455 // configs allow substitutions (mapping "Arial -> Helvetica" etc) and we 456 // wish to support that. 457 // 458 // Thus, if a specific family is requested we set @family_requested. Then we 459 // record two strings: the family name after config processing and the 460 // family name after resolving. If the two are equal, it's a good match. 461 // 462 // So consider the case where a user has mapped Arial to Helvetica in their 463 // config. 464 // requested family: "Arial" 465 // post_config_family: "Helvetica" 466 // post_match_family: "Helvetica" 467 // -> good match 468 // 469 // and for a missing font: 470 // requested family: "Monaco" 471 // post_config_family: "Monaco" 472 // post_match_family: "Times New Roman" 473 // -> BAD match 474 // 475 // However, we special-case fallback fonts; see IsFallbackFontAllowed(). 476 477 const char* post_config_family = get_name(pattern, FC_FAMILY); 478 if (!post_config_family) { 479 // we can just continue with an empty name, e.g. default font 480 post_config_family = ""; 481 } 482 483 FcResult result; 484 FcFontSet* font_set = FcFontSort(0, pattern, 0, 0, &result); 485 if (!font_set) { 486 FcPatternDestroy(pattern); 487 return false; 488 } 489 490 FcPattern* match = this->MatchFont(font_set, post_config_family, familyStr); 491 if (!match) { 492 FcPatternDestroy(pattern); 493 FcFontSetDestroy(font_set); 494 return false; 495 } 496 497 FcPatternDestroy(pattern); 498 499 // From here out we just extract our results from 'match' 500 501 post_config_family = get_name(match, FC_FAMILY); 502 if (!post_config_family) { 503 FcFontSetDestroy(font_set); 504 return false; 505 } 506 507 const char* c_filename = get_name(match, FC_FILE); 508 if (!c_filename) { 509 FcFontSetDestroy(font_set); 510 return false; 511 } 512 513 int face_index; 514 if (FcPatternGetInteger(match, FC_INDEX, 0, &face_index) != FcResultMatch) { 515 FcFontSetDestroy(font_set); 516 return false; 517 } 518 519 FcFontSetDestroy(font_set); 520 521 if (outIdentity) { 522 outIdentity->fTTCIndex = face_index; 523 outIdentity->fString.set(c_filename); 524 } 525 if (outFamilyName) { 526 outFamilyName->set(post_config_family); 527 } 528 if (outStyle) { 529 *outStyle = GetFontStyle(match); 530 } 531 return true; 532 } 533 534 SkStreamAsset* SkFontConfigInterfaceDirect::openStream(const FontIdentity& identity) { 535 return SkStream::NewFromFile(identity.fString.c_str()); 536 } 537 538 /////////////////////////////////////////////////////////////////////////////// 539 540 static bool find_name(const SkTDArray<const char*>& list, const char* str) { 541 int count = list.count(); 542 for (int i = 0; i < count; ++i) { 543 if (!strcmp(list[i], str)) { 544 return true; 545 } 546 } 547 return false; 548 } 549 550 SkDataTable* SkFontConfigInterfaceDirect::getFamilyNames() { 551 SkAutoMutexAcquire ac(mutex_); 552 553 FcPattern* pat = FcPatternCreate(); 554 SkAutoTCallVProc<FcPattern, FcPatternDestroy> autoDestroyPat(pat); 555 if (nullptr == pat) { 556 return nullptr; 557 } 558 559 FcObjectSet* os = FcObjectSetBuild(FC_FAMILY, (char *)0); 560 SkAutoTCallVProc<FcObjectSet, FcObjectSetDestroy> autoDestroyOs(os); 561 if (nullptr == os) { 562 return nullptr; 563 } 564 565 FcFontSet* fs = FcFontList(nullptr, pat, os); 566 SkAutoTCallVProc<FcFontSet, FcFontSetDestroy> autoDestroyFs(fs); 567 if (nullptr == fs) { 568 return nullptr; 569 } 570 571 SkTDArray<const char*> names; 572 SkTDArray<size_t> sizes; 573 for (int i = 0; i < fs->nfont; ++i) { 574 FcPattern* match = fs->fonts[i]; 575 const char* famName = get_name(match, FC_FAMILY); 576 if (famName && !find_name(names, famName)) { 577 *names.append() = famName; 578 *sizes.append() = strlen(famName) + 1; 579 } 580 } 581 582 return SkDataTable::NewCopyArrays((const void*const*)names.begin(), 583 sizes.begin(), names.count()); 584 } 585