1/* -*- Mode: ObjC; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- 2 * ***** BEGIN LICENSE BLOCK ***** 3 * Version: BSD 4 * 5 * Copyright (C) 2006-2009 Mozilla Corporation. All rights reserved. 6 * 7 * Contributor(s): 8 * Vladimir Vukicevic <vladimir@pobox.com> 9 * Masayuki Nakano <masayuki@d-toybox.com> 10 * John Daggett <jdaggett@mozilla.com> 11 * Jonathan Kew <jfkthame@gmail.com> 12 * 13 * Copyright (C) 2006 Apple Computer, Inc. All rights reserved. 14 * 15 * Redistribution and use in source and binary forms, with or without 16 * modification, are permitted provided that the following conditions 17 * are met: 18 * 19 * 1. Redistributions of source code must retain the above copyright 20 * notice, this list of conditions and the following disclaimer. 21 * 2. Redistributions in binary form must reproduce the above copyright 22 * notice, this list of conditions and the following disclaimer in the 23 * documentation and/or other materials provided with the distribution. 24 * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of 25 * its contributors may be used to endorse or promote products derived 26 * from this software without specific prior written permission. 27 * 28 * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY 29 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 30 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 31 * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY 32 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 33 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 34 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 35 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 36 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 37 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 38 * 39 * ***** END LICENSE BLOCK ***** */ 40 41#include "mozilla/Logging.h" 42 43#include <algorithm> 44 45#import <AppKit/AppKit.h> 46 47#include "gfxFontConstants.h" 48#include "gfxPlatformMac.h" 49#include "gfxMacPlatformFontList.h" 50#include "gfxMacFont.h" 51#include "gfxUserFontSet.h" 52#include "SharedFontList-impl.h" 53 54#include "harfbuzz/hb.h" 55 56#include "AppleUtils.h" 57#include "MainThreadUtils.h" 58#include "nsDirectoryServiceUtils.h" 59#include "nsDirectoryServiceDefs.h" 60#include "nsAppDirectoryServiceDefs.h" 61#include "nsIDirectoryEnumerator.h" 62#include "nsCharTraits.h" 63#include "nsCocoaFeatures.h" 64#include "nsCocoaUtils.h" 65#include "nsComponentManagerUtils.h" 66#include "nsServiceManagerUtils.h" 67#include "nsTArray.h" 68 69#include "mozilla/dom/ContentChild.h" 70#include "mozilla/dom/ContentParent.h" 71#include "mozilla/FontPropertyTypes.h" 72#include "mozilla/MemoryReporting.h" 73#include "mozilla/Preferences.h" 74#include "mozilla/ProfilerLabels.h" 75#include "mozilla/Sprintf.h" 76#include "mozilla/StaticPrefs_gfx.h" 77#include "mozilla/Telemetry.h" 78#include "mozilla/gfx/2D.h" 79 80#include <unistd.h> 81#include <time.h> 82#include <dlfcn.h> 83 84#include "StandardFonts-macos.inc" 85 86using namespace mozilla; 87using namespace mozilla::gfx; 88 89// indexes into the NSArray objects that the Cocoa font manager returns 90// as the available members of a family 91#define INDEX_FONT_POSTSCRIPT_NAME 0 92#define INDEX_FONT_FACE_NAME 1 93#define INDEX_FONT_WEIGHT 2 94#define INDEX_FONT_TRAITS 3 95 96static const int kAppleMaxWeight = 14; 97static const int kAppleExtraLightWeight = 3; 98static const int kAppleUltraLightWeight = 2; 99 100static const int gAppleWeightToCSSWeight[] = { 101 0, 102 1, // 1. 103 1, // 2. W1, ultralight 104 2, // 3. W2, extralight 105 3, // 4. W3, light 106 4, // 5. W4, semilight 107 5, // 6. W5, medium 108 6, // 7. 109 6, // 8. W6, semibold 110 7, // 9. W7, bold 111 8, // 10. W8, extrabold 112 8, // 11. 113 9, // 12. W9, ultrabold 114 9, // 13 115 9 // 14 116}; 117 118// cache Cocoa's "shared font manager" for performance 119static NSFontManager* sFontManager; 120 121static void GetStringForNSString(const NSString* aSrc, nsAString& aDest) { 122 aDest.SetLength([aSrc length]); 123 [aSrc getCharacters:reinterpret_cast<unichar*>(aDest.BeginWriting()) 124 range:NSMakeRange(0, [aSrc length])]; 125} 126 127static NSString* GetNSStringForString(const nsAString& aSrc) { 128 return [NSString stringWithCharacters:reinterpret_cast<const unichar*>(aSrc.BeginReading()) 129 length:aSrc.Length()]; 130} 131 132#define LOG_FONTLIST(args) \ 133 MOZ_LOG(gfxPlatform::GetLog(eGfxLog_fontlist), mozilla::LogLevel::Debug, args) 134#define LOG_FONTLIST_ENABLED() \ 135 MOZ_LOG_TEST(gfxPlatform::GetLog(eGfxLog_fontlist), mozilla::LogLevel::Debug) 136#define LOG_CMAPDATA_ENABLED() \ 137 MOZ_LOG_TEST(gfxPlatform::GetLog(eGfxLog_cmapdata), mozilla::LogLevel::Debug) 138 139#pragma mark - 140 141// Complex scripts will not render correctly unless appropriate AAT or OT 142// layout tables are present. 143// For OpenType, we also check that the GSUB table supports the relevant 144// script tag, to avoid using things like Arial Unicode MS for Lao (it has 145// the characters, but lacks OpenType support). 146 147// TODO: consider whether we should move this to gfxFontEntry and do similar 148// cmap-masking on other platforms to avoid using fonts that won't shape 149// properly. 150 151nsresult MacOSFontEntry::ReadCMAP(FontInfoData* aFontInfoData) { 152 // attempt this once, if errors occur leave a blank cmap 153 if (mCharacterMap || mShmemCharacterMap) { 154 return NS_OK; 155 } 156 157 RefPtr<gfxCharacterMap> charmap; 158 nsresult rv; 159 160 if (aFontInfoData && (charmap = GetCMAPFromFontInfo(aFontInfoData, mUVSOffset))) { 161 rv = NS_OK; 162 } else { 163 uint32_t kCMAP = TRUETYPE_TAG('c', 'm', 'a', 'p'); 164 charmap = new gfxCharacterMap(); 165 AutoTable cmapTable(this, kCMAP); 166 167 if (cmapTable) { 168 uint32_t cmapLen; 169 const uint8_t* cmapData = 170 reinterpret_cast<const uint8_t*>(hb_blob_get_data(cmapTable, &cmapLen)); 171 rv = gfxFontUtils::ReadCMAP(cmapData, cmapLen, *charmap, mUVSOffset); 172 } else { 173 rv = NS_ERROR_NOT_AVAILABLE; 174 } 175 } 176 177 if (NS_SUCCEEDED(rv) && !mIsDataUserFont && !HasGraphiteTables()) { 178 // For downloadable fonts, trust the author and don't 179 // try to munge the cmap based on script shaping support. 180 181 // We also assume a Graphite font knows what it's doing, 182 // and provides whatever shaping is needed for the 183 // characters it supports, so only check/clear the 184 // complex-script ranges for non-Graphite fonts 185 186 // for layout support, check for the presence of mort/morx/kerx and/or 187 // opentype layout tables 188 bool hasAATLayout = HasFontTable(TRUETYPE_TAG('m', 'o', 'r', 'x')) || 189 HasFontTable(TRUETYPE_TAG('m', 'o', 'r', 't')); 190 bool hasAppleKerning = HasFontTable(TRUETYPE_TAG('k', 'e', 'r', 'x')); 191 bool hasGSUB = HasFontTable(TRUETYPE_TAG('G', 'S', 'U', 'B')); 192 bool hasGPOS = HasFontTable(TRUETYPE_TAG('G', 'P', 'O', 'S')); 193 if ((hasAATLayout && !(hasGSUB || hasGPOS)) || hasAppleKerning) { 194 mRequiresAAT = true; // prefer CoreText if font has no OTL tables, 195 // or if it uses the Apple-specific 'kerx' 196 // variant of kerning table 197 } 198 199 for (const ScriptRange* sr = gfxPlatformFontList::sComplexScriptRanges; sr->rangeStart; sr++) { 200 // check to see if the cmap includes complex script codepoints 201 if (charmap->TestRange(sr->rangeStart, sr->rangeEnd)) { 202 if (hasAATLayout) { 203 // prefer CoreText for Apple's complex-script fonts, 204 // even if they also have some OpenType tables 205 // (e.g. Geeza Pro Bold on 10.6; see bug 614903) 206 mRequiresAAT = true; 207 // and don't mask off complex-script ranges, we assume 208 // the AAT tables will provide the necessary shaping 209 continue; 210 } 211 212 // We check for GSUB here, as GPOS alone would not be ok. 213 if (hasGSUB && SupportsScriptInGSUB(sr->tags, sr->numTags)) { 214 continue; 215 } 216 217 charmap->ClearRange(sr->rangeStart, sr->rangeEnd); 218 } 219 } 220 221 // Bug 1360309, 1393624: several of Apple's Chinese fonts have spurious 222 // blank glyphs for obscure Tibetan and Arabic-script codepoints. 223 // Blocklist these so that font fallback will not use them. 224 if (mRequiresAAT && 225 (FamilyName().EqualsLiteral("Songti SC") || FamilyName().EqualsLiteral("Songti TC") || 226 FamilyName().EqualsLiteral("STSong") || 227 // Bug 1390980: on 10.11, the Kaiti fonts are also affected. 228 FamilyName().EqualsLiteral("Kaiti SC") || FamilyName().EqualsLiteral("Kaiti TC") || 229 FamilyName().EqualsLiteral("STKaiti"))) { 230 charmap->ClearRange(0x0f6b, 0x0f70); 231 charmap->ClearRange(0x0f8c, 0x0f8f); 232 charmap->clear(0x0f98); 233 charmap->clear(0x0fbd); 234 charmap->ClearRange(0x0fcd, 0x0fff); 235 charmap->clear(0x0620); 236 charmap->clear(0x065f); 237 charmap->ClearRange(0x06ee, 0x06ef); 238 charmap->clear(0x06ff); 239 } 240 } 241 242 mHasCmapTable = NS_SUCCEEDED(rv); 243 if (mHasCmapTable) { 244 gfxPlatformFontList* pfl = gfxPlatformFontList::PlatformFontList(); 245 fontlist::FontList* sharedFontList = pfl->SharedFontList(); 246 if (!IsUserFont() && mShmemFace) { 247 mShmemFace->SetCharacterMap(sharedFontList, charmap); // async 248 if (!TrySetShmemCharacterMap()) { 249 // Temporarily retain charmap, until the shared version is 250 // ready for use. 251 mCharacterMap = charmap; 252 } 253 } else { 254 mCharacterMap = pfl->FindCharMap(charmap); 255 } 256 } else { 257 // if error occurred, initialize to null cmap 258 mCharacterMap = new gfxCharacterMap(); 259 } 260 261 LOG_FONTLIST(("(fontlist-cmap) name: %s, size: %zu hash: %8.8x%s\n", mName.get(), 262 charmap->SizeOfIncludingThis(moz_malloc_size_of), charmap->mHash, 263 mCharacterMap == charmap ? " new" : "")); 264 if (LOG_CMAPDATA_ENABLED()) { 265 char prefix[256]; 266 SprintfLiteral(prefix, "(cmapdata) name: %.220s", mName.get()); 267 charmap->Dump(prefix, eGfxLog_cmapdata); 268 } 269 270 return rv; 271} 272 273gfxFont* MacOSFontEntry::CreateFontInstance(const gfxFontStyle* aFontStyle) { 274 RefPtr<UnscaledFontMac> unscaledFont(mUnscaledFont); 275 if (!unscaledFont) { 276 CGFontRef baseFont = GetFontRef(); 277 if (!baseFont) { 278 return nullptr; 279 } 280 unscaledFont = new UnscaledFontMac(baseFont, mIsDataUserFont); 281 mUnscaledFont = unscaledFont; 282 } 283 284 return new gfxMacFont(unscaledFont, this, aFontStyle); 285} 286 287bool MacOSFontEntry::HasVariations() { 288 if (!mHasVariationsInitialized) { 289 mHasVariationsInitialized = true; 290 mHasVariations = gfxPlatform::GetPlatform()->HasVariationFontSupport() && 291 HasFontTable(TRUETYPE_TAG('f', 'v', 'a', 'r')); 292 } 293 294 return mHasVariations; 295} 296 297void MacOSFontEntry::GetVariationAxes(nsTArray<gfxFontVariationAxis>& aVariationAxes) { 298 // We could do this by creating a CTFont and calling CTFontCopyVariationAxes, 299 // but it is expensive to instantiate a CTFont for every face just to set up 300 // the axis information. 301 // Instead we use gfxFontUtils to read the font tables directly. 302 gfxFontUtils::GetVariationData(this, &aVariationAxes, nullptr); 303} 304 305void MacOSFontEntry::GetVariationInstances(nsTArray<gfxFontVariationInstance>& aInstances) { 306 // Core Text doesn't offer API for this, so we use gfxFontUtils to read the 307 // font tables directly. 308 gfxFontUtils::GetVariationData(this, nullptr, &aInstances); 309} 310 311bool MacOSFontEntry::IsCFF() { 312 if (!mIsCFFInitialized) { 313 mIsCFFInitialized = true; 314 mIsCFF = HasFontTable(TRUETYPE_TAG('C', 'F', 'F', ' ')); 315 } 316 317 return mIsCFF; 318} 319 320MacOSFontEntry::MacOSFontEntry(const nsACString& aPostscriptName, WeightRange aWeight, 321 bool aIsStandardFace, double aSizeHint) 322 : gfxFontEntry(aPostscriptName, aIsStandardFace), 323 mFontRef(NULL), 324 mSizeHint(aSizeHint), 325 mFontRefInitialized(false), 326 mRequiresAAT(false), 327 mIsCFF(false), 328 mIsCFFInitialized(false), 329 mHasVariations(false), 330 mHasVariationsInitialized(false), 331 mHasAATSmallCaps(false), 332 mHasAATSmallCapsInitialized(false) { 333 mWeightRange = aWeight; 334 mOpszAxis.mTag = 0; 335} 336 337MacOSFontEntry::MacOSFontEntry(const nsACString& aPostscriptName, CGFontRef aFontRef, 338 WeightRange aWeight, StretchRange aStretch, SlantStyleRange aStyle, 339 bool aIsDataUserFont, bool aIsLocalUserFont) 340 : gfxFontEntry(aPostscriptName, false), 341 mFontRef(NULL), 342 mSizeHint(0.0), 343 mFontRefInitialized(false), 344 mRequiresAAT(false), 345 mIsCFF(false), 346 mIsCFFInitialized(false), 347 mHasVariations(false), 348 mHasVariationsInitialized(false), 349 mHasAATSmallCaps(false), 350 mHasAATSmallCapsInitialized(false) { 351 mFontRef = aFontRef; 352 mFontRefInitialized = true; 353 ::CFRetain(mFontRef); 354 355 mWeightRange = aWeight; 356 mStretchRange = aStretch; 357 mFixedPitch = false; // xxx - do we need this for downloaded fonts? 358 mStyleRange = aStyle; 359 mOpszAxis.mTag = 0; 360 361 NS_ASSERTION(!(aIsDataUserFont && aIsLocalUserFont), 362 "userfont is either a data font or a local font"); 363 mIsDataUserFont = aIsDataUserFont; 364 mIsLocalUserFont = aIsLocalUserFont; 365} 366 367gfxFontEntry* MacOSFontEntry::Clone() const { 368 MOZ_ASSERT(!IsUserFont(), "we can only clone installed fonts!"); 369 MacOSFontEntry* fe = new MacOSFontEntry(Name(), Weight(), mStandardFace, mSizeHint); 370 fe->mStyleRange = mStyleRange; 371 fe->mStretchRange = mStretchRange; 372 fe->mFixedPitch = mFixedPitch; 373 return fe; 374} 375 376CGFontRef MacOSFontEntry::GetFontRef() { 377 if (!mFontRefInitialized) { 378 // Cache the CGFontRef, to be released by our destructor. 379 mFontRef = CreateOrCopyFontRef(); 380 mFontRefInitialized = true; 381 } 382 // Return a non-retained reference; caller does not need to release. 383 return mFontRef; 384} 385 386CGFontRef MacOSFontEntry::CreateOrCopyFontRef() { 387 if (mFontRef) { 388 // We have a cached CGFont, just add a reference. Caller must 389 // release, but we'll still own our reference. 390 ::CGFontRetain(mFontRef); 391 return mFontRef; 392 } 393 394 CrashReporter::AutoAnnotateCrashReport autoFontName(CrashReporter::Annotation::FontName, mName); 395 396 // Create a new CGFont; caller will own the only reference to it. 397 NSString* psname = GetNSStringForString(NS_ConvertUTF8toUTF16(mName)); 398 CGFontRef ref = CGFontCreateWithFontName(CFStringRef(psname)); 399 if (!ref) { 400 // This happens on macOS 10.12 for font entry names that start with 401 // .AppleSystemUIFont. For those fonts, we need to go through NSFont 402 // to get the correct CGFontRef. 403 // Both the Text and the Display variant of the display font use 404 // .AppleSystemUIFontSomethingSomething as their member names. 405 // That's why we're carrying along mSizeHint to this place so that 406 // we get the variant that we want for this family. 407 NSFont* font = [NSFont fontWithName:psname size:mSizeHint]; 408 if (font) { 409 ref = CTFontCopyGraphicsFont((CTFontRef)font, nullptr); 410 } 411 } 412 return ref; // Not saved in mFontRef; caller will own the reference 413} 414 415// For a logging build, we wrap the CFDataRef in a FontTableRec so that we can 416// use the MOZ_COUNT_[CD]TOR macros in it. A release build without logging 417// does not get this overhead. 418class FontTableRec { 419 public: 420 explicit FontTableRec(CFDataRef aDataRef) : mDataRef(aDataRef) { MOZ_COUNT_CTOR(FontTableRec); } 421 422 ~FontTableRec() { 423 MOZ_COUNT_DTOR(FontTableRec); 424 ::CFRelease(mDataRef); 425 } 426 427 private: 428 CFDataRef mDataRef; 429}; 430 431/*static*/ void MacOSFontEntry::DestroyBlobFunc(void* aUserData) { 432#ifdef NS_BUILD_REFCNT_LOGGING 433 FontTableRec* ftr = static_cast<FontTableRec*>(aUserData); 434 delete ftr; 435#else 436 ::CFRelease((CFDataRef)aUserData); 437#endif 438} 439 440hb_blob_t* MacOSFontEntry::GetFontTable(uint32_t aTag) { 441 AutoCFRelease<CGFontRef> fontRef = CreateOrCopyFontRef(); 442 if (!fontRef) { 443 return nullptr; 444 } 445 446 CFDataRef dataRef = ::CGFontCopyTableForTag(fontRef, aTag); 447 if (dataRef) { 448 return hb_blob_create((const char*)::CFDataGetBytePtr(dataRef), ::CFDataGetLength(dataRef), 449 HB_MEMORY_MODE_READONLY, 450#ifdef NS_BUILD_REFCNT_LOGGING 451 new FontTableRec(dataRef), 452#else 453 (void*)dataRef, 454#endif 455 DestroyBlobFunc); 456 } 457 458 return nullptr; 459} 460 461bool MacOSFontEntry::HasFontTable(uint32_t aTableTag) { 462 if (mAvailableTables.Count() == 0) { 463 nsAutoreleasePool localPool; 464 465 AutoCFRelease<CGFontRef> fontRef = CreateOrCopyFontRef(); 466 if (!fontRef) { 467 return false; 468 } 469 AutoCFRelease<CFArrayRef> tags = ::CGFontCopyTableTags(fontRef); 470 if (!tags) { 471 return false; 472 } 473 int numTags = (int)::CFArrayGetCount(tags); 474 for (int t = 0; t < numTags; t++) { 475 uint32_t tag = (uint32_t)(uintptr_t)::CFArrayGetValueAtIndex(tags, t); 476 mAvailableTables.PutEntry(tag); 477 } 478 } 479 480 return mAvailableTables.GetEntry(aTableTag); 481} 482 483static bool CheckForAATSmallCaps(CFArrayRef aFeatures) { 484 // Walk the array of feature descriptors from the font, and see whether 485 // a small-caps feature setting is available. 486 // Just bail out (returning false) if at any point we fail to find the 487 // expected dictionary keys, etc; if the font has bad data, we don't even 488 // try to search the rest of it. 489 auto numFeatures = CFArrayGetCount(aFeatures); 490 for (auto f = 0; f < numFeatures; ++f) { 491 auto featureDict = (CFDictionaryRef)CFArrayGetValueAtIndex(aFeatures, f); 492 if (!featureDict) { 493 return false; 494 } 495 auto featureNum = 496 (CFNumberRef)CFDictionaryGetValue(featureDict, CFSTR("CTFeatureTypeIdentifier")); 497 if (!featureNum) { 498 return false; 499 } 500 int16_t featureType; 501 if (!CFNumberGetValue(featureNum, kCFNumberSInt16Type, &featureType)) { 502 return false; 503 } 504 if (featureType == kLetterCaseType || featureType == kLowerCaseType) { 505 // Which selector to look for, depending whether we've found the 506 // legacy LetterCase feature or the new LowerCase one. 507 const uint16_t smallCaps = 508 (featureType == kLetterCaseType) ? kSmallCapsSelector : kLowerCaseSmallCapsSelector; 509 auto selectors = 510 (CFArrayRef)CFDictionaryGetValue(featureDict, CFSTR("CTFeatureTypeSelectors")); 511 if (!selectors) { 512 return false; 513 } 514 auto numSelectors = CFArrayGetCount(selectors); 515 for (auto s = 0; s < numSelectors; s++) { 516 auto selectorDict = (CFDictionaryRef)CFArrayGetValueAtIndex(selectors, s); 517 if (!selectorDict) { 518 return false; 519 } 520 auto selectorNum = 521 (CFNumberRef)CFDictionaryGetValue(selectorDict, CFSTR("CTFeatureSelectorIdentifier")); 522 if (!selectorNum) { 523 return false; 524 } 525 int16_t selectorValue; 526 if (!CFNumberGetValue(selectorNum, kCFNumberSInt16Type, &selectorValue)) { 527 return false; 528 } 529 if (selectorValue == smallCaps) { 530 return true; 531 } 532 } 533 } 534 } 535 return false; 536} 537 538bool MacOSFontEntry::SupportsOpenTypeFeature(Script aScript, uint32_t aFeatureTag) { 539 // If we're going to shape with Core Text, we don't support added 540 // OpenType features (aside from any CT applies by default), except 541 // for 'smcp' which we map to an AAT feature selector. 542 if (RequiresAATLayout()) { 543 if (aFeatureTag != HB_TAG('s', 'm', 'c', 'p')) { 544 return false; 545 } 546 if (mHasAATSmallCapsInitialized) { 547 return mHasAATSmallCaps; 548 } 549 mHasAATSmallCapsInitialized = true; 550 CGFontRef cgFont = GetFontRef(); 551 if (!cgFont) { 552 return mHasAATSmallCaps; 553 } 554 AutoCFRelease<CTFontRef> ctFont = CTFontCreateWithGraphicsFont(cgFont, 0.0, nullptr, nullptr); 555 if (ctFont) { 556 AutoCFRelease<CFArrayRef> features = CTFontCopyFeatures(ctFont); 557 if (features) { 558 mHasAATSmallCaps = CheckForAATSmallCaps(features); 559 } 560 } 561 return mHasAATSmallCaps; 562 } 563 return gfxFontEntry::SupportsOpenTypeFeature(aScript, aFeatureTag); 564} 565 566void MacOSFontEntry::AddSizeOfIncludingThis(MallocSizeOf aMallocSizeOf, 567 FontListSizes* aSizes) const { 568 aSizes->mFontListSize += aMallocSizeOf(this); 569 AddSizeOfExcludingThis(aMallocSizeOf, aSizes); 570} 571 572/* gfxMacFontFamily */ 573#pragma mark - 574 575class gfxMacFontFamily final : public gfxFontFamily { 576 public: 577 gfxMacFontFamily(const nsACString& aName, FontVisibility aVisibility, double aSizeHint) 578 : gfxFontFamily(aName, aVisibility), mSizeHint(aSizeHint) {} 579 580 virtual ~gfxMacFontFamily() = default; 581 582 virtual void LocalizedName(nsACString& aLocalizedName); 583 584 virtual void FindStyleVariations(FontInfoData* aFontInfoData = nullptr); 585 586 protected: 587 double mSizeHint; 588}; 589 590void gfxMacFontFamily::LocalizedName(nsACString& aLocalizedName) { 591 nsAutoreleasePool localPool; 592 593 // It's unsafe to call HasOtherFamilyNames off the main thread because 594 // it entrains FindStyleVariations, which calls GetWeightOverride, which 595 // retrieves prefs. And the pref names can change (via user overrides), 596 // so we can't use StaticPrefs to access them. 597 if (NS_IsMainThread() && !HasOtherFamilyNames()) { 598 aLocalizedName = mName; 599 return; 600 } 601 602 NSString* family = GetNSStringForString(NS_ConvertUTF8toUTF16(mName)); 603 NSString* localized = [sFontManager localizedNameForFamily:family face:nil]; 604 605 if (localized) { 606 nsAutoString locName; 607 GetStringForNSString(localized, locName); 608 CopyUTF16toUTF8(locName, aLocalizedName); 609 return; 610 } 611 612 // failed to get localized name, just use the canonical one 613 aLocalizedName = mName; 614} 615 616// Return the CSS weight value to use for the given face, overriding what 617// AppKit gives us (used to adjust families with bad weight values, see 618// bug 931426). 619// A return value of 0 indicates no override - use the existing weight. 620static inline int GetWeightOverride(const nsAString& aPSName) { 621 nsAutoCString prefName("font.weight-override."); 622 // The PostScript name is required to be ASCII; if it's not, the font is 623 // broken anyway, so we really don't care that this is lossy. 624 LossyAppendUTF16toASCII(aPSName, prefName); 625 return Preferences::GetInt(prefName.get(), 0); 626} 627 628void gfxMacFontFamily::FindStyleVariations(FontInfoData* aFontInfoData) { 629 if (mHasStyles) { 630 return; 631 } 632 633 AUTO_PROFILER_LABEL_DYNAMIC_NSCSTRING("gfxMacFontFamily::FindStyleVariations", LAYOUT, mName); 634 635 nsAutoreleasePool localPool; 636 637 NSString* family = GetNSStringForString(NS_ConvertUTF8toUTF16(mName)); 638 639 // create a font entry for each face 640 NSArray* fontfaces = [sFontManager 641 availableMembersOfFontFamily:family]; // returns an array of [psname, style name, weight, 642 // traits] elements, goofy api 643 int faceCount = [fontfaces count]; 644 int faceIndex; 645 646 for (faceIndex = 0; faceIndex < faceCount; faceIndex++) { 647 NSArray* face = [fontfaces objectAtIndex:faceIndex]; 648 NSString* psname = [face objectAtIndex:INDEX_FONT_POSTSCRIPT_NAME]; 649 int32_t appKitWeight = [[face objectAtIndex:INDEX_FONT_WEIGHT] unsignedIntValue]; 650 uint32_t macTraits = [[face objectAtIndex:INDEX_FONT_TRAITS] unsignedIntValue]; 651 NSString* facename = [face objectAtIndex:INDEX_FONT_FACE_NAME]; 652 bool isStandardFace = false; 653 654 if (appKitWeight == kAppleExtraLightWeight) { 655 // if the facename contains UltraLight, set the weight to the ultralight weight value 656 NSRange range = [facename rangeOfString:@"ultralight" options:NSCaseInsensitiveSearch]; 657 if (range.location != NSNotFound) { 658 appKitWeight = kAppleUltraLightWeight; 659 } 660 } 661 662 // make a nsString 663 nsAutoString postscriptFontName; 664 GetStringForNSString(psname, postscriptFontName); 665 666 int32_t cssWeight = gfxMacPlatformFontList::AppleWeightToCSSWeight(appKitWeight); 667 // If we are on the startup-time InitFontList thread, we skip this as it 668 // wants to retrieve faces for the default font family, but cannot safely 669 // call the Preferences APIs. Fortunately, the default font doesn't actually 670 // depend on a weight override pref. 671 if (!gfxPlatformFontList::IsInitFontListThread()) { 672 int32_t weightOverride = GetWeightOverride(postscriptFontName); 673 if (weightOverride) { 674 // scale down and clamp, to get a value from 1..9 675 cssWeight = ((weightOverride + 50) / 100); 676 cssWeight = std::max(1, std::min(cssWeight, 9)); 677 } 678 } 679 cssWeight *= 100; // scale up to CSS values 680 681 if ([facename isEqualToString:@"Regular"] || [facename isEqualToString:@"Bold"] || 682 [facename isEqualToString:@"Italic"] || [facename isEqualToString:@"Oblique"] || 683 [facename isEqualToString:@"Bold Italic"] || [facename isEqualToString:@"Bold Oblique"]) { 684 isStandardFace = true; 685 } 686 687 // create a font entry 688 MacOSFontEntry* fontEntry = 689 new MacOSFontEntry(NS_ConvertUTF16toUTF8(postscriptFontName), 690 WeightRange(FontWeight(cssWeight)), isStandardFace, mSizeHint); 691 if (!fontEntry) { 692 break; 693 } 694 695 // set additional properties based on the traits reported by Cocoa 696 if (macTraits & (NSCondensedFontMask | NSNarrowFontMask | NSCompressedFontMask)) { 697 fontEntry->mStretchRange = StretchRange(FontStretch::Condensed()); 698 } else if (macTraits & NSExpandedFontMask) { 699 fontEntry->mStretchRange = StretchRange(FontStretch::Expanded()); 700 } 701 // Cocoa fails to set the Italic traits bit for HelveticaLightItalic, 702 // at least (see bug 611855), so check for style name endings as well 703 if ((macTraits & NSItalicFontMask) || [facename hasSuffix:@"Italic"] || 704 [facename hasSuffix:@"Oblique"]) { 705 fontEntry->mStyleRange = SlantStyleRange(FontSlantStyle::Italic()); 706 } 707 if (macTraits & NSFixedPitchFontMask) { 708 fontEntry->mFixedPitch = true; 709 } 710 711 if (gfxPlatform::GetPlatform()->HasVariationFontSupport()) { 712 fontEntry->SetupVariationRanges(); 713 } 714 715 if (LOG_FONTLIST_ENABLED()) { 716 nsAutoCString weightString; 717 fontEntry->Weight().ToString(weightString); 718 nsAutoCString stretchString; 719 fontEntry->Stretch().ToString(stretchString); 720 LOG_FONTLIST(("(fontlist) added (%s) to family (%s)" 721 " with style: %s weight: %s stretch: %s" 722 " (apple-weight: %d macTraits: %8.8x)", 723 fontEntry->Name().get(), Name().get(), 724 fontEntry->IsItalic() ? "italic" : "normal", weightString.get(), 725 stretchString.get(), appKitWeight, macTraits)); 726 } 727 728 // insert into font entry array of family 729 AddFontEntry(fontEntry); 730 } 731 732 SortAvailableFonts(); 733 SetHasStyles(true); 734 735 if (mIsBadUnderlineFamily) { 736 SetBadUnderlineFonts(); 737 } 738 739 CheckForSimpleFamily(); 740} 741 742/* gfxSingleFaceMacFontFamily */ 743#pragma mark - 744 745class gfxSingleFaceMacFontFamily final : public gfxFontFamily { 746 public: 747 gfxSingleFaceMacFontFamily(const nsACString& aName, FontVisibility aVisibility) 748 : gfxFontFamily(aName, aVisibility) { 749 mFaceNamesInitialized = true; // omit from face name lists 750 } 751 752 virtual ~gfxSingleFaceMacFontFamily() = default; 753 754 virtual void LocalizedName(nsACString& aLocalizedName); 755 756 virtual void ReadOtherFamilyNames(gfxPlatformFontList* aPlatformFontList); 757 758 virtual bool IsSingleFaceFamily() const { return true; } 759}; 760 761void gfxSingleFaceMacFontFamily::LocalizedName(nsACString& aLocalizedName) { 762 nsAutoreleasePool localPool; 763 764 if (!HasOtherFamilyNames()) { 765 aLocalizedName = mName; 766 return; 767 } 768 769 gfxFontEntry* fe = mAvailableFonts[0]; 770 NSFont* font = [NSFont fontWithName:GetNSStringForString(NS_ConvertUTF8toUTF16(fe->Name())) 771 size:0.0]; 772 if (font) { 773 NSString* localized = [font displayName]; 774 if (localized) { 775 nsAutoString locName; 776 GetStringForNSString(localized, locName); 777 CopyUTF16toUTF8(locName, aLocalizedName); 778 return; 779 } 780 } 781 782 // failed to get localized name, just use the canonical one 783 aLocalizedName = mName; 784} 785 786void gfxSingleFaceMacFontFamily::ReadOtherFamilyNames(gfxPlatformFontList* aPlatformFontList) { 787 if (mOtherFamilyNamesInitialized) { 788 return; 789 } 790 791 gfxFontEntry* fe = mAvailableFonts[0]; 792 if (!fe) { 793 return; 794 } 795 796 const uint32_t kNAME = TRUETYPE_TAG('n', 'a', 'm', 'e'); 797 798 gfxFontEntry::AutoTable nameTable(fe, kNAME); 799 if (!nameTable) { 800 return; 801 } 802 803 mHasOtherFamilyNames = ReadOtherFamilyNamesForFace(aPlatformFontList, nameTable, true); 804 805 mOtherFamilyNamesInitialized = true; 806} 807 808/* gfxMacPlatformFontList */ 809#pragma mark - 810 811gfxMacPlatformFontList::gfxMacPlatformFontList() 812 : gfxPlatformFontList(false), mDefaultFont(nullptr), mUseSizeSensitiveSystemFont(false) { 813 CheckFamilyList(kBaseFonts); 814 815 // cache this in a static variable so that MacOSFontFamily objects 816 // don't have to repeatedly look it up 817 sFontManager = [NSFontManager sharedFontManager]; 818 819 // Load the font-list preferences now, so that we don't have to do it from 820 // Init[Shared]FontListForPlatform, which may be called off-main-thread. 821 gfxFontUtils::GetPrefsFontList("font.single-face-list", mSingleFaceFonts); 822 gfxFontUtils::GetPrefsFontList("font.preload-names-list", mPreloadFonts); 823} 824 825gfxMacPlatformFontList::~gfxMacPlatformFontList() { 826 if (XRE_IsParentProcess()) { 827 ::CFNotificationCenterRemoveObserver(::CFNotificationCenterGetLocalCenter(), this, 828 kCTFontManagerRegisteredFontsChangedNotification, 0); 829 } 830 831 if (mDefaultFont) { 832 ::CFRelease(mDefaultFont); 833 } 834} 835 836void gfxMacPlatformFontList::AddFamily(const nsACString& aFamilyName, FontVisibility aVisibility) { 837 double sizeHint = 0.0; 838 if (aVisibility == FontVisibility::Hidden && mUseSizeSensitiveSystemFont && 839 mSystemDisplayFontFamilyName.Equals(aFamilyName)) { 840 sizeHint = 128.0; 841 } 842 843 nsAutoCString key; 844 ToLowerCase(aFamilyName, key); 845 846 RefPtr<gfxFontFamily> familyEntry = new gfxMacFontFamily(aFamilyName, aVisibility, sizeHint); 847 mFontFamilies.InsertOrUpdate(key, RefPtr{familyEntry}); 848 849 // check the bad underline blocklist 850 if (mBadUnderlineFamilyNames.ContainsSorted(key)) { 851 familyEntry->SetBadUnderlineFamily(); 852 } 853} 854 855FontVisibility gfxMacPlatformFontList::GetVisibilityForFamily(const nsACString& aName) const { 856 if (aName[0] == '.' || aName.LowerCaseEqualsLiteral("lastresort")) { 857 return FontVisibility::Hidden; 858 } 859 if (FamilyInList(aName, kBaseFonts)) { 860 return FontVisibility::Base; 861 } 862 return FontVisibility::User; 863} 864 865void gfxMacPlatformFontList::AddFamily(CFStringRef aFamily) { 866 NSString* family = (NSString*)aFamily; 867 868 // CTFontManager includes weird internal family names and 869 // LastResort, skip over those 870 if (!family || [family caseInsensitiveCompare:@"LastResort"] == NSOrderedSame || 871 [family caseInsensitiveCompare:@".LastResort"] == NSOrderedSame) { 872 return; 873 } 874 875 nsAutoString familyName; 876 nsCocoaUtils::GetStringForNSString(family, familyName); 877 878 NS_ConvertUTF16toUTF8 nameUtf8(familyName); 879 AddFamily(nameUtf8, GetVisibilityForFamily(nameUtf8)); 880} 881 882void gfxMacPlatformFontList::ReadSystemFontList(dom::SystemFontList* aList) { 883 // Note: We rely on the records for mSystemTextFontFamilyName and 884 // mSystemDisplayFontFamilyName (if present) being *before* the main 885 // font list, so that those names are known in the content process 886 // by the time we add the actual family records to the font list. 887 aList->entries().AppendElement(FontFamilyListEntry( 888 mSystemTextFontFamilyName, FontVisibility::Unknown, kTextSizeSystemFontFamily)); 889 if (mUseSizeSensitiveSystemFont) { 890 aList->entries().AppendElement(FontFamilyListEntry( 891 mSystemDisplayFontFamilyName, FontVisibility::Unknown, kDisplaySizeSystemFontFamily)); 892 } 893 // Now collect the list of available families, with visibility attributes. 894 for (auto f = mFontFamilies.Iter(); !f.Done(); f.Next()) { 895 auto macFamily = f.Data().get(); 896 if (macFamily->IsSingleFaceFamily()) { 897 continue; // skip, this will be recreated separately in the child 898 } 899 aList->entries().AppendElement( 900 FontFamilyListEntry(macFamily->Name(), macFamily->Visibility(), kStandardFontFamily)); 901 } 902} 903 904void gfxMacPlatformFontList::PreloadNamesList() { 905 uint32_t numFonts = mPreloadFonts.Length(); 906 for (uint32_t i = 0; i < numFonts; i++) { 907 nsAutoCString key; 908 GenerateFontListKey(mPreloadFonts[i], key); 909 910 // only search canonical names! 911 gfxFontFamily* familyEntry = mFontFamilies.GetWeak(key); 912 if (familyEntry) { 913 familyEntry->ReadOtherFamilyNames(this); 914 } 915 } 916} 917 918nsresult gfxMacPlatformFontList::InitFontListForPlatform() { 919 nsAutoreleasePool localPool; 920 921 // The font registration thread was created early in startup, to give the 922 // system a head start on activating all the supplemental-language fonts. 923 // Here, we need to wait until it has finished its work. 924 gfxPlatformMac::WaitForFontRegistration(); 925 926 Telemetry::AutoTimer<Telemetry::MAC_INITFONTLIST_TOTAL> timer; 927 928 InitSystemFontNames(); 929 930 if (XRE_IsParentProcess()) { 931 static bool firstTime = true; 932 if (firstTime) { 933 ::CFNotificationCenterAddObserver(::CFNotificationCenterGetLocalCenter(), this, 934 RegisteredFontsChangedNotificationCallback, 935 kCTFontManagerRegisteredFontsChangedNotification, 0, 936 CFNotificationSuspensionBehaviorDeliverImmediately); 937 firstTime = false; 938 } 939 940 // We're not a content process, so get the available fonts directly 941 // from Core Text. 942 AutoCFRelease<CFArrayRef> familyNames = CTFontManagerCopyAvailableFontFamilyNames(); 943 for (NSString* familyName in (NSArray*)(CFArrayRef)familyNames) { 944 AddFamily((CFStringRef)familyName); 945 } 946 } else { 947 // Content process: use font list passed from the chrome process via 948 // the GetXPCOMProcessAttributes message, because it's much faster than 949 // querying Core Text again in the child. 950 auto& fontList = dom::ContentChild::GetSingleton()->SystemFontList(); 951 for (FontFamilyListEntry& ffe : fontList.entries()) { 952 switch (ffe.entryType()) { 953 case kStandardFontFamily: 954 // On Catalina or later, we pre-initialize system font-family entries 955 // in InitSystemFontNames(), so we can just skip them here. 956 if (nsCocoaFeatures::OnCatalinaOrLater() && 957 (ffe.familyName() == mSystemTextFontFamilyName || 958 ffe.familyName() == mSystemDisplayFontFamilyName)) { 959 continue; 960 } 961 AddFamily(ffe.familyName(), ffe.visibility()); 962 break; 963 case kTextSizeSystemFontFamily: 964 mSystemTextFontFamilyName = ffe.familyName(); 965 break; 966 case kDisplaySizeSystemFontFamily: 967 mSystemDisplayFontFamilyName = ffe.familyName(); 968 mUseSizeSensitiveSystemFont = true; 969 break; 970 } 971 } 972 fontList.entries().Clear(); 973 } 974 975 InitSingleFaceList(); 976 977 // to avoid full search of font name tables, seed the other names table with localized names from 978 // some of the prefs fonts which are accessed via their localized names. changes in the pref 979 // fonts will only cause a font lookup miss earlier. this is a simple optimization, it's not 980 // required for correctness 981 PreloadNamesList(); 982 983 // start the delayed cmap loader 984 GetPrefsAndStartLoader(); 985 986 return NS_OK; 987} 988 989void gfxMacPlatformFontList::InitSharedFontListForPlatform() { 990 nsAutoreleasePool localPool; 991 992 gfxPlatformMac::WaitForFontRegistration(); 993 994 InitSystemFontNames(); 995 996 if (XRE_IsParentProcess()) { 997 // Only the parent process listens for OS font-changed notifications; 998 // after rebuilding its list, it will update the content processes. 999 static bool firstTime = true; 1000 if (firstTime) { 1001 ::CFNotificationCenterAddObserver(::CFNotificationCenterGetLocalCenter(), this, 1002 RegisteredFontsChangedNotificationCallback, 1003 kCTFontManagerRegisteredFontsChangedNotification, 0, 1004 CFNotificationSuspensionBehaviorDeliverImmediately); 1005 firstTime = false; 1006 } 1007 1008 AutoCFRelease<CFArrayRef> familyNames = CTFontManagerCopyAvailableFontFamilyNames(); 1009 nsTArray<fontlist::Family::InitData> families; 1010 for (NSString* familyName in (NSArray*)(CFArrayRef)familyNames) { 1011 nsAutoString name16; 1012 GetStringForNSString(familyName, name16); 1013 NS_ConvertUTF16toUTF8 name(name16); 1014 nsAutoCString key; 1015 GenerateFontListKey(name, key); 1016 families.AppendElement(fontlist::Family::InitData(key, name, fontlist::Family::kNoIndex, 1017 GetVisibilityForFamily(name))); 1018 } 1019 SharedFontList()->SetFamilyNames(families); 1020 InitAliasesForSingleFaceList(); 1021 GetPrefsAndStartLoader(); 1022 } 1023} 1024 1025void gfxMacPlatformFontList::InitAliasesForSingleFaceList() { 1026 for (const auto& familyName : mSingleFaceFonts) { 1027 LOG_FONTLIST(("(fontlist-singleface) face name: %s\n", familyName.get())); 1028 // Each entry in the "single face families" list is expected to be a 1029 // colon-separated pair of FaceName:Family, 1030 // where FaceName is the individual face name (psname) of a font 1031 // that should be exposed as a separate family name, 1032 // and Family is the standard family to which that face belongs. 1033 // The only such face listed by default is 1034 // Osaka-Mono:Osaka 1035 auto colon = familyName.FindChar(':'); 1036 if (colon == kNotFound) { 1037 continue; 1038 } 1039 1040 // Look for the parent family in the main font family list, 1041 // and ensure we have loaded its list of available faces. 1042 nsAutoCString key; 1043 GenerateFontListKey(Substring(familyName, colon + 1), key); 1044 fontlist::Family* family = SharedFontList()->FindFamily(key); 1045 if (!family) { 1046 // The parent family is not present, so just ignore this entry. 1047 continue; 1048 } 1049 if (!family->IsInitialized()) { 1050 if (!gfxPlatformFontList::InitializeFamily(family)) { 1051 // This shouldn't ever fail, but if it does, we can safely ignore it. 1052 MOZ_ASSERT(false, "failed to initialize font family"); 1053 continue; 1054 } 1055 } 1056 1057 // Truncate the entry from prefs at the colon, so now it is just the 1058 // desired single-face-family name. 1059 nsAutoCString aliasName(Substring(familyName, 0, colon)); 1060 1061 // Look through the family's faces to see if this one is present. 1062 fontlist::FontList* list = SharedFontList(); 1063 const fontlist::Pointer* facePtrs = family->Faces(list); 1064 for (size_t i = 0; i < family->NumFaces(); i++) { 1065 if (facePtrs[i].IsNull()) { 1066 continue; 1067 } 1068 auto face = static_cast<const fontlist::Face*>(facePtrs[i].ToPtr(list)); 1069 if (face->mDescriptor.AsString(list).Equals(aliasName)) { 1070 // Found it! Create an entry in the Alias table. 1071 GenerateFontListKey(aliasName, key); 1072 if (SharedFontList()->FindFamily(key) || mAliasTable.Get(key)) { 1073 // If the family name is already known, something's misconfigured; 1074 // just ignore it. 1075 MOZ_ASSERT(false, "single-face family already known"); 1076 break; 1077 } 1078 auto aliasData = mAliasTable.GetOrInsertNew(key); 1079 // The "alias" here isn't based on an existing family, so we don't call 1080 // aliasData->InitFromFamily(); the various flags are left as defaults. 1081 aliasData->mFaces.AppendElement(facePtrs[i]); 1082 aliasData->mBaseFamily = aliasName; 1083 aliasData->mVisibility = family->Visibility(); 1084 break; 1085 } 1086 } 1087 } 1088 if (!mAliasTable.IsEmpty()) { 1089 // This will be updated when the font loader completes, but we require 1090 // at least the Osaka-Mono alias to be available immediately. 1091 SharedFontList()->SetAliases(mAliasTable); 1092 } 1093} 1094 1095void gfxMacPlatformFontList::InitSingleFaceList() { 1096 for (const auto& familyName : mSingleFaceFonts) { 1097 LOG_FONTLIST(("(fontlist-singleface) face name: %s\n", familyName.get())); 1098 // Each entry in the "single face families" list is expected to be a 1099 // colon-separated pair of FaceName:Family, 1100 // where FaceName is the individual face name (psname) of a font 1101 // that should be exposed as a separate family name, 1102 // and Family is the standard family to which that face belongs. 1103 // The only such face listed by default is 1104 // Osaka-Mono:Osaka 1105 auto colon = familyName.FindChar(':'); 1106 if (colon == kNotFound) { 1107 continue; 1108 } 1109 1110 // Look for the parent family in the main font family list, 1111 // and ensure we have loaded its list of available faces. 1112 nsAutoCString key(Substring(familyName, colon + 1)); 1113 ToLowerCase(key); 1114 gfxFontFamily* family = mFontFamilies.GetWeak(key); 1115 if (!family || family->IsHidden()) { 1116 continue; 1117 } 1118 family->FindStyleVariations(); 1119 1120 // Truncate the entry from prefs at the colon, so now it is just the 1121 // desired single-face-family name. 1122 nsAutoCString aliasName(Substring(familyName, 0, colon)); 1123 1124 // Look through the family's faces to see if this one is present. 1125 const gfxFontEntry* fe = nullptr; 1126 for (const auto& face : family->GetFontList()) { 1127 if (face->Name().Equals(aliasName)) { 1128 fe = face; 1129 break; 1130 } 1131 } 1132 if (!fe) { 1133 continue; 1134 } 1135 1136 // We found the correct face, so create the single-face family record. 1137 GenerateFontListKey(aliasName, key); 1138 LOG_FONTLIST(("(fontlist-singleface) family name: %s, key: %s\n", aliasName.get(), key.get())); 1139 1140 // add only if doesn't exist already 1141 if (!mFontFamilies.GetWeak(key)) { 1142 RefPtr<gfxFontFamily> familyEntry = 1143 new gfxSingleFaceMacFontFamily(aliasName, family->Visibility()); 1144 // We need a separate font entry, because its family name will 1145 // differ from the one we found in the main list. 1146 MacOSFontEntry* fontEntry = new MacOSFontEntry( 1147 fe->Name(), fe->Weight(), true, static_cast<const MacOSFontEntry*>(fe)->mSizeHint); 1148 familyEntry->AddFontEntry(fontEntry); 1149 familyEntry->SetHasStyles(true); 1150 mFontFamilies.InsertOrUpdate(key, std::move(familyEntry)); 1151 LOG_FONTLIST( 1152 ("(fontlist-singleface) added new family: %s, key: %s\n", aliasName.get(), key.get())); 1153 } 1154 } 1155} 1156 1157// System fonts under OSX may contain weird "meta" names but if we create 1158// a new font using just the Postscript name, the NSFont api returns an object 1159// with the actual real family name. For example, under OSX 10.11: 1160// 1161// [[NSFont menuFontOfSize:8.0] familyName] ==> .AppleSystemUIFont 1162// [[NSFont fontWithName:[[[NSFont menuFontOfSize:8.0] fontDescriptor] postscriptName] 1163// size:8.0] familyName] ==> .SF NS Text 1164 1165static NSString* GetRealFamilyName(NSFont* aFont) { 1166 NSString* psName = [[aFont fontDescriptor] postscriptName]; 1167 // With newer macOS versions and SDKs (e.g. when compiled against SDK 10.15), 1168 // [NSFont fontWithName:] fails for hidden system fonts, because the underlying 1169 // Core Text functions it uses reject such names and tell us to use the special 1170 // CTFontCreateUIFontForLanguage API instead. 1171 // To work around this, as we don't yet work directly with the CTFontUIFontType 1172 // identifiers, we create a Core Graphics font (as it doesn't reject system font 1173 // names), and use this to create a Core Text font that we can query for the 1174 // family name. 1175 // Eventually we should move to using CTFontUIFontType constants to identify 1176 // system fonts, and eliminate the need to instantiate them (indirectly) from 1177 // their postscript names. 1178 AutoCFRelease<CGFontRef> cgFont = CGFontCreateWithFontName(CFStringRef(psName)); 1179 if (!cgFont) { 1180 return [aFont familyName]; 1181 } 1182 1183 AutoCFRelease<CTFontRef> ctFont = CTFontCreateWithGraphicsFont(cgFont, 0.0, nullptr, nullptr); 1184 if (!ctFont) { 1185 return [aFont familyName]; 1186 } 1187 NSString* familyName = (NSString*)CTFontCopyFamilyName(ctFont); 1188 1189 return [familyName autorelease]; 1190} 1191 1192// Create a gfxFontFamily that corresponds to the "system" font name, 1193// and populate it with the given font face. We only use this on Catalina or later, 1194// so we expect the system font to be a variable-weight face rather than requiring 1195// a number of discrete faces of different weights. 1196static gfxFontFamily* CreateFamilyForSystemFont(NSFont* aFont, const nsACString& aFamilyName) { 1197 gfxFontFamily* familyEntry = new gfxFontFamily(aFamilyName, FontVisibility::Unknown); 1198 1199 NSString* psNameNS = [[aFont fontDescriptor] postscriptName]; 1200 nsAutoString nameUTF16; 1201 nsAutoCString psName; 1202 nsCocoaUtils::GetStringForNSString(psNameNS, nameUTF16); 1203 CopyUTF16toUTF8(nameUTF16, psName); 1204 1205 MacOSFontEntry* fe = new MacOSFontEntry(psName, WeightRange(FontWeight::Normal()), true, 0.0); 1206 MOZ_ASSERT(gfxPlatform::GetPlatform()->HasVariationFontSupport()); 1207 fe->SetupVariationRanges(); 1208 1209 familyEntry->AddFontEntry(fe); 1210 familyEntry->SetHasStyles(true); 1211 1212 return familyEntry; 1213} 1214 1215// System fonts under OSX 10.11 use a combination of two families, one 1216// for text sizes and another for larger, display sizes. Each has a 1217// different number of weights. There aren't efficient API's for looking 1218// this information up, so hard code the logic here but confirm via 1219// debug assertions that the logic is correct. 1220 1221const CGFloat kTextDisplayCrossover = 20.0; // use text family below this size 1222 1223void gfxMacPlatformFontList::InitSystemFontNames() { 1224 // On Catalina+, the system font uses optical sizing rather than individual 1225 // faces, so we don't need to look for a separate display-sized face. 1226 mUseSizeSensitiveSystemFont = !nsCocoaFeatures::OnCatalinaOrLater(); 1227 1228 // text font family 1229 NSFont* sys = [NSFont systemFontOfSize:0.0]; 1230 NSString* textFamilyName = GetRealFamilyName(sys); 1231 nsAutoString familyName; 1232 nsCocoaUtils::GetStringForNSString(textFamilyName, familyName); 1233 CopyUTF16toUTF8(familyName, mSystemTextFontFamilyName); 1234 1235 // On Catalina or later, we store an in-process gfxFontFamily for the system font 1236 // even if using the shared fontlist to manage "normal" fonts, because the hidden 1237 // system fonts may be excluded from the font list altogether. 1238 if (nsCocoaFeatures::OnCatalinaOrLater()) { 1239 RefPtr<gfxFontFamily> fam = CreateFamilyForSystemFont(sys, mSystemTextFontFamilyName); 1240 if (fam) { 1241 nsAutoCString key; 1242 GenerateFontListKey(mSystemTextFontFamilyName, key); 1243 mFontFamilies.InsertOrUpdate(key, std::move(fam)); 1244 } 1245 } 1246 1247 // display font family, if on OSX 10.11 - 10.14 1248 if (mUseSizeSensitiveSystemFont) { 1249 NSFont* displaySys = [NSFont systemFontOfSize:128.0]; 1250 NSString* displayFamilyName = GetRealFamilyName(displaySys); 1251 if ([displayFamilyName isEqualToString:textFamilyName]) { 1252 mUseSizeSensitiveSystemFont = false; 1253 } else { 1254 nsCocoaUtils::GetStringForNSString(displayFamilyName, familyName); 1255 CopyUTF16toUTF8(familyName, mSystemDisplayFontFamilyName); 1256 } 1257 } 1258 1259#ifdef DEBUG 1260 // different system font API's always map to the same family under OSX, so 1261 // just assume that and emit a warning if that ever changes 1262 NSString* sysFamily = GetRealFamilyName([NSFont systemFontOfSize:0.0]); 1263 if ([sysFamily compare:GetRealFamilyName([NSFont boldSystemFontOfSize:0.0])] != NSOrderedSame || 1264 [sysFamily compare:GetRealFamilyName([NSFont controlContentFontOfSize:0.0])] != 1265 NSOrderedSame || 1266 [sysFamily compare:GetRealFamilyName([NSFont menuBarFontOfSize:0.0])] != NSOrderedSame || 1267 [sysFamily compare:GetRealFamilyName([NSFont toolTipsFontOfSize:0.0])] != NSOrderedSame) { 1268 NS_WARNING("system font types map to different font families" 1269 " -- please log a bug!!"); 1270 } 1271#endif 1272} 1273 1274gfxFontFamily* gfxMacPlatformFontList::FindSystemFontFamily(const nsACString& aFamily) { 1275 nsAutoCString key; 1276 GenerateFontListKey(aFamily, key); 1277 1278 gfxFontFamily* familyEntry; 1279 if ((familyEntry = mFontFamilies.GetWeak(key))) { 1280 return CheckFamily(familyEntry); 1281 } 1282 1283 return nullptr; 1284} 1285 1286void gfxMacPlatformFontList::RegisteredFontsChangedNotificationCallback( 1287 CFNotificationCenterRef center, void* observer, CFStringRef name, const void* object, 1288 CFDictionaryRef userInfo) { 1289 if (!::CFEqual(name, kCTFontManagerRegisteredFontsChangedNotification)) { 1290 return; 1291 } 1292 1293 gfxMacPlatformFontList* fl = static_cast<gfxMacPlatformFontList*>(observer); 1294 if (!fl->IsInitialized()) { 1295 return; 1296 } 1297 1298 // xxx - should be carefully pruning the list of fonts, not rebuilding it from scratch 1299 fl->UpdateFontList(); 1300 1301 gfxPlatform::ForceGlobalReflow(gfxPlatform::NeedsReframe::Yes); 1302 dom::ContentParent::NotifyUpdatedFonts(true); 1303} 1304 1305gfxFontEntry* gfxMacPlatformFontList::PlatformGlobalFontFallback(nsPresContext* aPresContext, 1306 const uint32_t aCh, 1307 Script aRunScript, 1308 const gfxFontStyle* aMatchStyle, 1309 FontFamily& aMatchedFamily) { 1310 CFStringRef str; 1311 UniChar ch[2]; 1312 CFIndex length = 1; 1313 1314 if (IS_IN_BMP(aCh)) { 1315 ch[0] = aCh; 1316 str = ::CFStringCreateWithCharactersNoCopy(kCFAllocatorDefault, ch, 1, kCFAllocatorNull); 1317 } else { 1318 ch[0] = H_SURROGATE(aCh); 1319 ch[1] = L_SURROGATE(aCh); 1320 str = ::CFStringCreateWithCharactersNoCopy(kCFAllocatorDefault, ch, 2, kCFAllocatorNull); 1321 if (!str) { 1322 return nullptr; 1323 } 1324 length = 2; 1325 } 1326 1327 // use CoreText to find the fallback family 1328 1329 gfxFontEntry* fontEntry = nullptr; 1330 bool cantUseFallbackFont = false; 1331 1332 if (!mDefaultFont) { 1333 mDefaultFont = ::CTFontCreateWithName(CFSTR("LucidaGrande"), 12.f, NULL); 1334 } 1335 1336 AutoCFRelease<CTFontRef> fallback = 1337 ::CTFontCreateForString(mDefaultFont, str, ::CFRangeMake(0, length)); 1338 1339 if (fallback) { 1340 AutoCFRelease<CFStringRef> familyNameRef = ::CTFontCopyFamilyName(fallback); 1341 1342 if (familyNameRef && 1343 ::CFStringCompare(familyNameRef, CFSTR("LastResort"), kCFCompareCaseInsensitive) != 1344 kCFCompareEqualTo && 1345 ::CFStringCompare(familyNameRef, CFSTR(".LastResort"), kCFCompareCaseInsensitive) != 1346 kCFCompareEqualTo) { 1347 AutoTArray<UniChar, 1024> buffer; 1348 CFIndex familyNameLen = ::CFStringGetLength(familyNameRef); 1349 buffer.SetLength(familyNameLen + 1); 1350 ::CFStringGetCharacters(familyNameRef, ::CFRangeMake(0, familyNameLen), buffer.Elements()); 1351 buffer[familyNameLen] = 0; 1352 NS_ConvertUTF16toUTF8 familyNameString(reinterpret_cast<char16_t*>(buffer.Elements()), 1353 familyNameLen); 1354 1355 if (SharedFontList()) { 1356 fontlist::Family* family = FindSharedFamily(aPresContext, familyNameString); 1357 if (family) { 1358 fontlist::Face* face = family->FindFaceForStyle(SharedFontList(), *aMatchStyle); 1359 if (face) { 1360 fontEntry = GetOrCreateFontEntry(face, family); 1361 } 1362 if (fontEntry) { 1363 if (fontEntry->HasCharacter(aCh)) { 1364 aMatchedFamily = FontFamily(family); 1365 } else { 1366 fontEntry = nullptr; 1367 cantUseFallbackFont = true; 1368 } 1369 } 1370 } 1371 } 1372 1373 // The macOS system font does not appear in the shared font list, so if 1374 // we didn't find the fallback font above, we should also check for an 1375 // unshared fontFamily in the system list. 1376 if (!fontEntry) { 1377 gfxFontFamily* family = FindSystemFontFamily(familyNameString); 1378 if (family) { 1379 fontEntry = family->FindFontForStyle(*aMatchStyle); 1380 if (fontEntry) { 1381 if (fontEntry->HasCharacter(aCh)) { 1382 aMatchedFamily = FontFamily(family); 1383 } else { 1384 fontEntry = nullptr; 1385 cantUseFallbackFont = true; 1386 } 1387 } 1388 } 1389 } 1390 } 1391 } 1392 1393 if (cantUseFallbackFont) { 1394 Telemetry::Accumulate(Telemetry::BAD_FALLBACK_FONT, cantUseFallbackFont); 1395 } 1396 1397 ::CFRelease(str); 1398 1399 return fontEntry; 1400} 1401 1402FontFamily gfxMacPlatformFontList::GetDefaultFontForPlatform(nsPresContext* aPresContext, 1403 const gfxFontStyle* aStyle, 1404 nsAtom* aLanguage) { 1405 nsAutoreleasePool localPool; 1406 1407 NSString* defaultFamily = [[NSFont userFontOfSize:aStyle->size] familyName]; 1408 nsAutoString familyName; 1409 1410 GetStringForNSString(defaultFamily, familyName); 1411 return FindFamily(aPresContext, NS_ConvertUTF16toUTF8(familyName)); 1412} 1413 1414int32_t gfxMacPlatformFontList::AppleWeightToCSSWeight(int32_t aAppleWeight) { 1415 if (aAppleWeight < 1) 1416 aAppleWeight = 1; 1417 else if (aAppleWeight > kAppleMaxWeight) 1418 aAppleWeight = kAppleMaxWeight; 1419 return gAppleWeightToCSSWeight[aAppleWeight]; 1420} 1421 1422gfxFontEntry* gfxMacPlatformFontList::LookupLocalFont(nsPresContext* aPresContext, 1423 const nsACString& aFontName, 1424 WeightRange aWeightForEntry, 1425 StretchRange aStretchForEntry, 1426 SlantStyleRange aStyleForEntry) { 1427 if (aFontName.IsEmpty() || aFontName[0] == '.') { 1428 return nullptr; 1429 } 1430 1431 nsAutoreleasePool localPool; 1432 1433 NSString* faceName = GetNSStringForString(NS_ConvertUTF8toUTF16(aFontName)); 1434 1435 // lookup face based on postscript or full name 1436 AutoCFRelease<CGFontRef> fontRef = CGFontCreateWithFontName(CFStringRef(faceName)); 1437 if (!fontRef) { 1438 return nullptr; 1439 } 1440 1441 // It's possible for CGFontCreateWithFontName to return a font that has been 1442 // deactivated/uninstalled, or a font that is excluded from the font list due 1443 // to CSS font-visibility restriction. So we need to check whether this font is 1444 // allowed to be used. 1445 1446 // CGFontRef doesn't offer a family-name API, so we go via a CTFontRef. 1447 AutoCFRelease<CTFontRef> ctFont = CTFontCreateWithGraphicsFont(fontRef, 0.0, nullptr, nullptr); 1448 if (!ctFont) { 1449 return nullptr; 1450 } 1451 AutoCFRelease<CFStringRef> name = CTFontCopyFamilyName(ctFont); 1452 1453 // Convert the family name to a key suitable for font-list lookup (8-bit, lowercased). 1454 nsAutoCString key; 1455 // CFStringGetLength is in UTF-16 code units. The maximum this count can expand 1456 // when converted to UTF-8 is 3x. We add 1 to ensure there will also be space for 1457 // null-termination of the resulting C string. 1458 key.SetLength((CFStringGetLength(name) + 1) * 3); 1459 if (!CFStringGetCString(name, key.BeginWriting(), key.Length(), kCFStringEncodingUTF8)) { 1460 // This shouldn't ever happen, but if it does we just bail. 1461 NS_WARNING("Failed to get family name?"); 1462 key.Truncate(0); 1463 } 1464 if (key.IsEmpty()) { 1465 return nullptr; 1466 } 1467 // Reset our string length to match the actual C string we got, which will usually 1468 // be much shorter than the maximal buffer we allocated. 1469 key.Truncate(strlen(key.get())); 1470 ToLowerCase(key); 1471 // If the family can't be looked up, this font is not available for use. 1472 FontFamily family = FindFamily(aPresContext, key); 1473 if (family.IsNull()) { 1474 return nullptr; 1475 } 1476 1477 return new MacOSFontEntry(aFontName, fontRef, aWeightForEntry, aStretchForEntry, aStyleForEntry, 1478 false, true); 1479} 1480 1481static void ReleaseData(void* info, const void* data, size_t size) { free((void*)data); } 1482 1483gfxFontEntry* gfxMacPlatformFontList::MakePlatformFont(const nsACString& aFontName, 1484 WeightRange aWeightForEntry, 1485 StretchRange aStretchForEntry, 1486 SlantStyleRange aStyleForEntry, 1487 const uint8_t* aFontData, uint32_t aLength) { 1488 NS_ASSERTION(aFontData, "MakePlatformFont called with null data"); 1489 1490 // create the font entry 1491 nsAutoString uniqueName; 1492 1493 nsresult rv = gfxFontUtils::MakeUniqueUserFontName(uniqueName); 1494 if (NS_FAILED(rv)) { 1495 return nullptr; 1496 } 1497 1498 AutoCFRelease<CGDataProviderRef> provider = 1499 ::CGDataProviderCreateWithData(nullptr, aFontData, aLength, &ReleaseData); 1500 AutoCFRelease<CGFontRef> fontRef = ::CGFontCreateWithDataProvider(provider); 1501 if (!fontRef) { 1502 return nullptr; 1503 } 1504 1505 auto newFontEntry = 1506 MakeUnique<MacOSFontEntry>(NS_ConvertUTF16toUTF8(uniqueName), fontRef, aWeightForEntry, 1507 aStretchForEntry, aStyleForEntry, true, false); 1508 return newFontEntry.release(); 1509} 1510 1511// Webkit code uses a system font meta name, so mimic that here 1512// WebCore/platform/graphics/mac/FontCacheMac.mm 1513static const char kSystemFont_system[] = "-apple-system"; 1514 1515bool gfxMacPlatformFontList::FindAndAddFamilies(nsPresContext* aPresContext, 1516 StyleGenericFontFamily aGeneric, 1517 const nsACString& aFamily, 1518 nsTArray<FamilyAndGeneric>* aOutput, 1519 FindFamiliesFlags aFlags, gfxFontStyle* aStyle, 1520 nsAtom* aLanguage, gfxFloat aDevToCssSize) { 1521 if (aFamily.EqualsLiteral(kSystemFont_system)) { 1522 // Search for special system font name, -apple-system. This is not done via 1523 // the shared fontlist on Catalina or later, because the hidden system font 1524 // may not be included there; we create a separate gfxFontFamily to manage 1525 // this family. 1526 const nsCString& systemFontFamilyName = 1527 mUseSizeSensitiveSystemFont && aStyle && 1528 (aStyle->size * aDevToCssSize) >= kTextDisplayCrossover 1529 ? mSystemDisplayFontFamilyName 1530 : mSystemTextFontFamilyName; 1531 if (SharedFontList() && !nsCocoaFeatures::OnCatalinaOrLater()) { 1532 FindFamiliesFlags flags = aFlags | FindFamiliesFlags::eSearchHiddenFamilies; 1533 return gfxPlatformFontList::FindAndAddFamilies(aPresContext, aGeneric, systemFontFamilyName, 1534 aOutput, flags, aStyle, aLanguage, 1535 aDevToCssSize); 1536 } else { 1537 if (auto* fam = FindSystemFontFamily(systemFontFamilyName)) { 1538 aOutput->AppendElement(fam); 1539 return true; 1540 } 1541 } 1542 return false; 1543 } 1544 1545 return gfxPlatformFontList::FindAndAddFamilies(aPresContext, aGeneric, aFamily, aOutput, aFlags, 1546 aStyle, aLanguage, aDevToCssSize); 1547} 1548 1549void gfxMacPlatformFontList::LookupSystemFont(LookAndFeel::FontID aSystemFontID, 1550 nsACString& aSystemFontName, 1551 gfxFontStyle& aFontStyle) { 1552 // Provide a local pool because we may be called from stylo threads. 1553 nsAutoreleasePool localPool; 1554 1555 // code moved here from widget/cocoa/nsLookAndFeel.mm 1556 NSFont* font = nullptr; 1557 char* systemFontName = nullptr; 1558 switch (aSystemFontID) { 1559 case LookAndFeel::FontID::MessageBox: 1560 case LookAndFeel::FontID::StatusBar: 1561 case LookAndFeel::FontID::MozList: 1562 case LookAndFeel::FontID::MozField: 1563 case LookAndFeel::FontID::MozButton: 1564 font = [NSFont systemFontOfSize:[NSFont smallSystemFontSize]]; 1565 systemFontName = (char*)kSystemFont_system; 1566 break; 1567 1568 case LookAndFeel::FontID::SmallCaption: 1569 font = [NSFont boldSystemFontOfSize:[NSFont smallSystemFontSize]]; 1570 systemFontName = (char*)kSystemFont_system; 1571 break; 1572 1573 case LookAndFeel::FontID::Icon: // used in urlbar; tried labelFont, but too small 1574 case LookAndFeel::FontID::MozWorkspace: 1575 case LookAndFeel::FontID::MozDesktop: 1576 case LookAndFeel::FontID::MozInfo: 1577 font = [NSFont controlContentFontOfSize:0.0]; 1578 systemFontName = (char*)kSystemFont_system; 1579 break; 1580 1581 case LookAndFeel::FontID::MozPullDownMenu: 1582 font = [NSFont menuBarFontOfSize:0.0]; 1583 systemFontName = (char*)kSystemFont_system; 1584 break; 1585 1586 case LookAndFeel::FontID::Caption: 1587 case LookAndFeel::FontID::Menu: 1588 case LookAndFeel::FontID::MozDialog: 1589 default: 1590 font = [NSFont systemFontOfSize:0.0]; 1591 systemFontName = (char*)kSystemFont_system; 1592 break; 1593 } 1594 NS_ASSERTION(font, "system font not set"); 1595 NS_ASSERTION(systemFontName, "system font name not set"); 1596 1597 if (systemFontName) { 1598 aSystemFontName.AssignASCII(systemFontName); 1599 } 1600 1601 NSFontSymbolicTraits traits = [[font fontDescriptor] symbolicTraits]; 1602 aFontStyle.style = 1603 (traits & NSFontItalicTrait) ? FontSlantStyle::Italic() : FontSlantStyle::Normal(); 1604 aFontStyle.weight = (traits & NSFontBoldTrait) ? FontWeight::Bold() : FontWeight::Normal(); 1605 aFontStyle.stretch = (traits & NSFontExpandedTrait) ? FontStretch::Expanded() 1606 : (traits & NSFontCondensedTrait) ? FontStretch::Condensed() 1607 : FontStretch::Normal(); 1608 aFontStyle.size = [font pointSize]; 1609 aFontStyle.systemFont = true; 1610} 1611 1612// used to load system-wide font info on off-main thread 1613class MacFontInfo final : public FontInfoData { 1614 public: 1615 MacFontInfo(bool aLoadOtherNames, bool aLoadFaceNames, bool aLoadCmaps) 1616 : FontInfoData(aLoadOtherNames, aLoadFaceNames, aLoadCmaps) {} 1617 1618 virtual ~MacFontInfo() = default; 1619 1620 virtual void Load() { 1621 nsAutoreleasePool localPool; 1622 FontInfoData::Load(); 1623 } 1624 1625 // loads font data for all members of a given family 1626 virtual void LoadFontFamilyData(const nsACString& aFamilyName); 1627}; 1628 1629void MacFontInfo::LoadFontFamilyData(const nsACString& aFamilyName) { 1630 CrashReporter::AutoAnnotateCrashReport autoFontName(CrashReporter::Annotation::FontName, 1631 aFamilyName); 1632 1633 // family name ==> CTFontDescriptor 1634 NSString* famName = GetNSStringForString(NS_ConvertUTF8toUTF16(aFamilyName)); 1635 CFStringRef family = CFStringRef(famName); 1636 1637 AutoCFRelease<CFMutableDictionaryRef> attr = CFDictionaryCreateMutable( 1638 NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); 1639 CFDictionaryAddValue(attr, kCTFontFamilyNameAttribute, family); 1640 AutoCFRelease<CTFontDescriptorRef> fd = CTFontDescriptorCreateWithAttributes(attr); 1641 AutoCFRelease<CFArrayRef> matchingFonts = CTFontDescriptorCreateMatchingFontDescriptors(fd, NULL); 1642 if (!matchingFonts) { 1643 return; 1644 } 1645 1646 nsTArray<nsCString> otherFamilyNames; 1647 bool hasOtherFamilyNames = true; 1648 1649 // iterate over faces in the family 1650 int f, numFaces = (int)CFArrayGetCount(matchingFonts); 1651 for (f = 0; f < numFaces; f++) { 1652 mLoadStats.fonts++; 1653 1654 CTFontDescriptorRef faceDesc = (CTFontDescriptorRef)CFArrayGetValueAtIndex(matchingFonts, f); 1655 if (!faceDesc) { 1656 continue; 1657 } 1658 AutoCFRelease<CTFontRef> fontRef = CTFontCreateWithFontDescriptor(faceDesc, 0.0, nullptr); 1659 if (!fontRef) { 1660 NS_WARNING("failed to create a CTFontRef"); 1661 continue; 1662 } 1663 1664 if (mLoadCmaps) { 1665 // face name 1666 AutoCFRelease<CFStringRef> faceName = 1667 (CFStringRef)CTFontDescriptorCopyAttribute(faceDesc, kCTFontNameAttribute); 1668 1669 AutoTArray<UniChar, 1024> buffer; 1670 CFIndex len = CFStringGetLength(faceName); 1671 buffer.SetLength(len + 1); 1672 CFStringGetCharacters(faceName, ::CFRangeMake(0, len), buffer.Elements()); 1673 buffer[len] = 0; 1674 NS_ConvertUTF16toUTF8 fontName(reinterpret_cast<char16_t*>(buffer.Elements()), len); 1675 1676 // load the cmap data 1677 FontFaceData fontData; 1678 AutoCFRelease<CFDataRef> cmapTable = 1679 CTFontCopyTable(fontRef, kCTFontTableCmap, kCTFontTableOptionNoOptions); 1680 1681 if (cmapTable) { 1682 const uint8_t* cmapData = (const uint8_t*)CFDataGetBytePtr(cmapTable); 1683 uint32_t cmapLen = CFDataGetLength(cmapTable); 1684 RefPtr<gfxCharacterMap> charmap = new gfxCharacterMap(); 1685 uint32_t offset; 1686 nsresult rv; 1687 1688 rv = gfxFontUtils::ReadCMAP(cmapData, cmapLen, *charmap, offset); 1689 if (NS_SUCCEEDED(rv)) { 1690 fontData.mCharacterMap = charmap; 1691 fontData.mUVSOffset = offset; 1692 mLoadStats.cmaps++; 1693 } 1694 } 1695 1696 mFontFaceData.InsertOrUpdate(fontName, fontData); 1697 } 1698 1699 if (mLoadOtherNames && hasOtherFamilyNames) { 1700 AutoCFRelease<CFDataRef> nameTable = 1701 CTFontCopyTable(fontRef, kCTFontTableName, kCTFontTableOptionNoOptions); 1702 1703 if (nameTable) { 1704 const char* nameData = (const char*)CFDataGetBytePtr(nameTable); 1705 uint32_t nameLen = CFDataGetLength(nameTable); 1706 gfxFontUtils::ReadOtherFamilyNamesForFace(aFamilyName, nameData, nameLen, otherFamilyNames, 1707 false); 1708 hasOtherFamilyNames = otherFamilyNames.Length() != 0; 1709 } 1710 } 1711 } 1712 1713 // if found other names, insert them in the hash table 1714 if (otherFamilyNames.Length() != 0) { 1715 mOtherFamilyNames.InsertOrUpdate(aFamilyName, otherFamilyNames); 1716 mLoadStats.othernames += otherFamilyNames.Length(); 1717 } 1718} 1719 1720already_AddRefed<FontInfoData> gfxMacPlatformFontList::CreateFontInfoData() { 1721 bool loadCmaps = 1722 !UsesSystemFallback() || gfxPlatform::GetPlatform()->UseCmapsDuringSystemFallback(); 1723 1724 RefPtr<MacFontInfo> fi = new MacFontInfo(true, NeedFullnamePostscriptNames(), loadCmaps); 1725 return fi.forget(); 1726} 1727 1728gfxFontFamily* gfxMacPlatformFontList::CreateFontFamily(const nsACString& aName, 1729 FontVisibility aVisibility) const { 1730 return new gfxMacFontFamily(aName, aVisibility, 0.0); 1731} 1732 1733gfxFontEntry* gfxMacPlatformFontList::CreateFontEntry(fontlist::Face* aFace, 1734 const fontlist::Family* aFamily) { 1735 MacOSFontEntry* fe = 1736 new MacOSFontEntry(aFace->mDescriptor.AsString(SharedFontList()), aFace->mWeight, false, 1737 0.0); // XXX standardFace, sizeHint 1738 fe->InitializeFrom(aFace, aFamily); 1739 return fe; 1740} 1741 1742void gfxMacPlatformFontList::GetFacesInitDataForFamily(const fontlist::Family* aFamily, 1743 nsTArray<fontlist::Face::InitData>& aFaces, 1744 bool aLoadCmaps) const { 1745 nsAutoreleasePool localPool; 1746 1747 NS_ConvertUTF8toUTF16 name(aFamily->Key().AsString(SharedFontList())); 1748 NSString* family = GetNSStringForString(name); 1749 1750 // returns an array of [psname, style name, weight, traits] elements, goofy api 1751 NSArray* fontfaces = [sFontManager availableMembersOfFontFamily:family]; 1752 int faceCount = [fontfaces count]; 1753 for (int faceIndex = 0; faceIndex < faceCount; faceIndex++) { 1754 NSArray* face = [fontfaces objectAtIndex:faceIndex]; 1755 NSString* psname = [face objectAtIndex:INDEX_FONT_POSTSCRIPT_NAME]; 1756 int32_t appKitWeight = [[face objectAtIndex:INDEX_FONT_WEIGHT] unsignedIntValue]; 1757 uint32_t macTraits = [[face objectAtIndex:INDEX_FONT_TRAITS] unsignedIntValue]; 1758 NSString* facename = [face objectAtIndex:INDEX_FONT_FACE_NAME]; 1759 1760 if (appKitWeight == kAppleExtraLightWeight) { 1761 // if the facename contains UltraLight, set the weight to the ultralight weight value 1762 NSRange range = [facename rangeOfString:@"ultralight" options:NSCaseInsensitiveSearch]; 1763 if (range.location != NSNotFound) { 1764 appKitWeight = kAppleUltraLightWeight; 1765 } 1766 } 1767 1768 // make a nsString 1769 nsAutoString postscriptFontName; 1770 GetStringForNSString(psname, postscriptFontName); 1771 1772 int32_t cssWeight = gfxMacPlatformFontList::AppleWeightToCSSWeight(appKitWeight); 1773 if (PR_GetCurrentThread() != sInitFontListThread) { 1774 int32_t weightOverride = GetWeightOverride(postscriptFontName); 1775 if (weightOverride) { 1776 // scale down and clamp, to get a value from 1..9 1777 cssWeight = ((weightOverride + 50) / 100); 1778 cssWeight = std::max(1, std::min(cssWeight, 9)); 1779 } 1780 } 1781 cssWeight *= 100; // scale up to CSS values 1782 1783 StretchRange stretch(FontStretch::Normal()); 1784 if (macTraits & (NSCondensedFontMask | NSNarrowFontMask | NSCompressedFontMask)) { 1785 stretch = StretchRange(FontStretch::Condensed()); 1786 } else if (macTraits & NSExpandedFontMask) { 1787 stretch = StretchRange(FontStretch::Expanded()); 1788 } 1789 // Cocoa fails to set the Italic traits bit for HelveticaLightItalic, 1790 // at least (see bug 611855), so check for style name endings as well 1791 SlantStyleRange slantStyle(FontSlantStyle::Normal()); 1792 if ((macTraits & NSItalicFontMask) || [facename hasSuffix:@"Italic"] || 1793 [facename hasSuffix:@"Oblique"]) { 1794 slantStyle = SlantStyleRange(FontSlantStyle::Italic()); 1795 } 1796 1797 bool fixedPitch = (macTraits & NSFixedPitchFontMask) ? true : false; 1798 1799 RefPtr<gfxCharacterMap> charmap; 1800 if (aLoadCmaps) { 1801 AutoCFRelease<CGFontRef> font = CGFontCreateWithFontName(CFStringRef(psname)); 1802 if (font) { 1803 uint32_t kCMAP = TRUETYPE_TAG('c', 'm', 'a', 'p'); 1804 AutoCFRelease<CFDataRef> data = CGFontCopyTableForTag(font, kCMAP); 1805 if (data) { 1806 uint32_t offset; 1807 charmap = new gfxCharacterMap(); 1808 gfxFontUtils::ReadCMAP(CFDataGetBytePtr(data), CFDataGetLength(data), *charmap, offset); 1809 } 1810 } 1811 } 1812 1813 // Ensure that a face named "Regular" goes to the front of the list, so it 1814 // will take precedence over other faces with the same style attributes but 1815 // a different name (such as "Outline"). 1816 auto data = fontlist::Face::InitData{ 1817 NS_ConvertUTF16toUTF8(postscriptFontName), 1818 0, 1819 fixedPitch, 1820 WeightRange(FontWeight(cssWeight)), 1821 stretch, 1822 slantStyle, 1823 charmap, 1824 }; 1825 if ([facename caseInsensitiveCompare:@"Regular"] == NSOrderedSame) { 1826 aFaces.InsertElementAt(0, std::move(data)); 1827 } else { 1828 aFaces.AppendElement(std::move(data)); 1829 } 1830 } 1831} 1832 1833void gfxMacPlatformFontList::ReadFaceNamesForFamily(fontlist::Family* aFamily, 1834 bool aNeedFullnamePostscriptNames) { 1835 if (!aFamily->IsInitialized()) { 1836 if (!InitializeFamily(aFamily)) { 1837 return; 1838 } 1839 } 1840 const uint32_t kNAME = TRUETYPE_TAG('n', 'a', 'm', 'e'); 1841 fontlist::FontList* list = SharedFontList(); 1842 nsAutoCString canonicalName(aFamily->DisplayName().AsString(list)); 1843 const fontlist::Pointer* facePtrs = aFamily->Faces(list); 1844 for (uint32_t i = 0, n = aFamily->NumFaces(); i < n; i++) { 1845 auto face = static_cast<fontlist::Face*>(facePtrs[i].ToPtr(list)); 1846 if (!face) { 1847 continue; 1848 } 1849 nsAutoCString name(face->mDescriptor.AsString(list)); 1850 // We create a temporary MacOSFontEntry just to read family names from the 1851 // 'name' table in the font resource. The style attributes here are ignored 1852 // as this entry is not used for font style matching. 1853 // The size hint might be used to select which face is accessed in the case 1854 // of the macOS UI font; see MacOSFontEntry::GetFontRef(). We pass 16.0 in 1855 // order to get a standard text-size face in this case, although it's 1856 // unlikely to matter for the purpose of just reading family names. 1857 auto fe = MakeUnique<MacOSFontEntry>(name, WeightRange(FontWeight::Normal()), false, 16.0); 1858 if (!fe) { 1859 continue; 1860 } 1861 gfxFontEntry::AutoTable nameTable(fe.get(), kNAME); 1862 if (!nameTable) { 1863 continue; 1864 } 1865 uint32_t dataLength; 1866 const char* nameData = hb_blob_get_data(nameTable, &dataLength); 1867 AutoTArray<nsCString, 4> otherFamilyNames; 1868 gfxFontUtils::ReadOtherFamilyNamesForFace(canonicalName, nameData, dataLength, otherFamilyNames, 1869 false); 1870 for (const auto& alias : otherFamilyNames) { 1871 nsAutoCString key; 1872 GenerateFontListKey(alias, key); 1873 auto aliasData = mAliasTable.GetOrInsertNew(key); 1874 aliasData->InitFromFamily(aFamily, canonicalName); 1875 aliasData->mFaces.AppendElement(facePtrs[i]); 1876 } 1877 } 1878} 1879