gfx/thebes/gfxMacPlatformFontList.mm

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

     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 "nsISimpleEnumerator.h"
    62 #include "nsCharTraits.h"
    63 #include "nsCocoaFeatures.h"
    64 #include "gfxFontConstants.h"
    66 #include "mozilla/MemoryReporting.h"
    67 #include "mozilla/Preferences.h"
    68 #include "mozilla/Telemetry.h"
    69 #include "mozilla/gfx/2D.h"
    71 #include <unistd.h>
    72 #include <time.h>
    74 using namespace mozilla;
    76 class nsAutoreleasePool {
    77 public:
    78     nsAutoreleasePool()
    79     {
    80         mLocalPool = [[NSAutoreleasePool alloc] init];
    81     }
    82     ~nsAutoreleasePool()
    83     {
    84         [mLocalPool release];
    85     }
    86 private:
    87     NSAutoreleasePool *mLocalPool;
    88 };
    90 // indexes into the NSArray objects that the Cocoa font manager returns
    91 // as the available members of a family
    92 #define INDEX_FONT_POSTSCRIPT_NAME 0
    93 #define INDEX_FONT_FACE_NAME 1
    94 #define INDEX_FONT_WEIGHT 2
    95 #define INDEX_FONT_TRAITS 3
    97 static const int kAppleMaxWeight = 14;
    98 static const int kAppleExtraLightWeight = 3;
    99 static const int kAppleUltraLightWeight = 2;
   101 static const int gAppleWeightToCSSWeight[] = {
   102     0,
   103     1, // 1.
   104     1, // 2.  W1, ultralight
   105     2, // 3.  W2, extralight
   106     3, // 4.  W3, light
   107     4, // 5.  W4, semilight
   108     5, // 6.  W5, medium
   109     6, // 7.
   110     6, // 8.  W6, semibold
   111     7, // 9.  W7, bold
   112     8, // 10. W8, extrabold
   113     8, // 11.
   114     9, // 12. W9, ultrabold
   115     9, // 13
   116     9  // 14
   117 };
   119 // cache Cocoa's "shared font manager" for performance
   120 static NSFontManager *sFontManager;
   122 static void GetStringForNSString(const NSString *aSrc, nsAString& aDist)
   123 {
   124     aDist.SetLength([aSrc length]);
   125     [aSrc getCharacters:reinterpret_cast<unichar*>(aDist.BeginWriting())];
   126 }
   128 static NSString* GetNSStringForString(const nsAString& aSrc)
   129 {
   130     return [NSString stringWithCharacters:reinterpret_cast<const unichar*>(aSrc.BeginReading())
   131                                    length:aSrc.Length()];
   132 }
   134 #ifdef PR_LOGGING
   136 #define LOG_FONTLIST(args) PR_LOG(gfxPlatform::GetLog(eGfxLog_fontlist), \
   137                                PR_LOG_DEBUG, args)
   138 #define LOG_FONTLIST_ENABLED() PR_LOG_TEST( \
   139                                    gfxPlatform::GetLog(eGfxLog_fontlist), \
   140                                    PR_LOG_DEBUG)
   141 #define LOG_CMAPDATA_ENABLED() PR_LOG_TEST( \
   142                                    gfxPlatform::GetLog(eGfxLog_cmapdata), \
   143                                    PR_LOG_DEBUG)
   145 #endif // PR_LOGGING
   147 #pragma mark-
   149 // Complex scripts will not render correctly unless appropriate AAT or OT
   150 // layout tables are present.
   151 // For OpenType, we also check that the GSUB table supports the relevant
   152 // script tag, to avoid using things like Arial Unicode MS for Lao (it has
   153 // the characters, but lacks OpenType support).
   155 // TODO: consider whether we should move this to gfxFontEntry and do similar
   156 // cmap-masking on other platforms to avoid using fonts that won't shape
   157 // properly.
   159 nsresult
   160 MacOSFontEntry::ReadCMAP(FontInfoData *aFontInfoData)
   161 {
   162     // attempt this once, if errors occur leave a blank cmap
   163     if (mCharacterMap) {
   164         return NS_OK;
   165     }
   167     nsRefPtr<gfxCharacterMap> charmap;
   168     nsresult rv;
   169     bool symbolFont;
   171     if (aFontInfoData && (charmap = GetCMAPFromFontInfo(aFontInfoData,
   172                                                         mUVSOffset,
   173                                                         symbolFont))) {
   174         rv = NS_OK;
   175     } else {
   176         uint32_t kCMAP = TRUETYPE_TAG('c','m','a','p');
   177         charmap = new gfxCharacterMap();
   178         AutoTable cmapTable(this, kCMAP);
   180         if (cmapTable) {
   181             bool unicodeFont = false, symbolFont = false; // currently ignored
   182             uint32_t cmapLen;
   183             const uint8_t* cmapData =
   184                 reinterpret_cast<const uint8_t*>(hb_blob_get_data(cmapTable,
   185                                                                   &cmapLen));
   186             rv = gfxFontUtils::ReadCMAP(cmapData, cmapLen,
   187                                         *charmap, mUVSOffset,
   188                                         unicodeFont, symbolFont);
   189         } else {
   190             rv = NS_ERROR_NOT_AVAILABLE;
   191         }
   192     }
   194     if (NS_SUCCEEDED(rv) && !HasGraphiteTables()) {
   195         // We assume a Graphite font knows what it's doing,
   196         // and provides whatever shaping is needed for the
   197         // characters it supports, so only check/clear the
   198         // complex-script ranges for non-Graphite fonts
   200         // for layout support, check for the presence of mort/morx and/or
   201         // opentype layout tables
   202         bool hasAATLayout = HasFontTable(TRUETYPE_TAG('m','o','r','x')) ||
   203                             HasFontTable(TRUETYPE_TAG('m','o','r','t'));
   204         bool hasGSUB = HasFontTable(TRUETYPE_TAG('G','S','U','B'));
   205         bool hasGPOS = HasFontTable(TRUETYPE_TAG('G','P','O','S'));
   206         if (hasAATLayout && !(hasGSUB || hasGPOS)) {
   207             mRequiresAAT = true; // prefer CoreText if font has no OTL tables
   208         }
   210         for (const ScriptRange* sr = gfxPlatformFontList::sComplexScriptRanges;
   211              sr->rangeStart; sr++) {
   212             // check to see if the cmap includes complex script codepoints
   213             if (charmap->TestRange(sr->rangeStart, sr->rangeEnd)) {
   214                 if (hasAATLayout) {
   215                     // prefer CoreText for Apple's complex-script fonts,
   216                     // even if they also have some OpenType tables
   217                     // (e.g. Geeza Pro Bold on 10.6; see bug 614903)
   218                     mRequiresAAT = true;
   219                     // and don't mask off complex-script ranges, we assume
   220                     // the AAT tables will provide the necessary shaping
   221                     continue;
   222                 }
   224                 // We check for GSUB here, as GPOS alone would not be ok.
   225                 if (hasGSUB && SupportsScriptInGSUB(sr->tags)) {
   226                     continue;
   227                 }
   229                 charmap->ClearRange(sr->rangeStart, sr->rangeEnd);
   230             }
   231         }
   232     }
   234     mHasCmapTable = NS_SUCCEEDED(rv);
   235     if (mHasCmapTable) {
   236         gfxPlatformFontList *pfl = gfxPlatformFontList::PlatformFontList();
   237         mCharacterMap = pfl->FindCharMap(charmap);
   238     } else {
   239         // if error occurred, initialize to null cmap
   240         mCharacterMap = new gfxCharacterMap();
   241     }
   243 #ifdef PR_LOGGING
   244     LOG_FONTLIST(("(fontlist-cmap) name: %s, size: %d hash: %8.8x%s\n",
   245                   NS_ConvertUTF16toUTF8(mName).get(),
   246                   charmap->SizeOfIncludingThis(moz_malloc_size_of),
   247                   charmap->mHash, mCharacterMap == charmap ? " new" : ""));
   248     if (LOG_CMAPDATA_ENABLED()) {
   249         char prefix[256];
   250         sprintf(prefix, "(cmapdata) name: %.220s",
   251                 NS_ConvertUTF16toUTF8(mName).get());
   252         charmap->Dump(prefix, eGfxLog_cmapdata);
   253     }
   254 #endif
   256     return rv;
   257 }
   259 gfxFont*
   260 MacOSFontEntry::CreateFontInstance(const gfxFontStyle *aFontStyle, bool aNeedsBold)
   261 {
   262     return new gfxMacFont(this, aFontStyle, aNeedsBold);
   263 }
   265 bool
   266 MacOSFontEntry::IsCFF()
   267 {
   268     if (!mIsCFFInitialized) {
   269         mIsCFFInitialized = true;
   270         mIsCFF = HasFontTable(TRUETYPE_TAG('C','F','F',' '));
   271     }
   273     return mIsCFF;
   274 }
   276 MacOSFontEntry::MacOSFontEntry(const nsAString& aPostscriptName,
   277                                int32_t aWeight,
   278                                bool aIsStandardFace)
   279     : gfxFontEntry(aPostscriptName, aIsStandardFace),
   280       mFontRef(NULL),
   281       mFontRefInitialized(false),
   282       mRequiresAAT(false),
   283       mIsCFF(false),
   284       mIsCFFInitialized(false)
   285 {
   286     mWeight = aWeight;
   287 }
   289 MacOSFontEntry::MacOSFontEntry(const nsAString& aPostscriptName,
   290                                CGFontRef aFontRef,
   291                                uint16_t aWeight, uint16_t aStretch,
   292                                uint32_t aItalicStyle,
   293                                bool aIsUserFont, bool aIsLocal)
   294     : gfxFontEntry(aPostscriptName, false),
   295       mFontRef(NULL),
   296       mFontRefInitialized(false),
   297       mRequiresAAT(false),
   298       mIsCFF(false),
   299       mIsCFFInitialized(false)
   300 {
   301     mFontRef = aFontRef;
   302     mFontRefInitialized = true;
   303     ::CFRetain(mFontRef);
   305     mWeight = aWeight;
   306     mStretch = aStretch;
   307     mFixedPitch = false; // xxx - do we need this for downloaded fonts?
   308     mItalic = (aItalicStyle & (NS_FONT_STYLE_ITALIC | NS_FONT_STYLE_OBLIQUE)) != 0;
   309     mIsUserFont = aIsUserFont;
   310     mIsLocalUserFont = aIsLocal;
   311 }
   313 CGFontRef
   314 MacOSFontEntry::GetFontRef()
   315 {
   316     if (!mFontRefInitialized) {
   317         mFontRefInitialized = true;
   318         NSString *psname = GetNSStringForString(mName);
   319         mFontRef = ::CGFontCreateWithFontName(CFStringRef(psname));
   320     }
   321     return mFontRef;
   322 }
   324 /*static*/ void
   325 MacOSFontEntry::DestroyBlobFunc(void* aUserData)
   326 {
   327     ::CFRelease((CFDataRef)aUserData);
   328 }
   330 hb_blob_t *
   331 MacOSFontEntry::GetFontTable(uint32_t aTag)
   332 {
   333     CGFontRef fontRef = GetFontRef();
   334     if (!fontRef) {
   335         return nullptr;
   336     }
   338     CFDataRef dataRef = ::CGFontCopyTableForTag(fontRef, aTag);
   339     if (dataRef) {
   340         return hb_blob_create((const char*)::CFDataGetBytePtr(dataRef),
   341                               ::CFDataGetLength(dataRef),
   342                               HB_MEMORY_MODE_READONLY,
   343                               (void*)dataRef, DestroyBlobFunc);
   344     }
   346     return nullptr;
   347 }
   349 bool
   350 MacOSFontEntry::HasFontTable(uint32_t aTableTag)
   351 {
   352     nsAutoreleasePool localPool;
   354     CGFontRef fontRef = GetFontRef();
   355     if (!fontRef) {
   356         return false;
   357     }
   359     CFDataRef tableData = ::CGFontCopyTableForTag(fontRef, aTableTag);
   360     if (!tableData) {
   361         return false;
   362     }
   364     ::CFRelease(tableData);
   365     return true;
   366 }
   368 void
   369 MacOSFontEntry::AddSizeOfIncludingThis(MallocSizeOf aMallocSizeOf,
   370                                        FontListSizes* aSizes) const
   371 {
   372     aSizes->mFontListSize += aMallocSizeOf(this);
   373     AddSizeOfExcludingThis(aMallocSizeOf, aSizes);
   374 }
   376 /* gfxMacFontFamily */
   377 #pragma mark-
   379 class gfxMacFontFamily : public gfxFontFamily
   380 {
   381 public:
   382     gfxMacFontFamily(nsAString& aName) :
   383         gfxFontFamily(aName)
   384     {}
   386     virtual ~gfxMacFontFamily() {}
   388     virtual void LocalizedName(nsAString& aLocalizedName);
   390     virtual void FindStyleVariations(FontInfoData *aFontInfoData = nullptr);
   391 };
   393 void
   394 gfxMacFontFamily::LocalizedName(nsAString& aLocalizedName)
   395 {
   396     nsAutoreleasePool localPool;
   398     if (!HasOtherFamilyNames()) {
   399         aLocalizedName = mName;
   400         return;
   401     }
   403     NSString *family = GetNSStringForString(mName);
   404     NSString *localized = [sFontManager
   405                            localizedNameForFamily:family
   406                                              face:nil];
   408     if (localized) {
   409         GetStringForNSString(localized, aLocalizedName);
   410         return;
   411     }
   413     // failed to get localized name, just use the canonical one
   414     aLocalizedName = mName;
   415 }
   417 // Return the CSS weight value to use for the given face, overriding what
   418 // AppKit gives us (used to adjust families with bad weight values, see
   419 // bug 931426).
   420 // A return value of 0 indicates no override - use the existing weight.
   421 static inline int
   422 GetWeightOverride(const nsAString& aPSName)
   423 {
   424     nsAutoCString prefName("font.weight-override.");
   425     // The PostScript name is required to be ASCII; if it's not, the font is
   426     // broken anyway, so we really don't care that this is lossy.
   427     LossyAppendUTF16toASCII(aPSName, prefName);
   428     return Preferences::GetInt(prefName.get(), 0);
   429 }
   431 void
   432 gfxMacFontFamily::FindStyleVariations(FontInfoData *aFontInfoData)
   433 {
   434     if (mHasStyles)
   435         return;
   437     nsAutoreleasePool localPool;
   439     NSString *family = GetNSStringForString(mName);
   441     // create a font entry for each face
   442     NSArray *fontfaces = [sFontManager
   443                           availableMembersOfFontFamily:family];  // returns an array of [psname, style name, weight, traits] elements, goofy api
   444     int faceCount = [fontfaces count];
   445     int faceIndex;
   447     for (faceIndex = 0; faceIndex < faceCount; faceIndex++) {
   448         NSArray *face = [fontfaces objectAtIndex:faceIndex];
   449         NSString *psname = [face objectAtIndex:INDEX_FONT_POSTSCRIPT_NAME];
   450         int32_t appKitWeight = [[face objectAtIndex:INDEX_FONT_WEIGHT] unsignedIntValue];
   451         uint32_t macTraits = [[face objectAtIndex:INDEX_FONT_TRAITS] unsignedIntValue];
   452         NSString *facename = [face objectAtIndex:INDEX_FONT_FACE_NAME];
   453         bool isStandardFace = false;
   455         if (appKitWeight == kAppleExtraLightWeight) {
   456             // if the facename contains UltraLight, set the weight to the ultralight weight value
   457             NSRange range = [facename rangeOfString:@"ultralight" options:NSCaseInsensitiveSearch];
   458             if (range.location != NSNotFound) {
   459                 appKitWeight = kAppleUltraLightWeight;
   460             }
   461         }
   463         // make a nsString
   464         nsAutoString postscriptFontName;
   465         GetStringForNSString(psname, postscriptFontName);
   467         int32_t cssWeight = GetWeightOverride(postscriptFontName);
   468         if (cssWeight) {
   469             // scale down and clamp, to get a value from 1..9
   470             cssWeight = ((cssWeight + 50) / 100);
   471             cssWeight = std::max(1, std::min(cssWeight, 9));
   472         } else {
   473             cssWeight =
   474                 gfxMacPlatformFontList::AppleWeightToCSSWeight(appKitWeight);
   475         }
   476         cssWeight *= 100; // scale up to CSS values
   478         if ([facename isEqualToString:@"Regular"] ||
   479             [facename isEqualToString:@"Bold"] ||
   480             [facename isEqualToString:@"Italic"] ||
   481             [facename isEqualToString:@"Oblique"] ||
   482             [facename isEqualToString:@"Bold Italic"] ||
   483             [facename isEqualToString:@"Bold Oblique"])
   484         {
   485             isStandardFace = true;
   486         }
   488         // create a font entry
   489         MacOSFontEntry *fontEntry =
   490             new MacOSFontEntry(postscriptFontName, cssWeight, isStandardFace);
   491         if (!fontEntry) {
   492             break;
   493         }
   495         // set additional properties based on the traits reported by Cocoa
   496         if (macTraits & (NSCondensedFontMask | NSNarrowFontMask | NSCompressedFontMask)) {
   497             fontEntry->mStretch = NS_FONT_STRETCH_CONDENSED;
   498         } else if (macTraits & NSExpandedFontMask) {
   499             fontEntry->mStretch = NS_FONT_STRETCH_EXPANDED;
   500         }
   501         // Cocoa fails to set the Italic traits bit for HelveticaLightItalic,
   502         // at least (see bug 611855), so check for style name endings as well
   503         if ((macTraits & NSItalicFontMask) ||
   504             [facename hasSuffix:@"Italic"] ||
   505             [facename hasSuffix:@"Oblique"])
   506         {
   507             fontEntry->mItalic = true;
   508         }
   509         if (macTraits & NSFixedPitchFontMask) {
   510             fontEntry->mFixedPitch = true;
   511         }
   513 #ifdef PR_LOGGING
   514         if (LOG_FONTLIST_ENABLED()) {
   515             LOG_FONTLIST(("(fontlist) added (%s) to family (%s)"
   516                  " with style: %s weight: %d stretch: %d"
   517                  " (apple-weight: %d macTraits: %8.8x)",
   518                  NS_ConvertUTF16toUTF8(fontEntry->Name()).get(), 
   519                  NS_ConvertUTF16toUTF8(Name()).get(), 
   520                  fontEntry->IsItalic() ? "italic" : "normal",
   521                  cssWeight, fontEntry->Stretch(),
   522                  appKitWeight, macTraits));
   523         }
   524 #endif
   526         // insert into font entry array of family
   527         AddFontEntry(fontEntry);
   528     }
   530     SortAvailableFonts();
   531     SetHasStyles(true);
   533     if (mIsBadUnderlineFamily) {
   534         SetBadUnderlineFonts();
   535     }
   536 }
   539 /* gfxSingleFaceMacFontFamily */
   540 #pragma mark-
   542 class gfxSingleFaceMacFontFamily : public gfxFontFamily
   543 {
   544 public:
   545     gfxSingleFaceMacFontFamily(nsAString& aName) :
   546         gfxFontFamily(aName)
   547     {
   548         mFaceNamesInitialized = true; // omit from face name lists
   549     }
   551     virtual ~gfxSingleFaceMacFontFamily() {}
   553     virtual void LocalizedName(nsAString& aLocalizedName);
   555     virtual void ReadOtherFamilyNames(gfxPlatformFontList *aPlatformFontList);
   556 };
   558 void
   559 gfxSingleFaceMacFontFamily::LocalizedName(nsAString& aLocalizedName)
   560 {
   561     nsAutoreleasePool localPool;
   563     if (!HasOtherFamilyNames()) {
   564         aLocalizedName = mName;
   565         return;
   566     }
   568     gfxFontEntry *fe = mAvailableFonts[0];
   569     NSFont *font = [NSFont fontWithName:GetNSStringForString(fe->Name())
   570                                    size:0.0];
   571     if (font) {
   572         NSString *localized = [font displayName];
   573         if (localized) {
   574             GetStringForNSString(localized, aLocalizedName);
   575             return;
   576         }
   577     }
   579     // failed to get localized name, just use the canonical one
   580     aLocalizedName = mName;
   581 }
   583 void
   584 gfxSingleFaceMacFontFamily::ReadOtherFamilyNames(gfxPlatformFontList *aPlatformFontList)
   585 {
   586     if (mOtherFamilyNamesInitialized) {
   587         return;
   588     }
   590     gfxFontEntry *fe = mAvailableFonts[0];
   591     if (!fe) {
   592         return;
   593     }
   595     const uint32_t kNAME = TRUETYPE_TAG('n','a','m','e');
   597     gfxFontEntry::AutoTable nameTable(fe, kNAME);
   598     if (!nameTable) {
   599         return;
   600     }
   602     mHasOtherFamilyNames = ReadOtherFamilyNamesForFace(aPlatformFontList,
   603                                                        nameTable,
   604                                                        true);
   606     mOtherFamilyNamesInitialized = true;
   607 }
   610 /* gfxMacPlatformFontList */
   611 #pragma mark-
   613 gfxMacPlatformFontList::gfxMacPlatformFontList() :
   614     gfxPlatformFontList(false),
   615     mDefaultFont(nullptr)
   616 {
   617     ::CFNotificationCenterAddObserver(::CFNotificationCenterGetLocalCenter(),
   618                                       this,
   619                                       RegisteredFontsChangedNotificationCallback,
   620                                       kCTFontManagerRegisteredFontsChangedNotification,
   621                                       0,
   622                                       CFNotificationSuspensionBehaviorDeliverImmediately);
   624     // cache this in a static variable so that MacOSFontFamily objects
   625     // don't have to repeatedly look it up
   626     sFontManager = [NSFontManager sharedFontManager];
   627 }
   629 gfxMacPlatformFontList::~gfxMacPlatformFontList()
   630 {
   631     if (mDefaultFont) {
   632         ::CFRelease(mDefaultFont);
   633     }
   634 }
   636 nsresult
   637 gfxMacPlatformFontList::InitFontList()
   638 {
   639     nsAutoreleasePool localPool;
   641     Telemetry::AutoTimer<Telemetry::MAC_INITFONTLIST_TOTAL> timer;
   643     // reset font lists
   644     gfxPlatformFontList::InitFontList();
   646     // iterate over available families
   648     CFArrayRef familyNames = CTFontManagerCopyAvailableFontFamilyNames();
   650     // iterate over families
   651     uint32_t i, numFamilies;
   653     numFamilies = CFArrayGetCount(familyNames);
   654     for (i = 0; i < numFamilies; i++) {
   655         CFStringRef family = (CFStringRef)CFArrayGetValueAtIndex(familyNames, i);
   657         // CTFontManager includes weird internal family names and
   658         // LastResort, skip over those
   659         if (!family ||
   660             ::CFStringHasPrefix(family, CFSTR(".")) ||
   661             CFStringCompare(family, CFSTR("LastResort"),
   662                             kCFCompareCaseInsensitive) == kCFCompareEqualTo) {
   663             continue;
   664         }
   666         nsAutoTArray<UniChar, 1024> buffer;
   667         CFIndex len = ::CFStringGetLength(family);
   668         buffer.SetLength(len+1);
   669         ::CFStringGetCharacters(family, ::CFRangeMake(0, len),
   670                                 buffer.Elements());
   671         buffer[len] = 0;
   672         nsAutoString familyName(reinterpret_cast<char16_t*>(buffer.Elements()), len);
   674         // create a family entry
   675         gfxFontFamily *familyEntry = new gfxMacFontFamily(familyName);
   676         if (!familyEntry) break;
   678         // add the family entry to the hash table
   679         ToLowerCase(familyName);
   680         mFontFamilies.Put(familyName, familyEntry);
   682         // check the bad underline blacklist
   683         if (mBadUnderlineFamilyNames.Contains(familyName))
   684             familyEntry->SetBadUnderlineFamily();
   685     }
   687     CFRelease(familyNames);
   689     InitSingleFaceList();
   691     // to avoid full search of font name tables, seed the other names table with localized names from
   692     // some of the prefs fonts which are accessed via their localized names.  changes in the pref fonts will only cause
   693     // a font lookup miss earlier. this is a simple optimization, it's not required for correctness
   694     PreloadNamesList();
   696     // start the delayed cmap loader
   697     GetPrefsAndStartLoader();
   699     return NS_OK;
   700 }
   702 void
   703 gfxMacPlatformFontList::InitSingleFaceList()
   704 {
   705     nsAutoTArray<nsString, 10> singleFaceFonts;
   706     gfxFontUtils::GetPrefsFontList("font.single-face-list", singleFaceFonts);
   708     uint32_t numFonts = singleFaceFonts.Length();
   709     for (uint32_t i = 0; i < numFonts; i++) {
   710 #ifdef PR_LOGGING
   711         LOG_FONTLIST(("(fontlist-singleface) face name: %s\n",
   712                       NS_ConvertUTF16toUTF8(singleFaceFonts[i]).get()));
   713 #endif
   714         gfxFontEntry *fontEntry = LookupLocalFont(nullptr, singleFaceFonts[i]);
   715         if (fontEntry) {
   716             nsAutoString familyName, key;
   717             familyName = singleFaceFonts[i];
   718             GenerateFontListKey(familyName, key);
   719 #ifdef PR_LOGGING
   720             LOG_FONTLIST(("(fontlist-singleface) family name: %s, key: %s\n",
   721                           NS_ConvertUTF16toUTF8(familyName).get(),
   722                           NS_ConvertUTF16toUTF8(key).get()));
   723 #endif
   725             // add only if doesn't exist already
   726             if (!mFontFamilies.GetWeak(key)) {
   727                 gfxFontFamily *familyEntry =
   728                     new gfxSingleFaceMacFontFamily(familyName);
   729                 familyEntry->AddFontEntry(fontEntry);
   730                 familyEntry->SetHasStyles(true);
   731                 mFontFamilies.Put(key, familyEntry);
   732 #ifdef PR_LOGGING
   733                 LOG_FONTLIST(("(fontlist-singleface) added new family\n",
   734                               NS_ConvertUTF16toUTF8(familyName).get(),
   735                               NS_ConvertUTF16toUTF8(key).get()));
   736 #endif
   737             }
   738         }
   739     }
   740 }
   742 bool
   743 gfxMacPlatformFontList::GetStandardFamilyName(const nsAString& aFontName, nsAString& aFamilyName)
   744 {
   745     gfxFontFamily *family = FindFamily(aFontName);
   746     if (family) {
   747         family->LocalizedName(aFamilyName);
   748         return true;
   749     }
   751     return false;
   752 }
   754 void
   755 gfxMacPlatformFontList::RegisteredFontsChangedNotificationCallback(CFNotificationCenterRef center,
   756                                                                    void *observer,
   757                                                                    CFStringRef name,
   758                                                                    const void *object,
   759                                                                    CFDictionaryRef userInfo)
   760 {
   761     if (!::CFEqual(name, kCTFontManagerRegisteredFontsChangedNotification)) {
   762         return;
   763     }
   765     gfxMacPlatformFontList* fl = static_cast<gfxMacPlatformFontList*>(observer);
   767     // xxx - should be carefully pruning the list of fonts, not rebuilding it from scratch
   768     fl->UpdateFontList();
   770     // modify a preference that will trigger reflow everywhere
   771     fl->ForceGlobalReflow();
   772 }
   774 gfxFontEntry*
   775 gfxMacPlatformFontList::GlobalFontFallback(const uint32_t aCh,
   776                                            int32_t aRunScript,
   777                                            const gfxFontStyle* aMatchStyle,
   778                                            uint32_t& aCmapCount,
   779                                            gfxFontFamily** aMatchedFamily)
   780 {
   781     bool useCmaps = gfxPlatform::GetPlatform()->UseCmapsDuringSystemFallback();
   783     if (useCmaps) {
   784         return gfxPlatformFontList::GlobalFontFallback(aCh,
   785                                                        aRunScript,
   786                                                        aMatchStyle,
   787                                                        aCmapCount,
   788                                                        aMatchedFamily);
   789     }
   791     CFStringRef str;
   792     UniChar ch[2];
   793     CFIndex len = 1;
   795     if (IS_IN_BMP(aCh)) {
   796         ch[0] = aCh;
   797         str = ::CFStringCreateWithCharactersNoCopy(kCFAllocatorDefault, ch, 1,
   798                                                    kCFAllocatorNull);
   799     } else {
   800         ch[0] = H_SURROGATE(aCh);
   801         ch[1] = L_SURROGATE(aCh);
   802         str = ::CFStringCreateWithCharactersNoCopy(kCFAllocatorDefault, ch, 2,
   803                                                    kCFAllocatorNull);
   804         if (!str) {
   805             return nullptr;
   806         }
   807         len = 2;
   808     }
   810     // use CoreText to find the fallback family
   812     gfxFontEntry *fontEntry = nullptr;
   813     CTFontRef fallback;
   814     bool cantUseFallbackFont = false;
   816     if (!mDefaultFont) {
   817         mDefaultFont = ::CTFontCreateWithName(CFSTR("LucidaGrande"), 12.f,
   818                                               NULL);
   819     }
   821     fallback = ::CTFontCreateForString(mDefaultFont, str,
   822                                        ::CFRangeMake(0, len));
   824     if (fallback) {
   825         CFStringRef familyName = ::CTFontCopyFamilyName(fallback);
   826         ::CFRelease(fallback);
   828         if (familyName &&
   829             ::CFStringCompare(familyName, CFSTR("LastResort"),
   830                               kCFCompareCaseInsensitive) != kCFCompareEqualTo)
   831         {
   832             nsAutoTArray<UniChar, 1024> buffer;
   833             CFIndex len = ::CFStringGetLength(familyName);
   834             buffer.SetLength(len+1);
   835             ::CFStringGetCharacters(familyName, ::CFRangeMake(0, len),
   836                                     buffer.Elements());
   837             buffer[len] = 0;
   838             nsDependentString familyName(reinterpret_cast<char16_t*>(buffer.Elements()), len);
   840             bool needsBold;  // ignored in the system fallback case
   842             gfxFontFamily *family = FindFamily(familyName);
   843             if (family) {
   844                 fontEntry = family->FindFontForStyle(*aMatchStyle, needsBold);
   845                 if (fontEntry) {
   846                     if (fontEntry->TestCharacterMap(aCh)) {
   847                         *aMatchedFamily = family;
   848                     } else {
   849                         fontEntry = nullptr;
   850                         cantUseFallbackFont = true;
   851                     }
   852                 }
   853             }
   854         }
   856         if (familyName) {
   857             ::CFRelease(familyName);
   858         }
   859     }
   861     if (cantUseFallbackFont) {
   862         Telemetry::Accumulate(Telemetry::BAD_FALLBACK_FONT, cantUseFallbackFont);
   863     }
   865     ::CFRelease(str);
   867     return fontEntry;
   868 }
   870 gfxFontFamily*
   871 gfxMacPlatformFontList::GetDefaultFont(const gfxFontStyle* aStyle)
   872 {
   873     nsAutoreleasePool localPool;
   875     NSString *defaultFamily = [[NSFont userFontOfSize:aStyle->size] familyName];
   876     nsAutoString familyName;
   878     GetStringForNSString(defaultFamily, familyName);
   879     return FindFamily(familyName);
   880 }
   882 int32_t
   883 gfxMacPlatformFontList::AppleWeightToCSSWeight(int32_t aAppleWeight)
   884 {
   885     if (aAppleWeight < 1)
   886         aAppleWeight = 1;
   887     else if (aAppleWeight > kAppleMaxWeight)
   888         aAppleWeight = kAppleMaxWeight;
   889     return gAppleWeightToCSSWeight[aAppleWeight];
   890 }
   892 gfxFontEntry*
   893 gfxMacPlatformFontList::LookupLocalFont(const gfxProxyFontEntry *aProxyEntry,
   894                                         const nsAString& aFontName)
   895 {
   896     nsAutoreleasePool localPool;
   898     NSString *faceName = GetNSStringForString(aFontName);
   899     MacOSFontEntry *newFontEntry;
   901     // lookup face based on postscript or full name
   902     CGFontRef fontRef = ::CGFontCreateWithFontName(CFStringRef(faceName));
   903     if (!fontRef) {
   904         return nullptr;
   905     }
   907     if (aProxyEntry) {
   908         uint16_t w = aProxyEntry->mWeight;
   909         NS_ASSERTION(w >= 100 && w <= 900, "bogus font weight value!");
   911         newFontEntry =
   912             new MacOSFontEntry(aFontName, fontRef,
   913                                w, aProxyEntry->mStretch,
   914                                aProxyEntry->mItalic ?
   915                                    NS_FONT_STYLE_ITALIC : NS_FONT_STYLE_NORMAL,
   916                                true, true);
   917     } else {
   918         newFontEntry =
   919             new MacOSFontEntry(aFontName, fontRef,
   920                                400, 0, NS_FONT_STYLE_NORMAL,
   921                                false, false);
   922     }
   923     ::CFRelease(fontRef);
   925     return newFontEntry;
   926 }
   928 static void ReleaseData(void *info, const void *data, size_t size)
   929 {
   930     NS_Free((void*)data);
   931 }
   933 gfxFontEntry*
   934 gfxMacPlatformFontList::MakePlatformFont(const gfxProxyFontEntry *aProxyEntry,
   935                                          const uint8_t *aFontData,
   936                                          uint32_t aLength)
   937 {
   938     NS_ASSERTION(aFontData, "MakePlatformFont called with null data");
   940     uint16_t w = aProxyEntry->mWeight;
   941     NS_ASSERTION(w >= 100 && w <= 900, "bogus font weight value!");
   943     // create the font entry
   944     nsAutoString uniqueName;
   946     nsresult rv = gfxFontUtils::MakeUniqueUserFontName(uniqueName);
   947     if (NS_FAILED(rv)) {
   948         return nullptr;
   949     }
   951     CGDataProviderRef provider =
   952         ::CGDataProviderCreateWithData(nullptr, aFontData, aLength,
   953                                        &ReleaseData);
   954     CGFontRef fontRef = ::CGFontCreateWithDataProvider(provider);
   955     ::CGDataProviderRelease(provider);
   957     if (!fontRef) {
   958         return nullptr;
   959     }
   961     nsAutoPtr<MacOSFontEntry>
   962         newFontEntry(new MacOSFontEntry(uniqueName, fontRef, w,
   963                                         aProxyEntry->mStretch,
   964                                         aProxyEntry->mItalic ?
   965                                             NS_FONT_STYLE_ITALIC :
   966                                             NS_FONT_STYLE_NORMAL,
   967                                         true, false));
   968     ::CFRelease(fontRef);
   970     // if succeeded and font cmap is good, return the new font
   971     if (newFontEntry->mIsValid && NS_SUCCEEDED(newFontEntry->ReadCMAP())) {
   972         return newFontEntry.forget();
   973     }
   975     // if something is funky about this font, delete immediately
   977 #if DEBUG
   978     NS_WARNING("downloaded font not loaded properly");
   979 #endif
   981     return nullptr;
   982 }
   984 // used to load system-wide font info on off-main thread
   985 class MacFontInfo : public FontInfoData {
   986 public:
   987     MacFontInfo(bool aLoadOtherNames,
   988                 bool aLoadFaceNames,
   989                 bool aLoadCmaps) :
   990         FontInfoData(aLoadOtherNames, aLoadFaceNames, aLoadCmaps)
   991     {}
   993     virtual ~MacFontInfo() {}
   995     virtual void Load() {
   996         nsAutoreleasePool localPool;
   997         // bug 975460 - async font loader crashes sometimes under 10.6, disable
   998         if (nsCocoaFeatures::OnLionOrLater()) {
   999             FontInfoData::Load();
  1003     // loads font data for all members of a given family
  1004     virtual void LoadFontFamilyData(const nsAString& aFamilyName);
  1005 };
  1007 void
  1008 MacFontInfo::LoadFontFamilyData(const nsAString& aFamilyName)
  1010     // family name ==> CTFontDescriptor
  1011     NSString *famName = GetNSStringForString(aFamilyName);
  1012     CFStringRef family = CFStringRef(famName);
  1014     CFMutableDictionaryRef attr =
  1015         CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks,
  1016                                   &kCFTypeDictionaryValueCallBacks);
  1017     CFDictionaryAddValue(attr, kCTFontFamilyNameAttribute, family);
  1018     CTFontDescriptorRef fd = CTFontDescriptorCreateWithAttributes(attr);
  1019     CFRelease(attr);
  1020     CFArrayRef matchingFonts =
  1021         CTFontDescriptorCreateMatchingFontDescriptors(fd, NULL);
  1022     CFRelease(fd);
  1023     if (!matchingFonts) {
  1024         return;
  1027     nsTArray<nsString> otherFamilyNames;
  1028     bool hasOtherFamilyNames = true;
  1030     // iterate over faces in the family
  1031     int f, numFaces = (int) CFArrayGetCount(matchingFonts);
  1032     for (f = 0; f < numFaces; f++) {
  1033         mLoadStats.fonts++;
  1035         CTFontDescriptorRef faceDesc =
  1036             (CTFontDescriptorRef)CFArrayGetValueAtIndex(matchingFonts, f);
  1037         if (!faceDesc) {
  1038             continue;
  1040         CTFontRef fontRef = CTFontCreateWithFontDescriptor(faceDesc,
  1041                                                            0.0, nullptr);
  1042         if (!fontRef) {
  1043             NS_WARNING("failed to create a CTFontRef");
  1044             continue;
  1047         if (mLoadCmaps) {
  1048             // face name
  1049             CFStringRef faceName = (CFStringRef)
  1050                 CTFontDescriptorCopyAttribute(faceDesc, kCTFontNameAttribute);
  1052             nsAutoTArray<UniChar, 1024> buffer;
  1053             CFIndex len = CFStringGetLength(faceName);
  1054             buffer.SetLength(len+1);
  1055             CFStringGetCharacters(faceName, ::CFRangeMake(0, len),
  1056                                     buffer.Elements());
  1057             buffer[len] = 0;
  1058             nsAutoString fontName(reinterpret_cast<char16_t*>(buffer.Elements()),
  1059                                   len);
  1061             // load the cmap data
  1062             FontFaceData fontData;
  1063             CFDataRef cmapTable = CTFontCopyTable(fontRef, kCTFontTableCmap,
  1064                                                   kCTFontTableOptionNoOptions);
  1066             if (cmapTable) {
  1067                 bool unicodeFont = false, symbolFont = false; // ignored
  1068                 const uint8_t *cmapData =
  1069                     (const uint8_t*)CFDataGetBytePtr(cmapTable);
  1070                 uint32_t cmapLen = CFDataGetLength(cmapTable);
  1071                 nsRefPtr<gfxCharacterMap> charmap = new gfxCharacterMap();
  1072                 uint32_t offset;
  1073                 nsresult rv;
  1075                 rv = gfxFontUtils::ReadCMAP(cmapData, cmapLen, *charmap, offset,
  1076                                             unicodeFont, symbolFont);
  1077                 if (NS_SUCCEEDED(rv)) {
  1078                     fontData.mCharacterMap = charmap;
  1079                     fontData.mUVSOffset = offset;
  1080                     fontData.mSymbolFont = symbolFont;
  1081                     mLoadStats.cmaps++;
  1083                 CFRelease(cmapTable);
  1086             mFontFaceData.Put(fontName, fontData);
  1087             CFRelease(faceName);
  1090         if (mLoadOtherNames && hasOtherFamilyNames) {
  1091             CFDataRef nameTable = CTFontCopyTable(fontRef, kCTFontTableName,
  1092                                                   kCTFontTableOptionNoOptions);
  1094             if (nameTable) {
  1095                 const char *nameData = (const char*)CFDataGetBytePtr(nameTable);
  1096                 uint32_t nameLen = CFDataGetLength(nameTable);
  1097                 gfxFontFamily::ReadOtherFamilyNamesForFace(aFamilyName,
  1098                                                            nameData, nameLen,
  1099                                                            otherFamilyNames,
  1100                                                            false);
  1101                 hasOtherFamilyNames = otherFamilyNames.Length() != 0;
  1102                 CFRelease(nameTable);
  1106         CFRelease(fontRef);
  1108     CFRelease(matchingFonts);
  1110     // if found other names, insert them in the hash table
  1111     if (otherFamilyNames.Length() != 0) {
  1112         mOtherFamilyNames.Put(aFamilyName, otherFamilyNames);
  1113         mLoadStats.othernames += otherFamilyNames.Length();
  1117 already_AddRefed<FontInfoData>
  1118 gfxMacPlatformFontList::CreateFontInfoData()
  1120     bool loadCmaps = !UsesSystemFallback() ||
  1121         gfxPlatform::GetPlatform()->UseCmapsDuringSystemFallback();
  1123     nsRefPtr<MacFontInfo> fi =
  1124         new MacFontInfo(true, NeedFullnamePostscriptNames(), loadCmaps);
  1125     return fi.forget();

mercurial