Bumping manifests a=b2g-bump
[gecko.git] / gfx / thebes / gfxMacPlatformFontList.mm
blob06ca9e4cc91b112f5132e7cd8cec988c2b08323a
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 ***** */
41 #ifdef MOZ_LOGGING
42 #define FORCE_PR_LOG /* Allow logging in the release build */
43 #endif
44 #include "prlog.h"
46 #include <algorithm>
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"
57 #include "nsTArray.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"
72 #include <unistd.h>
73 #include <time.h>
75 using namespace mozilla;
77 class nsAutoreleasePool {
78 public:
79     nsAutoreleasePool()
80     {
81         mLocalPool = [[NSAutoreleasePool alloc] init];
82     }
83     ~nsAutoreleasePool()
84     {
85         [mLocalPool release];
86     }
87 private:
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[] = {
103     0,
104     1, // 1.
105     1, // 2.  W1, ultralight
106     2, // 3.  W2, extralight
107     3, // 4.  W3, light
108     4, // 5.  W4, semilight
109     5, // 6.  W5, medium
110     6, // 7.
111     6, // 8.  W6, semibold
112     7, // 9.  W7, bold
113     8, // 10. W8, extrabold
114     8, // 11.
115     9, // 12. W9, ultrabold
116     9, // 13
117     9  // 14
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()];
135 #ifdef PR_LOGGING
137 #define LOG_FONTLIST(args) PR_LOG(gfxPlatform::GetLog(eGfxLog_fontlist), \
138                                PR_LOG_DEBUG, args)
139 #define LOG_FONTLIST_ENABLED() PR_LOG_TEST( \
140                                    gfxPlatform::GetLog(eGfxLog_fontlist), \
141                                    PR_LOG_DEBUG)
142 #define LOG_CMAPDATA_ENABLED() PR_LOG_TEST( \
143                                    gfxPlatform::GetLog(eGfxLog_cmapdata), \
144                                    PR_LOG_DEBUG)
146 #endif // PR_LOGGING
148 #pragma mark-
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
158 // properly.
160 nsresult
161 MacOSFontEntry::ReadCMAP(FontInfoData *aFontInfoData)
163     // attempt this once, if errors occur leave a blank cmap
164     if (mCharacterMap) {
165         return NS_OK;
166     }
168     nsRefPtr<gfxCharacterMap> charmap;
169     nsresult rv;
170     bool symbolFont;
172     if (aFontInfoData && (charmap = GetCMAPFromFontInfo(aFontInfoData,
173                                                         mUVSOffset,
174                                                         symbolFont))) {
175         rv = NS_OK;
176     } else {
177         uint32_t kCMAP = TRUETYPE_TAG('c','m','a','p');
178         charmap = new gfxCharacterMap();
179         AutoTable cmapTable(this, kCMAP);
181         if (cmapTable) {
182             bool unicodeFont = false, symbolFont = false; // currently ignored
183             uint32_t cmapLen;
184             const uint8_t* cmapData =
185                 reinterpret_cast<const uint8_t*>(hb_blob_get_data(cmapTable,
186                                                                   &cmapLen));
187             rv = gfxFontUtils::ReadCMAP(cmapData, cmapLen,
188                                         *charmap, mUVSOffset,
189                                         unicodeFont, symbolFont);
190         } else {
191             rv = NS_ERROR_NOT_AVAILABLE;
192         }
193     }
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
209         }
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)) {
215                 if (hasAATLayout) {
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)
219                     mRequiresAAT = true;
220                     // and don't mask off complex-script ranges, we assume
221                     // the AAT tables will provide the necessary shaping
222                     continue;
223                 }
225                 // We check for GSUB here, as GPOS alone would not be ok.
226                 if (hasGSUB && SupportsScriptInGSUB(sr->tags)) {
227                     continue;
228                 }
230                 charmap->ClearRange(sr->rangeStart, sr->rangeEnd);
231             }
232         }
233     }
235     mHasCmapTable = NS_SUCCEEDED(rv);
236     if (mHasCmapTable) {
237         gfxPlatformFontList *pfl = gfxPlatformFontList::PlatformFontList();
238         mCharacterMap = pfl->FindCharMap(charmap);
239     } else {
240         // if error occurred, initialize to null cmap
241         mCharacterMap = new gfxCharacterMap();
242     }
244 #ifdef PR_LOGGING
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()) {
250         char prefix[256];
251         sprintf(prefix, "(cmapdata) name: %.220s",
252                 NS_ConvertUTF16toUTF8(mName).get());
253         charmap->Dump(prefix, eGfxLog_cmapdata);
254     }
255 #endif
257     return rv;
260 gfxFont*
261 MacOSFontEntry::CreateFontInstance(const gfxFontStyle *aFontStyle, bool aNeedsBold)
263     return new gfxMacFont(this, aFontStyle, aNeedsBold);
266 bool
267 MacOSFontEntry::IsCFF()
269     if (!mIsCFFInitialized) {
270         mIsCFFInitialized = true;
271         mIsCFF = HasFontTable(TRUETYPE_TAG('C','F','F',' '));
272     }
274     return mIsCFF;
277 MacOSFontEntry::MacOSFontEntry(const nsAString& aPostscriptName,
278                                int32_t aWeight,
279                                bool aIsStandardFace)
280     : gfxFontEntry(aPostscriptName, aIsStandardFace),
281       mFontRef(NULL),
282       mFontRefInitialized(false),
283       mRequiresAAT(false),
284       mIsCFF(false),
285       mIsCFFInitialized(false)
287     mWeight = aWeight;
290 MacOSFontEntry::MacOSFontEntry(const nsAString& aPostscriptName,
291                                CGFontRef aFontRef,
292                                uint16_t aWeight, uint16_t aStretch,
293                                uint32_t aItalicStyle,
294                                bool aIsUserFont, bool aIsLocal)
295     : gfxFontEntry(aPostscriptName, false),
296       mFontRef(NULL),
297       mFontRefInitialized(false),
298       mRequiresAAT(false),
299       mIsCFF(false),
300       mIsCFFInitialized(false)
302     mFontRef = aFontRef;
303     mFontRefInitialized = true;
304     ::CFRetain(mFontRef);
306     mWeight = aWeight;
307     mStretch = aStretch;
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;
314 CGFontRef
315 MacOSFontEntry::GetFontRef()
317     if (!mFontRefInitialized) {
318         mFontRefInitialized = true;
319         NSString *psname = GetNSStringForString(mName);
320         mFontRef = ::CGFontCreateWithFontName(CFStringRef(psname));
321     }
322     return mFontRef;
325 /*static*/ void
326 MacOSFontEntry::DestroyBlobFunc(void* aUserData)
328     ::CFRelease((CFDataRef)aUserData);
331 hb_blob_t *
332 MacOSFontEntry::GetFontTable(uint32_t aTag)
334     CGFontRef fontRef = GetFontRef();
335     if (!fontRef) {
336         return nullptr;
337     }
339     CFDataRef dataRef = ::CGFontCopyTableForTag(fontRef, aTag);
340     if (dataRef) {
341         return hb_blob_create((const char*)::CFDataGetBytePtr(dataRef),
342                               ::CFDataGetLength(dataRef),
343                               HB_MEMORY_MODE_READONLY,
344                               (void*)dataRef, DestroyBlobFunc);
345     }
347     return nullptr;
350 bool
351 MacOSFontEntry::HasFontTable(uint32_t aTableTag)
353     nsAutoreleasePool localPool;
355     CGFontRef fontRef = GetFontRef();
356     if (!fontRef) {
357         return false;
358     }
360     CFDataRef tableData = ::CGFontCopyTableForTag(fontRef, aTableTag);
361     if (!tableData) {
362         return false;
363     }
365     ::CFRelease(tableData);
366     return true;
369 void
370 MacOSFontEntry::AddSizeOfIncludingThis(MallocSizeOf aMallocSizeOf,
371                                        FontListSizes* aSizes) const
373     aSizes->mFontListSize += aMallocSizeOf(this);
374     AddSizeOfExcludingThis(aMallocSizeOf, aSizes);
377 /* gfxMacFontFamily */
378 #pragma mark-
380 class gfxMacFontFamily : public gfxFontFamily
382 public:
383     explicit gfxMacFontFamily(nsAString& aName) :
384         gfxFontFamily(aName)
385     {}
387     virtual ~gfxMacFontFamily() {}
389     virtual void LocalizedName(nsAString& aLocalizedName);
391     virtual void FindStyleVariations(FontInfoData *aFontInfoData = nullptr);
394 void
395 gfxMacFontFamily::LocalizedName(nsAString& aLocalizedName)
397     nsAutoreleasePool localPool;
399     if (!HasOtherFamilyNames()) {
400         aLocalizedName = mName;
401         return;
402     }
404     NSString *family = GetNSStringForString(mName);
405     NSString *localized = [sFontManager
406                            localizedNameForFamily:family
407                                              face:nil];
409     if (localized) {
410         GetStringForNSString(localized, aLocalizedName);
411         return;
412     }
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
420 // bug 931426).
421 // A return value of 0 indicates no override - use the existing weight.
422 static inline int
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);
432 void
433 gfxMacFontFamily::FindStyleVariations(FontInfoData *aFontInfoData)
435     if (mHasStyles)
436         return;
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];
446     int faceIndex;
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;
461             }
462         }
464         // make a nsString
465         nsAutoString postscriptFontName;
466         GetStringForNSString(psname, postscriptFontName);
468         int32_t cssWeight = GetWeightOverride(postscriptFontName);
469         if (cssWeight) {
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));
473         } else {
474             cssWeight =
475                 gfxMacPlatformFontList::AppleWeightToCSSWeight(appKitWeight);
476         }
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"])
485         {
486             isStandardFace = true;
487         }
489         // create a font entry
490         MacOSFontEntry *fontEntry =
491             new MacOSFontEntry(postscriptFontName, cssWeight, isStandardFace);
492         if (!fontEntry) {
493             break;
494         }
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;
501         }
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"])
507         {
508             fontEntry->mItalic = true;
509         }
510         if (macTraits & NSFixedPitchFontMask) {
511             fontEntry->mFixedPitch = true;
512         }
514 #ifdef PR_LOGGING
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));
524         }
525 #endif
527         // insert into font entry array of family
528         AddFontEntry(fontEntry);
529     }
531     SortAvailableFonts();
532     SetHasStyles(true);
534     if (mIsBadUnderlineFamily) {
535         SetBadUnderlineFonts();
536     }
540 /* gfxSingleFaceMacFontFamily */
541 #pragma mark-
543 class gfxSingleFaceMacFontFamily : public gfxFontFamily
545 public:
546     explicit gfxSingleFaceMacFontFamily(nsAString& aName) :
547         gfxFontFamily(aName)
548     {
549         mFaceNamesInitialized = true; // omit from face name lists
550     }
552     virtual ~gfxSingleFaceMacFontFamily() {}
554     virtual void LocalizedName(nsAString& aLocalizedName);
556     virtual void ReadOtherFamilyNames(gfxPlatformFontList *aPlatformFontList);
559 void
560 gfxSingleFaceMacFontFamily::LocalizedName(nsAString& aLocalizedName)
562     nsAutoreleasePool localPool;
564     if (!HasOtherFamilyNames()) {
565         aLocalizedName = mName;
566         return;
567     }
569     gfxFontEntry *fe = mAvailableFonts[0];
570     NSFont *font = [NSFont fontWithName:GetNSStringForString(fe->Name())
571                                    size:0.0];
572     if (font) {
573         NSString *localized = [font displayName];
574         if (localized) {
575             GetStringForNSString(localized, aLocalizedName);
576             return;
577         }
578     }
580     // failed to get localized name, just use the canonical one
581     aLocalizedName = mName;
584 void
585 gfxSingleFaceMacFontFamily::ReadOtherFamilyNames(gfxPlatformFontList *aPlatformFontList)
587     if (mOtherFamilyNamesInitialized) {
588         return;
589     }
591     gfxFontEntry *fe = mAvailableFonts[0];
592     if (!fe) {
593         return;
594     }
596     const uint32_t kNAME = TRUETYPE_TAG('n','a','m','e');
598     gfxFontEntry::AutoTable nameTable(fe, kNAME);
599     if (!nameTable) {
600         return;
601     }
603     mHasOtherFamilyNames = ReadOtherFamilyNamesForFace(aPlatformFontList,
604                                                        nameTable,
605                                                        true);
607     mOtherFamilyNamesInitialized = true;
611 /* gfxMacPlatformFontList */
612 #pragma mark-
614 gfxMacPlatformFontList::gfxMacPlatformFontList() :
615     gfxPlatformFontList(false),
616     mDefaultFont(nullptr)
618 #ifdef MOZ_BUNDLED_FONTS
619     ActivateBundledFonts();
620 #endif
622     ::CFNotificationCenterAddObserver(::CFNotificationCenterGetLocalCenter(),
623                                       this,
624                                       RegisteredFontsChangedNotificationCallback,
625                                       kCTFontManagerRegisteredFontsChangedNotification,
626                                       0,
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()
636     if (mDefaultFont) {
637         ::CFRelease(mDefaultFont);
638     }
641 nsresult
642 gfxMacPlatformFontList::InitFontList()
644     nsAutoreleasePool localPool;
646     Telemetry::AutoTimer<Telemetry::MAC_INITFONTLIST_TOTAL> timer;
648     // reset font lists
649     gfxPlatformFontList::InitFontList();
650     mSystemFontFamilies.Clear();
651     
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
665         if (!family ||
666             CFStringCompare(family, CFSTR("LastResort"),
667                             kCFCompareCaseInsensitive) == kCFCompareEqualTo) {
668             continue;
669         }
671         bool hiddenSystemFont = false;
672         if (::CFStringHasPrefix(family, CFSTR("."))) {
673             hiddenSystemFont = true;
674         }
676         nsAutoTArray<UniChar, 1024> buffer;
677         CFIndex len = ::CFStringGetLength(family);
678         buffer.SetLength(len+1);
679         ::CFStringGetCharacters(family, ::CFRangeMake(0, len),
680                                 buffer.Elements());
681         buffer[len] = 0;
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);
692         } else {
693             mSystemFontFamilies.Put(familyName, familyEntry);
694         }
696         // check the bad underline blacklist
697         if (mBadUnderlineFamilyNames.Contains(familyName))
698             familyEntry->SetBadUnderlineFamily();
699     }
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
708     PreloadNamesList();
710     // start the delayed cmap loader
711     GetPrefsAndStartLoader();
713     return NS_OK;
716 void
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++) {
724 #ifdef PR_LOGGING
725         LOG_FONTLIST(("(fontlist-singleface) face name: %s\n",
726                       NS_ConvertUTF16toUTF8(singleFaceFonts[i]).get()));
727 #endif
728         gfxFontEntry *fontEntry = LookupLocalFont(nullptr, singleFaceFonts[i]);
729         if (fontEntry) {
730             nsAutoString familyName, key;
731             familyName = singleFaceFonts[i];
732             GenerateFontListKey(familyName, key);
733 #ifdef PR_LOGGING
734             LOG_FONTLIST(("(fontlist-singleface) family name: %s, key: %s\n",
735                           NS_ConvertUTF16toUTF8(familyName).get(),
736                           NS_ConvertUTF16toUTF8(key).get()));
737 #endif
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);
746 #ifdef PR_LOGGING
747                 LOG_FONTLIST(("(fontlist-singleface) added new family\n",
748                               NS_ConvertUTF16toUTF8(familyName).get(),
749                               NS_ConvertUTF16toUTF8(key).get()));
750 #endif
751             }
752         }
753     }
756 bool
757 gfxMacPlatformFontList::GetStandardFamilyName(const nsAString& aFontName, nsAString& aFamilyName)
759     gfxFontFamily *family = FindFamily(aFontName);
760     if (family) {
761         family->LocalizedName(aFamilyName);
762         return true;
763     }
765     return false;
768 void
769 gfxMacPlatformFontList::RegisteredFontsChangedNotificationCallback(CFNotificationCenterRef center,
770                                                                    void *observer,
771                                                                    CFStringRef name,
772                                                                    const void *object,
773                                                                    CFDictionaryRef userInfo)
775     if (!::CFEqual(name, kCTFontManagerRegisteredFontsChangedNotification)) {
776         return;
777     }
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();
788 gfxFontEntry*
789 gfxMacPlatformFontList::GlobalFontFallback(const uint32_t aCh,
790                                            int32_t aRunScript,
791                                            const gfxFontStyle* aMatchStyle,
792                                            uint32_t& aCmapCount,
793                                            gfxFontFamily** aMatchedFamily)
795     bool useCmaps = gfxPlatform::GetPlatform()->UseCmapsDuringSystemFallback();
797     if (useCmaps) {
798         return gfxPlatformFontList::GlobalFontFallback(aCh,
799                                                        aRunScript,
800                                                        aMatchStyle,
801                                                        aCmapCount,
802                                                        aMatchedFamily);
803     }
805     CFStringRef str;
806     UniChar ch[2];
807     CFIndex len = 1;
809     if (IS_IN_BMP(aCh)) {
810         ch[0] = aCh;
811         str = ::CFStringCreateWithCharactersNoCopy(kCFAllocatorDefault, ch, 1,
812                                                    kCFAllocatorNull);
813     } else {
814         ch[0] = H_SURROGATE(aCh);
815         ch[1] = L_SURROGATE(aCh);
816         str = ::CFStringCreateWithCharactersNoCopy(kCFAllocatorDefault, ch, 2,
817                                                    kCFAllocatorNull);
818         if (!str) {
819             return nullptr;
820         }
821         len = 2;
822     }
824     // use CoreText to find the fallback family
826     gfxFontEntry *fontEntry = nullptr;
827     CTFontRef fallback;
828     bool cantUseFallbackFont = false;
830     if (!mDefaultFont) {
831         mDefaultFont = ::CTFontCreateWithName(CFSTR("LucidaGrande"), 12.f,
832                                               NULL);
833     }
835     fallback = ::CTFontCreateForString(mDefaultFont, str,
836                                        ::CFRangeMake(0, len));
838     if (fallback) {
839         CFStringRef familyName = ::CTFontCopyFamilyName(fallback);
840         ::CFRelease(fallback);
842         if (familyName &&
843             ::CFStringCompare(familyName, CFSTR("LastResort"),
844                               kCFCompareCaseInsensitive) != kCFCompareEqualTo)
845         {
846             nsAutoTArray<UniChar, 1024> buffer;
847             CFIndex len = ::CFStringGetLength(familyName);
848             buffer.SetLength(len+1);
849             ::CFStringGetCharacters(familyName, ::CFRangeMake(0, len),
850                                     buffer.Elements());
851             buffer[len] = 0;
852             nsDependentString familyName(reinterpret_cast<char16_t*>(buffer.Elements()), len);
854             bool needsBold;  // ignored in the system fallback case
856             gfxFontFamily *family = FindFamily(familyName);
857             if (family) {
858                 fontEntry = family->FindFontForStyle(*aMatchStyle, needsBold);
859                 if (fontEntry) {
860                     if (fontEntry->TestCharacterMap(aCh)) {
861                         *aMatchedFamily = family;
862                     } else {
863                         fontEntry = nullptr;
864                         cantUseFallbackFont = true;
865                     }
866                 }
867             }
868         }
870         if (familyName) {
871             ::CFRelease(familyName);
872         }
873     }
875     if (cantUseFallbackFont) {
876         Telemetry::Accumulate(Telemetry::BAD_FALLBACK_FONT, cantUseFallbackFont);
877     }
879     ::CFRelease(str);
881     return fontEntry;
884 gfxFontFamily*
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);
896 int32_t
897 gfxMacPlatformFontList::AppleWeightToCSSWeight(int32_t aAppleWeight)
899     if (aAppleWeight < 1)
900         aAppleWeight = 1;
901     else if (aAppleWeight > kAppleMaxWeight)
902         aAppleWeight = kAppleMaxWeight;
903     return gAppleWeightToCSSWeight[aAppleWeight];
906 gfxFontEntry*
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));
917     if (!fontRef) {
918         return nullptr;
919     }
921     if (aProxyEntry) {
922         uint16_t w = aProxyEntry->mWeight;
923         NS_ASSERTION(w >= 100 && w <= 900, "bogus font weight value!");
925         newFontEntry =
926             new MacOSFontEntry(aFontName, fontRef,
927                                w, aProxyEntry->mStretch,
928                                aProxyEntry->mItalic ?
929                                    NS_FONT_STYLE_ITALIC : NS_FONT_STYLE_NORMAL,
930                                true, true);
931     } else {
932         newFontEntry =
933             new MacOSFontEntry(aFontName, fontRef,
934                                400, 0, NS_FONT_STYLE_NORMAL,
935                                false, false);
936     }
937     ::CFRelease(fontRef);
939     return newFontEntry;
942 static void ReleaseData(void *info, const void *data, size_t size)
944     NS_Free((void*)data);
947 gfxFontEntry*
948 gfxMacPlatformFontList::MakePlatformFont(const gfxProxyFontEntry *aProxyEntry,
949                                          const uint8_t *aFontData,
950                                          uint32_t aLength)
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);
961     if (NS_FAILED(rv)) {
962         return nullptr;
963     }
965     CGDataProviderRef provider =
966         ::CGDataProviderCreateWithData(nullptr, aFontData, aLength,
967                                        &ReleaseData);
968     CGFontRef fontRef = ::CGFontCreateWithDataProvider(provider);
969     ::CGDataProviderRelease(provider);
971     if (!fontRef) {
972         return nullptr;
973     }
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,
981                                         true, false));
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();
987     }
989     // if something is funky about this font, delete immediately
991 #if DEBUG
992     NS_WARNING("downloaded font not loaded properly");
993 #endif
995     return nullptr;
998 // used to load system-wide font info on off-main thread
999 class MacFontInfo : public FontInfoData {
1000 public:
1001     MacFontInfo(bool aLoadOtherNames,
1002                 bool aLoadFaceNames,
1003                 bool aLoadCmaps) :
1004         FontInfoData(aLoadOtherNames, aLoadFaceNames, aLoadCmaps)
1005     {}
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();
1014         }
1015     }
1017     // loads font data for all members of a given family
1018     virtual void LoadFontFamilyData(const nsAString& aFamilyName);
1021 void
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);
1033     CFRelease(attr);
1034     CFArrayRef matchingFonts =
1035         CTFontDescriptorCreateMatchingFontDescriptors(fd, NULL);
1036     CFRelease(fd);
1037     if (!matchingFonts) {
1038         return;
1039     }
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++) {
1047         mLoadStats.fonts++;
1049         CTFontDescriptorRef faceDesc =
1050             (CTFontDescriptorRef)CFArrayGetValueAtIndex(matchingFonts, f);
1051         if (!faceDesc) {
1052             continue;
1053         }
1054         CTFontRef fontRef = CTFontCreateWithFontDescriptor(faceDesc,
1055                                                            0.0, nullptr);
1056         if (!fontRef) {
1057             NS_WARNING("failed to create a CTFontRef");
1058             continue;
1059         }
1061         if (mLoadCmaps) {
1062             // face name
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),
1070                                     buffer.Elements());
1071             buffer[len] = 0;
1072             nsAutoString fontName(reinterpret_cast<char16_t*>(buffer.Elements()),
1073                                   len);
1075             // load the cmap data
1076             FontFaceData fontData;
1077             CFDataRef cmapTable = CTFontCopyTable(fontRef, kCTFontTableCmap,
1078                                                   kCTFontTableOptionNoOptions);
1080             if (cmapTable) {
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();
1086                 uint32_t offset;
1087                 nsresult rv;
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;
1095                     mLoadStats.cmaps++;
1096                 }
1097                 CFRelease(cmapTable);
1098             }
1100             mFontFaceData.Put(fontName, fontData);
1101             CFRelease(faceName);
1102         }
1104         if (mLoadOtherNames && hasOtherFamilyNames) {
1105             CFDataRef nameTable = CTFontCopyTable(fontRef, kCTFontTableName,
1106                                                   kCTFontTableOptionNoOptions);
1108             if (nameTable) {
1109                 const char *nameData = (const char*)CFDataGetBytePtr(nameTable);
1110                 uint32_t nameLen = CFDataGetLength(nameTable);
1111                 gfxFontFamily::ReadOtherFamilyNamesForFace(aFamilyName,
1112                                                            nameData, nameLen,
1113                                                            otherFamilyNames,
1114                                                            false);
1115                 hasOtherFamilyNames = otherFamilyNames.Length() != 0;
1116                 CFRelease(nameTable);
1117             }
1118         }
1120         CFRelease(fontRef);
1121     }
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();
1128     }
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);
1139     return fi.forget();
1142 #ifdef MOZ_BUNDLED_FONTS
1144 void
1145 gfxMacPlatformFontList::ActivateBundledFonts()
1147     nsCOMPtr<nsIFile> localDir;
1148     nsresult rv = NS_GetSpecialDirectory(NS_GRE_DIR, getter_AddRefs(localDir));
1149     if (NS_FAILED(rv)) {
1150         return;
1151     }
1152     if (NS_FAILED(localDir->Append(NS_LITERAL_STRING("fonts")))) {
1153         return;
1154     }
1155     bool isDir;
1156     if (NS_FAILED(localDir->IsDirectory(&isDir)) || !isDir) {
1157         return;
1158     }
1160     nsCOMPtr<nsISimpleEnumerator> e;
1161     rv = localDir->GetDirectoryEntries(getter_AddRefs(e));
1162     if (NS_FAILED(rv)) {
1163         return;
1164     }
1166     bool hasMore;
1167     while (NS_SUCCEEDED(e->HasMoreElements(&hasMore)) && hasMore) {
1168         nsCOMPtr<nsISupports> entry;
1169         if (NS_FAILED(e->GetNext(getter_AddRefs(entry)))) {
1170             break;
1171         }
1172         nsCOMPtr<nsIFile> file = do_QueryInterface(entry);
1173         if (!file) {
1174             continue;
1175         }
1176         nsCString path;
1177         if (NS_FAILED(file->GetNativePath(path))) {
1178             continue;
1179         }
1180         CFURLRef fontURL =
1181             ::CFURLCreateFromFileSystemRepresentation(kCFAllocatorDefault,
1182                                                       (uint8_t*)path.get(),
1183                                                       path.Length(),
1184                                                       false);
1185         if (fontURL) {
1186             CFErrorRef error = nullptr;
1187             ::CTFontManagerRegisterFontsForURL(fontURL,
1188                                                kCTFontManagerScopeProcess,
1189                                                &error);
1190             ::CFRelease(fontURL);
1191         }
1192     }
1195 #endif