1 /* 2 * Copyright (C) 2006, 2007, 2008, 2009 Apple Inc. All rights reserved. 3 * Copyright (C) 2007 Nicholas Shanks <webkit (at) nickshanks.com> 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of 15 * its contributors may be used to endorse or promote products derived 16 * from this software without specific prior written permission. 17 * 18 * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY 19 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 20 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY 22 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 23 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 24 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 25 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 27 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 */ 29 30 #import "config.h" 31 #import "platform/fonts/FontCache.h" 32 33 #import <AppKit/AppKit.h> 34 #import "platform/LayoutTestSupport.h" 35 #import "platform/RuntimeEnabledFeatures.h" 36 #import "platform/fonts/FontDescription.h" 37 #import "platform/fonts/FontFaceCreationParams.h" 38 #import "platform/fonts/FontPlatformData.h" 39 #import "platform/fonts/SimpleFontData.h" 40 #import "platform/mac/WebFontCache.h" 41 #import <wtf/MainThread.h> 42 #import <wtf/StdLibExtras.h> 43 44 // Forward declare Mac SPIs. 45 // Request for public API: rdar://13803570 46 @interface NSFont (WebKitSPI) 47 + (NSFont*)findFontLike:(NSFont*)font forString:(NSString*)string withRange:(NSRange)range inLanguage:(id)useNil; 48 + (NSFont*)findFontLike:(NSFont*)font forCharacter:(UniChar)uc inLanguage:(id)useNil; 49 @end 50 51 namespace blink { 52 53 // The "void*" parameter makes the function match the prototype for callbacks from callOnMainThread. 54 static void invalidateFontCache(void*) 55 { 56 if (!isMainThread()) { 57 callOnMainThread(&invalidateFontCache, 0); 58 return; 59 } 60 FontCache::fontCache()->invalidate(); 61 } 62 63 static void fontCacheRegisteredFontsChangedNotificationCallback(CFNotificationCenterRef, void* observer, CFStringRef name, const void *, CFDictionaryRef) 64 { 65 ASSERT_UNUSED(observer, observer == FontCache::fontCache()); 66 ASSERT_UNUSED(name, CFEqual(name, kCTFontManagerRegisteredFontsChangedNotification)); 67 invalidateFontCache(0); 68 } 69 70 static bool useHinting() 71 { 72 // Enable hinting when subpixel font scaling is disabled or 73 // when running the set of standard non-subpixel layout tests, 74 // otherwise use subpixel glyph positioning. 75 return (LayoutTestSupport::isRunningLayoutTest() && !LayoutTestSupport::isFontAntialiasingEnabledForTest()) || !RuntimeEnabledFeatures::subpixelFontScalingEnabled(); 76 } 77 78 void FontCache::platformInit() 79 { 80 CFNotificationCenterAddObserver(CFNotificationCenterGetLocalCenter(), this, fontCacheRegisteredFontsChangedNotificationCallback, kCTFontManagerRegisteredFontsChangedNotification, 0, CFNotificationSuspensionBehaviorDeliverImmediately); 81 } 82 83 static int toAppKitFontWeight(FontWeight fontWeight) 84 { 85 static int appKitFontWeights[] = { 86 2, // FontWeight100 87 3, // FontWeight200 88 4, // FontWeight300 89 5, // FontWeight400 90 6, // FontWeight500 91 8, // FontWeight600 92 9, // FontWeight700 93 10, // FontWeight800 94 12, // FontWeight900 95 }; 96 return appKitFontWeights[fontWeight]; 97 } 98 99 static inline bool isAppKitFontWeightBold(NSInteger appKitFontWeight) 100 { 101 return appKitFontWeight >= 7; 102 } 103 104 PassRefPtr<SimpleFontData> FontCache::fallbackFontForCharacter(const FontDescription& fontDescription, UChar32 character, const SimpleFontData* fontDataToSubstitute) 105 { 106 // FIXME: We should fix getFallbackFamily to take a UChar32 107 // and remove this split-to-UChar16 code. 108 UChar codeUnits[2]; 109 int codeUnitsLength; 110 if (character <= 0xFFFF) { 111 codeUnits[0] = character; 112 codeUnitsLength = 1; 113 } else { 114 codeUnits[0] = U16_LEAD(character); 115 codeUnits[1] = U16_TRAIL(character); 116 codeUnitsLength = 2; 117 } 118 119 const FontPlatformData& platformData = fontDataToSubstitute->platformData(); 120 NSFont *nsFont = platformData.font(); 121 122 NSString *string = [[NSString alloc] initWithCharactersNoCopy:codeUnits length:codeUnitsLength freeWhenDone:NO]; 123 NSFont *substituteFont = [NSFont findFontLike:nsFont forString:string withRange:NSMakeRange(0, codeUnitsLength) inLanguage:nil]; 124 [string release]; 125 126 // FIXME: Remove this SPI usage: http://crbug.com/255122 127 if (!substituteFont && codeUnitsLength == 1) 128 substituteFont = [NSFont findFontLike:nsFont forCharacter:codeUnits[0] inLanguage:nil]; 129 if (!substituteFont) 130 return nullptr; 131 132 // Chromium can't render AppleColorEmoji. 133 if ([[substituteFont familyName] isEqual:@"Apple Color Emoji"]) 134 return nullptr; 135 136 // Use the family name from the AppKit-supplied substitute font, requesting the 137 // traits, weight, and size we want. One way this does better than the original 138 // AppKit request is that it takes synthetic bold and oblique into account. 139 // But it does create the possibility that we could end up with a font that 140 // doesn't actually cover the characters we need. 141 142 NSFontManager *fontManager = [NSFontManager sharedFontManager]; 143 144 NSFontTraitMask traits; 145 NSInteger weight; 146 CGFloat size; 147 148 if (nsFont) { 149 traits = [fontManager traitsOfFont:nsFont]; 150 if (platformData.m_syntheticBold) 151 traits |= NSBoldFontMask; 152 if (platformData.m_syntheticItalic) 153 traits |= NSFontItalicTrait; 154 weight = [fontManager weightOfFont:nsFont]; 155 size = [nsFont pointSize]; 156 } else { 157 // For custom fonts nsFont is nil. 158 traits = fontDescription.style() ? NSFontItalicTrait : 0; 159 weight = toAppKitFontWeight(fontDescription.weight()); 160 size = fontDescription.computedPixelSize(); 161 } 162 163 NSFontTraitMask substituteFontTraits = [fontManager traitsOfFont:substituteFont]; 164 NSInteger substituteFontWeight = [fontManager weightOfFont:substituteFont]; 165 166 if (traits != substituteFontTraits || weight != substituteFontWeight || !nsFont) { 167 if (NSFont *bestVariation = [fontManager fontWithFamily:[substituteFont familyName] traits:traits weight:weight size:size]) { 168 if ((!nsFont || [fontManager traitsOfFont:bestVariation] != substituteFontTraits || [fontManager weightOfFont:bestVariation] != substituteFontWeight) 169 && [[bestVariation coveredCharacterSet] longCharacterIsMember:character]) 170 substituteFont = bestVariation; 171 } 172 } 173 174 substituteFont = useHinting() ? [substituteFont screenFont] : [substituteFont printerFont]; 175 176 substituteFontTraits = [fontManager traitsOfFont:substituteFont]; 177 substituteFontWeight = [fontManager weightOfFont:substituteFont]; 178 179 FontPlatformData alternateFont(substituteFont, platformData.size(), 180 isAppKitFontWeightBold(weight) && !isAppKitFontWeightBold(substituteFontWeight), 181 (traits & NSFontItalicTrait) && !(substituteFontTraits & NSFontItalicTrait), 182 platformData.orientation()); 183 184 return fontDataFromFontPlatformData(&alternateFont, DoNotRetain); 185 } 186 187 PassRefPtr<SimpleFontData> FontCache::getLastResortFallbackFont(const FontDescription& fontDescription, ShouldRetain shouldRetain) 188 { 189 DEFINE_STATIC_LOCAL(AtomicString, timesStr, ("Times", AtomicString::ConstructFromLiteral)); 190 191 // FIXME: Would be even better to somehow get the user's default font here. For now we'll pick 192 // the default that the user would get without changing any prefs. 193 RefPtr<SimpleFontData> simpleFontData = getFontData(fontDescription, timesStr, false, shouldRetain); 194 if (simpleFontData) 195 return simpleFontData.release(); 196 197 // The Times fallback will almost always work, but in the highly unusual case where 198 // the user doesn't have it, we fall back on Lucida Grande because that's 199 // guaranteed to be there, according to Nathan Taylor. This is good enough 200 // to avoid a crash at least. 201 DEFINE_STATIC_LOCAL(AtomicString, lucidaGrandeStr, ("Lucida Grande", AtomicString::ConstructFromLiteral)); 202 return getFontData(fontDescription, lucidaGrandeStr, false, shouldRetain); 203 } 204 205 FontPlatformData* FontCache::createFontPlatformData(const FontDescription& fontDescription, const FontFaceCreationParams& creationParams, float fontSize) 206 { 207 NSFontTraitMask traits = fontDescription.style() ? NSFontItalicTrait : 0; 208 NSInteger weight = toAppKitFontWeight(fontDescription.weight()); 209 float size = fontSize; 210 211 NSFont *nsFont = [WebFontCache fontWithFamily:creationParams.family() traits:traits weight:weight size:size]; 212 if (!nsFont) 213 return 0; 214 215 NSFontManager *fontManager = [NSFontManager sharedFontManager]; 216 NSFontTraitMask actualTraits = 0; 217 if (fontDescription.style()) 218 actualTraits = [fontManager traitsOfFont:nsFont]; 219 NSInteger actualWeight = [fontManager weightOfFont:nsFont]; 220 221 NSFont *platformFont = useHinting() ? [nsFont screenFont] : [nsFont printerFont]; 222 bool syntheticBold = (isAppKitFontWeightBold(weight) && !isAppKitFontWeightBold(actualWeight)) || fontDescription.isSyntheticBold(); 223 bool syntheticItalic = ((traits & NSFontItalicTrait) && !(actualTraits & NSFontItalicTrait)) || fontDescription.isSyntheticItalic(); 224 225 // FontPlatformData::font() can be null for the case of Chromium out-of-process font loading. 226 // In that case, we don't want to use the platformData. 227 OwnPtr<FontPlatformData> platformData = adoptPtr(new FontPlatformData(platformFont, size, syntheticBold, syntheticItalic, fontDescription.orientation(), fontDescription.widthVariant())); 228 if (!platformData->font()) 229 return 0; 230 return platformData.leakPtr(); 231 } 232 233 } // namespace blink 234