1 /* -*- Mode: ObjC; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 * ***** BEGIN LICENSE BLOCK *****
5 * Copyright (C) 2006-2009 Mozilla Corporation. All rights reserved.
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>
13 * Copyright (C) 2006 Apple Computer, Inc. All rights reserved.
15 * Redistribution and use in source and binary forms, with or without
16 * modification, are permitted provided that the following conditions
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.
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.
39 * ***** END LICENSE BLOCK ***** */
42 #define FORCE_PR_LOG /* Allow logging in the release build */
48 #import <AppKit/AppKit.h>
50 #include "gfxPlatformMac.h"
51 #include "gfxMacPlatformFontList.h"
52 #include "gfxMacFont.h"
53 #include "gfxUserFontSet.h"
54 #include "harfbuzz/hb.h"
56 #include "nsServiceManagerUtils.h"
59 #include "nsDirectoryServiceUtils.h"
60 #include "nsDirectoryServiceDefs.h"
61 #include "nsAppDirectoryServiceDefs.h"
62 #include "nsISimpleEnumerator.h"
63 #include "nsCharTraits.h"
64 #include "nsCocoaFeatures.h"
65 #include "gfxFontConstants.h"
67 #include "mozilla/MemoryReporting.h"
68 #include "mozilla/Preferences.h"
69 #include "mozilla/Telemetry.h"
70 #include "mozilla/gfx/2D.h"
75 using namespace mozilla;
77 class nsAutoreleasePool {
81 mLocalPool = [[NSAutoreleasePool alloc] init];
88 NSAutoreleasePool *mLocalPool;
91 // indexes into the NSArray objects that the Cocoa font manager returns
92 // as the available members of a family
93 #define INDEX_FONT_POSTSCRIPT_NAME 0
94 #define INDEX_FONT_FACE_NAME 1
95 #define INDEX_FONT_WEIGHT 2
96 #define INDEX_FONT_TRAITS 3
98 static const int kAppleMaxWeight = 14;
99 static const int kAppleExtraLightWeight = 3;
100 static const int kAppleUltraLightWeight = 2;
102 static const int gAppleWeightToCSSWeight[] = {
105 1, // 2. W1, ultralight
106 2, // 3. W2, extralight
108 4, // 5. W4, semilight
111 6, // 8. W6, semibold
113 8, // 10. W8, extrabold
115 9, // 12. W9, ultrabold
120 // cache Cocoa's "shared font manager" for performance
121 static NSFontManager *sFontManager;
123 static void GetStringForNSString(const NSString *aSrc, nsAString& aDist)
125 aDist.SetLength([aSrc length]);
126 [aSrc getCharacters:reinterpret_cast<unichar*>(aDist.BeginWriting())];
129 static NSString* GetNSStringForString(const nsAString& aSrc)
131 return [NSString stringWithCharacters:reinterpret_cast<const unichar*>(aSrc.BeginReading())
132 length:aSrc.Length()];
137 #define LOG_FONTLIST(args) PR_LOG(gfxPlatform::GetLog(eGfxLog_fontlist), \
139 #define LOG_FONTLIST_ENABLED() PR_LOG_TEST( \
140 gfxPlatform::GetLog(eGfxLog_fontlist), \
142 #define LOG_CMAPDATA_ENABLED() PR_LOG_TEST( \
143 gfxPlatform::GetLog(eGfxLog_cmapdata), \
150 // Complex scripts will not render correctly unless appropriate AAT or OT
151 // layout tables are present.
152 // For OpenType, we also check that the GSUB table supports the relevant
153 // script tag, to avoid using things like Arial Unicode MS for Lao (it has
154 // the characters, but lacks OpenType support).
156 // TODO: consider whether we should move this to gfxFontEntry and do similar
157 // cmap-masking on other platforms to avoid using fonts that won't shape
161 MacOSFontEntry::ReadCMAP(FontInfoData *aFontInfoData)
163 // attempt this once, if errors occur leave a blank cmap
168 nsRefPtr<gfxCharacterMap> charmap;
172 if (aFontInfoData && (charmap = GetCMAPFromFontInfo(aFontInfoData,
177 uint32_t kCMAP = TRUETYPE_TAG('c','m','a','p');
178 charmap = new gfxCharacterMap();
179 AutoTable cmapTable(this, kCMAP);
182 bool unicodeFont = false, symbolFont = false; // currently ignored
184 const uint8_t* cmapData =
185 reinterpret_cast<const uint8_t*>(hb_blob_get_data(cmapTable,
187 rv = gfxFontUtils::ReadCMAP(cmapData, cmapLen,
188 *charmap, mUVSOffset,
189 unicodeFont, symbolFont);
191 rv = NS_ERROR_NOT_AVAILABLE;
195 if (NS_SUCCEEDED(rv) && !HasGraphiteTables()) {
196 // We assume a Graphite font knows what it's doing,
197 // and provides whatever shaping is needed for the
198 // characters it supports, so only check/clear the
199 // complex-script ranges for non-Graphite fonts
201 // for layout support, check for the presence of mort/morx and/or
202 // opentype layout tables
203 bool hasAATLayout = HasFontTable(TRUETYPE_TAG('m','o','r','x')) ||
204 HasFontTable(TRUETYPE_TAG('m','o','r','t'));
205 bool hasGSUB = HasFontTable(TRUETYPE_TAG('G','S','U','B'));
206 bool hasGPOS = HasFontTable(TRUETYPE_TAG('G','P','O','S'));
207 if (hasAATLayout && !(hasGSUB || hasGPOS)) {
208 mRequiresAAT = true; // prefer CoreText if font has no OTL tables
211 for (const ScriptRange* sr = gfxPlatformFontList::sComplexScriptRanges;
212 sr->rangeStart; sr++) {
213 // check to see if the cmap includes complex script codepoints
214 if (charmap->TestRange(sr->rangeStart, sr->rangeEnd)) {
216 // prefer CoreText for Apple's complex-script fonts,
217 // even if they also have some OpenType tables
218 // (e.g. Geeza Pro Bold on 10.6; see bug 614903)
220 // and don't mask off complex-script ranges, we assume
221 // the AAT tables will provide the necessary shaping
225 // We check for GSUB here, as GPOS alone would not be ok.
226 if (hasGSUB && SupportsScriptInGSUB(sr->tags)) {
230 charmap->ClearRange(sr->rangeStart, sr->rangeEnd);
235 mHasCmapTable = NS_SUCCEEDED(rv);
237 gfxPlatformFontList *pfl = gfxPlatformFontList::PlatformFontList();
238 mCharacterMap = pfl->FindCharMap(charmap);
240 // if error occurred, initialize to null cmap
241 mCharacterMap = new gfxCharacterMap();
245 LOG_FONTLIST(("(fontlist-cmap) name: %s, size: %d hash: %8.8x%s\n",
246 NS_ConvertUTF16toUTF8(mName).get(),
247 charmap->SizeOfIncludingThis(moz_malloc_size_of),
248 charmap->mHash, mCharacterMap == charmap ? " new" : ""));
249 if (LOG_CMAPDATA_ENABLED()) {
251 sprintf(prefix, "(cmapdata) name: %.220s",
252 NS_ConvertUTF16toUTF8(mName).get());
253 charmap->Dump(prefix, eGfxLog_cmapdata);
261 MacOSFontEntry::CreateFontInstance(const gfxFontStyle *aFontStyle, bool aNeedsBold)
263 return new gfxMacFont(this, aFontStyle, aNeedsBold);
267 MacOSFontEntry::IsCFF()
269 if (!mIsCFFInitialized) {
270 mIsCFFInitialized = true;
271 mIsCFF = HasFontTable(TRUETYPE_TAG('C','F','F',' '));
277 MacOSFontEntry::MacOSFontEntry(const nsAString& aPostscriptName,
279 bool aIsStandardFace)
280 : gfxFontEntry(aPostscriptName, aIsStandardFace),
282 mFontRefInitialized(false),
285 mIsCFFInitialized(false)
290 MacOSFontEntry::MacOSFontEntry(const nsAString& aPostscriptName,
292 uint16_t aWeight, uint16_t aStretch,
293 uint32_t aItalicStyle,
294 bool aIsUserFont, bool aIsLocal)
295 : gfxFontEntry(aPostscriptName, false),
297 mFontRefInitialized(false),
300 mIsCFFInitialized(false)
303 mFontRefInitialized = true;
304 ::CFRetain(mFontRef);
308 mFixedPitch = false; // xxx - do we need this for downloaded fonts?
309 mItalic = (aItalicStyle & (NS_FONT_STYLE_ITALIC | NS_FONT_STYLE_OBLIQUE)) != 0;
310 mIsUserFont = aIsUserFont;
311 mIsLocalUserFont = aIsLocal;
315 MacOSFontEntry::GetFontRef()
317 if (!mFontRefInitialized) {
318 mFontRefInitialized = true;
319 NSString *psname = GetNSStringForString(mName);
320 mFontRef = ::CGFontCreateWithFontName(CFStringRef(psname));
326 MacOSFontEntry::DestroyBlobFunc(void* aUserData)
328 ::CFRelease((CFDataRef)aUserData);
332 MacOSFontEntry::GetFontTable(uint32_t aTag)
334 CGFontRef fontRef = GetFontRef();
339 CFDataRef dataRef = ::CGFontCopyTableForTag(fontRef, aTag);
341 return hb_blob_create((const char*)::CFDataGetBytePtr(dataRef),
342 ::CFDataGetLength(dataRef),
343 HB_MEMORY_MODE_READONLY,
344 (void*)dataRef, DestroyBlobFunc);
351 MacOSFontEntry::HasFontTable(uint32_t aTableTag)
353 nsAutoreleasePool localPool;
355 CGFontRef fontRef = GetFontRef();
360 CFDataRef tableData = ::CGFontCopyTableForTag(fontRef, aTableTag);
365 ::CFRelease(tableData);
370 MacOSFontEntry::AddSizeOfIncludingThis(MallocSizeOf aMallocSizeOf,
371 FontListSizes* aSizes) const
373 aSizes->mFontListSize += aMallocSizeOf(this);
374 AddSizeOfExcludingThis(aMallocSizeOf, aSizes);
377 /* gfxMacFontFamily */
380 class gfxMacFontFamily : public gfxFontFamily
383 explicit gfxMacFontFamily(nsAString& aName) :
387 virtual ~gfxMacFontFamily() {}
389 virtual void LocalizedName(nsAString& aLocalizedName);
391 virtual void FindStyleVariations(FontInfoData *aFontInfoData = nullptr);
395 gfxMacFontFamily::LocalizedName(nsAString& aLocalizedName)
397 nsAutoreleasePool localPool;
399 if (!HasOtherFamilyNames()) {
400 aLocalizedName = mName;
404 NSString *family = GetNSStringForString(mName);
405 NSString *localized = [sFontManager
406 localizedNameForFamily:family
410 GetStringForNSString(localized, aLocalizedName);
414 // failed to get localized name, just use the canonical one
415 aLocalizedName = mName;
418 // Return the CSS weight value to use for the given face, overriding what
419 // AppKit gives us (used to adjust families with bad weight values, see
421 // A return value of 0 indicates no override - use the existing weight.
423 GetWeightOverride(const nsAString& aPSName)
425 nsAutoCString prefName("font.weight-override.");
426 // The PostScript name is required to be ASCII; if it's not, the font is
427 // broken anyway, so we really don't care that this is lossy.
428 LossyAppendUTF16toASCII(aPSName, prefName);
429 return Preferences::GetInt(prefName.get(), 0);
433 gfxMacFontFamily::FindStyleVariations(FontInfoData *aFontInfoData)
438 nsAutoreleasePool localPool;
440 NSString *family = GetNSStringForString(mName);
442 // create a font entry for each face
443 NSArray *fontfaces = [sFontManager
444 availableMembersOfFontFamily:family]; // returns an array of [psname, style name, weight, traits] elements, goofy api
445 int faceCount = [fontfaces count];
448 for (faceIndex = 0; faceIndex < faceCount; faceIndex++) {
449 NSArray *face = [fontfaces objectAtIndex:faceIndex];
450 NSString *psname = [face objectAtIndex:INDEX_FONT_POSTSCRIPT_NAME];
451 int32_t appKitWeight = [[face objectAtIndex:INDEX_FONT_WEIGHT] unsignedIntValue];
452 uint32_t macTraits = [[face objectAtIndex:INDEX_FONT_TRAITS] unsignedIntValue];
453 NSString *facename = [face objectAtIndex:INDEX_FONT_FACE_NAME];
454 bool isStandardFace = false;
456 if (appKitWeight == kAppleExtraLightWeight) {
457 // if the facename contains UltraLight, set the weight to the ultralight weight value
458 NSRange range = [facename rangeOfString:@"ultralight" options:NSCaseInsensitiveSearch];
459 if (range.location != NSNotFound) {
460 appKitWeight = kAppleUltraLightWeight;
465 nsAutoString postscriptFontName;
466 GetStringForNSString(psname, postscriptFontName);
468 int32_t cssWeight = GetWeightOverride(postscriptFontName);
470 // scale down and clamp, to get a value from 1..9
471 cssWeight = ((cssWeight + 50) / 100);
472 cssWeight = std::max(1, std::min(cssWeight, 9));
475 gfxMacPlatformFontList::AppleWeightToCSSWeight(appKitWeight);
477 cssWeight *= 100; // scale up to CSS values
479 if ([facename isEqualToString:@"Regular"] ||
480 [facename isEqualToString:@"Bold"] ||
481 [facename isEqualToString:@"Italic"] ||
482 [facename isEqualToString:@"Oblique"] ||
483 [facename isEqualToString:@"Bold Italic"] ||
484 [facename isEqualToString:@"Bold Oblique"])
486 isStandardFace = true;
489 // create a font entry
490 MacOSFontEntry *fontEntry =
491 new MacOSFontEntry(postscriptFontName, cssWeight, isStandardFace);
496 // set additional properties based on the traits reported by Cocoa
497 if (macTraits & (NSCondensedFontMask | NSNarrowFontMask | NSCompressedFontMask)) {
498 fontEntry->mStretch = NS_FONT_STRETCH_CONDENSED;
499 } else if (macTraits & NSExpandedFontMask) {
500 fontEntry->mStretch = NS_FONT_STRETCH_EXPANDED;
502 // Cocoa fails to set the Italic traits bit for HelveticaLightItalic,
503 // at least (see bug 611855), so check for style name endings as well
504 if ((macTraits & NSItalicFontMask) ||
505 [facename hasSuffix:@"Italic"] ||
506 [facename hasSuffix:@"Oblique"])
508 fontEntry->mItalic = true;
510 if (macTraits & NSFixedPitchFontMask) {
511 fontEntry->mFixedPitch = true;
515 if (LOG_FONTLIST_ENABLED()) {
516 LOG_FONTLIST(("(fontlist) added (%s) to family (%s)"
517 " with style: %s weight: %d stretch: %d"
518 " (apple-weight: %d macTraits: %8.8x)",
519 NS_ConvertUTF16toUTF8(fontEntry->Name()).get(),
520 NS_ConvertUTF16toUTF8(Name()).get(),
521 fontEntry->IsItalic() ? "italic" : "normal",
522 cssWeight, fontEntry->Stretch(),
523 appKitWeight, macTraits));
527 // insert into font entry array of family
528 AddFontEntry(fontEntry);
531 SortAvailableFonts();
534 if (mIsBadUnderlineFamily) {
535 SetBadUnderlineFonts();
540 /* gfxSingleFaceMacFontFamily */
543 class gfxSingleFaceMacFontFamily : public gfxFontFamily
546 explicit gfxSingleFaceMacFontFamily(nsAString& aName) :
549 mFaceNamesInitialized = true; // omit from face name lists
552 virtual ~gfxSingleFaceMacFontFamily() {}
554 virtual void LocalizedName(nsAString& aLocalizedName);
556 virtual void ReadOtherFamilyNames(gfxPlatformFontList *aPlatformFontList);
560 gfxSingleFaceMacFontFamily::LocalizedName(nsAString& aLocalizedName)
562 nsAutoreleasePool localPool;
564 if (!HasOtherFamilyNames()) {
565 aLocalizedName = mName;
569 gfxFontEntry *fe = mAvailableFonts[0];
570 NSFont *font = [NSFont fontWithName:GetNSStringForString(fe->Name())
573 NSString *localized = [font displayName];
575 GetStringForNSString(localized, aLocalizedName);
580 // failed to get localized name, just use the canonical one
581 aLocalizedName = mName;
585 gfxSingleFaceMacFontFamily::ReadOtherFamilyNames(gfxPlatformFontList *aPlatformFontList)
587 if (mOtherFamilyNamesInitialized) {
591 gfxFontEntry *fe = mAvailableFonts[0];
596 const uint32_t kNAME = TRUETYPE_TAG('n','a','m','e');
598 gfxFontEntry::AutoTable nameTable(fe, kNAME);
603 mHasOtherFamilyNames = ReadOtherFamilyNamesForFace(aPlatformFontList,
607 mOtherFamilyNamesInitialized = true;
611 /* gfxMacPlatformFontList */
614 gfxMacPlatformFontList::gfxMacPlatformFontList() :
615 gfxPlatformFontList(false),
616 mDefaultFont(nullptr)
618 #ifdef MOZ_BUNDLED_FONTS
619 ActivateBundledFonts();
622 ::CFNotificationCenterAddObserver(::CFNotificationCenterGetLocalCenter(),
624 RegisteredFontsChangedNotificationCallback,
625 kCTFontManagerRegisteredFontsChangedNotification,
627 CFNotificationSuspensionBehaviorDeliverImmediately);
629 // cache this in a static variable so that MacOSFontFamily objects
630 // don't have to repeatedly look it up
631 sFontManager = [NSFontManager sharedFontManager];
634 gfxMacPlatformFontList::~gfxMacPlatformFontList()
637 ::CFRelease(mDefaultFont);
642 gfxMacPlatformFontList::InitFontList()
644 nsAutoreleasePool localPool;
646 Telemetry::AutoTimer<Telemetry::MAC_INITFONTLIST_TOTAL> timer;
649 gfxPlatformFontList::InitFontList();
650 mSystemFontFamilies.Clear();
652 // iterate over available families
654 CFArrayRef familyNames = CTFontManagerCopyAvailableFontFamilyNames();
656 // iterate over families
657 uint32_t i, numFamilies;
659 numFamilies = CFArrayGetCount(familyNames);
660 for (i = 0; i < numFamilies; i++) {
661 CFStringRef family = (CFStringRef)CFArrayGetValueAtIndex(familyNames, i);
663 // CTFontManager includes weird internal family names and
664 // LastResort, skip over those
666 CFStringCompare(family, CFSTR("LastResort"),
667 kCFCompareCaseInsensitive) == kCFCompareEqualTo) {
671 bool hiddenSystemFont = false;
672 if (::CFStringHasPrefix(family, CFSTR("."))) {
673 hiddenSystemFont = true;
676 nsAutoTArray<UniChar, 1024> buffer;
677 CFIndex len = ::CFStringGetLength(family);
678 buffer.SetLength(len+1);
679 ::CFStringGetCharacters(family, ::CFRangeMake(0, len),
682 nsAutoString familyName(reinterpret_cast<char16_t*>(buffer.Elements()), len);
684 // create a family entry
685 gfxFontFamily *familyEntry = new gfxMacFontFamily(familyName);
686 if (!familyEntry) break;
688 // add the family entry to the hash table
689 ToLowerCase(familyName);
690 if (!hiddenSystemFont) {
691 mFontFamilies.Put(familyName, familyEntry);
693 mSystemFontFamilies.Put(familyName, familyEntry);
696 // check the bad underline blacklist
697 if (mBadUnderlineFamilyNames.Contains(familyName))
698 familyEntry->SetBadUnderlineFamily();
701 CFRelease(familyNames);
703 InitSingleFaceList();
705 // to avoid full search of font name tables, seed the other names table with localized names from
706 // some of the prefs fonts which are accessed via their localized names. changes in the pref fonts will only cause
707 // a font lookup miss earlier. this is a simple optimization, it's not required for correctness
710 // start the delayed cmap loader
711 GetPrefsAndStartLoader();
717 gfxMacPlatformFontList::InitSingleFaceList()
719 nsAutoTArray<nsString, 10> singleFaceFonts;
720 gfxFontUtils::GetPrefsFontList("font.single-face-list", singleFaceFonts);
722 uint32_t numFonts = singleFaceFonts.Length();
723 for (uint32_t i = 0; i < numFonts; i++) {
725 LOG_FONTLIST(("(fontlist-singleface) face name: %s\n",
726 NS_ConvertUTF16toUTF8(singleFaceFonts[i]).get()));
728 gfxFontEntry *fontEntry = LookupLocalFont(nullptr, singleFaceFonts[i]);
730 nsAutoString familyName, key;
731 familyName = singleFaceFonts[i];
732 GenerateFontListKey(familyName, key);
734 LOG_FONTLIST(("(fontlist-singleface) family name: %s, key: %s\n",
735 NS_ConvertUTF16toUTF8(familyName).get(),
736 NS_ConvertUTF16toUTF8(key).get()));
739 // add only if doesn't exist already
740 if (!mFontFamilies.GetWeak(key)) {
741 gfxFontFamily *familyEntry =
742 new gfxSingleFaceMacFontFamily(familyName);
743 familyEntry->AddFontEntry(fontEntry);
744 familyEntry->SetHasStyles(true);
745 mFontFamilies.Put(key, familyEntry);
747 LOG_FONTLIST(("(fontlist-singleface) added new family\n",
748 NS_ConvertUTF16toUTF8(familyName).get(),
749 NS_ConvertUTF16toUTF8(key).get()));
757 gfxMacPlatformFontList::GetStandardFamilyName(const nsAString& aFontName, nsAString& aFamilyName)
759 gfxFontFamily *family = FindFamily(aFontName);
761 family->LocalizedName(aFamilyName);
769 gfxMacPlatformFontList::RegisteredFontsChangedNotificationCallback(CFNotificationCenterRef center,
773 CFDictionaryRef userInfo)
775 if (!::CFEqual(name, kCTFontManagerRegisteredFontsChangedNotification)) {
779 gfxMacPlatformFontList* fl = static_cast<gfxMacPlatformFontList*>(observer);
781 // xxx - should be carefully pruning the list of fonts, not rebuilding it from scratch
782 fl->UpdateFontList();
784 // modify a preference that will trigger reflow everywhere
785 fl->ForceGlobalReflow();
789 gfxMacPlatformFontList::GlobalFontFallback(const uint32_t aCh,
791 const gfxFontStyle* aMatchStyle,
792 uint32_t& aCmapCount,
793 gfxFontFamily** aMatchedFamily)
795 bool useCmaps = gfxPlatform::GetPlatform()->UseCmapsDuringSystemFallback();
798 return gfxPlatformFontList::GlobalFontFallback(aCh,
809 if (IS_IN_BMP(aCh)) {
811 str = ::CFStringCreateWithCharactersNoCopy(kCFAllocatorDefault, ch, 1,
814 ch[0] = H_SURROGATE(aCh);
815 ch[1] = L_SURROGATE(aCh);
816 str = ::CFStringCreateWithCharactersNoCopy(kCFAllocatorDefault, ch, 2,
824 // use CoreText to find the fallback family
826 gfxFontEntry *fontEntry = nullptr;
828 bool cantUseFallbackFont = false;
831 mDefaultFont = ::CTFontCreateWithName(CFSTR("LucidaGrande"), 12.f,
835 fallback = ::CTFontCreateForString(mDefaultFont, str,
836 ::CFRangeMake(0, len));
839 CFStringRef familyName = ::CTFontCopyFamilyName(fallback);
840 ::CFRelease(fallback);
843 ::CFStringCompare(familyName, CFSTR("LastResort"),
844 kCFCompareCaseInsensitive) != kCFCompareEqualTo)
846 nsAutoTArray<UniChar, 1024> buffer;
847 CFIndex len = ::CFStringGetLength(familyName);
848 buffer.SetLength(len+1);
849 ::CFStringGetCharacters(familyName, ::CFRangeMake(0, len),
852 nsDependentString familyName(reinterpret_cast<char16_t*>(buffer.Elements()), len);
854 bool needsBold; // ignored in the system fallback case
856 gfxFontFamily *family = FindFamily(familyName);
858 fontEntry = family->FindFontForStyle(*aMatchStyle, needsBold);
860 if (fontEntry->TestCharacterMap(aCh)) {
861 *aMatchedFamily = family;
864 cantUseFallbackFont = true;
871 ::CFRelease(familyName);
875 if (cantUseFallbackFont) {
876 Telemetry::Accumulate(Telemetry::BAD_FALLBACK_FONT, cantUseFallbackFont);
885 gfxMacPlatformFontList::GetDefaultFont(const gfxFontStyle* aStyle)
887 nsAutoreleasePool localPool;
889 NSString *defaultFamily = [[NSFont userFontOfSize:aStyle->size] familyName];
890 nsAutoString familyName;
892 GetStringForNSString(defaultFamily, familyName);
893 return FindFamily(familyName);
897 gfxMacPlatformFontList::AppleWeightToCSSWeight(int32_t aAppleWeight)
899 if (aAppleWeight < 1)
901 else if (aAppleWeight > kAppleMaxWeight)
902 aAppleWeight = kAppleMaxWeight;
903 return gAppleWeightToCSSWeight[aAppleWeight];
907 gfxMacPlatformFontList::LookupLocalFont(const gfxProxyFontEntry *aProxyEntry,
908 const nsAString& aFontName)
910 nsAutoreleasePool localPool;
912 NSString *faceName = GetNSStringForString(aFontName);
913 MacOSFontEntry *newFontEntry;
915 // lookup face based on postscript or full name
916 CGFontRef fontRef = ::CGFontCreateWithFontName(CFStringRef(faceName));
922 uint16_t w = aProxyEntry->mWeight;
923 NS_ASSERTION(w >= 100 && w <= 900, "bogus font weight value!");
926 new MacOSFontEntry(aFontName, fontRef,
927 w, aProxyEntry->mStretch,
928 aProxyEntry->mItalic ?
929 NS_FONT_STYLE_ITALIC : NS_FONT_STYLE_NORMAL,
933 new MacOSFontEntry(aFontName, fontRef,
934 400, 0, NS_FONT_STYLE_NORMAL,
937 ::CFRelease(fontRef);
942 static void ReleaseData(void *info, const void *data, size_t size)
944 NS_Free((void*)data);
948 gfxMacPlatformFontList::MakePlatformFont(const gfxProxyFontEntry *aProxyEntry,
949 const uint8_t *aFontData,
952 NS_ASSERTION(aFontData, "MakePlatformFont called with null data");
954 uint16_t w = aProxyEntry->mWeight;
955 NS_ASSERTION(w >= 100 && w <= 900, "bogus font weight value!");
957 // create the font entry
958 nsAutoString uniqueName;
960 nsresult rv = gfxFontUtils::MakeUniqueUserFontName(uniqueName);
965 CGDataProviderRef provider =
966 ::CGDataProviderCreateWithData(nullptr, aFontData, aLength,
968 CGFontRef fontRef = ::CGFontCreateWithDataProvider(provider);
969 ::CGDataProviderRelease(provider);
975 nsAutoPtr<MacOSFontEntry>
976 newFontEntry(new MacOSFontEntry(uniqueName, fontRef, w,
977 aProxyEntry->mStretch,
978 aProxyEntry->mItalic ?
979 NS_FONT_STYLE_ITALIC :
980 NS_FONT_STYLE_NORMAL,
982 ::CFRelease(fontRef);
984 // if succeeded and font cmap is good, return the new font
985 if (newFontEntry->mIsValid && NS_SUCCEEDED(newFontEntry->ReadCMAP())) {
986 return newFontEntry.forget();
989 // if something is funky about this font, delete immediately
992 NS_WARNING("downloaded font not loaded properly");
998 // used to load system-wide font info on off-main thread
999 class MacFontInfo : public FontInfoData {
1001 MacFontInfo(bool aLoadOtherNames,
1002 bool aLoadFaceNames,
1004 FontInfoData(aLoadOtherNames, aLoadFaceNames, aLoadCmaps)
1007 virtual ~MacFontInfo() {}
1009 virtual void Load() {
1010 nsAutoreleasePool localPool;
1011 // bug 975460 - async font loader crashes sometimes under 10.6, disable
1012 if (nsCocoaFeatures::OnLionOrLater()) {
1013 FontInfoData::Load();
1017 // loads font data for all members of a given family
1018 virtual void LoadFontFamilyData(const nsAString& aFamilyName);
1022 MacFontInfo::LoadFontFamilyData(const nsAString& aFamilyName)
1024 // family name ==> CTFontDescriptor
1025 NSString *famName = GetNSStringForString(aFamilyName);
1026 CFStringRef family = CFStringRef(famName);
1028 CFMutableDictionaryRef attr =
1029 CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks,
1030 &kCFTypeDictionaryValueCallBacks);
1031 CFDictionaryAddValue(attr, kCTFontFamilyNameAttribute, family);
1032 CTFontDescriptorRef fd = CTFontDescriptorCreateWithAttributes(attr);
1034 CFArrayRef matchingFonts =
1035 CTFontDescriptorCreateMatchingFontDescriptors(fd, NULL);
1037 if (!matchingFonts) {
1041 nsTArray<nsString> otherFamilyNames;
1042 bool hasOtherFamilyNames = true;
1044 // iterate over faces in the family
1045 int f, numFaces = (int) CFArrayGetCount(matchingFonts);
1046 for (f = 0; f < numFaces; f++) {
1049 CTFontDescriptorRef faceDesc =
1050 (CTFontDescriptorRef)CFArrayGetValueAtIndex(matchingFonts, f);
1054 CTFontRef fontRef = CTFontCreateWithFontDescriptor(faceDesc,
1057 NS_WARNING("failed to create a CTFontRef");
1063 CFStringRef faceName = (CFStringRef)
1064 CTFontDescriptorCopyAttribute(faceDesc, kCTFontNameAttribute);
1066 nsAutoTArray<UniChar, 1024> buffer;
1067 CFIndex len = CFStringGetLength(faceName);
1068 buffer.SetLength(len+1);
1069 CFStringGetCharacters(faceName, ::CFRangeMake(0, len),
1072 nsAutoString fontName(reinterpret_cast<char16_t*>(buffer.Elements()),
1075 // load the cmap data
1076 FontFaceData fontData;
1077 CFDataRef cmapTable = CTFontCopyTable(fontRef, kCTFontTableCmap,
1078 kCTFontTableOptionNoOptions);
1081 bool unicodeFont = false, symbolFont = false; // ignored
1082 const uint8_t *cmapData =
1083 (const uint8_t*)CFDataGetBytePtr(cmapTable);
1084 uint32_t cmapLen = CFDataGetLength(cmapTable);
1085 nsRefPtr<gfxCharacterMap> charmap = new gfxCharacterMap();
1089 rv = gfxFontUtils::ReadCMAP(cmapData, cmapLen, *charmap, offset,
1090 unicodeFont, symbolFont);
1091 if (NS_SUCCEEDED(rv)) {
1092 fontData.mCharacterMap = charmap;
1093 fontData.mUVSOffset = offset;
1094 fontData.mSymbolFont = symbolFont;
1097 CFRelease(cmapTable);
1100 mFontFaceData.Put(fontName, fontData);
1101 CFRelease(faceName);
1104 if (mLoadOtherNames && hasOtherFamilyNames) {
1105 CFDataRef nameTable = CTFontCopyTable(fontRef, kCTFontTableName,
1106 kCTFontTableOptionNoOptions);
1109 const char *nameData = (const char*)CFDataGetBytePtr(nameTable);
1110 uint32_t nameLen = CFDataGetLength(nameTable);
1111 gfxFontFamily::ReadOtherFamilyNamesForFace(aFamilyName,
1115 hasOtherFamilyNames = otherFamilyNames.Length() != 0;
1116 CFRelease(nameTable);
1122 CFRelease(matchingFonts);
1124 // if found other names, insert them in the hash table
1125 if (otherFamilyNames.Length() != 0) {
1126 mOtherFamilyNames.Put(aFamilyName, otherFamilyNames);
1127 mLoadStats.othernames += otherFamilyNames.Length();
1131 already_AddRefed<FontInfoData>
1132 gfxMacPlatformFontList::CreateFontInfoData()
1134 bool loadCmaps = !UsesSystemFallback() ||
1135 gfxPlatform::GetPlatform()->UseCmapsDuringSystemFallback();
1137 nsRefPtr<MacFontInfo> fi =
1138 new MacFontInfo(true, NeedFullnamePostscriptNames(), loadCmaps);
1142 #ifdef MOZ_BUNDLED_FONTS
1145 gfxMacPlatformFontList::ActivateBundledFonts()
1147 nsCOMPtr<nsIFile> localDir;
1148 nsresult rv = NS_GetSpecialDirectory(NS_GRE_DIR, getter_AddRefs(localDir));
1149 if (NS_FAILED(rv)) {
1152 if (NS_FAILED(localDir->Append(NS_LITERAL_STRING("fonts")))) {
1156 if (NS_FAILED(localDir->IsDirectory(&isDir)) || !isDir) {
1160 nsCOMPtr<nsISimpleEnumerator> e;
1161 rv = localDir->GetDirectoryEntries(getter_AddRefs(e));
1162 if (NS_FAILED(rv)) {
1167 while (NS_SUCCEEDED(e->HasMoreElements(&hasMore)) && hasMore) {
1168 nsCOMPtr<nsISupports> entry;
1169 if (NS_FAILED(e->GetNext(getter_AddRefs(entry)))) {
1172 nsCOMPtr<nsIFile> file = do_QueryInterface(entry);
1177 if (NS_FAILED(file->GetNativePath(path))) {
1181 ::CFURLCreateFromFileSystemRepresentation(kCFAllocatorDefault,
1182 (uint8_t*)path.get(),
1186 CFErrorRef error = nullptr;
1187 ::CTFontManagerRegisterFontsForURL(fontURL,
1188 kCTFontManagerScopeProcess,
1190 ::CFRelease(fontURL);