CoreTextFontList.cpp (67611B)
1 /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- 2 * This Source Code Form is subject to the terms of the Mozilla Public 3 * License, v. 2.0. If a copy of the MPL was not distributed with this 4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 5 6 #include "AppleUtils.h" 7 #include "CoreTextFontList.h" 8 #include "gfxFontConstants.h" 9 #include "gfxMacFont.h" 10 #include "gfxUserFontSet.h" 11 12 #include "harfbuzz/hb.h" 13 14 #include "MainThreadUtils.h" 15 16 #include "mozilla/dom/ContentChild.h" 17 #include "mozilla/dom/ContentParent.h" 18 #include "mozilla/gfx/2D.h" 19 #include "mozilla/Logging.h" 20 #include "mozilla/Preferences.h" 21 #include "mozilla/ProfilerLabels.h" 22 #include "mozilla/Sprintf.h" 23 #include "mozilla/StaticPrefs_gfx.h" 24 #include "mozilla/glean/GfxMetrics.h" 25 26 #include "nsAppDirectoryServiceDefs.h" 27 #include "nsCharTraits.h" 28 #include "nsComponentManagerUtils.h" 29 #include "nsDirectoryServiceDefs.h" 30 #include "nsDirectoryServiceUtils.h" 31 #include "nsIDirectoryEnumerator.h" 32 #include "nsServiceManagerUtils.h" 33 #include "SharedFontList-impl.h" 34 35 using namespace mozilla; 36 using namespace mozilla::gfx; 37 38 #ifdef MOZ_WIDGET_COCOA 39 // Building with newer macOS SDKs can cause a bunch of font-family names to be 40 // hidden from the Core Text API we use to enumerate available fonts. Because 41 // some content still benefits from having these names recognized, we forcibly 42 // include them in the list. Some day we might want to drop support for these. 43 # define USE_DEPRECATED_FONT_FAMILY_NAMES 1 44 #endif 45 46 #if USE_DEPRECATED_FONT_FAMILY_NAMES 47 // List generated by diffing the arrays returned by 48 // CTFontManagerCopyAvailableFontFamilyNames() when built with 49 // MACOSX_DEPLOYMENT_TARGET=10.12 vs 11.0, to identify the font family names 50 // that Core Text is treating as "deprecated" and hiding from the app on newer 51 // systems. 52 constexpr nsLiteralCString kDeprecatedFontFamilies[] = { 53 // Dot-prefixed font families are supposed to be hidden from the 54 // user-visible 55 // font list anyhow, so we don't need to add them here. 56 // ".Al Bayan PUA"_ns, 57 // ".Al Nile PUA"_ns, 58 // ".Al Tarikh PUA"_ns, 59 // ".Apple Color Emoji UI"_ns, 60 // ".Apple SD Gothic NeoI"_ns, 61 // ".Aqua Kana"_ns, 62 // ".Arial Hebrew Desk Interface"_ns, 63 // ".Baghdad PUA"_ns, 64 // ".Beirut PUA"_ns, 65 // ".Damascus PUA"_ns, 66 // ".DecoType Naskh PUA"_ns, 67 // ".Diwan Kufi PUA"_ns, 68 // ".Farah PUA"_ns, 69 // ".Geeza Pro Interface"_ns, 70 // ".Geeza Pro PUA"_ns, 71 // ".Helvetica LT MM"_ns, 72 // ".Hiragino Kaku Gothic Interface"_ns, 73 // ".Hiragino Sans GB Interface"_ns, 74 // ".Keyboard"_ns, 75 // ".KufiStandardGK PUA"_ns, 76 // ".LastResort"_ns, 77 // ".Lucida Grande UI"_ns, 78 // ".Muna PUA"_ns, 79 // ".Nadeem PUA"_ns, 80 // ".New York"_ns, 81 // ".Noto Nastaliq Urdu UI"_ns, 82 // ".PingFang HK"_ns, 83 // ".PingFang SC"_ns, 84 // ".PingFang TC"_ns, 85 // ".Sana PUA"_ns, 86 // ".Savoye LET CC."_ns, 87 // ".SF Arabic"_ns, 88 // ".SF Compact Rounded"_ns, 89 // ".SF Compact"_ns, 90 // ".SF NS Mono"_ns, 91 // ".SF NS Rounded"_ns, 92 // ".SF NS"_ns, 93 // ".Times LT MM"_ns, 94 "Hiragino Kaku Gothic Pro"_ns, 95 "Hiragino Kaku Gothic ProN"_ns, 96 "Hiragino Kaku Gothic Std"_ns, 97 "Hiragino Kaku Gothic StdN"_ns, 98 "Hiragino Maru Gothic Pro"_ns, 99 "Hiragino Mincho Pro"_ns, 100 "Iowan Old Style"_ns, 101 "Noto Sans Adlam"_ns, 102 "Noto Sans Armenian"_ns, 103 "Noto Sans Avestan"_ns, 104 "Noto Sans Bamum"_ns, 105 "Noto Sans Bassa Vah"_ns, 106 "Noto Sans Batak"_ns, 107 "Noto Sans Bhaiksuki"_ns, 108 "Noto Sans Brahmi"_ns, 109 "Noto Sans Buginese"_ns, 110 "Noto Sans Buhid"_ns, 111 "Noto Sans Carian"_ns, 112 "Noto Sans Caucasian Albanian"_ns, 113 "Noto Sans Chakma"_ns, 114 "Noto Sans Cham"_ns, 115 "Noto Sans Coptic"_ns, 116 "Noto Sans Cuneiform"_ns, 117 "Noto Sans Cypriot"_ns, 118 "Noto Sans Duployan"_ns, 119 "Noto Sans Egyptian Hieroglyphs"_ns, 120 "Noto Sans Elbasan"_ns, 121 "Noto Sans Glagolitic"_ns, 122 "Noto Sans Gothic"_ns, 123 "Noto Sans Gunjala Gondi"_ns, 124 "Noto Sans Hanifi Rohingya"_ns, 125 "Noto Sans Hanunoo"_ns, 126 "Noto Sans Hatran"_ns, 127 "Noto Sans Imperial Aramaic"_ns, 128 "Noto Sans Inscriptional Pahlavi"_ns, 129 "Noto Sans Inscriptional Parthian"_ns, 130 "Noto Sans Javanese"_ns, 131 "Noto Sans Kaithi"_ns, 132 "Noto Sans Kayah Li"_ns, 133 "Noto Sans Kharoshthi"_ns, 134 "Noto Sans Khojki"_ns, 135 "Noto Sans Khudawadi"_ns, 136 "Noto Sans Lepcha"_ns, 137 "Noto Sans Limbu"_ns, 138 "Noto Sans Linear A"_ns, 139 "Noto Sans Linear B"_ns, 140 "Noto Sans Lisu"_ns, 141 "Noto Sans Lycian"_ns, 142 "Noto Sans Lydian"_ns, 143 "Noto Sans Mahajani"_ns, 144 "Noto Sans Mandaic"_ns, 145 "Noto Sans Manichaean"_ns, 146 "Noto Sans Marchen"_ns, 147 "Noto Sans Masaram Gondi"_ns, 148 "Noto Sans Meetei Mayek"_ns, 149 "Noto Sans Mende Kikakui"_ns, 150 "Noto Sans Meroitic"_ns, 151 "Noto Sans Miao"_ns, 152 "Noto Sans Modi"_ns, 153 "Noto Sans Mongolian"_ns, 154 "Noto Sans Mro"_ns, 155 "Noto Sans Multani"_ns, 156 "Noto Sans Nabataean"_ns, 157 "Noto Sans New Tai Lue"_ns, 158 "Noto Sans Newa"_ns, 159 "Noto Sans NKo"_ns, 160 "Noto Sans Ol Chiki"_ns, 161 "Noto Sans Old Hungarian"_ns, 162 "Noto Sans Old Italic"_ns, 163 "Noto Sans Old North Arabian"_ns, 164 "Noto Sans Old Permic"_ns, 165 "Noto Sans Old Persian"_ns, 166 "Noto Sans Old South Arabian"_ns, 167 "Noto Sans Old Turkic"_ns, 168 "Noto Sans Osage"_ns, 169 "Noto Sans Osmanya"_ns, 170 "Noto Sans Pahawh Hmong"_ns, 171 "Noto Sans Palmyrene"_ns, 172 "Noto Sans Pau Cin Hau"_ns, 173 "Noto Sans PhagsPa"_ns, 174 "Noto Sans Phoenician"_ns, 175 "Noto Sans Psalter Pahlavi"_ns, 176 "Noto Sans Rejang"_ns, 177 "Noto Sans Samaritan"_ns, 178 "Noto Sans Saurashtra"_ns, 179 "Noto Sans Sharada"_ns, 180 "Noto Sans Siddham"_ns, 181 "Noto Sans Sora Sompeng"_ns, 182 "Noto Sans Sundanese"_ns, 183 "Noto Sans Syloti Nagri"_ns, 184 "Noto Sans Syriac"_ns, 185 "Noto Sans Tagalog"_ns, 186 "Noto Sans Tagbanwa"_ns, 187 "Noto Sans Tai Le"_ns, 188 "Noto Sans Tai Tham"_ns, 189 "Noto Sans Tai Viet"_ns, 190 "Noto Sans Takri"_ns, 191 "Noto Sans Thaana"_ns, 192 "Noto Sans Tifinagh"_ns, 193 "Noto Sans Tirhuta"_ns, 194 "Noto Sans Ugaritic"_ns, 195 "Noto Sans Vai"_ns, 196 "Noto Sans Wancho"_ns, 197 "Noto Sans Warang Citi"_ns, 198 "Noto Sans Yi"_ns, 199 "Noto Sans Zawgyi"_ns, 200 "Noto Serif Ahom"_ns, 201 "Noto Serif Balinese"_ns, 202 "Noto Serif Yezidi"_ns, 203 "Athelas"_ns, 204 "Courier"_ns, 205 "Marion"_ns, 206 "Seravek"_ns, 207 "Superclarendon"_ns, 208 "Times"_ns, 209 }; 210 #endif // USE_DEPRECATED_FONT_FAMILY_NAMES 211 212 static void GetStringForCFString(CFStringRef aSrc, nsAString& aDest) { 213 auto len = CFStringGetLength(aSrc); 214 aDest.SetLength(len); 215 CFStringGetCharacters(aSrc, CFRangeMake(0, len), 216 (UniChar*)aDest.BeginWriting()); 217 } 218 219 static CFStringRef CreateCFStringForString(const nsACString& aSrc) { 220 return CFStringCreateWithBytes(kCFAllocatorDefault, 221 (const UInt8*)aSrc.BeginReading(), 222 aSrc.Length(), kCFStringEncodingUTF8, false); 223 } 224 225 #define LOG_FONTLIST(args) \ 226 MOZ_LOG(gfxPlatform::GetLog(eGfxLog_fontlist), mozilla::LogLevel::Debug, args) 227 #define LOG_FONTLIST_ENABLED() \ 228 MOZ_LOG_TEST(gfxPlatform::GetLog(eGfxLog_fontlist), mozilla::LogLevel::Debug) 229 #define LOG_CMAPDATA_ENABLED() \ 230 MOZ_LOG_TEST(gfxPlatform::GetLog(eGfxLog_cmapdata), mozilla::LogLevel::Debug) 231 232 #pragma mark - 233 234 // Complex scripts will not render correctly unless appropriate AAT or OT 235 // layout tables are present. 236 // For OpenType, we also check that the GSUB table supports the relevant 237 // script tag, to avoid using things like Arial Unicode MS for Lao (it has 238 // the characters, but lacks OpenType support). 239 240 // TODO: consider whether we should move this to gfxFontEntry and do similar 241 // cmap-masking on other platforms to avoid using fonts that won't shape 242 // properly. 243 244 nsresult CTFontEntry::ReadCMAP(FontInfoData* aFontInfoData) { 245 // attempt this once, if errors occur leave a blank cmap 246 if (mCharacterMap || mShmemCharacterMap) { 247 return NS_OK; 248 } 249 250 RefPtr<gfxCharacterMap> charmap; 251 nsresult rv; 252 253 uint32_t uvsOffset = 0; 254 if (aFontInfoData && 255 (charmap = GetCMAPFromFontInfo(aFontInfoData, uvsOffset))) { 256 rv = NS_OK; 257 } else { 258 uint32_t kCMAP = TRUETYPE_TAG('c', 'm', 'a', 'p'); 259 charmap = new gfxCharacterMap(256); 260 AutoTable cmapTable(this, kCMAP); 261 262 if (cmapTable) { 263 uint32_t cmapLen; 264 const uint8_t* cmapData = reinterpret_cast<const uint8_t*>( 265 hb_blob_get_data(cmapTable, &cmapLen)); 266 rv = gfxFontUtils::ReadCMAP(cmapData, cmapLen, *charmap, uvsOffset); 267 } else { 268 rv = NS_ERROR_NOT_AVAILABLE; 269 } 270 } 271 mUVSOffset.exchange(uvsOffset); 272 273 if (NS_SUCCEEDED(rv) && !mIsDataUserFont && !HasGraphiteTables()) { 274 // For downloadable fonts, trust the author and don't 275 // try to munge the cmap based on script shaping support. 276 277 // We also assume a Graphite font knows what it's doing, 278 // and provides whatever shaping is needed for the 279 // characters it supports, so only check/clear the 280 // complex-script ranges for non-Graphite fonts 281 282 // for layout support, check for the presence of mort/morx/kerx and/or 283 // opentype layout tables 284 bool hasAATLayout = HasFontTable(TRUETYPE_TAG('m', 'o', 'r', 'x')) || 285 HasFontTable(TRUETYPE_TAG('m', 'o', 'r', 't')); 286 bool hasAppleKerning = HasFontTable(TRUETYPE_TAG('k', 'e', 'r', 'x')); 287 bool hasGSUB = HasFontTable(TRUETYPE_TAG('G', 'S', 'U', 'B')); 288 bool hasGPOS = HasFontTable(TRUETYPE_TAG('G', 'P', 'O', 'S')); 289 if ((hasAATLayout && !(hasGSUB || hasGPOS)) || hasAppleKerning) { 290 mRequiresAAT = true; // prefer CoreText if font has no OTL tables, 291 // or if it uses the Apple-specific 'kerx' 292 // variant of kerning table 293 } 294 295 for (const ScriptRange* sr = gfxPlatformFontList::sComplexScriptRanges; 296 sr->rangeStart; sr++) { 297 // check to see if the cmap includes complex script codepoints 298 if (charmap->TestRange(sr->rangeStart, sr->rangeEnd)) { 299 if (hasAATLayout) { 300 // prefer CoreText for Apple's complex-script fonts, 301 // even if they also have some OpenType tables 302 // (e.g. Geeza Pro Bold on 10.6; see bug 614903) 303 mRequiresAAT = true; 304 // and don't mask off complex-script ranges, we assume 305 // the AAT tables will provide the necessary shaping 306 continue; 307 } 308 309 // We check for GSUB here, as GPOS alone would not be ok. 310 if (hasGSUB && SupportsScriptInGSUB(sr->tags, sr->numTags)) { 311 continue; 312 } 313 314 charmap->ClearRange(sr->rangeStart, sr->rangeEnd); 315 } 316 } 317 318 // Bug 1360309, 1393624: several of Apple's Chinese fonts have spurious 319 // blank glyphs for obscure Tibetan and Arabic-script codepoints. 320 // Blocklist these so that font fallback will not use them. 321 if (mRequiresAAT && 322 (FamilyName().EqualsLiteral("Songti SC") || 323 FamilyName().EqualsLiteral("Songti TC") || 324 FamilyName().EqualsLiteral("STSong") || 325 // Bug 1390980: on 10.11, the Kaiti fonts are also affected. 326 FamilyName().EqualsLiteral("Kaiti SC") || 327 FamilyName().EqualsLiteral("Kaiti TC") || 328 FamilyName().EqualsLiteral("STKaiti"))) { 329 charmap->ClearRange(0x0f6b, 0x0f70); 330 charmap->ClearRange(0x0f8c, 0x0f8f); 331 charmap->clear(0x0f98); 332 charmap->clear(0x0fbd); 333 charmap->ClearRange(0x0fcd, 0x0fff); 334 charmap->clear(0x0620); 335 charmap->clear(0x065f); 336 charmap->ClearRange(0x06ee, 0x06ef); 337 charmap->clear(0x06ff); 338 } 339 } 340 341 bool setCharMap = true; 342 if (NS_SUCCEEDED(rv)) { 343 gfxPlatformFontList* pfl = gfxPlatformFontList::PlatformFontList(); 344 fontlist::FontList* sharedFontList = pfl->SharedFontList(); 345 if (!IsUserFont() && mShmemFace && mShmemFamily) { 346 mShmemFace->SetCharacterMap(sharedFontList, charmap, mShmemFamily); 347 if (TrySetShmemCharacterMap()) { 348 setCharMap = false; 349 } 350 } else { 351 charmap = pfl->FindCharMap(charmap); 352 } 353 mHasCmapTable = true; 354 } else { 355 // if error occurred, initialize to null cmap 356 charmap = new gfxCharacterMap(0); 357 mHasCmapTable = false; 358 } 359 if (setCharMap) { 360 // Temporarily retain charmap, until the shared version is 361 // ready for use. 362 if (mCharacterMap.compareExchange(nullptr, charmap.get())) { 363 charmap.get()->AddRef(); 364 } 365 } 366 367 LOG_FONTLIST(("(fontlist-cmap) name: %s, size: %zu hash: %8.8x%s\n", 368 mName.get(), charmap->SizeOfIncludingThis(moz_malloc_size_of), 369 charmap->mHash, mCharacterMap == charmap ? " new" : "")); 370 if (LOG_CMAPDATA_ENABLED()) { 371 char prefix[256]; 372 SprintfLiteral(prefix, "(cmapdata) name: %.220s", mName.get()); 373 charmap->Dump(prefix, eGfxLog_cmapdata); 374 } 375 376 return rv; 377 } 378 379 gfxFont* CTFontEntry::CreateFontInstance(const gfxFontStyle* aFontStyle) { 380 RefPtr<UnscaledFontMac> unscaledFont(mUnscaledFont); 381 if (!unscaledFont) { 382 CGFontRef baseFont = GetFontRef(); 383 if (!baseFont) { 384 return nullptr; 385 } 386 unscaledFont = new UnscaledFontMac(baseFont, mIsDataUserFont); 387 mUnscaledFont = unscaledFont; 388 } 389 390 return new gfxMacFont(unscaledFont, this, aFontStyle); 391 } 392 393 bool CTFontEntry::HasVariations() { 394 if (!mHasVariationsInitialized) { 395 mHasVariationsInitialized = true; 396 mHasVariations = gfxPlatform::HasVariationFontSupport() && 397 HasFontTable(TRUETYPE_TAG('f', 'v', 'a', 'r')); 398 } 399 400 return mHasVariations; 401 } 402 403 void CTFontEntry::GetVariationAxes( 404 nsTArray<gfxFontVariationAxis>& aVariationAxes) { 405 // We could do this by creating a CTFont and calling CTFontCopyVariationAxes, 406 // but it is expensive to instantiate a CTFont for every face just to set up 407 // the axis information. 408 // Instead we use gfxFontUtils to read the font tables directly. 409 gfxFontUtils::GetVariationData(this, &aVariationAxes, nullptr); 410 } 411 412 void CTFontEntry::GetVariationInstances( 413 nsTArray<gfxFontVariationInstance>& aInstances) { 414 // Core Text doesn't offer API for this, so we use gfxFontUtils to read the 415 // font tables directly. 416 gfxFontUtils::GetVariationData(this, nullptr, &aInstances); 417 } 418 419 bool CTFontEntry::IsCFF() { 420 if (!mIsCFFInitialized) { 421 mIsCFFInitialized = true; 422 mIsCFF = HasFontTable(TRUETYPE_TAG('C', 'F', 'F', ' ')); 423 } 424 425 return mIsCFF; 426 } 427 428 CTFontEntry::CTFontEntry(const nsACString& aPostscriptName, WeightRange aWeight, 429 bool aIsStandardFace, double aSizeHint) 430 : gfxFontEntry(aPostscriptName, aIsStandardFace), 431 mFontRef(NULL), 432 mSizeHint(aSizeHint), 433 mFontRefInitialized(false), 434 mRequiresAAT(false), 435 mIsCFF(false), 436 mIsCFFInitialized(false), 437 mHasVariations(false), 438 mHasVariationsInitialized(false), 439 mHasAATSmallCaps(false), 440 mHasAATSmallCapsInitialized(false) { 441 mWeightRange = aWeight; 442 mOpszAxis.mTag = 0; 443 } 444 445 CTFontEntry::CTFontEntry(const nsACString& aPostscriptName, CGFontRef aFontRef, 446 WeightRange aWeight, StretchRange aStretch, 447 SlantStyleRange aStyle, bool aIsDataUserFont, 448 bool aIsLocalUserFont) 449 : gfxFontEntry(aPostscriptName, false), 450 mFontRef(NULL), 451 mSizeHint(0.0), 452 mFontRefInitialized(false), 453 mRequiresAAT(false), 454 mIsCFF(false), 455 mIsCFFInitialized(false), 456 mHasVariations(false), 457 mHasVariationsInitialized(false), 458 mHasAATSmallCaps(false), 459 mHasAATSmallCapsInitialized(false) { 460 mFontRef = aFontRef; 461 mFontRefInitialized = true; 462 CFRetain(mFontRef); 463 464 mWeightRange = aWeight; 465 mStretchRange = aStretch; 466 mFixedPitch = false; // xxx - do we need this for downloaded fonts? 467 mStyleRange = aStyle; 468 mOpszAxis.mTag = 0; 469 470 NS_ASSERTION(!(aIsDataUserFont && aIsLocalUserFont), 471 "userfont is either a data font or a local font"); 472 mIsDataUserFont = aIsDataUserFont; 473 mIsLocalUserFont = aIsLocalUserFont; 474 } 475 476 gfxFontEntry* CTFontEntry::Clone() const { 477 MOZ_ASSERT(!IsUserFont(), "we can only clone installed fonts!"); 478 CTFontEntry* fe = new CTFontEntry(Name(), Weight(), mStandardFace, mSizeHint); 479 fe->mStyleRange = mStyleRange; 480 fe->mStretchRange = mStretchRange; 481 fe->mFixedPitch = mFixedPitch; 482 return fe; 483 } 484 485 CGFontRef CTFontEntry::GetFontRef() { 486 { 487 AutoReadLock lock(mLock); 488 if (mFontRefInitialized) { 489 return mFontRef; 490 } 491 } 492 AutoWriteLock lock(mLock); 493 if (!mFontRefInitialized) { 494 // Cache the CGFontRef, to be released by our destructor. 495 mFontRef = CreateOrCopyFontRef(); 496 mFontRefInitialized = true; 497 } 498 // Return a non-retained reference; caller does not need to release. 499 return mFontRef; 500 } 501 502 CGFontRef CTFontEntry::CreateOrCopyFontRef() { 503 if (mFontRef) { 504 // We have a cached CGFont, just add a reference. Caller must 505 // release, but we'll still own our reference. 506 ::CGFontRetain(mFontRef); 507 return mFontRef; 508 } 509 510 CrashReporter::AutoRecordAnnotation autoFontName( 511 CrashReporter::Annotation::FontName, mName); 512 513 // Create a new CGFont; caller will own the only reference to it. 514 AutoCFTypeRef<CFStringRef> psname(CreateCFStringForString(mName)); 515 if (!psname) { 516 return nullptr; 517 } 518 519 CGFontRef ref = CGFontCreateWithFontName(psname); 520 return ref; // Not saved in mFontRef; caller will own the reference 521 } 522 523 // For a logging build, we wrap the CFDataRef in a FontTableRec so that we can 524 // use the MOZ_COUNT_[CD]TOR macros in it. A release build without logging 525 // does not get this overhead. 526 class FontTableRec { 527 public: 528 explicit FontTableRec(CFDataRef aDataRef) : mDataRef(aDataRef) { 529 MOZ_COUNT_CTOR(FontTableRec); 530 } 531 532 ~FontTableRec() { 533 MOZ_COUNT_DTOR(FontTableRec); 534 CFRelease(mDataRef); 535 } 536 537 private: 538 CFDataRef mDataRef; 539 }; 540 541 /*static*/ void CTFontEntry::DestroyBlobFunc(void* aUserData) { 542 #ifdef NS_BUILD_REFCNT_LOGGING 543 FontTableRec* ftr = static_cast<FontTableRec*>(aUserData); 544 delete ftr; 545 #else 546 CFRelease((CFDataRef)aUserData); 547 #endif 548 } 549 550 hb_blob_t* CTFontEntry::GetFontTable(uint32_t aTag) { 551 mLock.ReadLock(); 552 AutoCFTypeRef<CGFontRef> fontRef(CreateOrCopyFontRef()); 553 mLock.ReadUnlock(); 554 if (!fontRef) { 555 return nullptr; 556 } 557 558 CFDataRef dataRef = ::CGFontCopyTableForTag(fontRef, aTag); 559 if (dataRef) { 560 return hb_blob_create((const char*)CFDataGetBytePtr(dataRef), 561 CFDataGetLength(dataRef), HB_MEMORY_MODE_READONLY, 562 #ifdef NS_BUILD_REFCNT_LOGGING 563 new FontTableRec(dataRef), 564 #else 565 (void*)dataRef, 566 #endif 567 DestroyBlobFunc); 568 } 569 570 return nullptr; 571 } 572 573 bool CTFontEntry::HasFontTable(uint32_t aTableTag) { 574 { 575 // If we've already initialized mAvailableTables, we can return without 576 // needing to take an exclusive lock. 577 AutoReadLock lock(mLock); 578 if (mAvailableTables.Count()) { 579 return mAvailableTables.GetEntry(aTableTag); 580 } 581 } 582 583 AutoWriteLock lock(mLock); 584 if (mAvailableTables.Count() == 0) { 585 AutoCFTypeRef<CGFontRef> fontRef(CreateOrCopyFontRef()); 586 if (!fontRef) { 587 return false; 588 } 589 AutoCFTypeRef<CFArrayRef> tags(::CGFontCopyTableTags(fontRef)); 590 if (!tags) { 591 return false; 592 } 593 int numTags = (int)CFArrayGetCount(tags); 594 for (int t = 0; t < numTags; t++) { 595 uint32_t tag = (uint32_t)(uintptr_t)CFArrayGetValueAtIndex(tags, t); 596 mAvailableTables.PutEntry(tag); 597 } 598 } 599 600 return mAvailableTables.GetEntry(aTableTag); 601 } 602 603 static bool CheckForAATSmallCaps(CFArrayRef aFeatures) { 604 // Walk the array of feature descriptors from the font, and see whether 605 // a small-caps feature setting is available. 606 // Just bail out (returning false) if at any point we fail to find the 607 // expected dictionary keys, etc; if the font has bad data, we don't even 608 // try to search the rest of it. 609 auto numFeatures = CFArrayGetCount(aFeatures); 610 for (auto f = 0; f < numFeatures; ++f) { 611 auto featureDict = (CFDictionaryRef)CFArrayGetValueAtIndex(aFeatures, f); 612 if (!featureDict) { 613 return false; 614 } 615 auto featureNum = (CFNumberRef)CFDictionaryGetValue( 616 featureDict, CFSTR("CTFeatureTypeIdentifier")); 617 if (!featureNum) { 618 return false; 619 } 620 int16_t featureType; 621 if (!CFNumberGetValue(featureNum, kCFNumberSInt16Type, &featureType)) { 622 return false; 623 } 624 if (featureType == kLetterCaseType || featureType == kLowerCaseType) { 625 // Which selector to look for, depending whether we've found the 626 // legacy LetterCase feature or the new LowerCase one. 627 const uint16_t smallCaps = (featureType == kLetterCaseType) 628 ? kSmallCapsSelector 629 : kLowerCaseSmallCapsSelector; 630 auto selectors = (CFArrayRef)CFDictionaryGetValue( 631 featureDict, CFSTR("CTFeatureTypeSelectors")); 632 if (!selectors) { 633 return false; 634 } 635 auto numSelectors = CFArrayGetCount(selectors); 636 for (auto s = 0; s < numSelectors; s++) { 637 auto selectorDict = 638 (CFDictionaryRef)CFArrayGetValueAtIndex(selectors, s); 639 if (!selectorDict) { 640 return false; 641 } 642 auto selectorNum = (CFNumberRef)CFDictionaryGetValue( 643 selectorDict, CFSTR("CTFeatureSelectorIdentifier")); 644 if (!selectorNum) { 645 return false; 646 } 647 int16_t selectorValue; 648 if (!CFNumberGetValue(selectorNum, kCFNumberSInt16Type, 649 &selectorValue)) { 650 return false; 651 } 652 if (selectorValue == smallCaps) { 653 return true; 654 } 655 } 656 } 657 } 658 return false; 659 } 660 661 bool CTFontEntry::SupportsOpenTypeFeature(Script aScript, 662 uint32_t aFeatureTag) { 663 // If we're going to shape with Core Text, we don't support added 664 // OpenType features (aside from any CT applies by default), except 665 // for 'smcp' which we map to an AAT feature selector. 666 if (RequiresAATLayout()) { 667 if (aFeatureTag != HB_TAG('s', 'm', 'c', 'p')) { 668 return false; 669 } 670 if (mHasAATSmallCapsInitialized) { 671 return mHasAATSmallCaps; 672 } 673 mHasAATSmallCapsInitialized = true; 674 CGFontRef cgFont = GetFontRef(); 675 if (!cgFont) { 676 return mHasAATSmallCaps; 677 } 678 679 CrashReporter::AutoRecordAnnotation autoFontName( 680 CrashReporter::Annotation::FontName, FamilyName()); 681 682 AutoCFTypeRef<CTFontRef> ctFont( 683 CTFontCreateWithGraphicsFont(cgFont, 0.0, nullptr, nullptr)); 684 if (ctFont) { 685 AutoCFTypeRef<CFArrayRef> features(CTFontCopyFeatures(ctFont)); 686 if (features) { 687 mHasAATSmallCaps = CheckForAATSmallCaps(features); 688 } 689 } 690 return mHasAATSmallCaps; 691 } 692 return gfxFontEntry::SupportsOpenTypeFeature(aScript, aFeatureTag); 693 } 694 695 void CTFontEntry::AddSizeOfIncludingThis(MallocSizeOf aMallocSizeOf, 696 FontListSizes* aSizes) const { 697 aSizes->mFontListSize += aMallocSizeOf(this); 698 AddSizeOfExcludingThis(aMallocSizeOf, aSizes); 699 } 700 701 static CTFontDescriptorRef CreateDescriptorForFamily( 702 const nsACString& aFamilyName, bool aNormalized) { 703 AutoCFTypeRef<CFStringRef> family(CreateCFStringForString(aFamilyName)); 704 const void* values[] = {family}; 705 const void* keys[] = {kCTFontFamilyNameAttribute}; 706 AutoCFTypeRef<CFDictionaryRef> attributes(CFDictionaryCreate( 707 kCFAllocatorDefault, keys, values, 1, &kCFTypeDictionaryKeyCallBacks, 708 &kCFTypeDictionaryValueCallBacks)); 709 710 // Not AutoCFTypeRef, because we might return it. 711 CTFontDescriptorRef descriptor = 712 CTFontDescriptorCreateWithAttributes(attributes); 713 714 if (aNormalized) { 715 CTFontDescriptorRef normalized = 716 CTFontDescriptorCreateMatchingFontDescriptor(descriptor, nullptr); 717 if (normalized) { 718 CFRelease(descriptor); 719 return normalized; 720 } 721 } 722 723 return descriptor; 724 } 725 726 void CTFontFamily::LocalizedName(nsACString& aLocalizedName) { 727 AutoCFTypeRef<CTFontDescriptorRef> descriptor( 728 CreateDescriptorForFamily(mName, true)); 729 if (descriptor) { 730 AutoCFTypeRef<CFStringRef> name( 731 static_cast<CFStringRef>(CTFontDescriptorCopyLocalizedAttribute( 732 descriptor, kCTFontFamilyNameAttribute, nullptr))); 733 if (name) { 734 nsAutoString localized; 735 GetStringForCFString(name, localized); 736 if (!localized.IsEmpty()) { 737 CopyUTF16toUTF8(localized, aLocalizedName); 738 return; 739 } 740 } 741 } 742 743 // failed to get localized name, just use the canonical one 744 aLocalizedName = mName; 745 } 746 747 // Return the CSS weight value to use for the given face, overriding what 748 // AppKit gives us (used to adjust families with bad weight values, see 749 // bug 931426). 750 // A return value of 0 indicates no override - use the existing weight. 751 static inline int GetWeightOverride(const nsAString& aPSName) { 752 nsAutoCString prefName("font.weight-override."); 753 // The PostScript name is required to be ASCII; if it's not, the font is 754 // broken anyway, so we really don't care that this is lossy. 755 LossyAppendUTF16toASCII(aPSName, prefName); 756 return Preferences::GetInt(prefName.get(), 0); 757 } 758 759 // The Core Text weight trait is documented as 760 // 761 // ...a float value between -1.0 and 1.0 for normalized weight. 762 // The value of 0.0 corresponds to the regular or medium font weight. 763 // 764 // (https://developer.apple.com/documentation/coretext/kctfontweighttrait) 765 // 766 // CSS 'normal' font-weight is defined as 400, so we map 0.0 to this. 767 // The exact mapping to use for other values is not well defined; the table 768 // here is empirically determined by looking at what Core Text returns for 769 // the various system fonts that have a range of weights. 770 static inline int32_t CoreTextWeightToCSSWeight(CGFloat aCTWeight) { 771 using Mapping = std::pair<CGFloat, int32_t>; 772 constexpr Mapping kCoreTextToCSSWeights[] = { 773 // clang-format off 774 {-1.0, 1}, 775 {-0.8, 100}, 776 {-0.6, 200}, 777 {-0.4, 300}, 778 {0.0, 400}, // standard 'regular' weight 779 {0.23, 500}, 780 {0.3, 600}, 781 {0.4, 700}, // standard 'bold' weight 782 {0.56, 800}, 783 {0.62, 900}, // Core Text seems to return 0.62 for faces with both 784 // usWeightClass=800 and 900 in their OS/2 tables! 785 // We use 900 as there are also fonts that return 0.56, 786 // so we want an intermediate value for that. 787 {1.0, 1000}, 788 // clang-format on 789 }; 790 const auto* begin = &kCoreTextToCSSWeights[0]; 791 const auto* end = begin + std::size(kCoreTextToCSSWeights); 792 auto m = std::upper_bound(begin, end, aCTWeight, 793 [](CGFloat aValue, const Mapping& aMapping) { 794 return aValue <= aMapping.first; 795 }); 796 if (m == end) { 797 NS_WARNING("Core Text weight out of range"); 798 return 1000; 799 } 800 if (m->first == aCTWeight || m == begin) { 801 return m->second; 802 } 803 // Interpolate between the preceding and found entries: 804 const auto* prev = m - 1; 805 const auto t = (aCTWeight - prev->first) / (m->first - prev->first); 806 return NS_round(prev->second * (1.0 - t) + m->second * t); 807 } 808 809 // The Core Text width trait is documented as 810 // 811 // ...a float between -1.0 and 1.0. The value of 0.0 corresponds to regular 812 // glyph spacing, and negative values represent condensed glyph spacing 813 // 814 // (https://developer.apple.com/documentation/coretext/kctfontweighttrait) 815 // 816 // CSS 'normal' font-stretch is 100%; 'ultra-expanded' is 200%, and 'ultra- 817 // condensed' is 50%. We map the extremes of the Core Text trait to these 818 // values, and interpolate in between these and normal. 819 static inline FontStretch CoreTextWidthToCSSStretch(CGFloat aCTWidth) { 820 if (aCTWidth >= 0.0) { 821 return FontStretch::FromFloat(100.0 + aCTWidth * 100.0); 822 } 823 return FontStretch::FromFloat(100.0 + aCTWidth * 50.0); 824 } 825 826 void CTFontFamily::AddFace(CTFontDescriptorRef aFace) { 827 AutoCFTypeRef<CFStringRef> psname( 828 (CFStringRef)CTFontDescriptorCopyAttribute(aFace, kCTFontNameAttribute)); 829 AutoCFTypeRef<CFStringRef> facename( 830 (CFStringRef)CTFontDescriptorCopyAttribute(aFace, 831 kCTFontStyleNameAttribute)); 832 833 AutoCFTypeRef<CFDictionaryRef> traitsDict( 834 (CFDictionaryRef)CTFontDescriptorCopyAttribute(aFace, 835 kCTFontTraitsAttribute)); 836 CFNumberRef weight = 837 (CFNumberRef)CFDictionaryGetValue(traitsDict, kCTFontWeightTrait); 838 CFNumberRef width = 839 (CFNumberRef)CFDictionaryGetValue(traitsDict, kCTFontWidthTrait); 840 CFNumberRef symbolicTraits = 841 (CFNumberRef)CFDictionaryGetValue(traitsDict, kCTFontSymbolicTrait); 842 843 bool isStandardFace = false; 844 845 // make a nsString 846 nsAutoString postscriptFontName; 847 GetStringForCFString(psname, postscriptFontName); 848 849 int32_t cssWeight = GetWeightOverride(postscriptFontName); 850 if (cssWeight) { 851 // scale down and clamp, to get a value from 1..9 852 cssWeight = ((cssWeight + 50) / 100); 853 cssWeight = std::clamp(cssWeight, 1, 9); 854 cssWeight *= 100; // scale up to CSS values 855 } else { 856 CGFloat weightValue; 857 CFNumberGetValue(weight, kCFNumberCGFloatType, &weightValue); 858 cssWeight = CoreTextWeightToCSSWeight(weightValue); 859 } 860 861 if (kCFCompareEqualTo == CFStringCompare(facename, CFSTR("Regular"), 0) || 862 kCFCompareEqualTo == CFStringCompare(facename, CFSTR("Bold"), 0) || 863 kCFCompareEqualTo == CFStringCompare(facename, CFSTR("Italic"), 0) || 864 kCFCompareEqualTo == CFStringCompare(facename, CFSTR("Oblique"), 0) || 865 kCFCompareEqualTo == CFStringCompare(facename, CFSTR("Bold Italic"), 0) || 866 kCFCompareEqualTo == 867 CFStringCompare(facename, CFSTR("Bold Oblique"), 0)) { 868 isStandardFace = true; 869 } 870 871 // create a font entry 872 CTFontEntry* fontEntry = new CTFontEntry( 873 NS_ConvertUTF16toUTF8(postscriptFontName), 874 WeightRange(FontWeight::FromInt(cssWeight)), isStandardFace); 875 876 CGFloat widthValue; 877 CFNumberGetValue(width, kCFNumberCGFloatType, &widthValue); 878 fontEntry->mStretchRange = 879 StretchRange(CoreTextWidthToCSSStretch(widthValue)); 880 881 SInt32 traitsValue; 882 CFNumberGetValue(symbolicTraits, kCFNumberSInt32Type, &traitsValue); 883 if (traitsValue & kCTFontItalicTrait) { 884 fontEntry->mStyleRange = SlantStyleRange(FontSlantStyle::ITALIC); 885 } 886 887 if (traitsValue & kCTFontMonoSpaceTrait) { 888 fontEntry->mFixedPitch = true; 889 } 890 891 if (gfxPlatform::HasVariationFontSupport()) { 892 fontEntry->SetupVariationRanges(); 893 } 894 895 if (LOG_FONTLIST_ENABLED()) { 896 nsAutoCString weightString; 897 fontEntry->Weight().ToString(weightString); 898 nsAutoCString stretchString; 899 fontEntry->Stretch().ToString(stretchString); 900 LOG_FONTLIST( 901 ("(fontlist) added (%s) to family (%s)" 902 " with style: %s weight: %s stretch: %s", 903 fontEntry->Name().get(), Name().get(), 904 fontEntry->IsItalic() ? "italic" : "normal", weightString.get(), 905 stretchString.get())); 906 } 907 908 // insert into font entry array of family 909 AddFontEntryLocked(fontEntry); 910 } 911 912 void CTFontFamily::FindStyleVariationsLocked(FontInfoData* aFontInfoData) { 913 if (mHasStyles) { 914 return; 915 } 916 917 AUTO_PROFILER_LABEL_DYNAMIC_NSCSTRING("CTFontFamily::FindStyleVariations", 918 LAYOUT, mName); 919 920 if (mForSystemFont) { 921 MOZ_ASSERT(gfxPlatform::HasVariationFontSupport()); 922 923 auto addToFamily = [&](CTFontRef aFont) MOZ_REQUIRES(mLock) { 924 AutoCFTypeRef<CFStringRef> psName(CTFontCopyPostScriptName(aFont)); 925 nsAutoString nameUTF16; 926 nsAutoCString nameUTF8; 927 GetStringForCFString(psName, nameUTF16); 928 CopyUTF16toUTF8(nameUTF16, nameUTF8); 929 930 auto* fe = 931 new CTFontEntry(nameUTF8, WeightRange(FontWeight::NORMAL), true, 0.0); 932 933 // Set the appropriate style, assuming it may not have a variation range. 934 CTFontSymbolicTraits traits = CTFontGetSymbolicTraits(aFont); 935 fe->mStyleRange = SlantStyleRange((traits & kCTFontTraitItalic) 936 ? FontSlantStyle::ITALIC 937 : FontSlantStyle::NORMAL); 938 939 // Set up weight (and width, if present) ranges. 940 fe->SetupVariationRanges(); 941 AddFontEntryLocked(fe); 942 }; 943 944 addToFamily(mForSystemFont); 945 946 // See if there is a corresponding italic face, and add it to the family. 947 AutoCFTypeRef<CTFontRef> italicFont(CTFontCreateCopyWithSymbolicTraits( 948 mForSystemFont, 0.0, nullptr, kCTFontTraitItalic, kCTFontTraitItalic)); 949 if (italicFont != mForSystemFont) { 950 addToFamily(italicFont); 951 } 952 953 CFRelease(mForSystemFont); 954 mForSystemFont = nullptr; 955 956 SetHasStyles(true); 957 958 return; 959 } 960 961 struct Context { 962 CTFontFamily* family; 963 const void* prevValue = nullptr; 964 }; 965 966 auto addFaceFunc = [](const void* aValue, void* aContext) -> void { 967 Context* context = (Context*)aContext; 968 if (aValue == context->prevValue) { 969 return; 970 } 971 context->prevValue = aValue; 972 CTFontFamily* family = context->family; 973 // Calling family->AddFace requires that family->mLock is held. We know 974 // this will be true because FindStyleVariationsLocked already requires it, 975 // but the thread-safety analysis can't track that through into the lambda 976 // here, so we disable the check to avoid a spurious warning. 977 MOZ_PUSH_IGNORE_THREAD_SAFETY; 978 family->AddFace((CTFontDescriptorRef)aValue); 979 MOZ_POP_THREAD_SAFETY; 980 }; 981 982 AutoCFTypeRef<CTFontDescriptorRef> descriptor( 983 CreateDescriptorForFamily(mName, false)); 984 AutoCFTypeRef<CFArrayRef> faces( 985 CTFontDescriptorCreateMatchingFontDescriptors(descriptor, nullptr)); 986 987 if (faces) { 988 Context context{this}; 989 CFArrayApplyFunction(faces, CFRangeMake(0, CFArrayGetCount(faces)), 990 addFaceFunc, &context); 991 } 992 993 SortAvailableFonts(); 994 SetHasStyles(true); 995 996 if (mIsBadUnderlineFamily) { 997 SetBadUnderlineFonts(); 998 } 999 1000 CheckForSimpleFamily(); 1001 } 1002 1003 /* CoreTextFontList */ 1004 #pragma mark - 1005 1006 CoreTextFontList::CoreTextFontList() 1007 : gfxPlatformFontList(false), mDefaultFont(nullptr) { 1008 #ifdef MOZ_BUNDLED_FONTS 1009 // We activate bundled fonts if the pref is > 0 (on) or < 0 (auto), only an 1010 // explicit value of 0 (off) will disable them. 1011 if (StaticPrefs::gfx_bundled_fonts_activate_AtStartup() != 0) { 1012 auto timerId = glean::fontlist::bundledfonts_activate.Start(); 1013 ActivateBundledFonts(); 1014 glean::fontlist::bundledfonts_activate.StopAndAccumulate( 1015 std::move(timerId)); 1016 } 1017 #endif 1018 1019 // Load the font-list preferences now, so that we don't have to do it from 1020 // Init[Shared]FontListForPlatform, which may be called off-main-thread. 1021 gfxFontUtils::GetPrefsFontList("font.preload-names-list", mPreloadFonts); 1022 } 1023 1024 CoreTextFontList::~CoreTextFontList() { 1025 AutoLock lock(mLock); 1026 1027 if (XRE_IsParentProcess()) { 1028 CFNotificationCenterRemoveObserver( 1029 CFNotificationCenterGetLocalCenter(), this, 1030 kCTFontManagerRegisteredFontsChangedNotification, 0); 1031 } 1032 1033 if (mDefaultFont) { 1034 CFRelease(mDefaultFont); 1035 } 1036 } 1037 1038 void CoreTextFontList::AddFamily(const nsACString& aFamilyName, 1039 FontVisibility aVisibility) { 1040 nsAutoCString key; 1041 ToLowerCase(aFamilyName, key); 1042 1043 RefPtr<gfxFontFamily> familyEntry = 1044 new CTFontFamily(aFamilyName, aVisibility); 1045 mFontFamilies.InsertOrUpdate(key, RefPtr{familyEntry}); 1046 1047 // check the bad underline blocklist 1048 if (mBadUnderlineFamilyNames.ContainsSorted(key)) { 1049 familyEntry->SetBadUnderlineFamily(); 1050 } 1051 } 1052 1053 void CoreTextFontList::AddFamily(CFStringRef aFamily) { 1054 // CTFontManager includes internal family names and LastResort; skip those. 1055 if (!aFamily || 1056 CFStringCompare(aFamily, CFSTR("LastResort"), 1057 kCFCompareCaseInsensitive) == kCFCompareEqualTo || 1058 CFStringCompare(aFamily, CFSTR(".LastResort"), 1059 kCFCompareCaseInsensitive) == kCFCompareEqualTo) { 1060 return; 1061 } 1062 1063 nsAutoString familyName; 1064 GetStringForCFString(aFamily, familyName); 1065 1066 NS_ConvertUTF16toUTF8 nameUtf8(familyName); 1067 AddFamily(nameUtf8, GetVisibilityForFamily(nameUtf8)); 1068 } 1069 1070 /* static */ 1071 void CoreTextFontList::ActivateFontsFromDir( 1072 const nsACString& aDir, nsTHashSet<nsCStringHashKey>* aLoadedFamilies) { 1073 AutoCFTypeRef<CFURLRef> directory(CFURLCreateFromFileSystemRepresentation( 1074 kCFAllocatorDefault, (const UInt8*)nsPromiseFlatCString(aDir).get(), 1075 aDir.Length(), true)); 1076 if (!directory) { 1077 return; 1078 } 1079 AutoCFTypeRef<CFURLEnumeratorRef> enumerator( 1080 CFURLEnumeratorCreateForDirectoryURL(kCFAllocatorDefault, directory, 1081 kCFURLEnumeratorDefaultBehavior, 1082 nullptr)); 1083 if (!enumerator) { 1084 return; 1085 } 1086 AutoCFTypeRef<CFMutableArrayRef> urls( 1087 CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks)); 1088 if (!urls) { 1089 return; 1090 } 1091 1092 CFURLRef url; 1093 CFURLEnumeratorResult result; 1094 do { 1095 result = CFURLEnumeratorGetNextURL(enumerator, &url, nullptr); 1096 if (result != kCFURLEnumeratorSuccess) { 1097 continue; 1098 } 1099 CFArrayAppendValue(urls, url); 1100 1101 if (!aLoadedFamilies) { 1102 continue; 1103 } 1104 AutoCFTypeRef<CFArrayRef> descriptors( 1105 CTFontManagerCreateFontDescriptorsFromURL(url)); 1106 if (!descriptors || !CFArrayGetCount(descriptors)) { 1107 continue; 1108 } 1109 CTFontDescriptorRef desc = 1110 (CTFontDescriptorRef)CFArrayGetValueAtIndex(descriptors, 0); 1111 AutoCFTypeRef<CFStringRef> name((CFStringRef)CTFontDescriptorCopyAttribute( 1112 desc, kCTFontFamilyNameAttribute)); 1113 nsAutoCString key; 1114 key.SetLength((CFStringGetLength(name) + 1) * 3); 1115 if (CFStringGetCString(name, key.BeginWriting(), key.Length(), 1116 kCFStringEncodingUTF8)) { 1117 key.SetLength(strlen(key.get())); 1118 aLoadedFamilies->Insert(key); 1119 } 1120 } while (result != kCFURLEnumeratorEnd); 1121 1122 CTFontManagerRegisterFontURLs(urls, kCTFontManagerScopeProcess, false, 1123 nullptr); 1124 } 1125 1126 void CoreTextFontList::ReadSystemFontList(dom::SystemFontList* aList) 1127 MOZ_NO_THREAD_SAFETY_ANALYSIS { 1128 // Note: We rely on the records for mSystemFontFamilyName (if present) being 1129 // *before* the main font list, so that name is known in the content process 1130 // by the time we add the actual family records to the font list. 1131 aList->entries().AppendElement(FontFamilyListEntry( 1132 mSystemFontFamilyName, FontVisibility::Unknown, kSystemFontFamily)); 1133 1134 // Now collect the list of available families, with visibility attributes. 1135 for (auto f = mFontFamilies.Iter(); !f.Done(); f.Next()) { 1136 auto macFamily = f.Data().get(); 1137 aList->entries().AppendElement(FontFamilyListEntry( 1138 macFamily->Name(), macFamily->Visibility(), kStandardFontFamily)); 1139 } 1140 } 1141 1142 void CoreTextFontList::PreloadNamesList() { 1143 uint32_t numFonts = mPreloadFonts.Length(); 1144 for (uint32_t i = 0; i < numFonts; i++) { 1145 nsAutoCString key; 1146 GenerateFontListKey(mPreloadFonts[i], key); 1147 1148 // only search canonical names! 1149 gfxFontFamily* familyEntry = mFontFamilies.GetWeak(key); 1150 if (familyEntry) { 1151 familyEntry->ReadOtherFamilyNames(this); 1152 } 1153 } 1154 } 1155 1156 nsresult CoreTextFontList::InitFontListForPlatform() { 1157 // The font registration thread was created early in startup, to give the 1158 // system a head start on activating all the supplemental-language fonts. 1159 // Here, we need to wait until it has finished its work. 1160 gfxPlatformMac::WaitForFontRegistration(); 1161 1162 auto timer = glean::fontlist::mac_init_total.Measure(); 1163 1164 InitSystemFontNames(); 1165 1166 if (XRE_IsParentProcess()) { 1167 static bool firstTime = true; 1168 if (firstTime) { 1169 CFNotificationCenterAddObserver( 1170 CFNotificationCenterGetLocalCenter(), this, 1171 RegisteredFontsChangedNotificationCallback, 1172 kCTFontManagerRegisteredFontsChangedNotification, 0, 1173 CFNotificationSuspensionBehaviorDeliverImmediately); 1174 firstTime = false; 1175 } 1176 1177 // We're not a content process, so get the available fonts directly 1178 // from Core Text. 1179 AutoCFTypeRef<CFArrayRef> familyNames( 1180 CTFontManagerCopyAvailableFontFamilyNames()); 1181 for (CFIndex i = 0; i < CFArrayGetCount(familyNames); i++) { 1182 CFStringRef familyName = 1183 (CFStringRef)CFArrayGetValueAtIndex(familyNames, i); 1184 AddFamily(familyName); 1185 } 1186 #if USE_DEPRECATED_FONT_FAMILY_NAMES 1187 for (const auto& name : kDeprecatedFontFamilies) { 1188 if (DeprecatedFamilyIsAvailable(name)) { 1189 AddFamily(name, GetVisibilityForFamily(name)); 1190 } 1191 } 1192 #endif 1193 } else { 1194 // Content process: use font list passed from the chrome process via 1195 // the GetXPCOMProcessAttributes message, because it's much faster than 1196 // querying Core Text again in the child. 1197 auto& fontList = dom::ContentChild::GetSingleton()->SystemFontList(); 1198 for (FontFamilyListEntry& ffe : fontList.entries()) { 1199 switch (ffe.entryType()) { 1200 case kStandardFontFamily: 1201 if (ffe.familyName() == mSystemFontFamilyName) { 1202 continue; 1203 } 1204 AddFamily(ffe.familyName(), ffe.visibility()); 1205 break; 1206 case kSystemFontFamily: 1207 mSystemFontFamilyName = ffe.familyName(); 1208 break; 1209 } 1210 } 1211 fontList.entries().Clear(); 1212 } 1213 1214 InitSingleFaceList(); 1215 1216 // to avoid full search of font name tables, seed the other names table with 1217 // localized names from some of the prefs fonts which are accessed via their 1218 // localized names. changes in the pref fonts will only cause a font lookup 1219 // miss earlier. this is a simple optimization, it's not required for 1220 // correctness 1221 PreloadNamesList(); 1222 1223 // start the delayed cmap loader 1224 GetPrefsAndStartLoader(); 1225 1226 return NS_OK; 1227 } 1228 1229 void CoreTextFontList::InitSharedFontListForPlatform() { 1230 gfxPlatformMac::WaitForFontRegistration(); 1231 1232 InitSystemFontNames(); 1233 1234 if (XRE_IsParentProcess()) { 1235 // Only the parent process listens for OS font-changed notifications; 1236 // after rebuilding its list, it will update the content processes. 1237 static bool firstTime = true; 1238 if (firstTime) { 1239 CFNotificationCenterAddObserver( 1240 CFNotificationCenterGetLocalCenter(), this, 1241 RegisteredFontsChangedNotificationCallback, 1242 kCTFontManagerRegisteredFontsChangedNotification, 0, 1243 CFNotificationSuspensionBehaviorDeliverImmediately); 1244 firstTime = false; 1245 } 1246 1247 AutoCFTypeRef<CFArrayRef> familyNames( 1248 CTFontManagerCopyAvailableFontFamilyNames()); 1249 nsTArray<fontlist::Family::InitData> families; 1250 families.SetCapacity(CFArrayGetCount(familyNames) 1251 #if USE_DEPRECATED_FONT_FAMILY_NAMES 1252 + std::size(kDeprecatedFontFamilies) 1253 #endif 1254 ); 1255 for (CFIndex i = 0; i < CFArrayGetCount(familyNames); ++i) { 1256 nsAutoString name16; 1257 CFStringRef familyName = 1258 (CFStringRef)CFArrayGetValueAtIndex(familyNames, i); 1259 GetStringForCFString(familyName, name16); 1260 NS_ConvertUTF16toUTF8 name(name16); 1261 nsAutoCString key; 1262 GenerateFontListKey(name, key); 1263 families.AppendElement(fontlist::Family::InitData( 1264 key, name, fontlist::Family::kNoIndex, GetVisibilityForFamily(name))); 1265 } 1266 #if USE_DEPRECATED_FONT_FAMILY_NAMES 1267 for (const nsACString& name : kDeprecatedFontFamilies) { 1268 if (DeprecatedFamilyIsAvailable(name)) { 1269 nsAutoCString key; 1270 GenerateFontListKey(name, key); 1271 families.AppendElement( 1272 fontlist::Family::InitData(key, name, fontlist::Family::kNoIndex, 1273 GetVisibilityForFamily(name))); 1274 } 1275 } 1276 #endif 1277 SharedFontList()->SetFamilyNames(families); 1278 InitAliasesForSingleFaceList(); 1279 GetPrefsAndStartLoader(); 1280 } 1281 } 1282 1283 gfxFontFamily* CoreTextFontList::FindSystemFontFamily( 1284 const nsACString& aFamily) { 1285 nsAutoCString key; 1286 GenerateFontListKey(aFamily, key); 1287 1288 gfxFontFamily* familyEntry; 1289 if ((familyEntry = mFontFamilies.GetWeak(key))) { 1290 return CheckFamily(familyEntry); 1291 } 1292 1293 return nullptr; 1294 } 1295 1296 void CoreTextFontList::RegisteredFontsChangedNotificationCallback( 1297 CFNotificationCenterRef center, void* observer, CFStringRef name, 1298 const void* object, CFDictionaryRef userInfo) { 1299 if (!CFEqual(name, kCTFontManagerRegisteredFontsChangedNotification)) { 1300 return; 1301 } 1302 1303 CoreTextFontList* fl = static_cast<CoreTextFontList*>(observer); 1304 if (!fl->IsInitialized()) { 1305 return; 1306 } 1307 1308 // xxx - should be carefully pruning the list of fonts, not rebuilding it from 1309 // scratch 1310 fl->UpdateFontList(); 1311 1312 auto flags = gfxPlatform::GlobalReflowFlags::NeedsReframe | 1313 gfxPlatform::GlobalReflowFlags::FontsChanged; 1314 gfxPlatform::ForceGlobalReflow(flags); 1315 dom::ContentParent::NotifyUpdatedFonts(true); 1316 } 1317 1318 gfxFontEntry* CoreTextFontList::PlatformGlobalFontFallback( 1319 FontVisibilityProvider* aFontVisibilityProvider, const uint32_t aCh, 1320 Script aRunScript, const gfxFontStyle* aMatchStyle, 1321 FontFamily& aMatchedFamily) { 1322 CFStringRef str; 1323 UniChar ch[2]; 1324 CFIndex length = 1; 1325 1326 if (IS_IN_BMP(aCh)) { 1327 ch[0] = aCh; 1328 str = CFStringCreateWithCharactersNoCopy(kCFAllocatorDefault, ch, 1, 1329 kCFAllocatorNull); 1330 } else { 1331 ch[0] = H_SURROGATE(aCh); 1332 ch[1] = L_SURROGATE(aCh); 1333 str = CFStringCreateWithCharactersNoCopy(kCFAllocatorDefault, ch, 2, 1334 kCFAllocatorNull); 1335 length = 2; 1336 } 1337 if (!str) { 1338 return nullptr; 1339 } 1340 1341 // use CoreText to find the fallback family 1342 1343 gfxFontEntry* fontEntry = nullptr; 1344 bool cantUseFallbackFont = false; 1345 1346 if (!mDefaultFont) { 1347 mDefaultFont = CTFontCreateWithName(CFSTR("LucidaGrande"), 12.f, NULL); 1348 } 1349 1350 AutoCFTypeRef<CTFontRef> fallback( 1351 CTFontCreateForString(mDefaultFont, str, CFRangeMake(0, length))); 1352 1353 if (fallback) { 1354 AutoCFTypeRef<CFStringRef> familyNameRef(CTFontCopyFamilyName(fallback)); 1355 1356 if (familyNameRef && 1357 CFStringCompare(familyNameRef, CFSTR("LastResort"), 1358 kCFCompareCaseInsensitive) != kCFCompareEqualTo && 1359 CFStringCompare(familyNameRef, CFSTR(".LastResort"), 1360 kCFCompareCaseInsensitive) != kCFCompareEqualTo) { 1361 AutoTArray<UniChar, 1024> buffer; 1362 CFIndex familyNameLen = CFStringGetLength(familyNameRef); 1363 buffer.SetLength(familyNameLen + 1); 1364 CFStringGetCharacters(familyNameRef, CFRangeMake(0, familyNameLen), 1365 buffer.Elements()); 1366 buffer[familyNameLen] = 0; 1367 NS_ConvertUTF16toUTF8 familyNameString( 1368 reinterpret_cast<char16_t*>(buffer.Elements()), familyNameLen); 1369 1370 if (SharedFontList()) { 1371 fontlist::Family* family = 1372 FindSharedFamily(aFontVisibilityProvider, familyNameString); 1373 if (family) { 1374 fontlist::Face* face = 1375 family->FindFaceForStyle(SharedFontList(), *aMatchStyle); 1376 if (face) { 1377 fontEntry = GetOrCreateFontEntryLocked(face, family); 1378 } 1379 if (fontEntry) { 1380 if (fontEntry->HasCharacter(aCh)) { 1381 aMatchedFamily = FontFamily(family); 1382 } else { 1383 fontEntry = nullptr; 1384 cantUseFallbackFont = true; 1385 } 1386 } 1387 } 1388 } 1389 1390 // The macOS system font does not appear in the shared font list, so if 1391 // we didn't find the fallback font above, we should also check for an 1392 // unshared fontFamily in the system list. 1393 if (!fontEntry) { 1394 gfxFontFamily* family = FindSystemFontFamily(familyNameString); 1395 if (family) { 1396 fontEntry = family->FindFontForStyle(*aMatchStyle); 1397 if (fontEntry) { 1398 if (fontEntry->HasCharacter(aCh)) { 1399 aMatchedFamily = FontFamily(family); 1400 } else { 1401 fontEntry = nullptr; 1402 cantUseFallbackFont = true; 1403 } 1404 } 1405 } 1406 } 1407 } 1408 } 1409 1410 if (cantUseFallbackFont) { 1411 glean::fontlist::bad_fallback_font 1412 .EnumGet(static_cast<glean::fontlist::BadFallbackFontLabel>( 1413 cantUseFallbackFont)) 1414 .Add(); 1415 } 1416 1417 CFRelease(str); 1418 1419 return fontEntry; 1420 } 1421 1422 gfxFontEntry* CoreTextFontList::LookupLocalFont( 1423 FontVisibilityProvider* aFontVisibilityProvider, 1424 const nsACString& aFontName, WeightRange aWeightForEntry, 1425 StretchRange aStretchForEntry, SlantStyleRange aStyleForEntry) { 1426 if (aFontName.IsEmpty() || aFontName[0] == '.') { 1427 return nullptr; 1428 } 1429 1430 AutoLock lock(mLock); 1431 1432 CrashReporter::AutoRecordAnnotation autoFontName( 1433 CrashReporter::Annotation::FontName, aFontName); 1434 1435 AutoCFTypeRef<CFStringRef> faceName(CreateCFStringForString(aFontName)); 1436 if (!faceName) { 1437 return nullptr; 1438 } 1439 1440 // lookup face based on postscript or full name 1441 AutoCFTypeRef<CGFontRef> fontRef(CGFontCreateWithFontName(faceName)); 1442 if (!fontRef) { 1443 return nullptr; 1444 } 1445 1446 // It's possible for CGFontCreateWithFontName to return a font that has been 1447 // deactivated/uninstalled, or a font that is excluded from the font list due 1448 // to CSS font-visibility restriction. So we need to check whether this font 1449 // is allowed to be used. 1450 1451 // CGFontRef doesn't offer a family-name API, so we go via a CTFontRef. 1452 AutoCFTypeRef<CTFontRef> ctFont( 1453 CTFontCreateWithGraphicsFont(fontRef, 0.0, nullptr, nullptr)); 1454 if (!ctFont) { 1455 return nullptr; 1456 } 1457 AutoCFTypeRef<CFStringRef> name(CTFontCopyFamilyName(ctFont)); 1458 // Convert the family name to a key suitable for font-list lookup (8-bit, 1459 // lowercased). 1460 nsAutoCString key; 1461 if (name) { 1462 // CFStringGetLength is in UTF-16 code units. The maximum this count can 1463 // expand when converted to UTF-8 is 3x. We add 1 to ensure there will also 1464 // be space for null-termination of the resulting C string. 1465 key.SetLength((CFStringGetLength(name) + 1) * 3); 1466 } 1467 if (!name || !CFStringGetCString(name, key.BeginWriting(), key.Length(), 1468 kCFStringEncodingUTF8)) { 1469 // This shouldn't ever happen, but if it does we just bail. 1470 NS_WARNING("Failed to get family name?"); 1471 key.Truncate(0); 1472 } 1473 if (key.IsEmpty()) { 1474 return nullptr; 1475 } 1476 // Reset our string length to match the actual C string we got, which will 1477 // usually be much shorter than the maximal buffer we allocated. 1478 key.Truncate(strlen(key.get())); 1479 ToLowerCase(key); 1480 // If the family can't be looked up, this font is not available for use. 1481 FontFamily family = FindFamily(aFontVisibilityProvider, key); 1482 if (family.IsNull()) { 1483 return nullptr; 1484 } 1485 1486 return new CTFontEntry(aFontName, fontRef, aWeightForEntry, aStretchForEntry, 1487 aStyleForEntry, false, true); 1488 } 1489 1490 static void ReleaseData(void* info, const void* data, size_t size) { 1491 free((void*)data); 1492 } 1493 1494 MOZ_DEFINE_MALLOC_SIZE_OF_ON_ALLOC(UserFontMallocSizeOfOnAlloc) 1495 1496 gfxFontEntry* CoreTextFontList::MakePlatformFont(const nsACString& aFontName, 1497 WeightRange aWeightForEntry, 1498 StretchRange aStretchForEntry, 1499 SlantStyleRange aStyleForEntry, 1500 const uint8_t* aFontData, 1501 uint32_t aLength) { 1502 NS_ASSERTION(aFontData, "MakePlatformFont called with null data"); 1503 1504 // create the font entry 1505 nsAutoString uniqueName; 1506 1507 nsresult rv = gfxFontUtils::MakeUniqueUserFontName(uniqueName); 1508 if (NS_FAILED(rv)) { 1509 return nullptr; 1510 } 1511 1512 CrashReporter::AutoRecordAnnotation autoFontName( 1513 CrashReporter::Annotation::FontName, aFontName); 1514 1515 AutoCFTypeRef<CGDataProviderRef> provider(::CGDataProviderCreateWithData( 1516 nullptr, aFontData, aLength, &ReleaseData)); 1517 AutoCFTypeRef<CGFontRef> fontRef(::CGFontCreateWithDataProvider(provider)); 1518 if (!fontRef) { 1519 return nullptr; 1520 } 1521 1522 auto newFontEntry = MakeUnique<CTFontEntry>( 1523 NS_ConvertUTF16toUTF8(uniqueName), fontRef, aWeightForEntry, 1524 aStretchForEntry, aStyleForEntry, true, false); 1525 1526 // Record size for memory reporting purposes. 1527 // The *OnAlloc function will also tell DMD about this block, as the 1528 // OS font code may hold on to it for an extended period. 1529 newFontEntry->mComputedSizeOfUserFont = 1530 UserFontMallocSizeOfOnAlloc(aFontData); 1531 1532 return newFontEntry.release(); 1533 } 1534 1535 // Webkit code uses a system font meta name, so mimic that here 1536 // WebCore/platform/graphics/mac/FontCacheMac.mm 1537 static const char kSystemFont_system[] = "-apple-system"; 1538 1539 bool CoreTextFontList::FindAndAddFamiliesLocked( 1540 FontVisibilityProvider* aFontVisibilityProvider, 1541 StyleGenericFontFamily aGeneric, const nsACString& aFamily, 1542 nsTArray<FamilyAndGeneric>* aOutput, FindFamiliesFlags aFlags, 1543 gfxFontStyle* aStyle, nsAtom* aLanguage, gfxFloat aDevToCssSize) { 1544 if (aFamily.EqualsLiteral(kSystemFont_system)) { 1545 // Search for special system font name, -apple-system. This is not done via 1546 // the shared fontlist because the hidden system font may not be included 1547 // there; we create a separate gfxFontFamily to manage this family. 1548 if (auto* fam = FindSystemFontFamily(mSystemFontFamilyName)) { 1549 aOutput->AppendElement(fam); 1550 return true; 1551 } 1552 return false; 1553 } 1554 1555 return gfxPlatformFontList::FindAndAddFamiliesLocked( 1556 aFontVisibilityProvider, aGeneric, aFamily, aOutput, aFlags, aStyle, 1557 aLanguage, aDevToCssSize); 1558 } 1559 1560 // used to load system-wide font info on off-main thread 1561 class CTFontInfo final : public FontInfoData { 1562 public: 1563 CTFontInfo(bool aLoadOtherNames, bool aLoadFaceNames, bool aLoadCmaps, 1564 RecursiveMutex& aLock) 1565 : FontInfoData(aLoadOtherNames, aLoadFaceNames, aLoadCmaps), 1566 mLock(aLock) {} 1567 1568 virtual ~CTFontInfo() = default; 1569 1570 virtual void Load() { FontInfoData::Load(); } 1571 1572 // loads font data for all members of a given family 1573 virtual void LoadFontFamilyData(const nsACString& aFamilyName); 1574 1575 RecursiveMutex& mLock; 1576 }; 1577 1578 void CTFontInfo::LoadFontFamilyData(const nsACString& aFamilyName) { 1579 CrashReporter::AutoRecordAnnotation autoFontName( 1580 CrashReporter::Annotation::FontName, aFamilyName); 1581 // Prevent this from running concurrently with CGFont operations on the main 1582 // thread, because the macOS font cache is fragile with concurrent access. 1583 // This appears to be a vulnerability within CoreText in versions of macOS 1584 // before macOS 13. In time, we can remove this lock. 1585 RecursiveMutexAutoLock lock(mLock); 1586 1587 // family name ==> CTFontDescriptor 1588 AutoCFTypeRef<CFStringRef> family(CreateCFStringForString(aFamilyName)); 1589 1590 AutoCFTypeRef<CFMutableDictionaryRef> attr( 1591 CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, 1592 &kCFTypeDictionaryValueCallBacks)); 1593 CFDictionaryAddValue(attr, kCTFontFamilyNameAttribute, family); 1594 AutoCFTypeRef<CTFontDescriptorRef> fd( 1595 CTFontDescriptorCreateWithAttributes(attr)); 1596 AutoCFTypeRef<CFArrayRef> matchingFonts( 1597 CTFontDescriptorCreateMatchingFontDescriptors(fd, NULL)); 1598 if (!matchingFonts) { 1599 return; 1600 } 1601 1602 nsTArray<nsCString> otherFamilyNames; 1603 bool hasOtherFamilyNames = true; 1604 1605 // iterate over faces in the family 1606 int f, numFaces = (int)CFArrayGetCount(matchingFonts); 1607 CTFontDescriptorRef prevFace = nullptr; 1608 for (f = 0; f < numFaces; f++) { 1609 mLoadStats.fonts++; 1610 1611 CTFontDescriptorRef faceDesc = 1612 (CTFontDescriptorRef)CFArrayGetValueAtIndex(matchingFonts, f); 1613 if (!faceDesc) { 1614 continue; 1615 } 1616 1617 if (faceDesc == prevFace) { 1618 continue; 1619 } 1620 prevFace = faceDesc; 1621 1622 AutoCFTypeRef<CTFontRef> fontRef( 1623 CTFontCreateWithFontDescriptor(faceDesc, 0.0, nullptr)); 1624 if (!fontRef) { 1625 NS_WARNING("failed to create a CTFontRef"); 1626 continue; 1627 } 1628 1629 if (mLoadCmaps) { 1630 // face name 1631 AutoCFTypeRef<CFStringRef> faceName( 1632 (CFStringRef)CTFontDescriptorCopyAttribute(faceDesc, 1633 kCTFontNameAttribute)); 1634 1635 AutoTArray<UniChar, 1024> buffer; 1636 CFIndex len = CFStringGetLength(faceName); 1637 buffer.SetLength(len + 1); 1638 CFStringGetCharacters(faceName, CFRangeMake(0, len), buffer.Elements()); 1639 buffer[len] = 0; 1640 NS_ConvertUTF16toUTF8 fontName( 1641 reinterpret_cast<char16_t*>(buffer.Elements()), len); 1642 1643 // load the cmap data 1644 FontFaceData fontData; 1645 AutoCFTypeRef<CFDataRef> cmapTable(CTFontCopyTable( 1646 fontRef, kCTFontTableCmap, kCTFontTableOptionNoOptions)); 1647 1648 if (cmapTable) { 1649 const uint8_t* cmapData = (const uint8_t*)CFDataGetBytePtr(cmapTable); 1650 uint32_t cmapLen = CFDataGetLength(cmapTable); 1651 RefPtr<gfxCharacterMap> charmap = new gfxCharacterMap(256); 1652 uint32_t offset; 1653 nsresult rv; 1654 1655 rv = gfxFontUtils::ReadCMAP(cmapData, cmapLen, *charmap, offset); 1656 if (NS_SUCCEEDED(rv)) { 1657 fontData.mCharacterMap = charmap; 1658 fontData.mUVSOffset = offset; 1659 mLoadStats.cmaps++; 1660 } 1661 } 1662 1663 mFontFaceData.InsertOrUpdate(fontName, fontData); 1664 } 1665 1666 if (mLoadOtherNames && hasOtherFamilyNames) { 1667 AutoCFTypeRef<CFDataRef> nameTable(CTFontCopyTable( 1668 fontRef, kCTFontTableName, kCTFontTableOptionNoOptions)); 1669 1670 if (nameTable) { 1671 const char* nameData = (const char*)CFDataGetBytePtr(nameTable); 1672 uint32_t nameLen = CFDataGetLength(nameTable); 1673 gfxFontUtils::ReadOtherFamilyNamesForFace( 1674 aFamilyName, nameData, nameLen, otherFamilyNames, false); 1675 hasOtherFamilyNames = otherFamilyNames.Length() != 0; 1676 } 1677 } 1678 } 1679 1680 // if found other names, insert them in the hash table 1681 if (otherFamilyNames.Length() != 0) { 1682 mOtherFamilyNames.InsertOrUpdate(aFamilyName, otherFamilyNames); 1683 mLoadStats.othernames += otherFamilyNames.Length(); 1684 } 1685 } 1686 1687 already_AddRefed<FontInfoData> CoreTextFontList::CreateFontInfoData() { 1688 bool loadCmaps = !UsesSystemFallback() || 1689 gfxPlatform::GetPlatform()->UseCmapsDuringSystemFallback(); 1690 1691 mLock.AssertCurrentThreadIn(); 1692 RefPtr<CTFontInfo> fi = 1693 new CTFontInfo(true, NeedFullnamePostscriptNames(), loadCmaps, mLock); 1694 return fi.forget(); 1695 } 1696 1697 gfxFontFamily* CoreTextFontList::CreateFontFamily( 1698 const nsACString& aName, FontVisibility aVisibility) const { 1699 return new CTFontFamily(aName, aVisibility); 1700 } 1701 1702 gfxFontEntry* CoreTextFontList::CreateFontEntry( 1703 fontlist::Face* aFace, const fontlist::Family* aFamily) { 1704 CTFontEntry* fe = new CTFontEntry( 1705 aFace->mDescriptor.AsString(SharedFontList()), aFace->mWeight, false, 1706 0.0); // XXX standardFace, sizeHint 1707 fe->InitializeFrom(aFace, aFamily); 1708 return fe; 1709 } 1710 1711 void CoreTextFontList::AddFaceInitData( 1712 CTFontDescriptorRef aFontDesc, nsTArray<fontlist::Face::InitData>& aFaces, 1713 bool aLoadCmaps) { 1714 AutoCFTypeRef<CFStringRef> psname((CFStringRef)CTFontDescriptorCopyAttribute( 1715 aFontDesc, kCTFontNameAttribute)); 1716 AutoCFTypeRef<CFStringRef> facename( 1717 (CFStringRef)CTFontDescriptorCopyAttribute(aFontDesc, 1718 kCTFontStyleNameAttribute)); 1719 AutoCFTypeRef<CFDictionaryRef> traitsDict( 1720 (CFDictionaryRef)CTFontDescriptorCopyAttribute(aFontDesc, 1721 kCTFontTraitsAttribute)); 1722 1723 CFNumberRef weight = 1724 (CFNumberRef)CFDictionaryGetValue(traitsDict, kCTFontWeightTrait); 1725 CFNumberRef width = 1726 (CFNumberRef)CFDictionaryGetValue(traitsDict, kCTFontWidthTrait); 1727 CFNumberRef symbolicTraits = 1728 (CFNumberRef)CFDictionaryGetValue(traitsDict, kCTFontSymbolicTrait); 1729 1730 // make a nsString 1731 nsAutoString postscriptFontName; 1732 GetStringForCFString(psname, postscriptFontName); 1733 1734 int32_t cssWeight = PR_GetCurrentThread() == sInitFontListThread 1735 ? 0 1736 : GetWeightOverride(postscriptFontName); 1737 if (cssWeight) { 1738 // scale down and clamp, to get a value from 1..9 1739 cssWeight = ((cssWeight + 50) / 100); 1740 cssWeight = std::clamp(cssWeight, 1, 9); 1741 cssWeight *= 100; // scale up to CSS values 1742 } else { 1743 CGFloat weightValue; 1744 CFNumberGetValue(weight, kCFNumberCGFloatType, &weightValue); 1745 cssWeight = CoreTextWeightToCSSWeight(weightValue); 1746 } 1747 1748 CGFloat widthValue; 1749 CFNumberGetValue(width, kCFNumberCGFloatType, &widthValue); 1750 StretchRange stretch(CoreTextWidthToCSSStretch(widthValue)); 1751 1752 SlantStyleRange slantStyle(FontSlantStyle::NORMAL); 1753 SInt32 traitsValue; 1754 CFNumberGetValue(symbolicTraits, kCFNumberSInt32Type, &traitsValue); 1755 if (traitsValue & kCTFontItalicTrait) { 1756 slantStyle = SlantStyleRange(FontSlantStyle::ITALIC); 1757 } 1758 1759 bool fixedPitch = traitsValue & kCTFontMonoSpaceTrait; 1760 1761 RefPtr<gfxCharacterMap> charmap; 1762 if (aLoadCmaps) { 1763 AutoCFTypeRef<CGFontRef> font( 1764 CGFontCreateWithFontName(CFStringRef(psname))); 1765 if (font) { 1766 uint32_t kCMAP = TRUETYPE_TAG('c', 'm', 'a', 'p'); 1767 AutoCFTypeRef<CFDataRef> data(CGFontCopyTableForTag(font, kCMAP)); 1768 if (data) { 1769 uint32_t offset; 1770 charmap = new gfxCharacterMap(256); 1771 gfxFontUtils::ReadCMAP(CFDataGetBytePtr(data), CFDataGetLength(data), 1772 *charmap, offset); 1773 } 1774 } 1775 } 1776 1777 // Ensure that a face named "Regular" goes to the front of the list, so it 1778 // will take precedence over other faces with the same style attributes but 1779 // a different name (such as "Outline"). 1780 auto data = fontlist::Face::InitData{ 1781 NS_ConvertUTF16toUTF8(postscriptFontName), 1782 0, 1783 fixedPitch, 1784 WeightRange(FontWeight::FromInt(cssWeight)), 1785 stretch, 1786 slantStyle, 1787 charmap, 1788 }; 1789 if (kCFCompareEqualTo == CFStringCompare(facename, CFSTR("Regular"), 0)) { 1790 aFaces.InsertElementAt(0, std::move(data)); 1791 } else { 1792 aFaces.AppendElement(std::move(data)); 1793 } 1794 } 1795 1796 void CoreTextFontList::GetFacesInitDataForFamily( 1797 const fontlist::Family* aFamily, nsTArray<fontlist::Face::InitData>& aFaces, 1798 bool aLoadCmaps) const { 1799 auto name = aFamily->Key().AsString(SharedFontList()); 1800 CrashReporter::AutoRecordAnnotation autoFontName( 1801 CrashReporter::Annotation::FontName, name); 1802 1803 struct Context { 1804 nsTArray<fontlist::Face::InitData>& mFaces; 1805 bool mLoadCmaps; 1806 const void* prevValue = nullptr; 1807 }; 1808 auto addFaceFunc = [](const void* aValue, void* aContext) -> void { 1809 Context* context = (Context*)aContext; 1810 if (aValue == context->prevValue) { 1811 return; 1812 } 1813 context->prevValue = aValue; 1814 CTFontDescriptorRef fontDesc = (CTFontDescriptorRef)aValue; 1815 CoreTextFontList::AddFaceInitData(fontDesc, context->mFaces, 1816 context->mLoadCmaps); 1817 }; 1818 1819 AutoCFTypeRef<CTFontDescriptorRef> descriptor( 1820 CreateDescriptorForFamily(name, false)); 1821 AutoCFTypeRef<CFArrayRef> faces( 1822 CTFontDescriptorCreateMatchingFontDescriptors(descriptor, nullptr)); 1823 1824 if (faces) { 1825 Context context{aFaces, aLoadCmaps}; 1826 CFArrayApplyFunction(faces, CFRangeMake(0, CFArrayGetCount(faces)), 1827 addFaceFunc, &context); 1828 } 1829 } 1830 1831 void CoreTextFontList::ReadFaceNamesForFamily( 1832 fontlist::Family* aFamily, bool aNeedFullnamePostscriptNames) { 1833 if (!aFamily->IsInitialized()) { 1834 if (!InitializeFamily(aFamily)) { 1835 return; 1836 } 1837 } 1838 const uint32_t kNAME = TRUETYPE_TAG('n', 'a', 'm', 'e'); 1839 fontlist::FontList* list = SharedFontList(); 1840 nsAutoCString canonicalName(aFamily->DisplayName().AsString(list)); 1841 const auto* facePtrs = aFamily->Faces(list); 1842 for (uint32_t i = 0, n = aFamily->NumFaces(); i < n; i++) { 1843 auto* face = facePtrs[i].ToPtr<const fontlist::Face>(list); 1844 if (!face) { 1845 continue; 1846 } 1847 nsAutoCString name(face->mDescriptor.AsString(list)); 1848 // We create a temporary CTFontEntry just to read family names from the 1849 // 'name' table in the font resource. The style attributes here are ignored 1850 // as this entry is not used for font style matching. 1851 // The size hint might be used to select which face is accessed in the case 1852 // of the macOS UI font; see CTFontEntry::GetFontRef(). We pass 16.0 in 1853 // order to get a standard text-size face in this case, although it's 1854 // unlikely to matter for the purpose of just reading family names. 1855 auto fe = MakeUnique<CTFontEntry>(name, WeightRange(FontWeight::NORMAL), 1856 false, 16.0); 1857 if (!fe) { 1858 continue; 1859 } 1860 gfxFontEntry::AutoTable nameTable(fe.get(), kNAME); 1861 if (!nameTable) { 1862 continue; 1863 } 1864 uint32_t dataLength; 1865 const char* nameData = hb_blob_get_data(nameTable, &dataLength); 1866 AutoTArray<nsCString, 4> otherFamilyNames; 1867 gfxFontUtils::ReadOtherFamilyNamesForFace( 1868 canonicalName, nameData, dataLength, otherFamilyNames, false); 1869 for (const auto& alias : otherFamilyNames) { 1870 nsAutoCString key; 1871 GenerateFontListKey(alias, key); 1872 auto aliasData = mAliasTable.GetOrInsertNew(key); 1873 aliasData->InitFromFamily(aFamily, canonicalName); 1874 aliasData->mFaces.AppendElement(facePtrs[i]); 1875 } 1876 } 1877 } 1878 1879 static CFStringRef CopyRealFamilyName(CTFontRef aFont) { 1880 AutoCFTypeRef<CFStringRef> psName(CTFontCopyPostScriptName(aFont)); 1881 AutoCFTypeRef<CGFontRef> cgFont( 1882 CGFontCreateWithFontName(CFStringRef(psName))); 1883 if (!cgFont) { 1884 return CTFontCopyFamilyName(aFont); 1885 } 1886 AutoCFTypeRef<CTFontRef> ctFont( 1887 CTFontCreateWithGraphicsFont(cgFont, 0.0, nullptr, nullptr)); 1888 if (!ctFont) { 1889 return CTFontCopyFamilyName(aFont); 1890 } 1891 return CTFontCopyFamilyName(ctFont); 1892 } 1893 1894 void CoreTextFontList::InitSystemFontNames() { 1895 // text font family 1896 AutoCFTypeRef<CTFontRef> font(CTFontCreateUIFontForLanguage( 1897 kCTFontUIFontSystem, 0.0, nullptr)); // TODO: language 1898 AutoCFTypeRef<CFStringRef> name(CopyRealFamilyName(font)); 1899 1900 nsAutoString familyName; 1901 GetStringForCFString(name, familyName); 1902 CopyUTF16toUTF8(familyName, mSystemFontFamilyName); 1903 1904 // We store an in-process gfxFontFamily for the system font even if using the 1905 // shared fontlist to manage "normal" fonts, because the hidden system fonts 1906 // may be excluded from the font list altogether. This family will be 1907 // populated based on the given NSFont. 1908 RefPtr<gfxFontFamily> fam = new CTFontFamily(mSystemFontFamilyName, font); 1909 if (fam) { 1910 nsAutoCString key; 1911 GenerateFontListKey(mSystemFontFamilyName, key); 1912 mFontFamilies.InsertOrUpdate(key, std::move(fam)); 1913 } 1914 } 1915 1916 FontFamily CoreTextFontList::GetDefaultFontForPlatform( 1917 FontVisibilityProvider* aFontVisibilityProvider, const gfxFontStyle* aStyle, 1918 nsAtom* aLanguage) { 1919 AutoCFTypeRef<CTFontRef> font(CTFontCreateUIFontForLanguage( 1920 kCTFontUIFontUser, 0.0, nullptr)); // TODO: language 1921 AutoCFTypeRef<CFStringRef> name(CTFontCopyFamilyName(font)); 1922 1923 nsAutoString familyName; 1924 GetStringForCFString(name, familyName); 1925 1926 return FindFamily(aFontVisibilityProvider, NS_ConvertUTF16toUTF8(familyName)); 1927 } 1928 1929 #ifdef MOZ_BUNDLED_FONTS 1930 void CoreTextFontList::ActivateBundledFonts() { 1931 nsCOMPtr<nsIFile> localDir; 1932 if (NS_FAILED(NS_GetSpecialDirectory(NS_GRE_DIR, getter_AddRefs(localDir)))) { 1933 return; 1934 } 1935 if (NS_FAILED(localDir->Append(u"fonts"_ns))) { 1936 return; 1937 } 1938 nsAutoCString path; 1939 if (NS_FAILED(localDir->GetNativePath(path))) { 1940 return; 1941 } 1942 ActivateFontsFromDir(path, &mBundledFamilies); 1943 } 1944 #endif