| /* |
| * Copyright (C) 2006, 2007, 2008, 2009 Apple Inc. All rights reserved. |
| * Copyright (C) 2007 Nicholas Shanks <webkit@nickshanks.com> |
| * |
| * 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. |
| */ |
| |
| #import "platform/fonts/FontCache.h" |
| |
| #import <AppKit/AppKit.h> |
| #include "platform/LayoutTestSupport.h" |
| #include "platform/RuntimeEnabledFeatures.h" |
| #include "platform/WebTaskRunner.h" |
| #include "platform/fonts/FontDescription.h" |
| #include "platform/fonts/FontFaceCreationParams.h" |
| #include "platform/fonts/FontPlatformData.h" |
| #include "platform/fonts/SimpleFontData.h" |
| #include "platform/fonts/mac/FontFamilyMatcherMac.h" |
| #include "public/platform/Platform.h" |
| #include "public/platform/WebTraceLocation.h" |
| #include "wtf/Functional.h" |
| #include "wtf/PtrUtil.h" |
| #include "wtf/StdLibExtras.h" |
| #include <memory> |
| |
| // Forward declare Mac SPIs. |
| // Request for public API: rdar://13803570 |
| @interface NSFont (WebKitSPI) |
| + (NSFont*)findFontLike:(NSFont*)font |
| forString:(NSString*)string |
| withRange:(NSRange)range |
| inLanguage:(id)useNil; |
| + (NSFont*)findFontLike:(NSFont*)font |
| forCharacter:(UniChar)uc |
| inLanguage:(id)useNil; |
| @end |
| |
| namespace blink { |
| |
| const char* kColorEmojiFontMac = "Apple Color Emoji"; |
| |
| // static |
| const AtomicString& FontCache::legacySystemFontFamily() { |
| DEFINE_STATIC_LOCAL(AtomicString, legacySystemFontFamily, |
| ("BlinkMacSystemFont")); |
| return legacySystemFontFamily; |
| } |
| |
| static void invalidateFontCache() { |
| if (!isMainThread()) { |
| Platform::current()->mainThread()->getWebTaskRunner()->postTask( |
| BLINK_FROM_HERE, WTF::bind(&invalidateFontCache)); |
| return; |
| } |
| FontCache::fontCache()->invalidate(); |
| } |
| |
| static void fontCacheRegisteredFontsChangedNotificationCallback( |
| CFNotificationCenterRef, |
| void* observer, |
| CFStringRef name, |
| const void*, |
| CFDictionaryRef) { |
| DCHECK_EQ(observer, FontCache::fontCache()); |
| DCHECK(CFEqual(name, kCTFontManagerRegisteredFontsChangedNotification)); |
| invalidateFontCache(); |
| } |
| |
| static bool useHinting() { |
| // Enable hinting when subpixel font scaling is disabled or |
| // when running the set of standard non-subpixel layout tests, |
| // otherwise use subpixel glyph positioning. |
| return (LayoutTestSupport::isRunningLayoutTest() && |
| !LayoutTestSupport::isFontAntialiasingEnabledForTest()); |
| } |
| |
| void FontCache::platformInit() { |
| CFNotificationCenterAddObserver( |
| CFNotificationCenterGetLocalCenter(), this, |
| fontCacheRegisteredFontsChangedNotificationCallback, |
| kCTFontManagerRegisteredFontsChangedNotification, 0, |
| CFNotificationSuspensionBehaviorDeliverImmediately); |
| } |
| |
| static inline bool isAppKitFontWeightBold(NSInteger appKitFontWeight) { |
| return appKitFontWeight >= 7; |
| } |
| |
| PassRefPtr<SimpleFontData> FontCache::fallbackFontForCharacter( |
| const FontDescription& fontDescription, |
| UChar32 character, |
| const SimpleFontData* fontDataToSubstitute, |
| FontFallbackPriority fallbackPriority) { |
| if (fallbackPriority == FontFallbackPriority::EmojiEmoji) { |
| RefPtr<SimpleFontData> emojiFont = |
| getFontData(fontDescription, AtomicString(kColorEmojiFontMac)); |
| if (emojiFont) |
| return emojiFont; |
| } |
| |
| // FIXME: We should fix getFallbackFamily to take a UChar32 |
| // and remove this split-to-UChar16 code. |
| UChar codeUnits[2]; |
| int codeUnitsLength; |
| if (character <= 0xFFFF) { |
| codeUnits[0] = character; |
| codeUnitsLength = 1; |
| } else { |
| codeUnits[0] = U16_LEAD(character); |
| codeUnits[1] = U16_TRAIL(character); |
| codeUnitsLength = 2; |
| } |
| |
| const FontPlatformData& platformData = fontDataToSubstitute->platformData(); |
| NSFont* nsFont = toNSFont(platformData.ctFont()); |
| |
| NSString* string = [[NSString alloc] initWithCharactersNoCopy:codeUnits |
| length:codeUnitsLength |
| freeWhenDone:NO]; |
| NSFont* substituteFont = [NSFont findFontLike:nsFont |
| forString:string |
| withRange:NSMakeRange(0, codeUnitsLength) |
| inLanguage:nil]; |
| [string release]; |
| |
| // FIXME: Remove this SPI usage: http://crbug.com/255122 |
| if (!substituteFont && codeUnitsLength == 1) |
| substituteFont = |
| [NSFont findFontLike:nsFont forCharacter:codeUnits[0] inLanguage:nil]; |
| if (!substituteFont) |
| return nullptr; |
| |
| // Use the family name from the AppKit-supplied substitute font, requesting |
| // the traits, weight, and size we want. One way this does better than the |
| // original AppKit request is that it takes synthetic bold and oblique into |
| // account. But it does create the possibility that we could end up with a |
| // font that doesn't actually cover the characters we need. |
| |
| NSFontManager* fontManager = [NSFontManager sharedFontManager]; |
| |
| NSFontTraitMask traits; |
| NSInteger weight; |
| CGFloat size; |
| |
| if (nsFont) { |
| traits = [fontManager traitsOfFont:nsFont]; |
| if (platformData.m_syntheticBold) |
| traits |= NSBoldFontMask; |
| if (platformData.m_syntheticItalic) |
| traits |= NSFontItalicTrait; |
| weight = [fontManager weightOfFont:nsFont]; |
| size = [nsFont pointSize]; |
| } else { |
| // For custom fonts nsFont is nil. |
| traits = fontDescription.style() ? NSFontItalicTrait : 0; |
| weight = toAppKitFontWeight(fontDescription.weight()); |
| size = fontDescription.computedPixelSize(); |
| } |
| |
| NSFontTraitMask substituteFontTraits = |
| [fontManager traitsOfFont:substituteFont]; |
| NSInteger substituteFontWeight = [fontManager weightOfFont:substituteFont]; |
| |
| if (traits != substituteFontTraits || weight != substituteFontWeight || |
| !nsFont) { |
| if (NSFont* bestVariation = |
| [fontManager fontWithFamily:[substituteFont familyName] |
| traits:traits |
| weight:weight |
| size:size]) { |
| if ((!nsFont || |
| [fontManager traitsOfFont:bestVariation] != substituteFontTraits || |
| [fontManager weightOfFont:bestVariation] != substituteFontWeight) && |
| [[bestVariation coveredCharacterSet] longCharacterIsMember:character]) |
| substituteFont = bestVariation; |
| } |
| } |
| |
| substituteFont = |
| useHinting() ? [substituteFont screenFont] : [substituteFont printerFont]; |
| |
| substituteFontTraits = [fontManager traitsOfFont:substituteFont]; |
| substituteFontWeight = [fontManager weightOfFont:substituteFont]; |
| |
| // TODO(eae): Remove once skia supports bold emoji. See |
| // https://bugs.chromium.org/p/skia/issues/detail?id=4904 |
| // Bold emoji look the same as normal emoji, so syntheticBold isn't needed. |
| bool syntheticBold = |
| isAppKitFontWeightBold(weight) && |
| !isAppKitFontWeightBold(substituteFontWeight) && |
| ![substituteFont.familyName isEqual:@"Apple Color Emoji"]; |
| |
| FontPlatformData alternateFont( |
| substituteFont, platformData.size(), syntheticBold, |
| (traits & NSFontItalicTrait) && |
| !(substituteFontTraits & NSFontItalicTrait), |
| platformData.orientation(), |
| nullptr); // No variation paramaters in fallback. |
| |
| return fontDataFromFontPlatformData(&alternateFont, DoNotRetain); |
| } |
| |
| PassRefPtr<SimpleFontData> FontCache::getLastResortFallbackFont( |
| const FontDescription& fontDescription, |
| ShouldRetain shouldRetain) { |
| DEFINE_STATIC_LOCAL(AtomicString, timesStr, ("Times")); |
| |
| // FIXME: Would be even better to somehow get the user's default font here. |
| // For now we'll pick the default that the user would get without changing |
| // any prefs. |
| RefPtr<SimpleFontData> simpleFontData = |
| getFontData(fontDescription, timesStr, false, shouldRetain); |
| if (simpleFontData) |
| return simpleFontData.release(); |
| |
| // The Times fallback will almost always work, but in the highly unusual case |
| // where the user doesn't have it, we fall back on Lucida Grande because |
| // that's guaranteed to be there, according to Nathan Taylor. This is good |
| // enough to avoid a crash at least. |
| DEFINE_STATIC_LOCAL(AtomicString, lucidaGrandeStr, ("Lucida Grande")); |
| return getFontData(fontDescription, lucidaGrandeStr, false, shouldRetain); |
| } |
| |
| std::unique_ptr<FontPlatformData> FontCache::createFontPlatformData( |
| const FontDescription& fontDescription, |
| const FontFaceCreationParams& creationParams, |
| float fontSize) { |
| NSFontTraitMask traits = fontDescription.style() ? NSFontItalicTrait : 0; |
| float size = fontSize; |
| |
| NSFont* nsFont = MatchNSFontFamily(creationParams.family(), traits, |
| fontDescription.weight(), size); |
| if (!nsFont) |
| return nullptr; |
| |
| NSFontManager* fontManager = [NSFontManager sharedFontManager]; |
| NSFontTraitMask actualTraits = 0; |
| if (fontDescription.style()) |
| actualTraits = [fontManager traitsOfFont:nsFont]; |
| NSInteger actualWeight = [fontManager weightOfFont:nsFont]; |
| |
| NSFont* platformFont = |
| useHinting() ? [nsFont screenFont] : [nsFont printerFont]; |
| NSInteger appKitWeight = toAppKitFontWeight(fontDescription.weight()); |
| |
| // TODO(eae): Remove once skia supports bold emoji. See |
| // https://bugs.chromium.org/p/skia/issues/detail?id=4904 |
| // Bold emoji look the same as normal emoji, so syntheticBold isn't needed. |
| bool syntheticBold = [platformFont.familyName isEqual:@"Apple Color Emoji"] |
| ? false |
| : (isAppKitFontWeightBold(appKitWeight) && |
| !isAppKitFontWeightBold(actualWeight)) || |
| fontDescription.isSyntheticBold(); |
| |
| bool syntheticItalic = |
| ((traits & NSFontItalicTrait) && !(actualTraits & NSFontItalicTrait)) || |
| fontDescription.isSyntheticItalic(); |
| |
| // FontPlatformData::typeface() is null in the case of Chromium out-of-process |
| // font loading failing. Out-of-process loading occurs for registered fonts |
| // stored in non-system locations. When loading fails, we do not want to use |
| // the returned FontPlatformData since it will not have a valid SkTypeface. |
| std::unique_ptr<FontPlatformData> platformData = |
| WTF::makeUnique<FontPlatformData>( |
| platformFont, size, syntheticBold, syntheticItalic, |
| fontDescription.orientation(), fontDescription.variationSettings()); |
| if (!platformData->typeface()) { |
| return nullptr; |
| } |
| return platformData; |
| } |
| |
| } // namespace blink |