Fix layout test regressions from r94352.
[WebKit-https.git] / Source / WebCore / platform / graphics / mac / FontCacheMac.mm
1 /*
2  * Copyright (C) 2006, 2007, 2008, 2009 Apple Inc. All rights reserved.
3  * Copyright (C) 2007 Nicholas Shanks <webkit@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 "FontCache.h"
32
33 #import "Font.h"
34 #import "SimpleFontData.h"
35 #import "FontPlatformData.h"
36 #import "WebCoreSystemInterface.h"
37 #import "WebFontCache.h"
38 #import <AppKit/AppKit.h>
39 #import <wtf/MainThread.h>
40 #import <wtf/StdLibExtras.h>
41
42
43 namespace WebCore {
44
45 // The "void*" parameter makes the function match the prototype for callbacks from callOnMainThread.
46 static void invalidateFontCache(void*)
47 {
48     if (!isMainThread()) {
49         callOnMainThread(&invalidateFontCache, 0);
50         return;
51     }
52     fontCache()->invalidate();
53 }
54
55 #if !defined(BUILDING_ON_LEOPARD)
56 static void fontCacheRegisteredFontsChangedNotificationCallback(CFNotificationCenterRef, void* observer, CFStringRef name, const void *, CFDictionaryRef)
57 {
58     ASSERT_UNUSED(observer, observer == fontCache());
59     ASSERT_UNUSED(name, CFEqual(name, kCTFontManagerRegisteredFontsChangedNotification));
60     invalidateFontCache(0);
61 }
62 #else
63 static void fontCacheATSNotificationCallback(ATSFontNotificationInfoRef, void*)
64 {
65     invalidateFontCache(0);
66 }
67 #endif
68
69 void FontCache::platformInit()
70 {
71     wkSetUpFontCache();
72 #if !defined(BUILDING_ON_LEOPARD)
73     CFNotificationCenterAddObserver(CFNotificationCenterGetLocalCenter(), this, fontCacheRegisteredFontsChangedNotificationCallback, kCTFontManagerRegisteredFontsChangedNotification, 0, CFNotificationSuspensionBehaviorDeliverImmediately);
74 #else
75     // kCTFontManagerRegisteredFontsChangedNotification does not exist on Leopard and earlier.
76     // FIXME: Passing kATSFontNotifyOptionReceiveWhileSuspended may be an overkill and does not seem to work anyway.
77     ATSFontNotificationSubscribe(fontCacheATSNotificationCallback, kATSFontNotifyOptionReceiveWhileSuspended, 0, 0);
78 #endif
79 }
80
81 static int toAppKitFontWeight(FontWeight fontWeight)
82 {
83     static int appKitFontWeights[] = {
84         2,  // FontWeight100
85         3,  // FontWeight200
86         4,  // FontWeight300
87         5,  // FontWeight400
88         6,  // FontWeight500
89         8,  // FontWeight600
90         9,  // FontWeight700
91         10, // FontWeight800
92         12, // FontWeight900
93     };
94     return appKitFontWeights[fontWeight];
95 }
96
97 static inline bool isAppKitFontWeightBold(NSInteger appKitFontWeight)
98 {
99     return appKitFontWeight >= 7;
100 }
101
102 const SimpleFontData* FontCache::getFontDataForCharacters(const Font& font, const UChar* characters, int length)
103 {
104     UChar32 character;
105     U16_GET(characters, 0, 0, length, character);
106     const FontPlatformData& platformData = font.fontDataAt(0)->fontDataForCharacter(character)->platformData();
107     NSFont *nsFont = platformData.font();
108
109     NSString *string = [[NSString alloc] initWithCharactersNoCopy:const_cast<UChar*>(characters) length:length freeWhenDone:NO];
110     NSFont *substituteFont = wkGetFontInLanguageForRange(nsFont, string, NSMakeRange(0, length));
111     [string release];
112
113     if (!substituteFont && length == 1)
114         substituteFont = wkGetFontInLanguageForCharacter(nsFont, characters[0]);
115     if (!substituteFont)
116         return 0;
117
118     // Use the family name from the AppKit-supplied substitute font, requesting the
119     // traits, weight, and size we want. One way this does better than the original
120     // AppKit request is that it takes synthetic bold and oblique into account.
121     // But it does create the possibility that we could end up with a font that
122     // doesn't actually cover the characters we need.
123
124     NSFontManager *fontManager = [NSFontManager sharedFontManager];
125
126     NSFontTraitMask traits;
127     NSInteger weight;
128     CGFloat size;
129
130     if (nsFont) {
131         traits = [fontManager traitsOfFont:nsFont];
132         if (platformData.m_syntheticBold)
133             traits |= NSBoldFontMask;
134         if (platformData.m_syntheticOblique)
135             traits |= NSFontItalicTrait;
136         weight = [fontManager weightOfFont:nsFont];
137         size = [nsFont pointSize];
138     } else {
139         // For custom fonts nsFont is nil.
140         traits = font.italic() ? NSFontItalicTrait : 0;
141         weight = toAppKitFontWeight(font.weight());
142         size = font.pixelSize();
143     }
144
145     NSFontTraitMask substituteFontTraits = [fontManager traitsOfFont:substituteFont];
146     NSInteger substituteFontWeight = [fontManager weightOfFont:substituteFont];
147
148     if (traits != substituteFontTraits || weight != substituteFontWeight || !nsFont) {
149         if (NSFont *bestVariation = [fontManager fontWithFamily:[substituteFont familyName] traits:traits weight:weight size:size]) {
150             if (!nsFont || (([fontManager traitsOfFont:bestVariation] != substituteFontTraits || [fontManager weightOfFont:bestVariation] != substituteFontWeight)
151                 && [[bestVariation coveredCharacterSet] longCharacterIsMember:character]))
152                 substituteFont = bestVariation;
153         }
154     }
155
156     substituteFont = font.fontDescription().usePrinterFont() ? [substituteFont printerFont] : [substituteFont screenFont];
157
158     substituteFontTraits = [fontManager traitsOfFont:substituteFont];
159     substituteFontWeight = [fontManager weightOfFont:substituteFont];
160
161     FontPlatformData alternateFont(substituteFont, platformData.size(),
162         !font.isPlatformFont() && isAppKitFontWeightBold(weight) && !isAppKitFontWeightBold(substituteFontWeight),
163         !font.isPlatformFont() && (traits & NSFontItalicTrait) && !(substituteFontTraits & NSFontItalicTrait),
164         platformData.m_orientation);
165
166     return getCachedFontData(&alternateFont, DoNotRetain);
167 }
168
169 SimpleFontData* FontCache::getSimilarFontPlatformData(const Font& font)
170 {
171     // Attempt to find an appropriate font using a match based on 
172     // the presence of keywords in the the requested names.  For example, we'll
173     // match any name that contains "Arabic" to Geeza Pro.
174     SimpleFontData* simpleFontData = 0;
175     const FontFamily* currFamily = &font.fontDescription().family();
176     while (currFamily && !simpleFontData) {
177         if (currFamily->family().length()) {
178             static String* matchWords[3] = { new String("Arabic"), new String("Pashto"), new String("Urdu") };
179             DEFINE_STATIC_LOCAL(AtomicString, geezaStr, ("Geeza Pro"));
180             for (int j = 0; j < 3 && !simpleFontData; ++j)
181                 if (currFamily->family().contains(*matchWords[j], false))
182                     simpleFontData = getCachedFontData(font.fontDescription(), geezaStr);
183         }
184         currFamily = currFamily->next();
185     }
186
187     return simpleFontData;
188 }
189
190 SimpleFontData* FontCache::getLastResortFallbackFont(const FontDescription& fontDescription, ShouldRetain shouldRetain)
191 {
192     DEFINE_STATIC_LOCAL(AtomicString, timesStr, ("Times"));
193
194     // FIXME: Would be even better to somehow get the user's default font here.  For now we'll pick
195     // the default that the user would get without changing any prefs.
196     SimpleFontData* simpleFontData = getCachedFontData(fontDescription, timesStr, false, shouldRetain);
197     if (simpleFontData)
198         return simpleFontData;
199
200     // The Times fallback will almost always work, but in the highly unusual case where
201     // the user doesn't have it, we fall back on Lucida Grande because that's
202     // guaranteed to be there, according to Nathan Taylor. This is good enough
203     // to avoid a crash at least.
204     DEFINE_STATIC_LOCAL(AtomicString, lucidaGrandeStr, ("Lucida Grande"));
205     return getCachedFontData(fontDescription, lucidaGrandeStr, false, shouldRetain);
206 }
207
208 void FontCache::getTraitsInFamily(const AtomicString& familyName, Vector<unsigned>& traitsMasks)
209 {
210     [WebFontCache getTraits:traitsMasks inFamily:familyName];
211 }
212
213 FontPlatformData* FontCache::createFontPlatformData(const FontDescription& fontDescription, const AtomicString& family)
214 {
215     NSFontTraitMask traits = fontDescription.italic() ? NSFontItalicTrait : 0;
216     NSInteger weight = toAppKitFontWeight(fontDescription.weight());
217     float size = fontDescription.computedPixelSize();
218
219     NSFont *nsFont = [WebFontCache fontWithFamily:family traits:traits weight:weight size:size];
220     if (!nsFont)
221         return 0;
222
223     NSFontManager *fontManager = [NSFontManager sharedFontManager];
224     NSFontTraitMask actualTraits = 0;
225     if (fontDescription.italic())
226         actualTraits = [fontManager traitsOfFont:nsFont];
227     NSInteger actualWeight = [fontManager weightOfFont:nsFont];
228
229     NSFont *platformFont = fontDescription.usePrinterFont() ? [nsFont printerFont] : [nsFont screenFont];
230     bool syntheticBold = isAppKitFontWeightBold(weight) && !isAppKitFontWeightBold(actualWeight);
231     bool syntheticOblique = (traits & NSFontItalicTrait) && !(actualTraits & NSFontItalicTrait);
232
233     return new FontPlatformData(platformFont, size, syntheticBold, syntheticOblique, fontDescription.orientation(), fontDescription.textOrientation(), fontDescription.widthVariant());
234 }
235
236 } // namespace WebCore