2 * Copyright (C) 2006, 2007, 2008, 2009 Apple Inc. All rights reserved.
3 * Copyright (C) 2007 Nicholas Shanks <webkit@nickshanks.com>
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
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.
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.
31 #import "platform/fonts/FontCache.h"
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>
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;
53 // The "void*" parameter makes the function match the prototype for callbacks from callOnMainThread.
54 static void invalidateFontCache(void*)
56 if (!isMainThread()) {
57 callOnMainThread(&invalidateFontCache, 0);
60 FontCache::fontCache()->invalidate();
63 static void fontCacheRegisteredFontsChangedNotificationCallback(CFNotificationCenterRef, void* observer, CFStringRef name, const void *, CFDictionaryRef)
65 ASSERT_UNUSED(observer, observer == FontCache::fontCache());
66 ASSERT_UNUSED(name, CFEqual(name, kCTFontManagerRegisteredFontsChangedNotification));
67 invalidateFontCache(0);
70 static bool useHinting()
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();
78 void FontCache::platformInit()
80 CFNotificationCenterAddObserver(CFNotificationCenterGetLocalCenter(), this, fontCacheRegisteredFontsChangedNotificationCallback, kCTFontManagerRegisteredFontsChangedNotification, 0, CFNotificationSuspensionBehaviorDeliverImmediately);
83 static int toAppKitFontWeight(FontWeight fontWeight)
85 static int appKitFontWeights[] = {
96 return appKitFontWeights[fontWeight];
99 static inline bool isAppKitFontWeightBold(NSInteger appKitFontWeight)
101 return appKitFontWeight >= 7;
104 PassRefPtr<SimpleFontData> FontCache::fallbackFontForCharacter(const FontDescription& fontDescription, UChar32 character, const SimpleFontData* fontDataToSubstitute)
106 // FIXME: We should fix getFallbackFamily to take a UChar32
107 // and remove this split-to-UChar16 code.
110 if (character <= 0xFFFF) {
111 codeUnits[0] = character;
114 codeUnits[0] = U16_LEAD(character);
115 codeUnits[1] = U16_TRAIL(character);
119 const FontPlatformData& platformData = fontDataToSubstitute->platformData();
120 NSFont *nsFont = platformData.font();
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];
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];
132 // Chromium can't render AppleColorEmoji.
133 if ([[substituteFont familyName] isEqual:@"Apple Color Emoji"])
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.
142 NSFontManager *fontManager = [NSFontManager sharedFontManager];
144 NSFontTraitMask traits;
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];
157 // For custom fonts nsFont is nil.
158 traits = fontDescription.style() ? NSFontItalicTrait : 0;
159 weight = toAppKitFontWeight(fontDescription.weight());
160 size = fontDescription.computedPixelSize();
163 NSFontTraitMask substituteFontTraits = [fontManager traitsOfFont:substituteFont];
164 NSInteger substituteFontWeight = [fontManager weightOfFont:substituteFont];
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;
174 substituteFont = useHinting() ? [substituteFont screenFont] : [substituteFont printerFont];
176 substituteFontTraits = [fontManager traitsOfFont:substituteFont];
177 substituteFontWeight = [fontManager weightOfFont:substituteFont];
179 FontPlatformData alternateFont(substituteFont, platformData.size(),
180 isAppKitFontWeightBold(weight) && !isAppKitFontWeightBold(substituteFontWeight),
181 (traits & NSFontItalicTrait) && !(substituteFontTraits & NSFontItalicTrait),
182 platformData.orientation());
184 return fontDataFromFontPlatformData(&alternateFont, DoNotRetain);
187 PassRefPtr<SimpleFontData> FontCache::getLastResortFallbackFont(const FontDescription& fontDescription, ShouldRetain shouldRetain)
189 DEFINE_STATIC_LOCAL(AtomicString, timesStr, ("Times", AtomicString::ConstructFromLiteral));
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);
195 return simpleFontData.release();
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);
205 FontPlatformData* FontCache::createFontPlatformData(const FontDescription& fontDescription, const FontFaceCreationParams& creationParams, float fontSize)
207 NSFontTraitMask traits = fontDescription.style() ? NSFontItalicTrait : 0;
208 NSInteger weight = toAppKitFontWeight(fontDescription.weight());
209 float size = fontSize;
211 NSFont *nsFont = [WebFontCache fontWithFamily:creationParams.family() traits:traits weight:weight size:size];
215 NSFontManager *fontManager = [NSFontManager sharedFontManager];
216 NSFontTraitMask actualTraits = 0;
217 if (fontDescription.style())
218 actualTraits = [fontManager traitsOfFont:nsFont];
219 NSInteger actualWeight = [fontManager weightOfFont:nsFont];
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();
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())
230 return platformData.leakPtr();