1/* -*- Mode: ObjC; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*- 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 "gfxPlatformMac.h" 48#include "gfxMacPlatformFontList.h" 49#include "gfxMacFont.h" 50#include "gfxUserFontSet.h" 51#include "harfbuzz/hb.h" 52 53#include "nsServiceManagerUtils.h" 54#include "nsTArray.h" 55 56#include "nsDirectoryServiceUtils.h" 57#include "nsDirectoryServiceDefs.h" 58#include "nsAppDirectoryServiceDefs.h" 59#include "nsISimpleEnumerator.h" 60#include "nsCharTraits.h" 61#include "nsCocoaFeatures.h" 62#include "nsCocoaUtils.h" 63#include "gfxFontConstants.h" 64 65#include "mozilla/MemoryReporting.h" 66#include "mozilla/Preferences.h" 67#include "mozilla/Sprintf.h" 68#include "mozilla/Telemetry.h" 69#include "mozilla/gfx/2D.h" 70 71#include <unistd.h> 72#include <time.h> 73#include <dlfcn.h> 74 75using namespace mozilla; 76 77// indexes into the NSArray objects that the Cocoa font manager returns 78// as the available members of a family 79#define INDEX_FONT_POSTSCRIPT_NAME 0 80#define INDEX_FONT_FACE_NAME 1 81#define INDEX_FONT_WEIGHT 2 82#define INDEX_FONT_TRAITS 3 83 84static const int kAppleMaxWeight = 14; 85static const int kAppleExtraLightWeight = 3; 86static const int kAppleUltraLightWeight = 2; 87 88static const int gAppleWeightToCSSWeight[] = { 89 0, 90 1, // 1. 91 1, // 2. W1, ultralight 92 2, // 3. W2, extralight 93 3, // 4. W3, light 94 4, // 5. W4, semilight 95 5, // 6. W5, medium 96 6, // 7. 97 6, // 8. W6, semibold 98 7, // 9. W7, bold 99 8, // 10. W8, extrabold 100 8, // 11. 101 9, // 12. W9, ultrabold 102 9, // 13 103 9 // 14 104}; 105 106// cache Cocoa's "shared font manager" for performance 107static NSFontManager *sFontManager; 108 109static void GetStringForNSString(const NSString *aSrc, nsAString& aDist) 110{ 111 aDist.SetLength([aSrc length]); 112 [aSrc getCharacters:reinterpret_cast<unichar*>(aDist.BeginWriting())]; 113} 114 115static NSString* GetNSStringForString(const nsAString& aSrc) 116{ 117 return [NSString stringWithCharacters:reinterpret_cast<const unichar*>(aSrc.BeginReading()) 118 length:aSrc.Length()]; 119} 120 121#define LOG_FONTLIST(args) MOZ_LOG(gfxPlatform::GetLog(eGfxLog_fontlist), \ 122 mozilla::LogLevel::Debug, args) 123#define LOG_FONTLIST_ENABLED() MOZ_LOG_TEST( \ 124 gfxPlatform::GetLog(eGfxLog_fontlist), \ 125 mozilla::LogLevel::Debug) 126#define LOG_CMAPDATA_ENABLED() MOZ_LOG_TEST( \ 127 gfxPlatform::GetLog(eGfxLog_cmapdata), \ 128 mozilla::LogLevel::Debug) 129 130#pragma mark- 131 132// Complex scripts will not render correctly unless appropriate AAT or OT 133// layout tables are present. 134// For OpenType, we also check that the GSUB table supports the relevant 135// script tag, to avoid using things like Arial Unicode MS for Lao (it has 136// the characters, but lacks OpenType support). 137 138// TODO: consider whether we should move this to gfxFontEntry and do similar 139// cmap-masking on other platforms to avoid using fonts that won't shape 140// properly. 141 142nsresult 143MacOSFontEntry::ReadCMAP(FontInfoData *aFontInfoData) 144{ 145 // attempt this once, if errors occur leave a blank cmap 146 if (mCharacterMap) { 147 return NS_OK; 148 } 149 150 RefPtr<gfxCharacterMap> charmap; 151 nsresult rv; 152 bool symbolFont = false; // currently ignored 153 154 if (aFontInfoData && (charmap = GetCMAPFromFontInfo(aFontInfoData, 155 mUVSOffset, 156 symbolFont))) { 157 rv = NS_OK; 158 } else { 159 uint32_t kCMAP = TRUETYPE_TAG('c','m','a','p'); 160 charmap = new gfxCharacterMap(); 161 AutoTable cmapTable(this, kCMAP); 162 163 if (cmapTable) { 164 bool unicodeFont = false; // currently ignored 165 uint32_t cmapLen; 166 const uint8_t* cmapData = 167 reinterpret_cast<const uint8_t*>(hb_blob_get_data(cmapTable, 168 &cmapLen)); 169 rv = gfxFontUtils::ReadCMAP(cmapData, cmapLen, 170 *charmap, mUVSOffset, 171 unicodeFont, symbolFont); 172 } else { 173 rv = NS_ERROR_NOT_AVAILABLE; 174 } 175 } 176 177 if (NS_SUCCEEDED(rv) && !HasGraphiteTables()) { 178 // We assume a Graphite font knows what it's doing, 179 // and provides whatever shaping is needed for the 180 // characters it supports, so only check/clear the 181 // complex-script ranges for non-Graphite fonts 182 183 // for layout support, check for the presence of mort/morx and/or 184 // opentype layout tables 185 bool hasAATLayout = HasFontTable(TRUETYPE_TAG('m','o','r','x')) || 186 HasFontTable(TRUETYPE_TAG('m','o','r','t')); 187 bool hasGSUB = HasFontTable(TRUETYPE_TAG('G','S','U','B')); 188 bool hasGPOS = HasFontTable(TRUETYPE_TAG('G','P','O','S')); 189 if (hasAATLayout && !(hasGSUB || hasGPOS)) { 190 mRequiresAAT = true; // prefer CoreText if font has no OTL tables 191 } 192 193 for (const ScriptRange* sr = gfxPlatformFontList::sComplexScriptRanges; 194 sr->rangeStart; sr++) { 195 // check to see if the cmap includes complex script codepoints 196 if (charmap->TestRange(sr->rangeStart, sr->rangeEnd)) { 197 if (hasAATLayout) { 198 // prefer CoreText for Apple's complex-script fonts, 199 // even if they also have some OpenType tables 200 // (e.g. Geeza Pro Bold on 10.6; see bug 614903) 201 mRequiresAAT = true; 202 // and don't mask off complex-script ranges, we assume 203 // the AAT tables will provide the necessary shaping 204 continue; 205 } 206 207 // We check for GSUB here, as GPOS alone would not be ok. 208 if (hasGSUB && SupportsScriptInGSUB(sr->tags)) { 209 continue; 210 } 211 212 charmap->ClearRange(sr->rangeStart, sr->rangeEnd); 213 } 214 } 215 216 // Bug 1360309, 1393624: several of Apple's Chinese fonts have spurious 217 // blank glyphs for obscure Tibetan and Arabic-script codepoints. 218 // Blacklist these so that font fallback will not use them. 219 if (mRequiresAAT && (FamilyName().EqualsLiteral("Songti SC") || 220 FamilyName().EqualsLiteral("Songti TC") || 221 FamilyName().EqualsLiteral("STSong") || 222 // Bug 1390980: on 10.11, the Kaiti fonts are also affected. 223 FamilyName().EqualsLiteral("Kaiti SC") || 224 FamilyName().EqualsLiteral("Kaiti TC") || 225 FamilyName().EqualsLiteral("STKaiti"))) { 226 charmap->ClearRange(0x0f6b, 0x0f70); 227 charmap->ClearRange(0x0f8c, 0x0f8f); 228 charmap->clear(0x0f98); 229 charmap->clear(0x0fbd); 230 charmap->ClearRange(0x0fcd, 0x0fff); 231 charmap->clear(0x0620); 232 charmap->clear(0x065f); 233 charmap->ClearRange(0x06ee, 0x06ef); 234 charmap->clear(0x06ff); 235 } 236 } 237 238 mHasCmapTable = NS_SUCCEEDED(rv); 239 if (mHasCmapTable) { 240 gfxPlatformFontList *pfl = gfxPlatformFontList::PlatformFontList(); 241 mCharacterMap = pfl->FindCharMap(charmap); 242 } else { 243 // if error occurred, initialize to null cmap 244 mCharacterMap = new gfxCharacterMap(); 245 } 246 247 LOG_FONTLIST(("(fontlist-cmap) name: %s, size: %d hash: %8.8x%s\n", 248 NS_ConvertUTF16toUTF8(mName).get(), 249 charmap->SizeOfIncludingThis(moz_malloc_size_of), 250 charmap->mHash, mCharacterMap == charmap ? " new" : "")); 251 if (LOG_CMAPDATA_ENABLED()) { 252 char prefix[256]; 253 SprintfLiteral(prefix, "(cmapdata) name: %.220s", 254 NS_ConvertUTF16toUTF8(mName).get()); 255 charmap->Dump(prefix, eGfxLog_cmapdata); 256 } 257 258 return rv; 259} 260 261gfxFont* 262MacOSFontEntry::CreateFontInstance(const gfxFontStyle *aFontStyle, bool aNeedsBold) 263{ 264 return new gfxMacFont(this, aFontStyle, aNeedsBold); 265} 266 267bool 268MacOSFontEntry::IsCFF() 269{ 270 if (!mIsCFFInitialized) { 271 mIsCFFInitialized = true; 272 mIsCFF = HasFontTable(TRUETYPE_TAG('C','F','F',' ')); 273 } 274 275 return mIsCFF; 276} 277 278MacOSFontEntry::MacOSFontEntry(const nsAString& aPostscriptName, 279 int32_t aWeight, 280 bool aIsStandardFace, 281 double aSizeHint) 282 : gfxFontEntry(aPostscriptName, aIsStandardFace), 283 mFontRef(NULL), 284 mSizeHint(aSizeHint), 285 mFontRefInitialized(false), 286 mRequiresAAT(false), 287 mIsCFF(false), 288 mIsCFFInitialized(false) 289{ 290 mWeight = aWeight; 291} 292 293MacOSFontEntry::MacOSFontEntry(const nsAString& aPostscriptName, 294 CGFontRef aFontRef, 295 uint16_t aWeight, uint16_t aStretch, 296 uint8_t aStyle, 297 bool aIsDataUserFont, 298 bool aIsLocalUserFont) 299 : gfxFontEntry(aPostscriptName, false), 300 mFontRef(NULL), 301 mSizeHint(0.0), 302 mFontRefInitialized(false), 303 mRequiresAAT(false), 304 mIsCFF(false), 305 mIsCFFInitialized(false) 306{ 307 mFontRef = aFontRef; 308 mFontRefInitialized = true; 309 ::CFRetain(mFontRef); 310 311 mWeight = aWeight; 312 mStretch = aStretch; 313 mFixedPitch = false; // xxx - do we need this for downloaded fonts? 314 mStyle = aStyle; 315 316 NS_ASSERTION(!(aIsDataUserFont && aIsLocalUserFont), 317 "userfont is either a data font or a local font"); 318 mIsDataUserFont = aIsDataUserFont; 319 mIsLocalUserFont = aIsLocalUserFont; 320} 321 322CGFontRef 323MacOSFontEntry::GetFontRef() 324{ 325 if (!mFontRefInitialized) { 326 mFontRefInitialized = true; 327 NSString *psname = GetNSStringForString(mName); 328 mFontRef = ::CGFontCreateWithFontName(CFStringRef(psname)); 329 if (!mFontRef) { 330 // This happens on macOS 10.12 for font entry names that start with 331 // .AppleSystemUIFont. For those fonts, we need to go through NSFont 332 // to get the correct CGFontRef. 333 // Both the Text and the Display variant of the display font use 334 // .AppleSystemUIFontSomethingSomething as their member names. 335 // That's why we're carrying along mSizeHint to this place so that 336 // we get the variant that we want for this family. 337 NSFont* font = [NSFont fontWithName:psname size:mSizeHint]; 338 if (font) { 339 mFontRef = CTFontCopyGraphicsFont((CTFontRef)font, nullptr); 340 } 341 } 342 } 343 return mFontRef; 344} 345 346// For a logging build, we wrap the CFDataRef in a FontTableRec so that we can 347// use the MOZ_COUNT_[CD]TOR macros in it. A release build without logging 348// does not get this overhead. 349class FontTableRec { 350public: 351 explicit FontTableRec(CFDataRef aDataRef) 352 : mDataRef(aDataRef) 353 { 354 MOZ_COUNT_CTOR(FontTableRec); 355 } 356 357 ~FontTableRec() { 358 MOZ_COUNT_DTOR(FontTableRec); 359 ::CFRelease(mDataRef); 360 } 361 362private: 363 CFDataRef mDataRef; 364}; 365 366/*static*/ void 367MacOSFontEntry::DestroyBlobFunc(void* aUserData) 368{ 369#ifdef NS_BUILD_REFCNT_LOGGING 370 FontTableRec *ftr = static_cast<FontTableRec*>(aUserData); 371 delete ftr; 372#else 373 ::CFRelease((CFDataRef)aUserData); 374#endif 375} 376 377hb_blob_t * 378MacOSFontEntry::GetFontTable(uint32_t aTag) 379{ 380 CGFontRef fontRef = GetFontRef(); 381 if (!fontRef) { 382 return nullptr; 383 } 384 385 CFDataRef dataRef = ::CGFontCopyTableForTag(fontRef, aTag); 386 if (dataRef) { 387 return hb_blob_create((const char*)::CFDataGetBytePtr(dataRef), 388 ::CFDataGetLength(dataRef), 389 HB_MEMORY_MODE_READONLY, 390#ifdef NS_BUILD_REFCNT_LOGGING 391 new FontTableRec(dataRef), 392#else 393 (void*)dataRef, 394#endif 395 DestroyBlobFunc); 396 } 397 398 return nullptr; 399} 400 401bool 402MacOSFontEntry::HasFontTable(uint32_t aTableTag) 403{ 404 if (mAvailableTables.Count() == 0) { 405 nsAutoreleasePool localPool; 406 407 CGFontRef fontRef = GetFontRef(); 408 if (!fontRef) { 409 return false; 410 } 411 CFArrayRef tags = ::CGFontCopyTableTags(fontRef); 412 if (!tags) { 413 return false; 414 } 415 int numTags = (int) ::CFArrayGetCount(tags); 416 for (int t = 0; t < numTags; t++) { 417 uint32_t tag = (uint32_t)(uintptr_t)::CFArrayGetValueAtIndex(tags, t); 418 mAvailableTables.PutEntry(tag); 419 } 420 ::CFRelease(tags); 421 } 422 423 return mAvailableTables.GetEntry(aTableTag); 424} 425 426void 427MacOSFontEntry::AddSizeOfIncludingThis(MallocSizeOf aMallocSizeOf, 428 FontListSizes* aSizes) const 429{ 430 aSizes->mFontListSize += aMallocSizeOf(this); 431 AddSizeOfExcludingThis(aMallocSizeOf, aSizes); 432} 433 434/* gfxMacFontFamily */ 435#pragma mark- 436 437class gfxMacFontFamily : public gfxFontFamily 438{ 439public: 440 explicit gfxMacFontFamily(nsAString& aName, double aSizeHint) : 441 gfxFontFamily(aName), 442 mSizeHint(aSizeHint) 443 {} 444 445 virtual ~gfxMacFontFamily() {} 446 447 virtual void LocalizedName(nsAString& aLocalizedName); 448 449 virtual void FindStyleVariations(FontInfoData *aFontInfoData = nullptr); 450 451protected: 452 double mSizeHint; 453}; 454 455void 456gfxMacFontFamily::LocalizedName(nsAString& aLocalizedName) 457{ 458 nsAutoreleasePool localPool; 459 460 if (!HasOtherFamilyNames()) { 461 aLocalizedName = mName; 462 return; 463 } 464 465 NSString *family = GetNSStringForString(mName); 466 NSString *localized = [sFontManager 467 localizedNameForFamily:family 468 face:nil]; 469 470 if (localized) { 471 GetStringForNSString(localized, aLocalizedName); 472 return; 473 } 474 475 // failed to get localized name, just use the canonical one 476 aLocalizedName = mName; 477} 478 479// Return the CSS weight value to use for the given face, overriding what 480// AppKit gives us (used to adjust families with bad weight values, see 481// bug 931426). 482// A return value of 0 indicates no override - use the existing weight. 483static inline int 484GetWeightOverride(const nsAString& aPSName) 485{ 486 nsAutoCString prefName("font.weight-override."); 487 // The PostScript name is required to be ASCII; if it's not, the font is 488 // broken anyway, so we really don't care that this is lossy. 489 LossyAppendUTF16toASCII(aPSName, prefName); 490 return Preferences::GetInt(prefName.get(), 0); 491} 492 493void 494gfxMacFontFamily::FindStyleVariations(FontInfoData *aFontInfoData) 495{ 496 if (mHasStyles) 497 return; 498 499 nsAutoreleasePool localPool; 500 501 NSString *family = GetNSStringForString(mName); 502 503 // create a font entry for each face 504 NSArray *fontfaces = [sFontManager 505 availableMembersOfFontFamily:family]; // returns an array of [psname, style name, weight, traits] elements, goofy api 506 int faceCount = [fontfaces count]; 507 int faceIndex; 508 509 for (faceIndex = 0; faceIndex < faceCount; faceIndex++) { 510 NSArray *face = [fontfaces objectAtIndex:faceIndex]; 511 NSString *psname = [face objectAtIndex:INDEX_FONT_POSTSCRIPT_NAME]; 512 int32_t appKitWeight = [[face objectAtIndex:INDEX_FONT_WEIGHT] unsignedIntValue]; 513 uint32_t macTraits = [[face objectAtIndex:INDEX_FONT_TRAITS] unsignedIntValue]; 514 NSString *facename = [face objectAtIndex:INDEX_FONT_FACE_NAME]; 515 bool isStandardFace = false; 516 517 if (appKitWeight == kAppleExtraLightWeight) { 518 // if the facename contains UltraLight, set the weight to the ultralight weight value 519 NSRange range = [facename rangeOfString:@"ultralight" options:NSCaseInsensitiveSearch]; 520 if (range.location != NSNotFound) { 521 appKitWeight = kAppleUltraLightWeight; 522 } 523 } 524 525 // make a nsString 526 nsAutoString postscriptFontName; 527 GetStringForNSString(psname, postscriptFontName); 528 529 int32_t cssWeight = GetWeightOverride(postscriptFontName); 530 if (cssWeight) { 531 // scale down and clamp, to get a value from 1..9 532 cssWeight = ((cssWeight + 50) / 100); 533 cssWeight = std::max(1, std::min(cssWeight, 9)); 534 } else { 535 cssWeight = 536 gfxMacPlatformFontList::AppleWeightToCSSWeight(appKitWeight); 537 } 538 cssWeight *= 100; // scale up to CSS values 539 540 if ([facename isEqualToString:@"Regular"] || 541 [facename isEqualToString:@"Bold"] || 542 [facename isEqualToString:@"Italic"] || 543 [facename isEqualToString:@"Oblique"] || 544 [facename isEqualToString:@"Bold Italic"] || 545 [facename isEqualToString:@"Bold Oblique"]) 546 { 547 isStandardFace = true; 548 } 549 550 // create a font entry 551 MacOSFontEntry *fontEntry = 552 new MacOSFontEntry(postscriptFontName, cssWeight, isStandardFace, mSizeHint); 553 if (!fontEntry) { 554 break; 555 } 556 557 // set additional properties based on the traits reported by Cocoa 558 if (macTraits & (NSCondensedFontMask | NSNarrowFontMask | NSCompressedFontMask)) { 559 fontEntry->mStretch = NS_FONT_STRETCH_CONDENSED; 560 } else if (macTraits & NSExpandedFontMask) { 561 fontEntry->mStretch = NS_FONT_STRETCH_EXPANDED; 562 } 563 // Cocoa fails to set the Italic traits bit for HelveticaLightItalic, 564 // at least (see bug 611855), so check for style name endings as well 565 if ((macTraits & NSItalicFontMask) || 566 [facename hasSuffix:@"Italic"] || 567 [facename hasSuffix:@"Oblique"]) 568 { 569 fontEntry->mStyle = NS_FONT_STYLE_ITALIC; 570 } 571 if (macTraits & NSFixedPitchFontMask) { 572 fontEntry->mFixedPitch = true; 573 } 574 575 if (LOG_FONTLIST_ENABLED()) { 576 LOG_FONTLIST(("(fontlist) added (%s) to family (%s)" 577 " with style: %s weight: %d stretch: %d" 578 " (apple-weight: %d macTraits: %8.8x)", 579 NS_ConvertUTF16toUTF8(fontEntry->Name()).get(), 580 NS_ConvertUTF16toUTF8(Name()).get(), 581 fontEntry->IsItalic() ? "italic" : "normal", 582 cssWeight, fontEntry->Stretch(), 583 appKitWeight, macTraits)); 584 } 585 586 // insert into font entry array of family 587 AddFontEntry(fontEntry); 588 } 589 590 SortAvailableFonts(); 591 SetHasStyles(true); 592 593 if (mIsBadUnderlineFamily) { 594 SetBadUnderlineFonts(); 595 } 596} 597 598/* gfxSingleFaceMacFontFamily */ 599#pragma mark- 600 601class gfxSingleFaceMacFontFamily : public gfxFontFamily 602{ 603public: 604 explicit gfxSingleFaceMacFontFamily(nsAString& aName) : 605 gfxFontFamily(aName) 606 { 607 mFaceNamesInitialized = true; // omit from face name lists 608 } 609 610 virtual ~gfxSingleFaceMacFontFamily() {} 611 612 virtual void LocalizedName(nsAString& aLocalizedName); 613 614 virtual void ReadOtherFamilyNames(gfxPlatformFontList *aPlatformFontList); 615}; 616 617void 618gfxSingleFaceMacFontFamily::LocalizedName(nsAString& aLocalizedName) 619{ 620 nsAutoreleasePool localPool; 621 622 if (!HasOtherFamilyNames()) { 623 aLocalizedName = mName; 624 return; 625 } 626 627 gfxFontEntry *fe = mAvailableFonts[0]; 628 NSFont *font = [NSFont fontWithName:GetNSStringForString(fe->Name()) 629 size:0.0]; 630 if (font) { 631 NSString *localized = [font displayName]; 632 if (localized) { 633 GetStringForNSString(localized, aLocalizedName); 634 return; 635 } 636 } 637 638 // failed to get localized name, just use the canonical one 639 aLocalizedName = mName; 640} 641 642void 643gfxSingleFaceMacFontFamily::ReadOtherFamilyNames(gfxPlatformFontList *aPlatformFontList) 644{ 645 if (mOtherFamilyNamesInitialized) { 646 return; 647 } 648 649 gfxFontEntry *fe = mAvailableFonts[0]; 650 if (!fe) { 651 return; 652 } 653 654 const uint32_t kNAME = TRUETYPE_TAG('n','a','m','e'); 655 656 gfxFontEntry::AutoTable nameTable(fe, kNAME); 657 if (!nameTable) { 658 return; 659 } 660 661 mHasOtherFamilyNames = ReadOtherFamilyNamesForFace(aPlatformFontList, 662 nameTable, 663 true); 664 665 mOtherFamilyNamesInitialized = true; 666} 667 668/* gfxMacPlatformFontList */ 669#pragma mark- 670 671gfxMacPlatformFontList::gfxMacPlatformFontList() : 672 gfxPlatformFontList(false), 673 mDefaultFont(nullptr), 674 mUseSizeSensitiveSystemFont(false) 675{ 676#ifdef MOZ_BUNDLED_FONTS 677 ActivateBundledFonts(); 678#endif 679 680 ::CFNotificationCenterAddObserver(::CFNotificationCenterGetLocalCenter(), 681 this, 682 RegisteredFontsChangedNotificationCallback, 683 kCTFontManagerRegisteredFontsChangedNotification, 684 0, 685 CFNotificationSuspensionBehaviorDeliverImmediately); 686 687 // cache this in a static variable so that MacOSFontFamily objects 688 // don't have to repeatedly look it up 689 sFontManager = [NSFontManager sharedFontManager]; 690} 691 692gfxMacPlatformFontList::~gfxMacPlatformFontList() 693{ 694 if (mDefaultFont) { 695 ::CFRelease(mDefaultFont); 696 } 697} 698 699void 700gfxMacPlatformFontList::AddFamily(CFStringRef aFamily) 701{ 702 NSString* family = (NSString*)aFamily; 703 704 // CTFontManager includes weird internal family names and 705 // LastResort, skip over those 706 if (!family || [family caseInsensitiveCompare:@"LastResort"] == NSOrderedSame) { 707 return; 708 } 709 710 bool hiddenSystemFont = [family hasPrefix:@"."]; 711 712 FontFamilyTable& table = 713 hiddenSystemFont ? mSystemFontFamilies : mFontFamilies; 714 715 nsAutoString familyName; 716 nsCocoaUtils::GetStringForNSString(family, familyName); 717 718 double sizeHint = 0.0; 719 if (hiddenSystemFont && mUseSizeSensitiveSystemFont && 720 mSystemDisplayFontFamilyName.Equals(familyName)) { 721 sizeHint = 128.0; 722 } 723 724 nsAutoString key; 725 ToLowerCase(familyName, key); 726 727 RefPtr<gfxFontFamily> familyEntry = new gfxMacFontFamily(familyName, sizeHint); 728 table.Put(key, familyEntry); 729 730 // check the bad underline blacklist 731 if (mBadUnderlineFamilyNames.Contains(key)) { 732 familyEntry->SetBadUnderlineFamily(); 733 } 734} 735 736nsresult 737gfxMacPlatformFontList::InitFontListForPlatform() 738{ 739 nsAutoreleasePool localPool; 740 741 Telemetry::AutoTimer<Telemetry::MAC_INITFONTLIST_TOTAL> timer; 742 743 // reset system font list 744 mSystemFontFamilies.Clear(); 745 746 // iterate over available families 747 748 InitSystemFontNames(); 749 750 CFArrayRef familyNames = CTFontManagerCopyAvailableFontFamilyNames(); 751 752 for (NSString* familyName in (NSArray*)familyNames) { 753 AddFamily((CFStringRef)familyName); 754 } 755 756 CFRelease(familyNames); 757 758 InitSingleFaceList(); 759 760 // to avoid full search of font name tables, seed the other names table with localized names from 761 // some of the prefs fonts which are accessed via their localized names. changes in the pref fonts will only cause 762 // a font lookup miss earlier. this is a simple optimization, it's not required for correctness 763 PreloadNamesList(); 764 765 // start the delayed cmap loader 766 GetPrefsAndStartLoader(); 767 768 return NS_OK; 769} 770 771void 772gfxMacPlatformFontList::InitSingleFaceList() 773{ 774 AutoTArray<nsString, 10> singleFaceFonts; 775 gfxFontUtils::GetPrefsFontList("font.single-face-list", singleFaceFonts); 776 777 for (const auto& singleFaceFamily : singleFaceFonts) { 778 LOG_FONTLIST(("(fontlist-singleface) face name: %s\n", 779 NS_ConvertUTF16toUTF8(singleFaceFamily).get())); 780 // Each entry in the "single face families" list is expected to be a 781 // colon-separated pair of FaceName:Family, 782 // where FaceName is the individual face name (psname) of a font 783 // that should be exposed as a separate family name, 784 // and Family is the standard family to which that face belongs. 785 // The only such face listed by default is 786 // Osaka-Mono:Osaka 787 nsAutoString familyName(singleFaceFamily); 788 auto colon = familyName.FindChar(':'); 789 if (colon == kNotFound) { 790 continue; 791 } 792 793 // Look for the parent family in the main font family list, 794 // and ensure we have loaded its list of available faces. 795 nsAutoString key(Substring(familyName, colon + 1)); 796 ToLowerCase(key); 797 gfxFontFamily* family = mFontFamilies.GetWeak(key); 798 if (!family) { 799 continue; 800 } 801 family->FindStyleVariations(); 802 803 // Truncate the entry from prefs at the colon, so now it is just the 804 // desired single-face-family name. 805 familyName.Truncate(colon); 806 807 // Look through the family's faces to see if this one is present. 808 const gfxFontEntry* fe = nullptr; 809 for (const auto& face : family->GetFontList()) { 810 if (face->Name().Equals(familyName)) { 811 fe = face; 812 break; 813 } 814 } 815 if (!fe) { 816 continue; 817 } 818 819 // We found the correct face, so create the single-face family record. 820 GenerateFontListKey(familyName, key); 821 LOG_FONTLIST(("(fontlist-singleface) family name: %s, key: %s\n", 822 NS_ConvertUTF16toUTF8(familyName).get(), 823 NS_ConvertUTF16toUTF8(key).get())); 824 825 // add only if doesn't exist already 826 if (!mFontFamilies.GetWeak(key)) { 827 RefPtr<gfxFontFamily> familyEntry = 828 new gfxSingleFaceMacFontFamily(familyName); 829 // We need a separate font entry, because its family name will 830 // differ from the one we found in the main list. 831 MacOSFontEntry* fontEntry = 832 new MacOSFontEntry(fe->Name(), fe->mWeight, true, 833 static_cast<const MacOSFontEntry*>(fe)-> 834 mSizeHint); 835 familyEntry->AddFontEntry(fontEntry); 836 familyEntry->SetHasStyles(true); 837 mFontFamilies.Put(key, familyEntry); 838 LOG_FONTLIST(("(fontlist-singleface) added new family\n", 839 NS_ConvertUTF16toUTF8(familyName).get(), 840 NS_ConvertUTF16toUTF8(key).get())); 841 } 842 } 843} 844 845// System fonts under OSX may contain weird "meta" names but if we create 846// a new font using just the Postscript name, the NSFont api returns an object 847// with the actual real family name. For example, under OSX 10.11: 848// 849// [[NSFont menuFontOfSize:8.0] familyName] ==> .AppleSystemUIFont 850// [[NSFont fontWithName:[[[NSFont menuFontOfSize:8.0] fontDescriptor] postscriptName] 851// size:8.0] familyName] ==> .SF NS Text 852 853static NSString* GetRealFamilyName(NSFont* aFont) 854{ 855 NSFont* f = [NSFont fontWithName: [[aFont fontDescriptor] postscriptName] 856 size: 0.0]; 857 return [f familyName]; 858} 859 860// System fonts under OSX 10.11 use a combination of two families, one 861// for text sizes and another for larger, display sizes. Each has a 862// different number of weights. There aren't efficient API's for looking 863// this information up, so hard code the logic here but confirm via 864// debug assertions that the logic is correct. 865 866const CGFloat kTextDisplayCrossover = 20.0; // use text family below this size 867 868void 869gfxMacPlatformFontList::InitSystemFontNames() 870{ 871 // system font under 10.11 are two distinct families for text/display sizes 872 if (nsCocoaFeatures::OnElCapitanOrLater()) { 873 mUseSizeSensitiveSystemFont = true; 874 } 875 876 // text font family 877 NSFont* sys = [NSFont systemFontOfSize: 0.0]; 878 NSString* textFamilyName = GetRealFamilyName(sys); 879 nsAutoString familyName; 880 nsCocoaUtils::GetStringForNSString(textFamilyName, familyName); 881 mSystemTextFontFamilyName = familyName; 882 883 // display font family, if on OSX 10.11 884 if (mUseSizeSensitiveSystemFont) { 885 NSFont* displaySys = [NSFont systemFontOfSize: 128.0]; 886 NSString* displayFamilyName = GetRealFamilyName(displaySys); 887 nsCocoaUtils::GetStringForNSString(displayFamilyName, familyName); 888 mSystemDisplayFontFamilyName = familyName; 889 890#if DEBUG 891 // confirm that the optical size switch is at 20.0 892 NS_ASSERTION([textFamilyName compare:displayFamilyName] != NSOrderedSame, 893 "system text/display fonts are the same!"); 894 NSString* fam19 = GetRealFamilyName([NSFont systemFontOfSize: 895 (kTextDisplayCrossover - 1.0)]); 896 NSString* fam20 = GetRealFamilyName([NSFont systemFontOfSize: 897 kTextDisplayCrossover]); 898 NS_ASSERTION(fam19 && fam20 && [fam19 compare:fam20] != NSOrderedSame, 899 "system text/display font size switch point is not as expected!"); 900#endif 901 } 902 903#ifdef DEBUG 904 // different system font API's always map to the same family under OSX, so 905 // just assume that and emit a warning if that ever changes 906 NSString *sysFamily = GetRealFamilyName([NSFont systemFontOfSize:0.0]); 907 if ([sysFamily compare:GetRealFamilyName([NSFont boldSystemFontOfSize:0.0])] != NSOrderedSame || 908 [sysFamily compare:GetRealFamilyName([NSFont controlContentFontOfSize:0.0])] != NSOrderedSame || 909 [sysFamily compare:GetRealFamilyName([NSFont menuBarFontOfSize:0.0])] != NSOrderedSame || 910 [sysFamily compare:GetRealFamilyName([NSFont toolTipsFontOfSize:0.0])] != NSOrderedSame) { 911 NS_WARNING("system font types map to different font families" 912 " -- please log a bug!!"); 913 } 914#endif 915} 916 917gfxFontFamily* 918gfxMacPlatformFontList::FindSystemFontFamily(const nsAString& aFamily) 919{ 920 nsAutoString key; 921 GenerateFontListKey(aFamily, key); 922 923 gfxFontFamily* familyEntry; 924 925 // lookup in hidden system family name list 926 if ((familyEntry = mSystemFontFamilies.GetWeak(key))) { 927 return CheckFamily(familyEntry); 928 } 929 930 // lookup in user-exposed family name list 931 if ((familyEntry = mFontFamilies.GetWeak(key))) { 932 return CheckFamily(familyEntry); 933 } 934 935 return nullptr; 936} 937 938bool 939gfxMacPlatformFontList::GetStandardFamilyName(const nsAString& aFontName, nsAString& aFamilyName) 940{ 941 gfxFontFamily *family = FindFamily(aFontName); 942 if (family) { 943 family->LocalizedName(aFamilyName); 944 return true; 945 } 946 947 return false; 948} 949 950void 951gfxMacPlatformFontList::RegisteredFontsChangedNotificationCallback(CFNotificationCenterRef center, 952 void *observer, 953 CFStringRef name, 954 const void *object, 955 CFDictionaryRef userInfo) 956{ 957 if (!::CFEqual(name, kCTFontManagerRegisteredFontsChangedNotification)) { 958 return; 959 } 960 961 gfxMacPlatformFontList* fl = static_cast<gfxMacPlatformFontList*>(observer); 962 963 // xxx - should be carefully pruning the list of fonts, not rebuilding it from scratch 964 fl->UpdateFontList(); 965 966 // modify a preference that will trigger reflow everywhere 967 fl->ForceGlobalReflow(); 968} 969 970gfxFontEntry* 971gfxMacPlatformFontList::PlatformGlobalFontFallback(const uint32_t aCh, 972 Script aRunScript, 973 const gfxFontStyle* aMatchStyle, 974 gfxFontFamily** aMatchedFamily) 975{ 976 CFStringRef str; 977 UniChar ch[2]; 978 CFIndex length = 1; 979 980 if (IS_IN_BMP(aCh)) { 981 ch[0] = aCh; 982 str = ::CFStringCreateWithCharactersNoCopy(kCFAllocatorDefault, ch, 1, 983 kCFAllocatorNull); 984 } else { 985 ch[0] = H_SURROGATE(aCh); 986 ch[1] = L_SURROGATE(aCh); 987 str = ::CFStringCreateWithCharactersNoCopy(kCFAllocatorDefault, ch, 2, 988 kCFAllocatorNull); 989 if (!str) { 990 return nullptr; 991 } 992 length = 2; 993 } 994 995 // use CoreText to find the fallback family 996 997 gfxFontEntry *fontEntry = nullptr; 998 CTFontRef fallback; 999 bool cantUseFallbackFont = false; 1000 1001 if (!mDefaultFont) { 1002 mDefaultFont = ::CTFontCreateWithName(CFSTR("LucidaGrande"), 12.f, 1003 NULL); 1004 } 1005 1006 fallback = ::CTFontCreateForString(mDefaultFont, str, 1007 ::CFRangeMake(0, length)); 1008 1009 if (fallback) { 1010 CFStringRef familyNameRef = ::CTFontCopyFamilyName(fallback); 1011 ::CFRelease(fallback); 1012 1013 if (familyNameRef && 1014 ::CFStringCompare(familyNameRef, CFSTR("LastResort"), 1015 kCFCompareCaseInsensitive) != kCFCompareEqualTo) 1016 { 1017 AutoTArray<UniChar, 1024> buffer; 1018 CFIndex familyNameLen = ::CFStringGetLength(familyNameRef); 1019 buffer.SetLength(familyNameLen+1); 1020 ::CFStringGetCharacters(familyNameRef, ::CFRangeMake(0, familyNameLen), 1021 buffer.Elements()); 1022 buffer[familyNameLen] = 0; 1023 nsDependentString familyNameString(reinterpret_cast<char16_t*>(buffer.Elements()), familyNameLen); 1024 1025 bool needsBold; // ignored in the system fallback case 1026 1027 gfxFontFamily *family = FindFamily(familyNameString); 1028 if (family) { 1029 fontEntry = family->FindFontForStyle(*aMatchStyle, needsBold); 1030 if (fontEntry) { 1031 if (fontEntry->HasCharacter(aCh)) { 1032 *aMatchedFamily = family; 1033 } else { 1034 fontEntry = nullptr; 1035 cantUseFallbackFont = true; 1036 } 1037 } 1038 } 1039 } 1040 1041 if (familyNameRef) { 1042 ::CFRelease(familyNameRef); 1043 } 1044 } 1045 1046 if (cantUseFallbackFont) { 1047 Telemetry::Accumulate(Telemetry::BAD_FALLBACK_FONT, cantUseFallbackFont); 1048 } 1049 1050 ::CFRelease(str); 1051 1052 return fontEntry; 1053} 1054 1055gfxFontFamily* 1056gfxMacPlatformFontList::GetDefaultFontForPlatform(const gfxFontStyle* aStyle) 1057{ 1058 nsAutoreleasePool localPool; 1059 1060 NSString *defaultFamily = [[NSFont userFontOfSize:aStyle->size] familyName]; 1061 nsAutoString familyName; 1062 1063 GetStringForNSString(defaultFamily, familyName); 1064 return FindFamily(familyName); 1065} 1066 1067int32_t 1068gfxMacPlatformFontList::AppleWeightToCSSWeight(int32_t aAppleWeight) 1069{ 1070 if (aAppleWeight < 1) 1071 aAppleWeight = 1; 1072 else if (aAppleWeight > kAppleMaxWeight) 1073 aAppleWeight = kAppleMaxWeight; 1074 return gAppleWeightToCSSWeight[aAppleWeight]; 1075} 1076 1077gfxFontEntry* 1078gfxMacPlatformFontList::LookupLocalFont(const nsAString& aFontName, 1079 uint16_t aWeight, 1080 int16_t aStretch, 1081 uint8_t aStyle) 1082{ 1083 nsAutoreleasePool localPool; 1084 1085 NSString *faceName = GetNSStringForString(aFontName); 1086 MacOSFontEntry *newFontEntry; 1087 1088 // lookup face based on postscript or full name 1089 CGFontRef fontRef = ::CGFontCreateWithFontName(CFStringRef(faceName)); 1090 if (!fontRef) { 1091 return nullptr; 1092 } 1093 1094 NS_ASSERTION(aWeight >= 100 && aWeight <= 900, 1095 "bogus font weight value!"); 1096 1097 newFontEntry = 1098 new MacOSFontEntry(aFontName, fontRef, aWeight, aStretch, aStyle, 1099 false, true); 1100 ::CFRelease(fontRef); 1101 1102 return newFontEntry; 1103} 1104 1105static void ReleaseData(void *info, const void *data, size_t size) 1106{ 1107 free((void*)data); 1108} 1109 1110gfxFontEntry* 1111gfxMacPlatformFontList::MakePlatformFont(const nsAString& aFontName, 1112 uint16_t aWeight, 1113 int16_t aStretch, 1114 uint8_t aStyle, 1115 const uint8_t* aFontData, 1116 uint32_t aLength) 1117{ 1118 NS_ASSERTION(aFontData, "MakePlatformFont called with null data"); 1119 1120 NS_ASSERTION(aWeight >= 100 && aWeight <= 900, "bogus font weight value!"); 1121 1122 // create the font entry 1123 nsAutoString uniqueName; 1124 1125 nsresult rv = gfxFontUtils::MakeUniqueUserFontName(uniqueName); 1126 if (NS_FAILED(rv)) { 1127 return nullptr; 1128 } 1129 1130 CGDataProviderRef provider = 1131 ::CGDataProviderCreateWithData(nullptr, aFontData, aLength, 1132 &ReleaseData); 1133 CGFontRef fontRef = ::CGFontCreateWithDataProvider(provider); 1134 ::CGDataProviderRelease(provider); 1135 1136 if (!fontRef) { 1137 return nullptr; 1138 } 1139 1140 auto newFontEntry = 1141 MakeUnique<MacOSFontEntry>(uniqueName, fontRef, aWeight, aStretch, 1142 aStyle, true, false); 1143 ::CFRelease(fontRef); 1144 1145 // if succeeded and font cmap is good, return the new font 1146 if (newFontEntry->mIsValid && NS_SUCCEEDED(newFontEntry->ReadCMAP())) { 1147 return newFontEntry.release(); 1148 } 1149 1150 // if something is funky about this font, delete immediately 1151 1152#if DEBUG 1153 NS_WARNING("downloaded font not loaded properly"); 1154#endif 1155 1156 return nullptr; 1157} 1158 1159// Webkit code uses a system font meta name, so mimic that here 1160// WebCore/platform/graphics/mac/FontCacheMac.mm 1161static const char kSystemFont_system[] = "-apple-system"; 1162 1163bool 1164gfxMacPlatformFontList::FindAndAddFamilies(const nsAString& aFamily, 1165 nsTArray<gfxFontFamily*>* aOutput, 1166 gfxFontStyle* aStyle, 1167 gfxFloat aDevToCssSize) 1168{ 1169 // search for special system font name, -apple-system 1170 if (aFamily.EqualsLiteral(kSystemFont_system)) { 1171 if (mUseSizeSensitiveSystemFont && 1172 aStyle && (aStyle->size * aDevToCssSize) >= kTextDisplayCrossover) { 1173 aOutput->AppendElement(FindSystemFontFamily(mSystemDisplayFontFamilyName)); 1174 return true; 1175 } 1176 aOutput->AppendElement(FindSystemFontFamily(mSystemTextFontFamilyName)); 1177 return true; 1178 } 1179 1180 return gfxPlatformFontList::FindAndAddFamilies(aFamily, aOutput, aStyle, 1181 aDevToCssSize); 1182} 1183 1184void 1185gfxMacPlatformFontList::LookupSystemFont(LookAndFeel::FontID aSystemFontID, 1186 nsAString& aSystemFontName, 1187 gfxFontStyle &aFontStyle, 1188 float aDevPixPerCSSPixel) 1189{ 1190 // code moved here from widget/cocoa/nsLookAndFeel.mm 1191 NSFont *font = nullptr; 1192 char* systemFontName = nullptr; 1193 switch (aSystemFontID) { 1194 case LookAndFeel::eFont_MessageBox: 1195 case LookAndFeel::eFont_StatusBar: 1196 case LookAndFeel::eFont_List: 1197 case LookAndFeel::eFont_Field: 1198 case LookAndFeel::eFont_Button: 1199 case LookAndFeel::eFont_Widget: 1200 font = [NSFont systemFontOfSize:[NSFont smallSystemFontSize]]; 1201 systemFontName = (char*) kSystemFont_system; 1202 break; 1203 1204 case LookAndFeel::eFont_SmallCaption: 1205 font = [NSFont boldSystemFontOfSize:[NSFont smallSystemFontSize]]; 1206 systemFontName = (char*) kSystemFont_system; 1207 break; 1208 1209 case LookAndFeel::eFont_Icon: // used in urlbar; tried labelFont, but too small 1210 case LookAndFeel::eFont_Workspace: 1211 case LookAndFeel::eFont_Desktop: 1212 case LookAndFeel::eFont_Info: 1213 font = [NSFont controlContentFontOfSize:0.0]; 1214 systemFontName = (char*) kSystemFont_system; 1215 break; 1216 1217 case LookAndFeel::eFont_PullDownMenu: 1218 font = [NSFont menuBarFontOfSize:0.0]; 1219 systemFontName = (char*) kSystemFont_system; 1220 break; 1221 1222 case LookAndFeel::eFont_Tooltips: 1223 font = [NSFont toolTipsFontOfSize:0.0]; 1224 systemFontName = (char*) kSystemFont_system; 1225 break; 1226 1227 case LookAndFeel::eFont_Caption: 1228 case LookAndFeel::eFont_Menu: 1229 case LookAndFeel::eFont_Dialog: 1230 default: 1231 font = [NSFont systemFontOfSize:0.0]; 1232 systemFontName = (char*) kSystemFont_system; 1233 break; 1234 } 1235 NS_ASSERTION(font, "system font not set"); 1236 NS_ASSERTION(systemFontName, "system font name not set"); 1237 1238 if (systemFontName) { 1239 aSystemFontName.AssignASCII(systemFontName); 1240 } 1241 1242 NSFontSymbolicTraits traits = [[font fontDescriptor] symbolicTraits]; 1243 aFontStyle.style = 1244 (traits & NSFontItalicTrait) ? NS_FONT_STYLE_ITALIC : NS_FONT_STYLE_NORMAL; 1245 aFontStyle.weight = 1246 (traits & NSFontBoldTrait) ? NS_FONT_WEIGHT_BOLD : NS_FONT_WEIGHT_NORMAL; 1247 aFontStyle.stretch = 1248 (traits & NSFontExpandedTrait) ? 1249 NS_FONT_STRETCH_EXPANDED : (traits & NSFontCondensedTrait) ? 1250 NS_FONT_STRETCH_CONDENSED : NS_FONT_STRETCH_NORMAL; 1251 // convert size from css pixels to device pixels 1252 aFontStyle.size = [font pointSize] * aDevPixPerCSSPixel; 1253 aFontStyle.systemFont = true; 1254} 1255 1256// used to load system-wide font info on off-main thread 1257class MacFontInfo : public FontInfoData { 1258public: 1259 MacFontInfo(bool aLoadOtherNames, 1260 bool aLoadFaceNames, 1261 bool aLoadCmaps) : 1262 FontInfoData(aLoadOtherNames, aLoadFaceNames, aLoadCmaps) 1263 {} 1264 1265 virtual ~MacFontInfo() {} 1266 1267 virtual void Load() { 1268 nsAutoreleasePool localPool; 1269 FontInfoData::Load(); 1270 } 1271 1272 // loads font data for all members of a given family 1273 virtual void LoadFontFamilyData(const nsAString& aFamilyName); 1274}; 1275 1276void 1277MacFontInfo::LoadFontFamilyData(const nsAString& aFamilyName) 1278{ 1279 // family name ==> CTFontDescriptor 1280 NSString *famName = GetNSStringForString(aFamilyName); 1281 CFStringRef family = CFStringRef(famName); 1282 1283 CFMutableDictionaryRef attr = 1284 CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, 1285 &kCFTypeDictionaryValueCallBacks); 1286 CFDictionaryAddValue(attr, kCTFontFamilyNameAttribute, family); 1287 CTFontDescriptorRef fd = CTFontDescriptorCreateWithAttributes(attr); 1288 CFRelease(attr); 1289 CFArrayRef matchingFonts = 1290 CTFontDescriptorCreateMatchingFontDescriptors(fd, NULL); 1291 CFRelease(fd); 1292 if (!matchingFonts) { 1293 return; 1294 } 1295 1296 nsTArray<nsString> otherFamilyNames; 1297 bool hasOtherFamilyNames = true; 1298 1299 // iterate over faces in the family 1300 int f, numFaces = (int) CFArrayGetCount(matchingFonts); 1301 for (f = 0; f < numFaces; f++) { 1302 mLoadStats.fonts++; 1303 1304 CTFontDescriptorRef faceDesc = 1305 (CTFontDescriptorRef)CFArrayGetValueAtIndex(matchingFonts, f); 1306 if (!faceDesc) { 1307 continue; 1308 } 1309 CTFontRef fontRef = CTFontCreateWithFontDescriptor(faceDesc, 1310 0.0, nullptr); 1311 if (!fontRef) { 1312 NS_WARNING("failed to create a CTFontRef"); 1313 continue; 1314 } 1315 1316 if (mLoadCmaps) { 1317 // face name 1318 CFStringRef faceName = (CFStringRef) 1319 CTFontDescriptorCopyAttribute(faceDesc, kCTFontNameAttribute); 1320 1321 AutoTArray<UniChar, 1024> buffer; 1322 CFIndex len = CFStringGetLength(faceName); 1323 buffer.SetLength(len+1); 1324 CFStringGetCharacters(faceName, ::CFRangeMake(0, len), 1325 buffer.Elements()); 1326 buffer[len] = 0; 1327 nsAutoString fontName(reinterpret_cast<char16_t*>(buffer.Elements()), 1328 len); 1329 1330 // load the cmap data 1331 FontFaceData fontData; 1332 CFDataRef cmapTable = CTFontCopyTable(fontRef, kCTFontTableCmap, 1333 kCTFontTableOptionNoOptions); 1334 1335 if (cmapTable) { 1336 const uint8_t *cmapData = 1337 (const uint8_t*)CFDataGetBytePtr(cmapTable); 1338 uint32_t cmapLen = CFDataGetLength(cmapTable); 1339 RefPtr<gfxCharacterMap> charmap = new gfxCharacterMap(); 1340 uint32_t offset; 1341 bool unicodeFont = false; // ignored 1342 bool symbolFont = false; 1343 nsresult rv; 1344 1345 rv = gfxFontUtils::ReadCMAP(cmapData, cmapLen, *charmap, offset, 1346 unicodeFont, symbolFont); 1347 if (NS_SUCCEEDED(rv)) { 1348 fontData.mCharacterMap = charmap; 1349 fontData.mUVSOffset = offset; 1350 fontData.mSymbolFont = symbolFont; 1351 mLoadStats.cmaps++; 1352 } 1353 CFRelease(cmapTable); 1354 } 1355 1356 mFontFaceData.Put(fontName, fontData); 1357 CFRelease(faceName); 1358 } 1359 1360 if (mLoadOtherNames && hasOtherFamilyNames) { 1361 CFDataRef nameTable = CTFontCopyTable(fontRef, kCTFontTableName, 1362 kCTFontTableOptionNoOptions); 1363 1364 if (nameTable) { 1365 const char *nameData = (const char*)CFDataGetBytePtr(nameTable); 1366 uint32_t nameLen = CFDataGetLength(nameTable); 1367 gfxFontFamily::ReadOtherFamilyNamesForFace(aFamilyName, 1368 nameData, nameLen, 1369 otherFamilyNames, 1370 false); 1371 hasOtherFamilyNames = otherFamilyNames.Length() != 0; 1372 CFRelease(nameTable); 1373 } 1374 } 1375 1376 CFRelease(fontRef); 1377 } 1378 CFRelease(matchingFonts); 1379 1380 // if found other names, insert them in the hash table 1381 if (otherFamilyNames.Length() != 0) { 1382 mOtherFamilyNames.Put(aFamilyName, otherFamilyNames); 1383 mLoadStats.othernames += otherFamilyNames.Length(); 1384 } 1385} 1386 1387already_AddRefed<FontInfoData> 1388gfxMacPlatformFontList::CreateFontInfoData() 1389{ 1390 bool loadCmaps = !UsesSystemFallback() || 1391 gfxPlatform::GetPlatform()->UseCmapsDuringSystemFallback(); 1392 1393 RefPtr<MacFontInfo> fi = 1394 new MacFontInfo(true, NeedFullnamePostscriptNames(), loadCmaps); 1395 return fi.forget(); 1396} 1397 1398#ifdef MOZ_BUNDLED_FONTS 1399 1400void 1401gfxMacPlatformFontList::ActivateBundledFonts() 1402{ 1403 nsCOMPtr<nsIFile> localDir; 1404 nsresult rv = NS_GetSpecialDirectory(NS_GRE_DIR, getter_AddRefs(localDir)); 1405 if (NS_FAILED(rv)) { 1406 return; 1407 } 1408 if (NS_FAILED(localDir->Append(NS_LITERAL_STRING("fonts")))) { 1409 return; 1410 } 1411 bool isDir; 1412 if (NS_FAILED(localDir->IsDirectory(&isDir)) || !isDir) { 1413 return; 1414 } 1415 1416 nsCOMPtr<nsISimpleEnumerator> e; 1417 rv = localDir->GetDirectoryEntries(getter_AddRefs(e)); 1418 if (NS_FAILED(rv)) { 1419 return; 1420 } 1421 1422 bool hasMore; 1423 while (NS_SUCCEEDED(e->HasMoreElements(&hasMore)) && hasMore) { 1424 nsCOMPtr<nsISupports> entry; 1425 if (NS_FAILED(e->GetNext(getter_AddRefs(entry)))) { 1426 break; 1427 } 1428 nsCOMPtr<nsIFile> file = do_QueryInterface(entry); 1429 if (!file) { 1430 continue; 1431 } 1432 nsCString path; 1433 if (NS_FAILED(file->GetNativePath(path))) { 1434 continue; 1435 } 1436 CFURLRef fontURL = 1437 ::CFURLCreateFromFileSystemRepresentation(kCFAllocatorDefault, 1438 (uint8_t*)path.get(), 1439 path.Length(), 1440 false); 1441 if (fontURL) { 1442 CFErrorRef error = nullptr; 1443 ::CTFontManagerRegisterFontsForURL(fontURL, 1444 kCTFontManagerScopeProcess, 1445 &error); 1446 ::CFRelease(fontURL); 1447 } 1448 } 1449} 1450 1451#endif 1452