diff options
Diffstat (limited to 'gfx/thebes/gfxMacPlatformFontList.mm')
-rw-r--r-- | gfx/thebes/gfxMacPlatformFontList.mm | 1451 |
1 files changed, 1451 insertions, 0 deletions
diff --git a/gfx/thebes/gfxMacPlatformFontList.mm b/gfx/thebes/gfxMacPlatformFontList.mm new file mode 100644 index 000000000..bf958a90b --- /dev/null +++ b/gfx/thebes/gfxMacPlatformFontList.mm @@ -0,0 +1,1451 @@ +/* -*- Mode: ObjC; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * ***** BEGIN LICENSE BLOCK ***** + * Version: BSD + * + * Copyright (C) 2006-2009 Mozilla Corporation. All rights reserved. + * + * Contributor(s): + * Vladimir Vukicevic <vladimir@pobox.com> + * Masayuki Nakano <masayuki@d-toybox.com> + * John Daggett <jdaggett@mozilla.com> + * Jonathan Kew <jfkthame@gmail.com> + * + * Copyright (C) 2006 Apple Computer, Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * ***** END LICENSE BLOCK ***** */ + +#include "mozilla/Logging.h" + +#include <algorithm> + +#import <AppKit/AppKit.h> + +#include "gfxPlatformMac.h" +#include "gfxMacPlatformFontList.h" +#include "gfxMacFont.h" +#include "gfxUserFontSet.h" +#include "harfbuzz/hb.h" + +#include "nsServiceManagerUtils.h" +#include "nsTArray.h" + +#include "nsDirectoryServiceUtils.h" +#include "nsDirectoryServiceDefs.h" +#include "nsAppDirectoryServiceDefs.h" +#include "nsISimpleEnumerator.h" +#include "nsCharTraits.h" +#include "nsCocoaFeatures.h" +#include "nsCocoaUtils.h" +#include "gfxFontConstants.h" + +#include "mozilla/MemoryReporting.h" +#include "mozilla/Preferences.h" +#include "mozilla/Sprintf.h" +#include "mozilla/Telemetry.h" +#include "mozilla/gfx/2D.h" + +#include <unistd.h> +#include <time.h> +#include <dlfcn.h> + +using namespace mozilla; + +// indexes into the NSArray objects that the Cocoa font manager returns +// as the available members of a family +#define INDEX_FONT_POSTSCRIPT_NAME 0 +#define INDEX_FONT_FACE_NAME 1 +#define INDEX_FONT_WEIGHT 2 +#define INDEX_FONT_TRAITS 3 + +static const int kAppleMaxWeight = 14; +static const int kAppleExtraLightWeight = 3; +static const int kAppleUltraLightWeight = 2; + +static const int gAppleWeightToCSSWeight[] = { + 0, + 1, // 1. + 1, // 2. W1, ultralight + 2, // 3. W2, extralight + 3, // 4. W3, light + 4, // 5. W4, semilight + 5, // 6. W5, medium + 6, // 7. + 6, // 8. W6, semibold + 7, // 9. W7, bold + 8, // 10. W8, extrabold + 8, // 11. + 9, // 12. W9, ultrabold + 9, // 13 + 9 // 14 +}; + +// cache Cocoa's "shared font manager" for performance +static NSFontManager *sFontManager; + +static void GetStringForNSString(const NSString *aSrc, nsAString& aDist) +{ + aDist.SetLength([aSrc length]); + [aSrc getCharacters:reinterpret_cast<unichar*>(aDist.BeginWriting())]; +} + +static NSString* GetNSStringForString(const nsAString& aSrc) +{ + return [NSString stringWithCharacters:reinterpret_cast<const unichar*>(aSrc.BeginReading()) + length:aSrc.Length()]; +} + +#define LOG_FONTLIST(args) MOZ_LOG(gfxPlatform::GetLog(eGfxLog_fontlist), \ + mozilla::LogLevel::Debug, args) +#define LOG_FONTLIST_ENABLED() MOZ_LOG_TEST( \ + gfxPlatform::GetLog(eGfxLog_fontlist), \ + mozilla::LogLevel::Debug) +#define LOG_CMAPDATA_ENABLED() MOZ_LOG_TEST( \ + gfxPlatform::GetLog(eGfxLog_cmapdata), \ + mozilla::LogLevel::Debug) + +#pragma mark- + +// Complex scripts will not render correctly unless appropriate AAT or OT +// layout tables are present. +// For OpenType, we also check that the GSUB table supports the relevant +// script tag, to avoid using things like Arial Unicode MS for Lao (it has +// the characters, but lacks OpenType support). + +// TODO: consider whether we should move this to gfxFontEntry and do similar +// cmap-masking on other platforms to avoid using fonts that won't shape +// properly. + +nsresult +MacOSFontEntry::ReadCMAP(FontInfoData *aFontInfoData) +{ + // attempt this once, if errors occur leave a blank cmap + if (mCharacterMap) { + return NS_OK; + } + + RefPtr<gfxCharacterMap> charmap; + nsresult rv; + bool symbolFont = false; // currently ignored + + if (aFontInfoData && (charmap = GetCMAPFromFontInfo(aFontInfoData, + mUVSOffset, + symbolFont))) { + rv = NS_OK; + } else { + uint32_t kCMAP = TRUETYPE_TAG('c','m','a','p'); + charmap = new gfxCharacterMap(); + AutoTable cmapTable(this, kCMAP); + + if (cmapTable) { + bool unicodeFont = false; // currently ignored + uint32_t cmapLen; + const uint8_t* cmapData = + reinterpret_cast<const uint8_t*>(hb_blob_get_data(cmapTable, + &cmapLen)); + rv = gfxFontUtils::ReadCMAP(cmapData, cmapLen, + *charmap, mUVSOffset, + unicodeFont, symbolFont); + } else { + rv = NS_ERROR_NOT_AVAILABLE; + } + } + + if (NS_SUCCEEDED(rv) && !HasGraphiteTables()) { + // We assume a Graphite font knows what it's doing, + // and provides whatever shaping is needed for the + // characters it supports, so only check/clear the + // complex-script ranges for non-Graphite fonts + + // for layout support, check for the presence of mort/morx and/or + // opentype layout tables + bool hasAATLayout = HasFontTable(TRUETYPE_TAG('m','o','r','x')) || + HasFontTable(TRUETYPE_TAG('m','o','r','t')); + bool hasGSUB = HasFontTable(TRUETYPE_TAG('G','S','U','B')); + bool hasGPOS = HasFontTable(TRUETYPE_TAG('G','P','O','S')); + if (hasAATLayout && !(hasGSUB || hasGPOS)) { + mRequiresAAT = true; // prefer CoreText if font has no OTL tables + } + + for (const ScriptRange* sr = gfxPlatformFontList::sComplexScriptRanges; + sr->rangeStart; sr++) { + // check to see if the cmap includes complex script codepoints + if (charmap->TestRange(sr->rangeStart, sr->rangeEnd)) { + if (hasAATLayout) { + // prefer CoreText for Apple's complex-script fonts, + // even if they also have some OpenType tables + // (e.g. Geeza Pro Bold on 10.6; see bug 614903) + mRequiresAAT = true; + // and don't mask off complex-script ranges, we assume + // the AAT tables will provide the necessary shaping + continue; + } + + // We check for GSUB here, as GPOS alone would not be ok. + if (hasGSUB && SupportsScriptInGSUB(sr->tags)) { + continue; + } + + charmap->ClearRange(sr->rangeStart, sr->rangeEnd); + } + } + + // Bug 1360309, 1393624: several of Apple's Chinese fonts have spurious + // blank glyphs for obscure Tibetan and Arabic-script codepoints. + // Blacklist these so that font fallback will not use them. + if (mRequiresAAT && (FamilyName().EqualsLiteral("Songti SC") || + FamilyName().EqualsLiteral("Songti TC") || + FamilyName().EqualsLiteral("STSong") || + // Bug 1390980: on 10.11, the Kaiti fonts are also affected. + FamilyName().EqualsLiteral("Kaiti SC") || + FamilyName().EqualsLiteral("Kaiti TC") || + FamilyName().EqualsLiteral("STKaiti"))) { + charmap->ClearRange(0x0f6b, 0x0f70); + charmap->ClearRange(0x0f8c, 0x0f8f); + charmap->clear(0x0f98); + charmap->clear(0x0fbd); + charmap->ClearRange(0x0fcd, 0x0fff); + charmap->clear(0x0620); + charmap->clear(0x065f); + charmap->ClearRange(0x06ee, 0x06ef); + charmap->clear(0x06ff); + } + } + + mHasCmapTable = NS_SUCCEEDED(rv); + if (mHasCmapTable) { + gfxPlatformFontList *pfl = gfxPlatformFontList::PlatformFontList(); + mCharacterMap = pfl->FindCharMap(charmap); + } else { + // if error occurred, initialize to null cmap + mCharacterMap = new gfxCharacterMap(); + } + + LOG_FONTLIST(("(fontlist-cmap) name: %s, size: %d hash: %8.8x%s\n", + NS_ConvertUTF16toUTF8(mName).get(), + charmap->SizeOfIncludingThis(moz_malloc_size_of), + charmap->mHash, mCharacterMap == charmap ? " new" : "")); + if (LOG_CMAPDATA_ENABLED()) { + char prefix[256]; + SprintfLiteral(prefix, "(cmapdata) name: %.220s", + NS_ConvertUTF16toUTF8(mName).get()); + charmap->Dump(prefix, eGfxLog_cmapdata); + } + + return rv; +} + +gfxFont* +MacOSFontEntry::CreateFontInstance(const gfxFontStyle *aFontStyle, bool aNeedsBold) +{ + return new gfxMacFont(this, aFontStyle, aNeedsBold); +} + +bool +MacOSFontEntry::IsCFF() +{ + if (!mIsCFFInitialized) { + mIsCFFInitialized = true; + mIsCFF = HasFontTable(TRUETYPE_TAG('C','F','F',' ')); + } + + return mIsCFF; +} + +MacOSFontEntry::MacOSFontEntry(const nsAString& aPostscriptName, + int32_t aWeight, + bool aIsStandardFace, + double aSizeHint) + : gfxFontEntry(aPostscriptName, aIsStandardFace), + mFontRef(NULL), + mSizeHint(aSizeHint), + mFontRefInitialized(false), + mRequiresAAT(false), + mIsCFF(false), + mIsCFFInitialized(false) +{ + mWeight = aWeight; +} + +MacOSFontEntry::MacOSFontEntry(const nsAString& aPostscriptName, + CGFontRef aFontRef, + uint16_t aWeight, uint16_t aStretch, + uint8_t aStyle, + bool aIsDataUserFont, + bool aIsLocalUserFont) + : gfxFontEntry(aPostscriptName, false), + mFontRef(NULL), + mSizeHint(0.0), + mFontRefInitialized(false), + mRequiresAAT(false), + mIsCFF(false), + mIsCFFInitialized(false) +{ + mFontRef = aFontRef; + mFontRefInitialized = true; + ::CFRetain(mFontRef); + + mWeight = aWeight; + mStretch = aStretch; + mFixedPitch = false; // xxx - do we need this for downloaded fonts? + mStyle = aStyle; + + NS_ASSERTION(!(aIsDataUserFont && aIsLocalUserFont), + "userfont is either a data font or a local font"); + mIsDataUserFont = aIsDataUserFont; + mIsLocalUserFont = aIsLocalUserFont; +} + +CGFontRef +MacOSFontEntry::GetFontRef() +{ + if (!mFontRefInitialized) { + mFontRefInitialized = true; + NSString *psname = GetNSStringForString(mName); + mFontRef = ::CGFontCreateWithFontName(CFStringRef(psname)); + if (!mFontRef) { + // This happens on macOS 10.12 for font entry names that start with + // .AppleSystemUIFont. For those fonts, we need to go through NSFont + // to get the correct CGFontRef. + // Both the Text and the Display variant of the display font use + // .AppleSystemUIFontSomethingSomething as their member names. + // That's why we're carrying along mSizeHint to this place so that + // we get the variant that we want for this family. + NSFont* font = [NSFont fontWithName:psname size:mSizeHint]; + if (font) { + mFontRef = CTFontCopyGraphicsFont((CTFontRef)font, nullptr); + } + } + } + return mFontRef; +} + +// For a logging build, we wrap the CFDataRef in a FontTableRec so that we can +// use the MOZ_COUNT_[CD]TOR macros in it. A release build without logging +// does not get this overhead. +class FontTableRec { +public: + explicit FontTableRec(CFDataRef aDataRef) + : mDataRef(aDataRef) + { + MOZ_COUNT_CTOR(FontTableRec); + } + + ~FontTableRec() { + MOZ_COUNT_DTOR(FontTableRec); + ::CFRelease(mDataRef); + } + +private: + CFDataRef mDataRef; +}; + +/*static*/ void +MacOSFontEntry::DestroyBlobFunc(void* aUserData) +{ +#ifdef NS_BUILD_REFCNT_LOGGING + FontTableRec *ftr = static_cast<FontTableRec*>(aUserData); + delete ftr; +#else + ::CFRelease((CFDataRef)aUserData); +#endif +} + +hb_blob_t * +MacOSFontEntry::GetFontTable(uint32_t aTag) +{ + CGFontRef fontRef = GetFontRef(); + if (!fontRef) { + return nullptr; + } + + CFDataRef dataRef = ::CGFontCopyTableForTag(fontRef, aTag); + if (dataRef) { + return hb_blob_create((const char*)::CFDataGetBytePtr(dataRef), + ::CFDataGetLength(dataRef), + HB_MEMORY_MODE_READONLY, +#ifdef NS_BUILD_REFCNT_LOGGING + new FontTableRec(dataRef), +#else + (void*)dataRef, +#endif + DestroyBlobFunc); + } + + return nullptr; +} + +bool +MacOSFontEntry::HasFontTable(uint32_t aTableTag) +{ + if (mAvailableTables.Count() == 0) { + nsAutoreleasePool localPool; + + CGFontRef fontRef = GetFontRef(); + if (!fontRef) { + return false; + } + CFArrayRef tags = ::CGFontCopyTableTags(fontRef); + if (!tags) { + return false; + } + int numTags = (int) ::CFArrayGetCount(tags); + for (int t = 0; t < numTags; t++) { + uint32_t tag = (uint32_t)(uintptr_t)::CFArrayGetValueAtIndex(tags, t); + mAvailableTables.PutEntry(tag); + } + ::CFRelease(tags); + } + + return mAvailableTables.GetEntry(aTableTag); +} + +void +MacOSFontEntry::AddSizeOfIncludingThis(MallocSizeOf aMallocSizeOf, + FontListSizes* aSizes) const +{ + aSizes->mFontListSize += aMallocSizeOf(this); + AddSizeOfExcludingThis(aMallocSizeOf, aSizes); +} + +/* gfxMacFontFamily */ +#pragma mark- + +class gfxMacFontFamily : public gfxFontFamily +{ +public: + explicit gfxMacFontFamily(nsAString& aName, double aSizeHint) : + gfxFontFamily(aName), + mSizeHint(aSizeHint) + {} + + virtual ~gfxMacFontFamily() {} + + virtual void LocalizedName(nsAString& aLocalizedName); + + virtual void FindStyleVariations(FontInfoData *aFontInfoData = nullptr); + +protected: + double mSizeHint; +}; + +void +gfxMacFontFamily::LocalizedName(nsAString& aLocalizedName) +{ + nsAutoreleasePool localPool; + + if (!HasOtherFamilyNames()) { + aLocalizedName = mName; + return; + } + + NSString *family = GetNSStringForString(mName); + NSString *localized = [sFontManager + localizedNameForFamily:family + face:nil]; + + if (localized) { + GetStringForNSString(localized, aLocalizedName); + return; + } + + // failed to get localized name, just use the canonical one + aLocalizedName = mName; +} + +// Return the CSS weight value to use for the given face, overriding what +// AppKit gives us (used to adjust families with bad weight values, see +// bug 931426). +// A return value of 0 indicates no override - use the existing weight. +static inline int +GetWeightOverride(const nsAString& aPSName) +{ + nsAutoCString prefName("font.weight-override."); + // The PostScript name is required to be ASCII; if it's not, the font is + // broken anyway, so we really don't care that this is lossy. + LossyAppendUTF16toASCII(aPSName, prefName); + return Preferences::GetInt(prefName.get(), 0); +} + +void +gfxMacFontFamily::FindStyleVariations(FontInfoData *aFontInfoData) +{ + if (mHasStyles) + return; + + nsAutoreleasePool localPool; + + NSString *family = GetNSStringForString(mName); + + // create a font entry for each face + NSArray *fontfaces = [sFontManager + availableMembersOfFontFamily:family]; // returns an array of [psname, style name, weight, traits] elements, goofy api + int faceCount = [fontfaces count]; + int faceIndex; + + for (faceIndex = 0; faceIndex < faceCount; faceIndex++) { + NSArray *face = [fontfaces objectAtIndex:faceIndex]; + NSString *psname = [face objectAtIndex:INDEX_FONT_POSTSCRIPT_NAME]; + int32_t appKitWeight = [[face objectAtIndex:INDEX_FONT_WEIGHT] unsignedIntValue]; + uint32_t macTraits = [[face objectAtIndex:INDEX_FONT_TRAITS] unsignedIntValue]; + NSString *facename = [face objectAtIndex:INDEX_FONT_FACE_NAME]; + bool isStandardFace = false; + + if (appKitWeight == kAppleExtraLightWeight) { + // if the facename contains UltraLight, set the weight to the ultralight weight value + NSRange range = [facename rangeOfString:@"ultralight" options:NSCaseInsensitiveSearch]; + if (range.location != NSNotFound) { + appKitWeight = kAppleUltraLightWeight; + } + } + + // make a nsString + nsAutoString postscriptFontName; + GetStringForNSString(psname, postscriptFontName); + + int32_t cssWeight = GetWeightOverride(postscriptFontName); + if (cssWeight) { + // scale down and clamp, to get a value from 1..9 + cssWeight = ((cssWeight + 50) / 100); + cssWeight = std::max(1, std::min(cssWeight, 9)); + } else { + cssWeight = + gfxMacPlatformFontList::AppleWeightToCSSWeight(appKitWeight); + } + cssWeight *= 100; // scale up to CSS values + + if ([facename isEqualToString:@"Regular"] || + [facename isEqualToString:@"Bold"] || + [facename isEqualToString:@"Italic"] || + [facename isEqualToString:@"Oblique"] || + [facename isEqualToString:@"Bold Italic"] || + [facename isEqualToString:@"Bold Oblique"]) + { + isStandardFace = true; + } + + // create a font entry + MacOSFontEntry *fontEntry = + new MacOSFontEntry(postscriptFontName, cssWeight, isStandardFace, mSizeHint); + if (!fontEntry) { + break; + } + + // set additional properties based on the traits reported by Cocoa + if (macTraits & (NSCondensedFontMask | NSNarrowFontMask | NSCompressedFontMask)) { + fontEntry->mStretch = NS_FONT_STRETCH_CONDENSED; + } else if (macTraits & NSExpandedFontMask) { + fontEntry->mStretch = NS_FONT_STRETCH_EXPANDED; + } + // Cocoa fails to set the Italic traits bit for HelveticaLightItalic, + // at least (see bug 611855), so check for style name endings as well + if ((macTraits & NSItalicFontMask) || + [facename hasSuffix:@"Italic"] || + [facename hasSuffix:@"Oblique"]) + { + fontEntry->mStyle = NS_FONT_STYLE_ITALIC; + } + if (macTraits & NSFixedPitchFontMask) { + fontEntry->mFixedPitch = true; + } + + if (LOG_FONTLIST_ENABLED()) { + LOG_FONTLIST(("(fontlist) added (%s) to family (%s)" + " with style: %s weight: %d stretch: %d" + " (apple-weight: %d macTraits: %8.8x)", + NS_ConvertUTF16toUTF8(fontEntry->Name()).get(), + NS_ConvertUTF16toUTF8(Name()).get(), + fontEntry->IsItalic() ? "italic" : "normal", + cssWeight, fontEntry->Stretch(), + appKitWeight, macTraits)); + } + + // insert into font entry array of family + AddFontEntry(fontEntry); + } + + SortAvailableFonts(); + SetHasStyles(true); + + if (mIsBadUnderlineFamily) { + SetBadUnderlineFonts(); + } +} + +/* gfxSingleFaceMacFontFamily */ +#pragma mark- + +class gfxSingleFaceMacFontFamily : public gfxFontFamily +{ +public: + explicit gfxSingleFaceMacFontFamily(nsAString& aName) : + gfxFontFamily(aName) + { + mFaceNamesInitialized = true; // omit from face name lists + } + + virtual ~gfxSingleFaceMacFontFamily() {} + + virtual void LocalizedName(nsAString& aLocalizedName); + + virtual void ReadOtherFamilyNames(gfxPlatformFontList *aPlatformFontList); +}; + +void +gfxSingleFaceMacFontFamily::LocalizedName(nsAString& aLocalizedName) +{ + nsAutoreleasePool localPool; + + if (!HasOtherFamilyNames()) { + aLocalizedName = mName; + return; + } + + gfxFontEntry *fe = mAvailableFonts[0]; + NSFont *font = [NSFont fontWithName:GetNSStringForString(fe->Name()) + size:0.0]; + if (font) { + NSString *localized = [font displayName]; + if (localized) { + GetStringForNSString(localized, aLocalizedName); + return; + } + } + + // failed to get localized name, just use the canonical one + aLocalizedName = mName; +} + +void +gfxSingleFaceMacFontFamily::ReadOtherFamilyNames(gfxPlatformFontList *aPlatformFontList) +{ + if (mOtherFamilyNamesInitialized) { + return; + } + + gfxFontEntry *fe = mAvailableFonts[0]; + if (!fe) { + return; + } + + const uint32_t kNAME = TRUETYPE_TAG('n','a','m','e'); + + gfxFontEntry::AutoTable nameTable(fe, kNAME); + if (!nameTable) { + return; + } + + mHasOtherFamilyNames = ReadOtherFamilyNamesForFace(aPlatformFontList, + nameTable, + true); + + mOtherFamilyNamesInitialized = true; +} + +/* gfxMacPlatformFontList */ +#pragma mark- + +gfxMacPlatformFontList::gfxMacPlatformFontList() : + gfxPlatformFontList(false), + mDefaultFont(nullptr), + mUseSizeSensitiveSystemFont(false) +{ +#ifdef MOZ_BUNDLED_FONTS + ActivateBundledFonts(); +#endif + + ::CFNotificationCenterAddObserver(::CFNotificationCenterGetLocalCenter(), + this, + RegisteredFontsChangedNotificationCallback, + kCTFontManagerRegisteredFontsChangedNotification, + 0, + CFNotificationSuspensionBehaviorDeliverImmediately); + + // cache this in a static variable so that MacOSFontFamily objects + // don't have to repeatedly look it up + sFontManager = [NSFontManager sharedFontManager]; +} + +gfxMacPlatformFontList::~gfxMacPlatformFontList() +{ + if (mDefaultFont) { + ::CFRelease(mDefaultFont); + } +} + +void +gfxMacPlatformFontList::AddFamily(CFStringRef aFamily) +{ + NSString* family = (NSString*)aFamily; + + // CTFontManager includes weird internal family names and + // LastResort, skip over those + if (!family || [family caseInsensitiveCompare:@"LastResort"] == NSOrderedSame) { + return; + } + + bool hiddenSystemFont = [family hasPrefix:@"."]; + + FontFamilyTable& table = + hiddenSystemFont ? mSystemFontFamilies : mFontFamilies; + + nsAutoString familyName; + nsCocoaUtils::GetStringForNSString(family, familyName); + + double sizeHint = 0.0; + if (hiddenSystemFont && mUseSizeSensitiveSystemFont && + mSystemDisplayFontFamilyName.Equals(familyName)) { + sizeHint = 128.0; + } + + nsAutoString key; + ToLowerCase(familyName, key); + + RefPtr<gfxFontFamily> familyEntry = new gfxMacFontFamily(familyName, sizeHint); + table.Put(key, familyEntry); + + // check the bad underline blacklist + if (mBadUnderlineFamilyNames.Contains(key)) { + familyEntry->SetBadUnderlineFamily(); + } +} + +nsresult +gfxMacPlatformFontList::InitFontListForPlatform() +{ + nsAutoreleasePool localPool; + + Telemetry::AutoTimer<Telemetry::MAC_INITFONTLIST_TOTAL> timer; + + // reset system font list + mSystemFontFamilies.Clear(); + + // iterate over available families + + InitSystemFontNames(); + + CFArrayRef familyNames = CTFontManagerCopyAvailableFontFamilyNames(); + + for (NSString* familyName in (NSArray*)familyNames) { + AddFamily((CFStringRef)familyName); + } + + CFRelease(familyNames); + + InitSingleFaceList(); + + // to avoid full search of font name tables, seed the other names table with localized names from + // some of the prefs fonts which are accessed via their localized names. changes in the pref fonts will only cause + // a font lookup miss earlier. this is a simple optimization, it's not required for correctness + PreloadNamesList(); + + // start the delayed cmap loader + GetPrefsAndStartLoader(); + + return NS_OK; +} + +void +gfxMacPlatformFontList::InitSingleFaceList() +{ + AutoTArray<nsString, 10> singleFaceFonts; + gfxFontUtils::GetPrefsFontList("font.single-face-list", singleFaceFonts); + + for (const auto& singleFaceFamily : singleFaceFonts) { + LOG_FONTLIST(("(fontlist-singleface) face name: %s\n", + NS_ConvertUTF16toUTF8(singleFaceFamily).get())); + // Each entry in the "single face families" list is expected to be a + // colon-separated pair of FaceName:Family, + // where FaceName is the individual face name (psname) of a font + // that should be exposed as a separate family name, + // and Family is the standard family to which that face belongs. + // The only such face listed by default is + // Osaka-Mono:Osaka + nsAutoString familyName(singleFaceFamily); + auto colon = familyName.FindChar(':'); + if (colon == kNotFound) { + continue; + } + + // Look for the parent family in the main font family list, + // and ensure we have loaded its list of available faces. + nsAutoString key(Substring(familyName, colon + 1)); + ToLowerCase(key); + gfxFontFamily* family = mFontFamilies.GetWeak(key); + if (!family) { + continue; + } + family->FindStyleVariations(); + + // Truncate the entry from prefs at the colon, so now it is just the + // desired single-face-family name. + familyName.Truncate(colon); + + // Look through the family's faces to see if this one is present. + const gfxFontEntry* fe = nullptr; + for (const auto& face : family->GetFontList()) { + if (face->Name().Equals(familyName)) { + fe = face; + break; + } + } + if (!fe) { + continue; + } + + // We found the correct face, so create the single-face family record. + GenerateFontListKey(familyName, key); + LOG_FONTLIST(("(fontlist-singleface) family name: %s, key: %s\n", + NS_ConvertUTF16toUTF8(familyName).get(), + NS_ConvertUTF16toUTF8(key).get())); + + // add only if doesn't exist already + if (!mFontFamilies.GetWeak(key)) { + RefPtr<gfxFontFamily> familyEntry = + new gfxSingleFaceMacFontFamily(familyName); + // We need a separate font entry, because its family name will + // differ from the one we found in the main list. + MacOSFontEntry* fontEntry = + new MacOSFontEntry(fe->Name(), fe->mWeight, true, + static_cast<const MacOSFontEntry*>(fe)-> + mSizeHint); + familyEntry->AddFontEntry(fontEntry); + familyEntry->SetHasStyles(true); + mFontFamilies.Put(key, familyEntry); + LOG_FONTLIST(("(fontlist-singleface) added new family\n", + NS_ConvertUTF16toUTF8(familyName).get(), + NS_ConvertUTF16toUTF8(key).get())); + } + } +} + +// System fonts under OSX may contain weird "meta" names but if we create +// a new font using just the Postscript name, the NSFont api returns an object +// with the actual real family name. For example, under OSX 10.11: +// +// [[NSFont menuFontOfSize:8.0] familyName] ==> .AppleSystemUIFont +// [[NSFont fontWithName:[[[NSFont menuFontOfSize:8.0] fontDescriptor] postscriptName] +// size:8.0] familyName] ==> .SF NS Text + +static NSString* GetRealFamilyName(NSFont* aFont) +{ + NSFont* f = [NSFont fontWithName: [[aFont fontDescriptor] postscriptName] + size: 0.0]; + return [f familyName]; +} + +// System fonts under OSX 10.11 use a combination of two families, one +// for text sizes and another for larger, display sizes. Each has a +// different number of weights. There aren't efficient API's for looking +// this information up, so hard code the logic here but confirm via +// debug assertions that the logic is correct. + +const CGFloat kTextDisplayCrossover = 20.0; // use text family below this size + +void +gfxMacPlatformFontList::InitSystemFontNames() +{ + // system font under 10.11 are two distinct families for text/display sizes + if (nsCocoaFeatures::OnElCapitanOrLater()) { + mUseSizeSensitiveSystemFont = true; + } + + // text font family + NSFont* sys = [NSFont systemFontOfSize: 0.0]; + NSString* textFamilyName = GetRealFamilyName(sys); + nsAutoString familyName; + nsCocoaUtils::GetStringForNSString(textFamilyName, familyName); + mSystemTextFontFamilyName = familyName; + + // display font family, if on OSX 10.11 + if (mUseSizeSensitiveSystemFont) { + NSFont* displaySys = [NSFont systemFontOfSize: 128.0]; + NSString* displayFamilyName = GetRealFamilyName(displaySys); + nsCocoaUtils::GetStringForNSString(displayFamilyName, familyName); + mSystemDisplayFontFamilyName = familyName; + +#if DEBUG + // confirm that the optical size switch is at 20.0 + NS_ASSERTION([textFamilyName compare:displayFamilyName] != NSOrderedSame, + "system text/display fonts are the same!"); + NSString* fam19 = GetRealFamilyName([NSFont systemFontOfSize: + (kTextDisplayCrossover - 1.0)]); + NSString* fam20 = GetRealFamilyName([NSFont systemFontOfSize: + kTextDisplayCrossover]); + NS_ASSERTION(fam19 && fam20 && [fam19 compare:fam20] != NSOrderedSame, + "system text/display font size switch point is not as expected!"); +#endif + } + +#ifdef DEBUG + // different system font API's always map to the same family under OSX, so + // just assume that and emit a warning if that ever changes + NSString *sysFamily = GetRealFamilyName([NSFont systemFontOfSize:0.0]); + if ([sysFamily compare:GetRealFamilyName([NSFont boldSystemFontOfSize:0.0])] != NSOrderedSame || + [sysFamily compare:GetRealFamilyName([NSFont controlContentFontOfSize:0.0])] != NSOrderedSame || + [sysFamily compare:GetRealFamilyName([NSFont menuBarFontOfSize:0.0])] != NSOrderedSame || + [sysFamily compare:GetRealFamilyName([NSFont toolTipsFontOfSize:0.0])] != NSOrderedSame) { + NS_WARNING("system font types map to different font families" + " -- please log a bug!!"); + } +#endif +} + +gfxFontFamily* +gfxMacPlatformFontList::FindSystemFontFamily(const nsAString& aFamily) +{ + nsAutoString key; + GenerateFontListKey(aFamily, key); + + gfxFontFamily* familyEntry; + + // lookup in hidden system family name list + if ((familyEntry = mSystemFontFamilies.GetWeak(key))) { + return CheckFamily(familyEntry); + } + + // lookup in user-exposed family name list + if ((familyEntry = mFontFamilies.GetWeak(key))) { + return CheckFamily(familyEntry); + } + + return nullptr; +} + +bool +gfxMacPlatformFontList::GetStandardFamilyName(const nsAString& aFontName, nsAString& aFamilyName) +{ + gfxFontFamily *family = FindFamily(aFontName); + if (family) { + family->LocalizedName(aFamilyName); + return true; + } + + return false; +} + +void +gfxMacPlatformFontList::RegisteredFontsChangedNotificationCallback(CFNotificationCenterRef center, + void *observer, + CFStringRef name, + const void *object, + CFDictionaryRef userInfo) +{ + if (!::CFEqual(name, kCTFontManagerRegisteredFontsChangedNotification)) { + return; + } + + gfxMacPlatformFontList* fl = static_cast<gfxMacPlatformFontList*>(observer); + + // xxx - should be carefully pruning the list of fonts, not rebuilding it from scratch + fl->UpdateFontList(); + + // modify a preference that will trigger reflow everywhere + fl->ForceGlobalReflow(); +} + +gfxFontEntry* +gfxMacPlatformFontList::PlatformGlobalFontFallback(const uint32_t aCh, + Script aRunScript, + const gfxFontStyle* aMatchStyle, + gfxFontFamily** aMatchedFamily) +{ + CFStringRef str; + UniChar ch[2]; + CFIndex length = 1; + + if (IS_IN_BMP(aCh)) { + ch[0] = aCh; + str = ::CFStringCreateWithCharactersNoCopy(kCFAllocatorDefault, ch, 1, + kCFAllocatorNull); + } else { + ch[0] = H_SURROGATE(aCh); + ch[1] = L_SURROGATE(aCh); + str = ::CFStringCreateWithCharactersNoCopy(kCFAllocatorDefault, ch, 2, + kCFAllocatorNull); + if (!str) { + return nullptr; + } + length = 2; + } + + // use CoreText to find the fallback family + + gfxFontEntry *fontEntry = nullptr; + CTFontRef fallback; + bool cantUseFallbackFont = false; + + if (!mDefaultFont) { + mDefaultFont = ::CTFontCreateWithName(CFSTR("LucidaGrande"), 12.f, + NULL); + } + + fallback = ::CTFontCreateForString(mDefaultFont, str, + ::CFRangeMake(0, length)); + + if (fallback) { + CFStringRef familyNameRef = ::CTFontCopyFamilyName(fallback); + ::CFRelease(fallback); + + if (familyNameRef && + ::CFStringCompare(familyNameRef, CFSTR("LastResort"), + kCFCompareCaseInsensitive) != kCFCompareEqualTo) + { + AutoTArray<UniChar, 1024> buffer; + CFIndex familyNameLen = ::CFStringGetLength(familyNameRef); + buffer.SetLength(familyNameLen+1); + ::CFStringGetCharacters(familyNameRef, ::CFRangeMake(0, familyNameLen), + buffer.Elements()); + buffer[familyNameLen] = 0; + nsDependentString familyNameString(reinterpret_cast<char16_t*>(buffer.Elements()), familyNameLen); + + bool needsBold; // ignored in the system fallback case + + gfxFontFamily *family = FindFamily(familyNameString); + if (family) { + fontEntry = family->FindFontForStyle(*aMatchStyle, needsBold); + if (fontEntry) { + if (fontEntry->HasCharacter(aCh)) { + *aMatchedFamily = family; + } else { + fontEntry = nullptr; + cantUseFallbackFont = true; + } + } + } + } + + if (familyNameRef) { + ::CFRelease(familyNameRef); + } + } + + if (cantUseFallbackFont) { + Telemetry::Accumulate(Telemetry::BAD_FALLBACK_FONT, cantUseFallbackFont); + } + + ::CFRelease(str); + + return fontEntry; +} + +gfxFontFamily* +gfxMacPlatformFontList::GetDefaultFontForPlatform(const gfxFontStyle* aStyle) +{ + nsAutoreleasePool localPool; + + NSString *defaultFamily = [[NSFont userFontOfSize:aStyle->size] familyName]; + nsAutoString familyName; + + GetStringForNSString(defaultFamily, familyName); + return FindFamily(familyName); +} + +int32_t +gfxMacPlatformFontList::AppleWeightToCSSWeight(int32_t aAppleWeight) +{ + if (aAppleWeight < 1) + aAppleWeight = 1; + else if (aAppleWeight > kAppleMaxWeight) + aAppleWeight = kAppleMaxWeight; + return gAppleWeightToCSSWeight[aAppleWeight]; +} + +gfxFontEntry* +gfxMacPlatformFontList::LookupLocalFont(const nsAString& aFontName, + uint16_t aWeight, + int16_t aStretch, + uint8_t aStyle) +{ + nsAutoreleasePool localPool; + + NSString *faceName = GetNSStringForString(aFontName); + MacOSFontEntry *newFontEntry; + + // lookup face based on postscript or full name + CGFontRef fontRef = ::CGFontCreateWithFontName(CFStringRef(faceName)); + if (!fontRef) { + return nullptr; + } + + NS_ASSERTION(aWeight >= 100 && aWeight <= 900, + "bogus font weight value!"); + + newFontEntry = + new MacOSFontEntry(aFontName, fontRef, aWeight, aStretch, aStyle, + false, true); + ::CFRelease(fontRef); + + return newFontEntry; +} + +static void ReleaseData(void *info, const void *data, size_t size) +{ + free((void*)data); +} + +gfxFontEntry* +gfxMacPlatformFontList::MakePlatformFont(const nsAString& aFontName, + uint16_t aWeight, + int16_t aStretch, + uint8_t aStyle, + const uint8_t* aFontData, + uint32_t aLength) +{ + NS_ASSERTION(aFontData, "MakePlatformFont called with null data"); + + NS_ASSERTION(aWeight >= 100 && aWeight <= 900, "bogus font weight value!"); + + // create the font entry + nsAutoString uniqueName; + + nsresult rv = gfxFontUtils::MakeUniqueUserFontName(uniqueName); + if (NS_FAILED(rv)) { + return nullptr; + } + + CGDataProviderRef provider = + ::CGDataProviderCreateWithData(nullptr, aFontData, aLength, + &ReleaseData); + CGFontRef fontRef = ::CGFontCreateWithDataProvider(provider); + ::CGDataProviderRelease(provider); + + if (!fontRef) { + return nullptr; + } + + auto newFontEntry = + MakeUnique<MacOSFontEntry>(uniqueName, fontRef, aWeight, aStretch, + aStyle, true, false); + ::CFRelease(fontRef); + + // if succeeded and font cmap is good, return the new font + if (newFontEntry->mIsValid && NS_SUCCEEDED(newFontEntry->ReadCMAP())) { + return newFontEntry.release(); + } + + // if something is funky about this font, delete immediately + +#if DEBUG + NS_WARNING("downloaded font not loaded properly"); +#endif + + return nullptr; +} + +// Webkit code uses a system font meta name, so mimic that here +// WebCore/platform/graphics/mac/FontCacheMac.mm +static const char kSystemFont_system[] = "-apple-system"; + +bool +gfxMacPlatformFontList::FindAndAddFamilies(const nsAString& aFamily, + nsTArray<gfxFontFamily*>* aOutput, + gfxFontStyle* aStyle, + gfxFloat aDevToCssSize) +{ + // search for special system font name, -apple-system + if (aFamily.EqualsLiteral(kSystemFont_system)) { + if (mUseSizeSensitiveSystemFont && + aStyle && (aStyle->size * aDevToCssSize) >= kTextDisplayCrossover) { + aOutput->AppendElement(FindSystemFontFamily(mSystemDisplayFontFamilyName)); + return true; + } + aOutput->AppendElement(FindSystemFontFamily(mSystemTextFontFamilyName)); + return true; + } + + return gfxPlatformFontList::FindAndAddFamilies(aFamily, aOutput, aStyle, + aDevToCssSize); +} + +void +gfxMacPlatformFontList::LookupSystemFont(LookAndFeel::FontID aSystemFontID, + nsAString& aSystemFontName, + gfxFontStyle &aFontStyle, + float aDevPixPerCSSPixel) +{ + // code moved here from widget/cocoa/nsLookAndFeel.mm + NSFont *font = nullptr; + char* systemFontName = nullptr; + switch (aSystemFontID) { + case LookAndFeel::eFont_MessageBox: + case LookAndFeel::eFont_StatusBar: + case LookAndFeel::eFont_List: + case LookAndFeel::eFont_Field: + case LookAndFeel::eFont_Button: + case LookAndFeel::eFont_Widget: + font = [NSFont systemFontOfSize:[NSFont smallSystemFontSize]]; + systemFontName = (char*) kSystemFont_system; + break; + + case LookAndFeel::eFont_SmallCaption: + font = [NSFont boldSystemFontOfSize:[NSFont smallSystemFontSize]]; + systemFontName = (char*) kSystemFont_system; + break; + + case LookAndFeel::eFont_Icon: // used in urlbar; tried labelFont, but too small + case LookAndFeel::eFont_Workspace: + case LookAndFeel::eFont_Desktop: + case LookAndFeel::eFont_Info: + font = [NSFont controlContentFontOfSize:0.0]; + systemFontName = (char*) kSystemFont_system; + break; + + case LookAndFeel::eFont_PullDownMenu: + font = [NSFont menuBarFontOfSize:0.0]; + systemFontName = (char*) kSystemFont_system; + break; + + case LookAndFeel::eFont_Tooltips: + font = [NSFont toolTipsFontOfSize:0.0]; + systemFontName = (char*) kSystemFont_system; + break; + + case LookAndFeel::eFont_Caption: + case LookAndFeel::eFont_Menu: + case LookAndFeel::eFont_Dialog: + default: + font = [NSFont systemFontOfSize:0.0]; + systemFontName = (char*) kSystemFont_system; + break; + } + NS_ASSERTION(font, "system font not set"); + NS_ASSERTION(systemFontName, "system font name not set"); + + if (systemFontName) { + aSystemFontName.AssignASCII(systemFontName); + } + + NSFontSymbolicTraits traits = [[font fontDescriptor] symbolicTraits]; + aFontStyle.style = + (traits & NSFontItalicTrait) ? NS_FONT_STYLE_ITALIC : NS_FONT_STYLE_NORMAL; + aFontStyle.weight = + (traits & NSFontBoldTrait) ? NS_FONT_WEIGHT_BOLD : NS_FONT_WEIGHT_NORMAL; + aFontStyle.stretch = + (traits & NSFontExpandedTrait) ? + NS_FONT_STRETCH_EXPANDED : (traits & NSFontCondensedTrait) ? + NS_FONT_STRETCH_CONDENSED : NS_FONT_STRETCH_NORMAL; + // convert size from css pixels to device pixels + aFontStyle.size = [font pointSize] * aDevPixPerCSSPixel; + aFontStyle.systemFont = true; +} + +// used to load system-wide font info on off-main thread +class MacFontInfo : public FontInfoData { +public: + MacFontInfo(bool aLoadOtherNames, + bool aLoadFaceNames, + bool aLoadCmaps) : + FontInfoData(aLoadOtherNames, aLoadFaceNames, aLoadCmaps) + {} + + virtual ~MacFontInfo() {} + + virtual void Load() { + nsAutoreleasePool localPool; + FontInfoData::Load(); + } + + // loads font data for all members of a given family + virtual void LoadFontFamilyData(const nsAString& aFamilyName); +}; + +void +MacFontInfo::LoadFontFamilyData(const nsAString& aFamilyName) +{ + // family name ==> CTFontDescriptor + NSString *famName = GetNSStringForString(aFamilyName); + CFStringRef family = CFStringRef(famName); + + CFMutableDictionaryRef attr = + CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, + &kCFTypeDictionaryValueCallBacks); + CFDictionaryAddValue(attr, kCTFontFamilyNameAttribute, family); + CTFontDescriptorRef fd = CTFontDescriptorCreateWithAttributes(attr); + CFRelease(attr); + CFArrayRef matchingFonts = + CTFontDescriptorCreateMatchingFontDescriptors(fd, NULL); + CFRelease(fd); + if (!matchingFonts) { + return; + } + + nsTArray<nsString> otherFamilyNames; + bool hasOtherFamilyNames = true; + + // iterate over faces in the family + int f, numFaces = (int) CFArrayGetCount(matchingFonts); + for (f = 0; f < numFaces; f++) { + mLoadStats.fonts++; + + CTFontDescriptorRef faceDesc = + (CTFontDescriptorRef)CFArrayGetValueAtIndex(matchingFonts, f); + if (!faceDesc) { + continue; + } + CTFontRef fontRef = CTFontCreateWithFontDescriptor(faceDesc, + 0.0, nullptr); + if (!fontRef) { + NS_WARNING("failed to create a CTFontRef"); + continue; + } + + if (mLoadCmaps) { + // face name + CFStringRef faceName = (CFStringRef) + CTFontDescriptorCopyAttribute(faceDesc, kCTFontNameAttribute); + + AutoTArray<UniChar, 1024> buffer; + CFIndex len = CFStringGetLength(faceName); + buffer.SetLength(len+1); + CFStringGetCharacters(faceName, ::CFRangeMake(0, len), + buffer.Elements()); + buffer[len] = 0; + nsAutoString fontName(reinterpret_cast<char16_t*>(buffer.Elements()), + len); + + // load the cmap data + FontFaceData fontData; + CFDataRef cmapTable = CTFontCopyTable(fontRef, kCTFontTableCmap, + kCTFontTableOptionNoOptions); + + if (cmapTable) { + const uint8_t *cmapData = + (const uint8_t*)CFDataGetBytePtr(cmapTable); + uint32_t cmapLen = CFDataGetLength(cmapTable); + RefPtr<gfxCharacterMap> charmap = new gfxCharacterMap(); + uint32_t offset; + bool unicodeFont = false; // ignored + bool symbolFont = false; + nsresult rv; + + rv = gfxFontUtils::ReadCMAP(cmapData, cmapLen, *charmap, offset, + unicodeFont, symbolFont); + if (NS_SUCCEEDED(rv)) { + fontData.mCharacterMap = charmap; + fontData.mUVSOffset = offset; + fontData.mSymbolFont = symbolFont; + mLoadStats.cmaps++; + } + CFRelease(cmapTable); + } + + mFontFaceData.Put(fontName, fontData); + CFRelease(faceName); + } + + if (mLoadOtherNames && hasOtherFamilyNames) { + CFDataRef nameTable = CTFontCopyTable(fontRef, kCTFontTableName, + kCTFontTableOptionNoOptions); + + if (nameTable) { + const char *nameData = (const char*)CFDataGetBytePtr(nameTable); + uint32_t nameLen = CFDataGetLength(nameTable); + gfxFontFamily::ReadOtherFamilyNamesForFace(aFamilyName, + nameData, nameLen, + otherFamilyNames, + false); + hasOtherFamilyNames = otherFamilyNames.Length() != 0; + CFRelease(nameTable); + } + } + + CFRelease(fontRef); + } + CFRelease(matchingFonts); + + // if found other names, insert them in the hash table + if (otherFamilyNames.Length() != 0) { + mOtherFamilyNames.Put(aFamilyName, otherFamilyNames); + mLoadStats.othernames += otherFamilyNames.Length(); + } +} + +already_AddRefed<FontInfoData> +gfxMacPlatformFontList::CreateFontInfoData() +{ + bool loadCmaps = !UsesSystemFallback() || + gfxPlatform::GetPlatform()->UseCmapsDuringSystemFallback(); + + RefPtr<MacFontInfo> fi = + new MacFontInfo(true, NeedFullnamePostscriptNames(), loadCmaps); + return fi.forget(); +} + +#ifdef MOZ_BUNDLED_FONTS + +void +gfxMacPlatformFontList::ActivateBundledFonts() +{ + nsCOMPtr<nsIFile> localDir; + nsresult rv = NS_GetSpecialDirectory(NS_GRE_DIR, getter_AddRefs(localDir)); + if (NS_FAILED(rv)) { + return; + } + if (NS_FAILED(localDir->Append(NS_LITERAL_STRING("fonts")))) { + return; + } + bool isDir; + if (NS_FAILED(localDir->IsDirectory(&isDir)) || !isDir) { + return; + } + + nsCOMPtr<nsISimpleEnumerator> e; + rv = localDir->GetDirectoryEntries(getter_AddRefs(e)); + if (NS_FAILED(rv)) { + return; + } + + bool hasMore; + while (NS_SUCCEEDED(e->HasMoreElements(&hasMore)) && hasMore) { + nsCOMPtr<nsISupports> entry; + if (NS_FAILED(e->GetNext(getter_AddRefs(entry)))) { + break; + } + nsCOMPtr<nsIFile> file = do_QueryInterface(entry); + if (!file) { + continue; + } + nsCString path; + if (NS_FAILED(file->GetNativePath(path))) { + continue; + } + CFURLRef fontURL = + ::CFURLCreateFromFileSystemRepresentation(kCFAllocatorDefault, + (uint8_t*)path.get(), + path.Length(), + false); + if (fontURL) { + CFErrorRef error = nullptr; + ::CTFontManagerRegisterFontsForURL(fontURL, + kCTFontManagerScopeProcess, + &error); + ::CFRelease(fontURL); + } + } +} + +#endif |