<rdar://problem/10191243> Glyph variants (line final swashes) appear where they shoul...
[WebKit-https.git] / Source / WebCore / platform / graphics / cocoa / FontPlatformDataCocoa.mm
1 /*
2  * This file is part of the internal font implementation.
3  *
4  * Copyright (C) 2006, 2007, 2008, 2009, 2010 Apple Inc. All rights reserved.
5  * Copyright (c) 2010 Google Inc. All rights reserved.
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Library General Public
9  * License as published by the Free Software Foundation; either
10  * version 2 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Library General Public License for more details.
16  *
17  * You should have received a copy of the GNU Library General Public License
18  * along with this library; see the file COPYING.LIB.  If not, write to
19  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
20  * Boston, MA 02110-1301, USA.
21  *
22  */
23
24 #import "config.h"
25 #import "FontPlatformData.h"
26
27 #import "PlatformString.h"
28 #import "WebCoreSystemInterface.h"
29 #import <AppKit/NSFont.h>
30
31 namespace WebCore {
32
33 // These CoreText Text Spacing feature selectors are not defined in CoreText.
34 enum TextSpacingCTFeatureSelector { TextSpacingProportional, TextSpacingFullWidth, TextSpacingHalfWidth, TextSpacingThirdWidth, TextSpacingQuarterWidth };
35
36 #if PLATFORM(MAC)
37 void FontPlatformData::loadFont(NSFont* nsFont, float, NSFont*& outNSFont, CGFontRef& cgFont)
38 {
39     outNSFont = nsFont;
40     cgFont = CTFontCopyGraphicsFont(toCTFontRef(nsFont), 0);
41 }
42 #endif  // PLATFORM(MAC)
43
44 FontPlatformData::FontPlatformData(NSFont *nsFont, float size, bool syntheticBold, bool syntheticOblique, FontOrientation orientation,
45                                    TextOrientation textOrientation, FontWidthVariant widthVariant)
46     : m_syntheticBold(syntheticBold)
47     , m_syntheticOblique(syntheticOblique)
48     , m_orientation(orientation)
49     , m_textOrientation(textOrientation)
50     , m_size(size)
51     , m_widthVariant(widthVariant)
52     , m_font(nsFont)
53 #if !defined(BUILDING_ON_LEOPARD) && !defined(BUILDING_ON_SNOW_LEOPARD)
54     // FIXME: Chromium: The following code isn't correct for the Chromium port since the sandbox might
55     // have blocked font loading, in which case we'll only have the real loaded font file after the call to loadFont().
56     , m_isColorBitmapFont(CTFontGetSymbolicTraits(toCTFontRef(nsFont)) & kCTFontColorGlyphsTrait)
57 #else
58     , m_isColorBitmapFont(false)
59 #endif
60 {
61     ASSERT_ARG(nsFont, nsFont);
62
63     CGFontRef cgFont = 0;
64     loadFont(nsFont, size, m_font, cgFont);
65
66     if (m_font)
67         CFRetain(m_font);
68
69     m_cgFont.adoptCF(cgFont);
70 }
71
72 FontPlatformData:: ~FontPlatformData()
73 {
74     if (m_font && m_font != reinterpret_cast<NSFont *>(-1))
75         CFRelease(m_font);
76 }
77
78 void FontPlatformData::platformDataInit(const FontPlatformData& f)
79 {
80     m_font = f.m_font && f.m_font != reinterpret_cast<NSFont *>(-1) ? const_cast<NSFont *>(static_cast<const NSFont *>(CFRetain(f.m_font))) : f.m_font;
81
82     m_cgFont = f.m_cgFont;
83     m_CTFont = f.m_CTFont;
84
85 #if PLATFORM(CHROMIUM) && OS(DARWIN)
86     m_inMemoryFont = f.m_inMemoryFont;
87 #endif
88 }
89
90 const FontPlatformData& FontPlatformData::platformDataAssign(const FontPlatformData& f)
91 {
92     m_cgFont = f.m_cgFont;
93     if (m_font == f.m_font)
94         return *this;
95     if (f.m_font && f.m_font != reinterpret_cast<NSFont *>(-1))
96         CFRetain(f.m_font);
97     if (m_font && m_font != reinterpret_cast<NSFont *>(-1))
98         CFRelease(m_font);
99     m_font = f.m_font;
100     m_CTFont = f.m_CTFont;
101 #if PLATFORM(CHROMIUM) && OS(DARWIN)
102     m_inMemoryFont = f.m_inMemoryFont;
103 #endif
104     return *this;
105 }
106
107 bool FontPlatformData::platformIsEqual(const FontPlatformData& other) const
108 {
109     if (m_font || other.m_font)
110         return m_font == other.m_font;
111     return m_cgFont == other.m_cgFont;
112 }
113
114 void FontPlatformData::setFont(NSFont *font)
115 {
116     ASSERT_ARG(font, font);
117     ASSERT(m_font != reinterpret_cast<NSFont *>(-1));
118
119     if (m_font == font)
120         return;
121
122     CFRetain(font);
123     if (m_font)
124         CFRelease(m_font);
125     m_font = font;
126     m_size = [font pointSize];
127     
128     CGFontRef cgFont = 0;
129     NSFont* loadedFont = 0;
130     loadFont(m_font, m_size, loadedFont, cgFont);
131     
132 #if PLATFORM(CHROMIUM) && OS(DARWIN)
133     // If loadFont replaced m_font with a fallback font, then release the
134     // previous font to counter the retain above. Then retain the new font.
135     if (loadedFont != m_font) {
136         CFRelease(m_font);
137         CFRetain(loadedFont);
138         m_font = loadedFont;
139     }
140 #endif
141     
142     m_cgFont.adoptCF(cgFont);
143 #if !defined(BUILDING_ON_LEOPARD) && !defined(BUILDING_ON_SNOW_LEOPARD)
144     m_isColorBitmapFont = CTFontGetSymbolicTraits(toCTFontRef(m_font)) & kCTFontColorGlyphsTrait;
145 #endif
146     m_CTFont = 0;
147 }
148
149 bool FontPlatformData::roundsGlyphAdvances() const
150 {
151     return [m_font renderingMode] == NSFontAntialiasedIntegerAdvancementsRenderingMode;
152 }
153
154 bool FontPlatformData::allowsLigatures() const
155 {
156     return ![[m_font coveredCharacterSet] characterIsMember:'a'];
157 }
158
159 inline int mapFontWidthVariantToCTFeatureSelector(FontWidthVariant variant)
160 {
161     switch(variant) {
162     case RegularWidth:
163         return TextSpacingProportional;
164
165     case HalfWidth:
166         return TextSpacingHalfWidth;
167
168     case ThirdWidth:
169         return TextSpacingThirdWidth;
170
171     case QuarterWidth:
172         return TextSpacingQuarterWidth;
173     }
174
175     ASSERT_NOT_REACHED();
176     return TextSpacingProportional;
177 }
178
179 static CFDictionaryRef createFeatureSettingDictionary(int featureTypeIdentifier, int featureSelectorIdentifier)
180 {
181     RetainPtr<CFNumberRef> featureTypeIdentifierNumber(AdoptCF, CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &featureTypeIdentifier));
182     RetainPtr<CFNumberRef> featureSelectorIdentifierNumber(AdoptCF, CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &featureSelectorIdentifier));
183
184     const void* settingKeys[] = { kCTFontFeatureTypeIdentifierKey, kCTFontFeatureSelectorIdentifierKey };
185     const void* settingValues[] = { featureTypeIdentifierNumber.get(), featureSelectorIdentifierNumber.get() };
186
187     return CFDictionaryCreate(kCFAllocatorDefault, settingKeys, settingValues, WTF_ARRAY_LENGTH(settingKeys), &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
188 }
189
190 static CTFontDescriptorRef cascadeToLastResortFontDescriptor()
191 {
192     static CTFontDescriptorRef descriptor;
193     if (descriptor)
194         return descriptor;
195
196     const void* keys[] = { kCTFontCascadeListAttribute };
197     const void* descriptors[] = { CTFontDescriptorCreateWithNameAndSize(CFSTR("LastResort"), 0) };
198     const void* values[] = { CFArrayCreate(kCFAllocatorDefault, descriptors, WTF_ARRAY_LENGTH(descriptors), &kCFTypeArrayCallBacks) };
199     RetainPtr<CFDictionaryRef> attributes(AdoptCF, CFDictionaryCreate(kCFAllocatorDefault, keys, values, WTF_ARRAY_LENGTH(keys), &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks));
200
201     descriptor = CTFontDescriptorCreateWithAttributes(attributes.get());
202
203     return descriptor;
204 }
205
206 static CTFontDescriptorRef cascadeToLastResortAndDisableSwashesFontDescriptor()
207 {
208     static CTFontDescriptorRef descriptor;
209     if (descriptor)
210         return descriptor;
211
212     RetainPtr<CFDictionaryRef> lineInitialSwashesOffSetting(AdoptCF, createFeatureSettingDictionary(kSmartSwashType, kLineInitialSwashesOffSelector));
213     RetainPtr<CFDictionaryRef> lineFinalSwashesOffSetting(AdoptCF, createFeatureSettingDictionary(kSmartSwashType, kLineFinalSwashesOffSelector));
214
215     const void* settingDictionaries[] = { lineInitialSwashesOffSetting.get(), lineFinalSwashesOffSetting.get() };
216     RetainPtr<CFArrayRef> featureSettings(AdoptCF, CFArrayCreate(kCFAllocatorDefault, settingDictionaries, WTF_ARRAY_LENGTH(settingDictionaries), &kCFTypeArrayCallBacks));
217
218     const void* keys[] = { kCTFontFeatureSettingsAttribute };
219     const void* values[] = { featureSettings.get() };
220     RetainPtr<CFDictionaryRef> attributes(AdoptCF, CFDictionaryCreate(kCFAllocatorDefault, keys, values, WTF_ARRAY_LENGTH(keys), &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks));
221
222     descriptor = CTFontDescriptorCreateCopyWithAttributes(cascadeToLastResortFontDescriptor(), attributes.get());
223
224     return descriptor;
225 }
226
227 // Adding a cascade list breaks the font on Leopard
228 static bool canSetCascadeListForCustomFont()
229 {
230 #if PLATFORM(CHROMIUM)
231     static SInt32 systemVersion;
232     if (!systemVersion) {
233         if (Gestalt(gestaltSystemVersion, &systemVersion) != noErr)
234             return false;
235     }
236
237     return systemVersion >= 0x1060;
238 #elif !defined(BUILDING_ON_LEOPARD)
239     return true;
240 #else
241     return false;
242 #endif
243 }
244
245 CTFontRef FontPlatformData::ctFont() const
246 {
247     if (m_CTFont)
248         return m_CTFont.get();
249
250     m_CTFont = toCTFontRef(m_font);
251     if (m_CTFont) {
252         CTFontDescriptorRef fontDescriptor;
253         RetainPtr<CFStringRef> postScriptName(AdoptCF, CTFontCopyPostScriptName(m_CTFont.get()));
254         // Hoefler Text Italic has line-initial and -final swashes enabled by default, so disable them.
255         if (CFEqual(postScriptName.get(), CFSTR("HoeflerText-Italic")))
256             fontDescriptor = cascadeToLastResortAndDisableSwashesFontDescriptor();
257         else
258             fontDescriptor = cascadeToLastResortFontDescriptor();
259         m_CTFont.adoptCF(CTFontCreateCopyWithAttributes(m_CTFont.get(), m_size, 0, fontDescriptor));
260     } else
261         m_CTFont.adoptCF(CTFontCreateWithGraphicsFont(m_cgFont.get(), m_size, 0, canSetCascadeListForCustomFont() ? cascadeToLastResortFontDescriptor() : 0));
262
263     if (m_widthVariant != RegularWidth) {
264         int featureTypeValue = kTextSpacingType;
265         int featureSelectorValue = mapFontWidthVariantToCTFeatureSelector(m_widthVariant);
266         RetainPtr<CTFontDescriptorRef> sourceDescriptor(AdoptCF, CTFontCopyFontDescriptor(m_CTFont.get()));
267         RetainPtr<CFNumberRef> featureType(AdoptCF, CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &featureTypeValue));
268         RetainPtr<CFNumberRef> featureSelector(AdoptCF, CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &featureSelectorValue));
269         RetainPtr<CTFontDescriptorRef> newDescriptor(AdoptCF, CTFontDescriptorCreateCopyWithFeature(sourceDescriptor.get(), featureType.get(), featureSelector.get()));
270         RetainPtr<CTFontRef> newFont(AdoptCF, CTFontCreateWithFontDescriptor(newDescriptor.get(), m_size, 0));
271
272         if (newFont)
273             m_CTFont = newFont;
274     }
275
276     return m_CTFont.get();
277 }
278
279 #ifndef NDEBUG
280 String FontPlatformData::description() const
281 {
282     RetainPtr<CFStringRef> cgFontDescription(AdoptCF, CFCopyDescription(cgFont()));
283     return String(cgFontDescription.get()) + " " + String::number(m_size)
284             + (m_syntheticBold ? " synthetic bold" : "") + (m_syntheticOblique ? " synthetic oblique" : "") + (m_orientation ? " vertical orientation" : "");
285 }
286 #endif
287
288 } // namespace WebCore