c7debe5bb0d3718717b5255229b75efa52ccc075
[WebKit-https.git] / WebCore / platform / mac / FontCacheMac.mm
1 /*
2  * Copyright (C) 2006 Apple Computer, Inc.  All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  *
8  * 1.  Redistributions of source code must retain the above copyright
9  *     notice, this list of conditions and the following disclaimer. 
10  * 2.  Redistributions in binary form must reproduce the above copyright
11  *     notice, this list of conditions and the following disclaimer in the
12  *     documentation and/or other materials provided with the distribution. 
13  * 3.  Neither the name of Apple Computer, Inc. ("Apple") nor the names of
14  *     its contributors may be used to endorse or promote products derived
15  *     from this software without specific prior written permission. 
16  *
17  * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
18  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
19  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
20  * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
21  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
22  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
23  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
24  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27  */
28
29 #import "config.h"
30 #import "FontCache.h"
31
32 #import "Font.h"
33 #import "FontData.h"
34 #import "FontPlatformData.h"
35 #import "WebCoreStringTruncator.h"
36 #import "WebCoreSystemInterface.h"
37 #import "WebFontCache.h"
38
39 namespace WebCore {
40
41 static bool getAppDefaultValue(CFStringRef key, int *v)
42 {
43     CFPropertyListRef value;
44
45     value = CFPreferencesCopyValue(key, kCFPreferencesCurrentApplication,
46                                    kCFPreferencesAnyUser,
47                                    kCFPreferencesAnyHost);
48     if (value == 0) {
49         value = CFPreferencesCopyValue(key, kCFPreferencesCurrentApplication,
50                                        kCFPreferencesCurrentUser,
51                                        kCFPreferencesAnyHost);
52         if (value == 0)
53             return false;
54     }
55
56     if (CFGetTypeID(value) == CFNumberGetTypeID()) {
57         if (v != 0)
58             CFNumberGetValue((const CFNumberRef)value, kCFNumberIntType, v);
59     } else if (CFGetTypeID(value) == CFStringGetTypeID()) {
60         if (v != 0)
61             *v = CFStringGetIntValue((const CFStringRef)value);
62     } else {
63         CFRelease(value);
64         return false;
65     }
66
67     CFRelease(value);
68     return true;
69 }
70
71 static bool getUserDefaultValue(CFStringRef key, int *v)
72 {
73     CFPropertyListRef value;
74
75     value = CFPreferencesCopyValue(key, kCFPreferencesAnyApplication,
76                                    kCFPreferencesCurrentUser,
77                                    kCFPreferencesCurrentHost);
78     if (value == 0)
79         return false;
80
81     if (CFGetTypeID(value) == CFNumberGetTypeID()) {
82         if (v != 0)
83             CFNumberGetValue((const CFNumberRef)value, kCFNumberIntType, v);
84     } else if (CFGetTypeID(value) == CFStringGetTypeID()) {
85         if (v != 0)
86             *v = CFStringGetIntValue((const CFStringRef)value);
87     } else {
88         CFRelease(value);
89         return false;
90     }
91
92     CFRelease(value);
93     return true;
94 }
95
96 static int getLCDScaleParameters(void)
97 {
98     int mode;
99     CFStringRef key;
100
101     key = CFSTR("AppleFontSmoothing");
102     if (!getAppDefaultValue(key, &mode)) {
103         if (!getUserDefaultValue(key, &mode))
104             return 1;
105     }
106
107     if (wkFontSmoothingModeIsLCD(mode))
108         return 4;
109     return 1;
110 }
111
112 #define MINIMUM_GLYPH_CACHE_SIZE 1536 * 1024
113
114 void FontCache::platformInit()
115 {
116     size_t s = MINIMUM_GLYPH_CACHE_SIZE*getLCDScaleParameters();
117
118     wkSetUpFontCache(s);
119 }
120
121 const FontData* FontCache::getFontDataForCharacters(const Font& font, const UChar* characters, int length)
122 {
123     NSFont* nsFont = font.primaryFont()->getNSFont();
124     NSString *string = [[NSString alloc] initWithCharactersNoCopy:(UniChar*)characters length:length freeWhenDone:NO];
125     NSFont* substituteFont = wkGetFontInLanguageForRange(nsFont, string, NSMakeRange(0, [string length]));
126     if (!substituteFont && [string length] == 1)
127         substituteFont = wkGetFontInLanguageForCharacter(nsFont, [string characterAtIndex:0]);
128     [string release];
129     
130     // Now that we have a substitute font, attempt to match it to the best variation.
131     // If we have a good match return that, otherwise return the font the AppKit has found.
132     if (substituteFont) {
133         NSFontTraitMask traits = 0;
134         if (font.fontDescription().italic())
135             traits |= NSItalicFontMask;
136         if (font.fontDescription().weight() >= cBoldWeight)
137             traits |= NSBoldFontMask;
138         float size = font.fontDescription().computedPixelSize();
139     
140         NSFontManager *manager = [NSFontManager sharedFontManager];
141         NSFont *bestVariation = [manager fontWithFamily:[substituteFont familyName]
142             traits:traits
143             weight:[manager weightOfFont:nsFont]
144             size:size];
145         if (bestVariation)
146             substituteFont = bestVariation;
147         
148         substituteFont = font.fontDescription().usePrinterFont() ? [substituteFont printerFont] : [substituteFont screenFont];
149
150         NSFontTraitMask actualTraits = [manager traitsOfFont:substituteFont];
151         FontPlatformData alternateFont(substituteFont, 
152                                        (traits & NSBoldFontMask) && !(actualTraits & NSBoldFontMask),
153                                        (traits & NSItalicFontMask) && !(actualTraits & NSItalicFontMask));
154         return getCachedFontData(&alternateFont);
155     }
156
157     return 0;
158 }
159
160 FontPlatformData* FontCache::getSimilarFontPlatformData(const Font& font)
161 {
162     // Attempt to find an appropriate font using a match based on 
163     // the presence of keywords in the the requested names.  For example, we'll
164     // match any name that contains "Arabic" to Geeza Pro.
165     FontPlatformData* platformData = 0;
166     const FontFamily* currFamily = &font.fontDescription().family();
167     while (currFamily && !platformData) {
168         if (currFamily->family().length()) {
169             static String matchWords[3] = { String("Arabic"), String("Pashto"), String("Urdu") };
170             static AtomicString geezaStr("Geeza Pro");
171             for (int j = 0; j < 3 && !platformData; ++j)
172                 if (currFamily->family().contains(matchWords[j], false))
173                     platformData = getCachedFontPlatformData(font.fontDescription(), geezaStr);
174         }
175         currFamily = currFamily->next();
176     }
177
178     return platformData;
179 }
180
181 FontPlatformData* FontCache::getLastResortFallbackFont(const Font& font)
182 {
183     static AtomicString timesStr("Times");
184     static AtomicString lucidaGrandeStr("Lucida Grande");
185
186     // FIXME: Would be even better to somehow get the user's default font here.  For now we'll pick
187     // the default that the user would get without changing any prefs.
188     FontPlatformData* platformFont = getCachedFontPlatformData(font.fontDescription(), timesStr);
189     if (!platformFont)
190         // The Times fallback will almost always work, but in the highly unusual case where
191         // the user doesn't have it, we fall back on Lucida Grande because that's
192         // guaranteed to be there, according to Nathan Taylor. This is good enough
193         // to avoid a crash at least.
194         platformFont = getCachedFontPlatformData(font.fontDescription(), lucidaGrandeStr);
195
196     return platformFont;
197 }
198
199 FontPlatformData* FontCache::createFontPlatformData(const FontDescription& fontDescription, const AtomicString& family)
200 {
201     NSFontTraitMask traits = 0;
202     if (fontDescription.italic())
203         traits |= NSItalicFontMask;
204     if (fontDescription.bold())
205         traits |= NSBoldFontMask;
206     switch (fontDescription.stretch()) {
207         case FontStretchCondensed:
208             traits |= NSFontCondensedTrait;
209             break;
210         case FontStretchExpanded:
211             traits |= NSFontExpandedTrait;
212             break;
213         case FontStretchNormal:
214             break;
215     }
216     float size = fontDescription.computedPixelSize();
217     
218     NSFont* nsFont = [WebFontCache fontWithFamily:family traits:traits size:size];
219     if (!nsFont)
220         return 0;
221
222     NSFontTraitMask actualTraits = 0;
223     if ((traits & NSFontBoldTrait) || (traits & NSFontItalicTrait))
224         actualTraits = [[NSFontManager sharedFontManager] traitsOfFont:nsFont];
225     
226     FontPlatformData* result = new FontPlatformData;
227     
228     // Use the correct font for print vs. screen.
229     result->font = fontDescription.usePrinterFont() ? [nsFont printerFont] : [nsFont screenFont];
230     result->syntheticBold = (traits & NSBoldFontMask) && !(actualTraits & NSBoldFontMask);
231     result->syntheticOblique = (traits & NSItalicFontMask) && !(actualTraits & NSItalicFontMask);
232     return result;
233 }
234
235 } // namespace WebCore