platform/mac/editing/input/devanagari-ligature.html is flaky on Yosemite, ligature...
[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 "CoreTextSPI.h"
28 #import "SharedBuffer.h"
29 #import "WebCoreSystemInterface.h"
30 #import <wtf/text/WTFString.h>
31
32 #if !PLATFORM(IOS)
33 #import <AppKit/NSFont.h>
34 #else
35 #import "CoreGraphicsSPI.h"
36 #import <CoreText/CoreText.h>
37 #endif
38
39 namespace WebCore {
40
41 // These CoreText Text Spacing feature selectors are not defined in CoreText.
42 enum TextSpacingCTFeatureSelector { TextSpacingProportional, TextSpacingFullWidth, TextSpacingHalfWidth, TextSpacingThirdWidth, TextSpacingQuarterWidth };
43
44 FontPlatformData::FontPlatformData(CTFontRef font, float size, bool isPrinterFont, bool syntheticBold, bool syntheticOblique, FontOrientation orientation, FontWidthVariant widthVariant)
45     : m_syntheticBold(syntheticBold)
46     , m_syntheticOblique(syntheticOblique)
47     , m_orientation(orientation)
48     , m_size(size)
49     , m_widthVariant(widthVariant)
50     , m_font(font)
51     , m_cgFont(adoptCF(CTFontCopyGraphicsFont(font, NULL)))
52     , m_isColorBitmapFont(CTFontGetSymbolicTraits(font) & kCTFontTraitColorGlyphs)
53     , m_isCompositeFontReference(CTFontGetSymbolicTraits(font) & kCTFontCompositeTrait)
54     , m_isPrinterFont(isPrinterFont)
55 {
56     ASSERT_ARG(font, font);
57     CFRetain(m_font);
58 }
59
60 #if USE(APPKIT)
61 // FIXME: Remove this when all NSFont usage is removed.
62 FontPlatformData::FontPlatformData(NSFont *font, float size, bool isPrinterFont, bool syntheticBold, bool syntheticOblique, FontOrientation orientation, FontWidthVariant widthVariant)
63     : FontPlatformData((CTFontRef)font, size, isPrinterFont, syntheticBold, syntheticOblique, orientation, widthVariant)
64 {
65 }
66 #endif
67
68 FontPlatformData::~FontPlatformData()
69 {
70     if (m_font && m_font != reinterpret_cast<CTFontRef>(-1))
71         CFRelease(m_font);
72 }
73
74 void FontPlatformData::platformDataInit(const FontPlatformData& f)
75 {
76     m_font = f.m_font && f.m_font != reinterpret_cast<CTFontRef>(-1) ? static_cast<CTFontRef>(const_cast<void *>(CFRetain(f.m_font))) : f.m_font;
77
78 #if PLATFORM(IOS)
79     m_isEmoji = f.m_isEmoji;
80 #endif
81     m_cgFont = f.m_cgFont;
82     m_ctFont = f.m_ctFont;
83     m_isPrinterFont = f.m_isPrinterFont;
84 }
85
86 const FontPlatformData& FontPlatformData::platformDataAssign(const FontPlatformData& f)
87 {
88     m_cgFont = f.m_cgFont;
89 #if PLATFORM(IOS)
90     m_isEmoji = f.m_isEmoji;
91 #endif
92     if (m_font && m_font != reinterpret_cast<CTFontRef>(-1) && f.m_font && f.m_font != reinterpret_cast<CTFontRef>(-1) && CFEqual(m_font, f.m_font))
93         return *this;
94     if (f.m_font && f.m_font != reinterpret_cast<CTFontRef>(-1))
95         CFRetain(f.m_font);
96     if (m_font && m_font != reinterpret_cast<CTFontRef>(-1))
97         CFRelease(m_font);
98     m_font = f.m_font;
99     m_ctFont = f.m_ctFont;
100     m_isPrinterFont = f.m_isPrinterFont;
101
102     return *this;
103 }
104
105 bool FontPlatformData::platformIsEqual(const FontPlatformData& other) const
106 {
107     bool result = false;
108     if (m_font || other.m_font) {
109 #if PLATFORM(IOS)
110         result = m_font && m_font != reinterpret_cast<CTFontRef>(-1) && other.m_font && other.m_font != reinterpret_cast<CTFontRef>(-1) && CFEqual(m_font, other.m_font);
111 #if !ASSERT_DISABLED
112         if (result)
113             ASSERT(m_isEmoji == other.m_isEmoji);
114 #endif
115 #else
116         result = m_font == other.m_font;
117 #endif // PLATFORM(IOS)
118         return result;
119     }
120 #if PLATFORM(IOS)
121 #if !ASSERT_DISABLED
122     if (m_cgFont == other.m_cgFont)
123         ASSERT(m_isEmoji == other.m_isEmoji);
124 #endif
125 #endif // PLATFORM(IOS)
126     return m_cgFont == other.m_cgFont;
127 }
128
129 void FontPlatformData::setFont(CTFontRef font)
130 {
131     ASSERT_ARG(font, font);
132     ASSERT(m_font != reinterpret_cast<CTFontRef>(-1));
133
134     if (m_font == font)
135         return;
136
137     CFRetain(font);
138     if (m_font)
139         CFRelease(m_font);
140     m_font = font;
141     m_size = CTFontGetSize(font);
142     m_cgFont = adoptCF(CTFontCopyGraphicsFont(font, nullptr));
143
144     CTFontSymbolicTraits traits = CTFontGetSymbolicTraits(m_font);
145     m_isColorBitmapFont = traits & kCTFontTraitColorGlyphs;
146     m_isCompositeFontReference = traits & kCTFontCompositeTrait;
147     
148     m_ctFont = nullptr;
149 }
150
151 bool FontPlatformData::roundsGlyphAdvances() const
152 {
153 #if USE(APPKIT)
154     return [(NSFont *)m_font renderingMode] == NSFontAntialiasedIntegerAdvancementsRenderingMode;
155 #else
156     return false;
157 #endif
158 }
159
160
161 bool FontPlatformData::allowsLigatures() const
162 {
163 #if USE(APPKIT)
164     return ![[(NSFont *)m_font coveredCharacterSet] characterIsMember:'a'];
165 #else
166     if (!m_font)
167         return false;
168
169     RetainPtr<CFCharacterSetRef> characterSet = adoptCF(CTFontCopyCharacterSet(ctFont()));
170     return !(characterSet.get() && CFCharacterSetIsCharacterMember(characterSet.get(), 'a'));
171 #endif
172 }
173
174 inline int mapFontWidthVariantToCTFeatureSelector(FontWidthVariant variant)
175 {
176     switch(variant) {
177     case RegularWidth:
178         return TextSpacingProportional;
179
180     case HalfWidth:
181         return TextSpacingHalfWidth;
182
183     case ThirdWidth:
184         return TextSpacingThirdWidth;
185
186     case QuarterWidth:
187         return TextSpacingQuarterWidth;
188     }
189
190     ASSERT_NOT_REACHED();
191     return TextSpacingProportional;
192 }
193
194 static CFDictionaryRef createFeatureSettingDictionary(int featureTypeIdentifier, int featureSelectorIdentifier)
195 {
196     RetainPtr<CFNumberRef> featureTypeIdentifierNumber = adoptCF(CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &featureTypeIdentifier));
197     RetainPtr<CFNumberRef> featureSelectorIdentifierNumber = adoptCF(CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &featureSelectorIdentifier));
198
199     const void* settingKeys[] = { kCTFontFeatureTypeIdentifierKey, kCTFontFeatureSelectorIdentifierKey };
200     const void* settingValues[] = { featureTypeIdentifierNumber.get(), featureSelectorIdentifierNumber.get() };
201
202     return CFDictionaryCreate(kCFAllocatorDefault, settingKeys, settingValues, WTF_ARRAY_LENGTH(settingKeys), &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
203 }
204
205 static CTFontDescriptorRef cascadeToLastResortFontDescriptor()
206 {
207     static CTFontDescriptorRef descriptor;
208     if (descriptor)
209         return descriptor;
210
211     RetainPtr<CTFontDescriptorRef> lastResort = adoptCF(CTFontDescriptorCreateWithNameAndSize(CFSTR("LastResort"), 0));
212
213     const void* descriptors[] = { lastResort.get() };
214     RetainPtr<CFArrayRef> array = adoptCF(CFArrayCreate(kCFAllocatorDefault, descriptors, WTF_ARRAY_LENGTH(descriptors), &kCFTypeArrayCallBacks));
215
216     const void* keys[] = { kCTFontCascadeListAttribute };
217     const void* values[] = { array.get() };
218     RetainPtr<CFDictionaryRef> attributes = adoptCF(CFDictionaryCreate(kCFAllocatorDefault, keys, values, WTF_ARRAY_LENGTH(keys), &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks));
219
220     descriptor = CTFontDescriptorCreateWithAttributes(attributes.get());
221
222     return descriptor;
223 }
224
225 static CTFontDescriptorRef cascadeToLastResortAndDisableSwashesFontDescriptor()
226 {
227     static CTFontDescriptorRef descriptor;
228     if (descriptor)
229         return descriptor;
230
231     RetainPtr<CFDictionaryRef> lineInitialSwashesOffSetting = adoptCF(createFeatureSettingDictionary(kSmartSwashType, kLineInitialSwashesOffSelector));
232     RetainPtr<CFDictionaryRef> lineFinalSwashesOffSetting = adoptCF(createFeatureSettingDictionary(kSmartSwashType, kLineFinalSwashesOffSelector));
233
234     const void* settingDictionaries[] = { lineInitialSwashesOffSetting.get(), lineFinalSwashesOffSetting.get() };
235     RetainPtr<CFArrayRef> featureSettings = adoptCF(CFArrayCreate(kCFAllocatorDefault, settingDictionaries, WTF_ARRAY_LENGTH(settingDictionaries), &kCFTypeArrayCallBacks));
236
237     const void* keys[] = { kCTFontFeatureSettingsAttribute };
238     const void* values[] = { featureSettings.get() };
239     RetainPtr<CFDictionaryRef> attributes = adoptCF(CFDictionaryCreate(kCFAllocatorDefault, keys, values, WTF_ARRAY_LENGTH(keys), &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks));
240
241     descriptor = CTFontDescriptorCreateCopyWithAttributes(cascadeToLastResortFontDescriptor(), attributes.get());
242
243     return descriptor;
244 }
245
246 CTFontRef FontPlatformData::ctFont() const
247 {
248     if (m_ctFont)
249         return m_ctFont.get();
250
251     ASSERT(m_cgFont.get());
252 #if !PLATFORM(IOS)
253     m_ctFont = m_font;
254     if (m_ctFont) {
255         CTFontDescriptorRef fontDescriptor;
256         RetainPtr<CFStringRef> postScriptName = adoptCF(CTFontCopyPostScriptName(m_ctFont.get()));
257         // Hoefler Text Italic has line-initial and -final swashes enabled by default, so disable them.
258         if (CFEqual(postScriptName.get(), CFSTR("HoeflerText-Italic")) || CFEqual(postScriptName.get(), CFSTR("HoeflerText-BlackItalic")))
259             fontDescriptor = cascadeToLastResortAndDisableSwashesFontDescriptor();
260         else
261             fontDescriptor = cascadeToLastResortFontDescriptor();
262         m_ctFont = adoptCF(CTFontCreateCopyWithAttributes(m_ctFont.get(), m_size, 0, fontDescriptor));
263     } else
264         m_ctFont = adoptCF(CTFontCreateWithGraphicsFont(m_cgFont.get(), m_size, 0, cascadeToLastResortFontDescriptor()));
265 #else
266     // Apple Color Emoji size is adjusted (and then re-adjusted by Core Text) and capped.
267     CGFloat size = !m_isEmoji ? m_size : m_size <= 15 ? 4 * (m_size + 2) / static_cast<CGFloat>(5) : 16;
268     CTFontDescriptorRef fontDescriptor;
269     const char* postScriptName = CGFontGetPostScriptName(m_cgFont.get());
270     if (postScriptName && (!strcmp(postScriptName, "HoeflerText-Italic") || !strcmp(postScriptName, "HoeflerText-BlackItalic")))
271         fontDescriptor = cascadeToLastResortAndDisableSwashesFontDescriptor();
272     else
273         fontDescriptor = cascadeToLastResortFontDescriptor();
274     m_ctFont = adoptCF(CTFontCreateWithGraphicsFont(m_cgFont.get(), size, 0, fontDescriptor));
275 #endif // !PLATFORM(IOS)
276
277     if (m_widthVariant != RegularWidth) {
278         int featureTypeValue = kTextSpacingType;
279         int featureSelectorValue = mapFontWidthVariantToCTFeatureSelector(m_widthVariant);
280         RetainPtr<CTFontDescriptorRef> sourceDescriptor = adoptCF(CTFontCopyFontDescriptor(m_ctFont.get()));
281         RetainPtr<CFNumberRef> featureType = adoptCF(CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &featureTypeValue));
282         RetainPtr<CFNumberRef> featureSelector = adoptCF(CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &featureSelectorValue));
283         RetainPtr<CTFontDescriptorRef> newDescriptor = adoptCF(CTFontDescriptorCreateCopyWithFeature(sourceDescriptor.get(), featureType.get(), featureSelector.get()));
284         RetainPtr<CTFontRef> newFont = adoptCF(CTFontCreateWithFontDescriptor(newDescriptor.get(), m_size, 0));
285
286         if (newFont)
287             m_ctFont = newFont;
288     }
289
290     return m_ctFont.get();
291 }
292
293 RetainPtr<CFTypeRef> FontPlatformData::objectForEqualityCheck(CTFontRef ctFont)
294 {
295 #if (PLATFORM(IOS) && __IPHONE_OS_VERSION_MIN_REQUIRED <= 80000) || (PLATFORM(MAC) && __MAC_OS_X_VERSION_MIN_REQUIRED <= 101000)
296     auto fontDescriptor = adoptCF(CTFontCopyFontDescriptor(ctFont));
297     // FIXME: https://bugs.webkit.org/show_bug.cgi?id=138683 This is a shallow pointer compare for web fonts
298     // because the URL contains the address of the font. This means we might erroneously get false negatives.
299     RetainPtr<CFURLRef> url = adoptCF(static_cast<CFURLRef>(CTFontDescriptorCopyAttribute(fontDescriptor.get(), kCTFontReferenceURLAttribute)));
300     ASSERT(CFGetTypeID(url.get()) == CFURLGetTypeID());
301     return url;
302 #else
303     return adoptCF(CTFontCopyGraphicsFont(ctFont, 0));
304 #endif
305 }
306
307 RetainPtr<CFTypeRef> FontPlatformData::objectForEqualityCheck() const
308 {
309     return objectForEqualityCheck(ctFont());
310 }
311
312 PassRefPtr<SharedBuffer> FontPlatformData::openTypeTable(uint32_t table) const
313 {
314     if (RetainPtr<CFDataRef> data = adoptCF(CGFontCopyTableForTag(cgFont(), table)))
315         return SharedBuffer::wrapCFData(data.get());
316     
317     return nullptr;
318 }
319
320 #ifndef NDEBUG
321 String FontPlatformData::description() const
322 {
323     RetainPtr<CFStringRef> cgFontDescription = adoptCF(CFCopyDescription(cgFont()));
324     return String(cgFontDescription.get()) + " " + String::number(m_size)
325             + (m_syntheticBold ? " synthetic bold" : "") + (m_syntheticOblique ? " synthetic oblique" : "") + (m_orientation ? " vertical orientation" : "");
326 }
327 #endif
328
329 } // namespace WebCore