9feb2d938d71fc52fd99feaf272a14286039ced8
[WebKit-https.git] / Source / WebCore / platform / graphics / cocoa / FontCocoa.mm
1 /*
2  * Copyright (C) 2005, 2006, 2010, 2011 Apple Inc. All rights reserved.
3  * Copyright (C) 2006 Alexey Proskuryakov
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  * 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  *
14  * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
15  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
16  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
17  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
18  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
19  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
20  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
21  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
22  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
23  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
24  * THE POSSIBILITY OF SUCH DAMAGE.
25  */
26
27 #import "config.h"
28 #import "Font.h"
29
30 #import "Color.h"
31 #import "FloatRect.h"
32 #import "FontCache.h"
33 #import "FontCascade.h"
34 #import "FontDescription.h"
35 #import "OpenTypeCG.h"
36 #import "SharedBuffer.h"
37 #import "WebCoreSystemInterface.h"
38 #import <float.h>
39 #import <pal/spi/cg/CoreGraphicsSPI.h>
40 #import <pal/spi/cocoa/CoreTextSPI.h>
41 #import <unicode/uchar.h>
42 #import <wtf/Assertions.h>
43 #import <wtf/RetainPtr.h>
44 #import <wtf/StdLibExtras.h>
45
46 #if USE(APPKIT)
47 #import <AppKit/AppKit.h>
48 #import <ApplicationServices/ApplicationServices.h>
49 #else
50 #import <CoreText/CoreText.h>
51 #endif
52
53 #if USE(APPKIT)
54 @interface NSFont (WebAppKitSecretAPI)
55 - (BOOL)_isFakeFixedPitch;
56 @end
57 #endif
58
59 namespace WebCore {
60
61 static inline bool caseInsensitiveCompare(CFStringRef a, CFStringRef b)
62 {
63     return a && CFStringCompare(a, b, kCFCompareCaseInsensitive) == kCFCompareEqualTo;
64 }
65
66 static bool fontHasVerticalGlyphs(CTFontRef ctFont)
67 {
68     // The check doesn't look neat but this is what AppKit does for vertical writing...
69     RetainPtr<CFArrayRef> tableTags = adoptCF(CTFontCopyAvailableTables(ctFont, kCTFontTableOptionNoOptions));
70     if (!tableTags)
71         return false;
72     CFIndex numTables = CFArrayGetCount(tableTags.get());
73     for (CFIndex index = 0; index < numTables; ++index) {
74         CTFontTableTag tag = (CTFontTableTag)(uintptr_t)CFArrayGetValueAtIndex(tableTags.get(), index);
75         if (tag == kCTFontTableVhea || tag == kCTFontTableVORG)
76             return true;
77     }
78     return false;
79 }
80
81 #if PLATFORM(IOS)
82 bool fontFamilyShouldNotBeUsedForArabic(CFStringRef fontFamilyName)
83 {
84     if (!fontFamilyName)
85         return false;
86
87     // Times New Roman and Arial are not performant enough to use. <rdar://problem/21333326>
88     // FIXME <rdar://problem/12096835> remove this function once the above bug is fixed.
89     return (CFStringCompare(CFSTR("Times New Roman"), fontFamilyName, 0) == kCFCompareEqualTo)
90         || (CFStringCompare(CFSTR("Arial"), fontFamilyName, 0) == kCFCompareEqualTo);
91 }
92
93 static const float kLineHeightAdjustment = 0.15f;
94
95 static bool shouldUseAdjustment(CTFontRef font)
96 {
97     RetainPtr<CFStringRef> familyName = adoptCF(CTFontCopyFamilyName(font));
98
99     if (!familyName || !CFStringGetLength(familyName.get()))
100         return false;
101
102     return caseInsensitiveCompare(familyName.get(), CFSTR("Times"))
103         || caseInsensitiveCompare(familyName.get(), CFSTR("Helvetica"))
104         || caseInsensitiveCompare(familyName.get(), CFSTR(".Helvetica NeueUI"));
105 }
106
107 #else
108
109 static bool needsAscentAdjustment(CFStringRef familyName)
110 {
111     return familyName && (caseInsensitiveCompare(familyName, CFSTR("Times"))
112         || caseInsensitiveCompare(familyName, CFSTR("Helvetica"))
113         || caseInsensitiveCompare(familyName, CFSTR("Courier")));
114 }
115
116 #endif
117
118 void Font::platformInit()
119 {
120 #if PLATFORM(IOS)
121     m_syntheticBoldOffset = m_platformData.syntheticBold() ? ceilf(m_platformData.size() / 24.0f) : 0.f;
122 #else
123     m_syntheticBoldOffset = m_platformData.syntheticBold() ? 1.0f : 0.f;
124 #endif
125
126     unsigned unitsPerEm = CTFontGetUnitsPerEm(m_platformData.font());
127     float pointSize = m_platformData.size();
128     CGFloat capHeight = pointSize ? CTFontGetCapHeight(m_platformData.font()) : 0;
129     CGFloat lineGap = pointSize ? CTFontGetLeading(m_platformData.font()) : 0;
130     CGFloat ascent = pointSize ? CTFontGetAscent(m_platformData.font()) : 0;
131     CGFloat descent = pointSize ? CTFontGetDescent(m_platformData.font()) : 0;
132
133     // The Open Font Format describes the OS/2 USE_TYPO_METRICS flag as follows:
134     // "If set, it is strongly recommended to use OS/2.sTypoAscender - OS/2.sTypoDescender+ OS/2.sTypoLineGap as a value for default line spacing for this font."
135     // On OS X, we only apply this rule in the important case of fonts with a MATH table.
136     if (OpenType::fontHasMathTable(m_platformData.ctFont())) {
137         short typoAscent, typoDescent, typoLineGap;
138         if (OpenType::tryGetTypoMetrics(m_platformData.font(), typoAscent, typoDescent, typoLineGap)) {
139             ascent = scaleEmToUnits(typoAscent, unitsPerEm) * pointSize;
140             descent = -scaleEmToUnits(typoDescent, unitsPerEm) * pointSize;
141             lineGap = scaleEmToUnits(typoLineGap, unitsPerEm) * pointSize;
142         }
143     }
144
145     auto familyName = adoptCF(CTFontCopyFamilyName(m_platformData.font()));
146 #if PLATFORM(MAC)
147     // We need to adjust Times, Helvetica, and Courier to closely match the
148     // vertical metrics of their Microsoft counterparts that are the de facto
149     // web standard. The AppKit adjustment of 20% is too big and is
150     // incorrectly added to line spacing, so we use a 15% adjustment instead
151     // and add it to the ascent.
152     if (origin() == Origin::Local && needsAscentAdjustment(familyName.get()))
153         ascent += std::round((ascent + descent) * 0.15f);
154 #endif
155
156     // Compute line spacing before the line metrics hacks are applied.
157     float lineSpacing = lroundf(ascent) + lroundf(descent) + lroundf(lineGap);
158
159 #if PLATFORM(MAC)
160     // Hack Hiragino line metrics to allow room for marked text underlines.
161     // <rdar://problem/5386183>
162     if (descent < 3 && lineGap >= 3 && familyName && CFStringHasPrefix(familyName.get(), CFSTR("Hiragino"))) {
163         lineGap -= 3 - descent;
164         descent = 3;
165     }
166 #endif
167     
168     if (platformData().orientation() == Vertical && !isTextOrientationFallback())
169         m_hasVerticalGlyphs = fontHasVerticalGlyphs(m_platformData.ctFont());
170
171 #if PLATFORM(IOS)
172     CGFloat adjustment = shouldUseAdjustment(m_platformData.font()) ? ceil((ascent + descent) * kLineHeightAdjustment) : 0;
173
174     lineGap = ceilf(lineGap);
175     lineSpacing = ceil(ascent) + adjustment + ceil(descent) + lineGap;
176     ascent = ceilf(ascent + adjustment);
177     descent = ceilf(descent);
178
179     m_shouldNotBeUsedForArabic = fontFamilyShouldNotBeUsedForArabic(familyName.get());
180 #endif
181
182     CGFloat xHeight = 0;
183     if (m_platformData.size()) {
184         if (platformData().orientation() == Horizontal) {
185             // Measure the actual character "x", since it's possible for it to extend below the baseline, and we need the
186             // reported x-height to only include the portion of the glyph that is above the baseline.
187             Glyph xGlyph = glyphForCharacter('x');
188             if (xGlyph)
189                 xHeight = -CGRectGetMinY(platformBoundsForGlyph(xGlyph));
190             else
191                 xHeight = CTFontGetXHeight(m_platformData.font());
192         } else
193             xHeight = verticalRightOrientationFont().fontMetrics().xHeight();
194     }
195
196     m_fontMetrics.setUnitsPerEm(unitsPerEm);
197     m_fontMetrics.setAscent(ascent);
198     m_fontMetrics.setDescent(descent);
199     m_fontMetrics.setCapHeight(capHeight);
200     m_fontMetrics.setLineGap(lineGap);
201     m_fontMetrics.setXHeight(xHeight);
202     m_fontMetrics.setLineSpacing(lineSpacing);
203 }
204
205 void Font::platformCharWidthInit()
206 {
207     m_avgCharWidth = 0;
208     m_maxCharWidth = 0;
209     
210 #if PLATFORM(MAC)
211     auto os2Table = adoptCF(CTFontCopyTable(m_platformData.font(), kCTFontTableOS2, kCTFontTableOptionNoOptions));
212     if (os2Table && CFDataGetLength(os2Table.get()) >= 4) {
213         const UInt8* os2 = CFDataGetBytePtr(os2Table.get());
214         SInt16 os2AvgCharWidth = os2[2] * 256 + os2[3];
215         m_avgCharWidth = scaleEmToUnits(os2AvgCharWidth, m_fontMetrics.unitsPerEm()) * m_platformData.size();
216     }
217
218     auto headTable = adoptCF(CTFontCopyTable(m_platformData.font(), kCTFontTableHead, kCTFontTableOptionNoOptions));
219     if (headTable && CFDataGetLength(headTable.get()) >= 42) {
220         const UInt8* head = CFDataGetBytePtr(headTable.get());
221         ushort uxMin = head[36] * 256 + head[37];
222         ushort uxMax = head[40] * 256 + head[41];
223         SInt16 xMin = static_cast<SInt16>(uxMin);
224         SInt16 xMax = static_cast<SInt16>(uxMax);
225         float diff = static_cast<float>(xMax - xMin);
226         m_maxCharWidth = scaleEmToUnits(diff, m_fontMetrics.unitsPerEm()) * m_platformData.size();
227     }
228 #endif
229
230     // Fallback to a cross-platform estimate, which will populate these values if they are non-positive.
231     initCharWidths();
232 }
233
234 void Font::platformDestroy()
235 {
236 }
237
238 bool Font::variantCapsSupportsCharacterForSynthesis(FontVariantCaps fontVariantCaps, UChar32 character) const
239 {
240 #if (PLATFORM(IOS) && TARGET_OS_IOS) || (PLATFORM(MAC) && __MAC_OS_X_VERSION_MIN_REQUIRED >= 101200)
241     Glyph glyph = glyphForCharacter(character);
242     if (!glyph)
243         return false;
244
245     switch (fontVariantCaps) {
246     case FontVariantCaps::Small: {
247         auto& supported = glyphsSupportedBySmallCaps();
248         return supported.size() > glyph && supported.get(glyph);
249     }
250     case FontVariantCaps::Petite: {
251         auto& supported = glyphsSupportedByPetiteCaps();
252         return supported.size() > glyph && supported.get(glyph);
253     }
254     case FontVariantCaps::AllSmall: {
255         auto& supported = glyphsSupportedByAllSmallCaps();
256         return supported.size() > glyph && supported.get(glyph);
257     }
258     case FontVariantCaps::AllPetite: {
259         auto& supported = glyphsSupportedByAllPetiteCaps();
260         return supported.size() > glyph && supported.get(glyph);
261     }
262     default:
263         // Synthesis only supports the variant-caps values listed above.
264         return true;
265     }
266 #else
267     UNUSED_PARAM(character);
268
269     switch (fontVariantCaps) {
270     case FontVariantCaps::Small:
271     case FontVariantCaps::Petite:
272     case FontVariantCaps::AllSmall:
273     case FontVariantCaps::AllPetite:
274         return false;
275     default:
276         // Synthesis only supports the variant-caps values listed above.
277         return true;
278     }
279 #endif
280 }
281
282 #if (PLATFORM(IOS) && TARGET_OS_IOS) || (PLATFORM(MAC) && __MAC_OS_X_VERSION_MIN_REQUIRED >= 101200)
283 static RetainPtr<CFDictionaryRef> smallCapsOpenTypeDictionary(CFStringRef key, int rawValue)
284 {
285     RetainPtr<CFNumberRef> value = adoptCF(CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &rawValue));
286     CFTypeRef keys[] = { kCTFontOpenTypeFeatureTag, kCTFontOpenTypeFeatureValue };
287     CFTypeRef values[] = { key, value.get() };
288     return adoptCF(CFDictionaryCreate(kCFAllocatorDefault, keys, values, WTF_ARRAY_LENGTH(keys), &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks));
289 }
290
291 static RetainPtr<CFDictionaryRef> smallCapsTrueTypeDictionary(int rawKey, int rawValue)
292 {
293     RetainPtr<CFNumberRef> key = adoptCF(CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &rawKey));
294     RetainPtr<CFNumberRef> value = adoptCF(CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &rawValue));
295     CFTypeRef keys[] = { kCTFontFeatureTypeIdentifierKey, kCTFontFeatureSelectorIdentifierKey };
296     CFTypeRef values[] = { key.get(), value.get() };
297     return adoptCF(CFDictionaryCreate(kCFAllocatorDefault, keys, values, WTF_ARRAY_LENGTH(keys), &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks));
298 }
299
300 static void unionBitVectors(BitVector& result, CFBitVectorRef source)
301 {
302     CFIndex length = CFBitVectorGetCount(source);
303     result.ensureSize(length);
304     CFIndex min = 0;
305     while (min < length) {
306         CFIndex nextIndex = CFBitVectorGetFirstIndexOfBit(source, CFRangeMake(min, length - min), 1);
307         if (nextIndex == kCFNotFound)
308             break;
309         result.set(nextIndex, true);
310         min = nextIndex + 1;
311     }
312 }
313
314 static void injectOpenTypeCoverage(CFStringRef feature, CTFontRef font, BitVector& result)
315 {
316     RetainPtr<CFBitVectorRef> source = adoptCF(CTFontCopyGlyphCoverageForFeature(font, smallCapsOpenTypeDictionary(feature, 1).get()));
317     unionBitVectors(result, source.get());
318 }
319
320 static void injectTrueTypeCoverage(int type, int selector, CTFontRef font, BitVector& result)
321 {
322     RetainPtr<CFBitVectorRef> source = adoptCF(CTFontCopyGlyphCoverageForFeature(font, smallCapsTrueTypeDictionary(type, selector).get()));
323     unionBitVectors(result, source.get());
324 }
325
326 const BitVector& Font::glyphsSupportedBySmallCaps() const
327 {
328     if (!m_glyphsSupportedBySmallCaps) {
329         m_glyphsSupportedBySmallCaps = BitVector();
330         injectOpenTypeCoverage(CFSTR("smcp"), platformData().font(), m_glyphsSupportedBySmallCaps.value());
331         injectTrueTypeCoverage(kLowerCaseType, kLowerCaseSmallCapsSelector, platformData().font(), m_glyphsSupportedBySmallCaps.value());
332     }
333     return m_glyphsSupportedBySmallCaps.value();
334 }
335
336 const BitVector& Font::glyphsSupportedByAllSmallCaps() const
337 {
338     if (!m_glyphsSupportedByAllSmallCaps) {
339         m_glyphsSupportedByAllSmallCaps = BitVector();
340         injectOpenTypeCoverage(CFSTR("smcp"), platformData().font(), m_glyphsSupportedByAllSmallCaps.value());
341         injectOpenTypeCoverage(CFSTR("c2sc"), platformData().font(), m_glyphsSupportedByAllSmallCaps.value());
342         injectTrueTypeCoverage(kLowerCaseType, kLowerCaseSmallCapsSelector, platformData().font(), m_glyphsSupportedByAllSmallCaps.value());
343         injectTrueTypeCoverage(kUpperCaseType, kUpperCaseSmallCapsSelector, platformData().font(), m_glyphsSupportedByAllSmallCaps.value());
344     }
345     return m_glyphsSupportedByAllSmallCaps.value();
346 }
347
348 const BitVector& Font::glyphsSupportedByPetiteCaps() const
349 {
350     if (!m_glyphsSupportedByPetiteCaps) {
351         m_glyphsSupportedByPetiteCaps = BitVector();
352         injectOpenTypeCoverage(CFSTR("pcap"), platformData().font(), m_glyphsSupportedByPetiteCaps.value());
353         injectTrueTypeCoverage(kLowerCaseType, kLowerCasePetiteCapsSelector, platformData().font(), m_glyphsSupportedByPetiteCaps.value());
354     }
355     return m_glyphsSupportedByPetiteCaps.value();
356 }
357
358 const BitVector& Font::glyphsSupportedByAllPetiteCaps() const
359 {
360     if (!m_glyphsSupportedByAllPetiteCaps) {
361         m_glyphsSupportedByAllPetiteCaps = BitVector();
362         injectOpenTypeCoverage(CFSTR("pcap"), platformData().font(), m_glyphsSupportedByAllPetiteCaps.value());
363         injectOpenTypeCoverage(CFSTR("c2pc"), platformData().font(), m_glyphsSupportedByAllPetiteCaps.value());
364         injectTrueTypeCoverage(kLowerCaseType, kLowerCasePetiteCapsSelector, platformData().font(), m_glyphsSupportedByAllPetiteCaps.value());
365         injectTrueTypeCoverage(kUpperCaseType, kUpperCasePetiteCapsSelector, platformData().font(), m_glyphsSupportedByAllPetiteCaps.value());
366     }
367     return m_glyphsSupportedByAllPetiteCaps.value();
368 }
369 #endif
370
371 static RefPtr<Font> createDerivativeFont(CTFontRef font, float size, FontOrientation orientation, CTFontSymbolicTraits fontTraits, bool syntheticBold, bool syntheticItalic)
372 {
373     if (!font)
374         return nullptr;
375
376     if (syntheticBold)
377         fontTraits |= kCTFontBoldTrait;
378     if (syntheticItalic)
379         fontTraits |= kCTFontItalicTrait;
380
381     CTFontSymbolicTraits scaledFontTraits = CTFontGetSymbolicTraits(font);
382
383     bool usedSyntheticBold = (fontTraits & kCTFontBoldTrait) && !(scaledFontTraits & kCTFontTraitBold);
384     bool usedSyntheticOblique = (fontTraits & kCTFontItalicTrait) && !(scaledFontTraits & kCTFontTraitItalic);
385     FontPlatformData scaledFontData(font, size, usedSyntheticBold, usedSyntheticOblique, orientation);
386
387     return Font::create(scaledFontData);
388 }
389
390 static inline bool isOpenTypeFeature(CFDictionaryRef feature)
391 {
392     return CFDictionaryContainsKey(feature, kCTFontOpenTypeFeatureTag) && CFDictionaryContainsKey(feature, kCTFontOpenTypeFeatureValue);
393 }
394
395 static inline bool isTrueTypeFeature(CFDictionaryRef feature)
396 {
397     return CFDictionaryContainsKey(feature, kCTFontFeatureTypeIdentifierKey) && CFDictionaryContainsKey(feature, kCTFontFeatureSelectorIdentifierKey);
398 }
399
400 static inline std::optional<CFStringRef> openTypeFeature(CFDictionaryRef feature)
401 {
402     ASSERT(isOpenTypeFeature(feature));
403     CFStringRef tag = static_cast<CFStringRef>(CFDictionaryGetValue(feature, kCTFontOpenTypeFeatureTag));
404     int rawValue;
405     CFNumberRef value = static_cast<CFNumberRef>(CFDictionaryGetValue(feature, kCTFontOpenTypeFeatureValue));
406     auto success = CFNumberGetValue(value, kCFNumberIntType, &rawValue);
407     ASSERT_UNUSED(success, success);
408     return rawValue ? std::optional<CFStringRef>(tag) : std::nullopt;
409 }
410
411 static inline std::pair<int, int> trueTypeFeature(CFDictionaryRef feature)
412 {
413     ASSERT(isTrueTypeFeature(feature));
414     int rawType;
415     CFNumberRef type = static_cast<CFNumberRef>(CFDictionaryGetValue(feature, kCTFontFeatureTypeIdentifierKey));
416     auto success = CFNumberGetValue(type, kCFNumberIntType, &rawType);
417     ASSERT_UNUSED(success, success);
418     int rawSelector;
419     CFNumberRef selector = static_cast<CFNumberRef>(CFDictionaryGetValue(feature, kCTFontFeatureSelectorIdentifierKey));
420     success = CFNumberGetValue(selector, kCFNumberIntType, &rawSelector);
421     ASSERT_UNUSED(success, success);
422     return std::make_pair(rawType, rawSelector);
423 }
424
425 static inline CFNumberRef defaultSelectorForTrueTypeFeature(int key, CTFontRef font)
426 {
427     RetainPtr<CFArrayRef> features = adoptCF(CTFontCopyFeatures(font));
428     CFIndex featureCount = CFArrayGetCount(features.get());
429     for (CFIndex i = 0; i < featureCount; ++i) {
430         CFDictionaryRef featureType = static_cast<CFDictionaryRef>(CFArrayGetValueAtIndex(features.get(), i));
431         CFNumberRef featureKey = static_cast<CFNumberRef>(CFDictionaryGetValue(featureType, kCTFontFeatureTypeIdentifierKey));
432         if (!featureKey)
433             continue;
434         int rawFeatureKey;
435         CFNumberGetValue(featureKey, kCFNumberIntType, &rawFeatureKey);
436         if (rawFeatureKey != key)
437             continue;
438
439         CFArrayRef featureSelectors = static_cast<CFArrayRef>(CFDictionaryGetValue(featureType, kCTFontFeatureTypeSelectorsKey));
440         if (!featureSelectors)
441             continue;
442         CFIndex selectorsCount = CFArrayGetCount(featureSelectors);
443         for (CFIndex j = 0; j < selectorsCount; ++j) {
444             CFDictionaryRef featureSelector = static_cast<CFDictionaryRef>(CFArrayGetValueAtIndex(featureSelectors, j));
445             CFNumberRef isDefault = static_cast<CFNumberRef>(CFDictionaryGetValue(featureSelector, kCTFontFeatureSelectorDefaultKey));
446             if (!isDefault)
447                 continue;
448             int rawIsDefault;
449             CFNumberGetValue(isDefault, kCFNumberIntType, &rawIsDefault);
450             if (!rawIsDefault)
451                 continue;
452             return static_cast<CFNumberRef>(CFDictionaryGetValue(featureSelector, kCTFontFeatureSelectorIdentifierKey));
453         }
454     }
455     return nullptr;
456 }
457
458 static inline RetainPtr<CFDictionaryRef> removedFeature(CFDictionaryRef feature, CTFontRef font)
459 {
460     bool isOpenType = isOpenTypeFeature(feature);
461     bool isTrueType = isTrueTypeFeature(feature);
462     if (!isOpenType && !isTrueType)
463         return feature; // We don't understand this font format.
464     RetainPtr<CFMutableDictionaryRef> result = adoptCF(CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks));
465     if (isOpenType) {
466         auto featureTag = openTypeFeature(feature);
467         if (featureTag && (CFEqual(featureTag.value(), CFSTR("smcp"))
468             || CFEqual(featureTag.value(), CFSTR("c2sc"))
469             || CFEqual(featureTag.value(), CFSTR("pcap"))
470             || CFEqual(featureTag.value(), CFSTR("c2pc")))) {
471             int rawZero = 0;
472             RetainPtr<CFNumberRef> zero = adoptCF(CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &rawZero));
473             CFDictionaryAddValue(result.get(), kCTFontOpenTypeFeatureTag, featureTag.value());
474             CFDictionaryAddValue(result.get(), kCTFontOpenTypeFeatureValue, zero.get());
475         } else {
476             CFDictionaryAddValue(result.get(), kCTFontOpenTypeFeatureTag, CFDictionaryGetValue(feature, kCTFontOpenTypeFeatureTag));
477             CFDictionaryAddValue(result.get(), kCTFontOpenTypeFeatureValue, CFDictionaryGetValue(feature, kCTFontOpenTypeFeatureValue));
478         }
479     }
480     if (isTrueType) {
481         auto trueTypeFeaturePair = trueTypeFeature(feature);
482         if (trueTypeFeaturePair.first == kLowerCaseType && (trueTypeFeaturePair.second == kLowerCaseSmallCapsSelector || trueTypeFeaturePair.second == kLowerCasePetiteCapsSelector)) {
483             CFDictionaryAddValue(result.get(), kCTFontFeatureTypeIdentifierKey, CFDictionaryGetValue(feature, kCTFontFeatureTypeIdentifierKey));
484             if (CFNumberRef defaultSelector = defaultSelectorForTrueTypeFeature(kLowerCaseType, font))
485                 CFDictionaryAddValue(result.get(), kCTFontFeatureSelectorIdentifierKey, defaultSelector);
486             else
487                 CFDictionaryAddValue(result.get(), kCTFontFeatureSelectorIdentifierKey, CFDictionaryGetValue(feature, kCTFontFeatureSelectorIdentifierKey));
488         } else if (trueTypeFeaturePair.first == kUpperCaseType && (trueTypeFeaturePair.second == kUpperCaseSmallCapsSelector || trueTypeFeaturePair.second == kUpperCasePetiteCapsSelector)) {
489             CFDictionaryAddValue(result.get(), kCTFontFeatureTypeIdentifierKey, CFDictionaryGetValue(feature, kCTFontFeatureTypeIdentifierKey));
490             if (CFNumberRef defaultSelector = defaultSelectorForTrueTypeFeature(kUpperCaseType, font))
491                 CFDictionaryAddValue(result.get(), kCTFontFeatureSelectorIdentifierKey, defaultSelector);
492             else
493                 CFDictionaryAddValue(result.get(), kCTFontFeatureSelectorIdentifierKey, CFDictionaryGetValue(feature, kCTFontFeatureSelectorIdentifierKey));
494         } else {
495             CFDictionaryAddValue(result.get(), kCTFontFeatureTypeIdentifierKey, CFDictionaryGetValue(feature, kCTFontFeatureTypeIdentifierKey));
496             CFDictionaryAddValue(result.get(), kCTFontFeatureSelectorIdentifierKey, CFDictionaryGetValue(feature, kCTFontFeatureSelectorIdentifierKey));
497         }
498     }
499     return result;
500 }
501
502 static RetainPtr<CTFontRef> createCTFontWithoutSynthesizableFeatures(CTFontRef font)
503 {
504     RetainPtr<CFArrayRef> features = adoptCF(static_cast<CFArrayRef>(CTFontCopyAttribute(font, kCTFontFeatureSettingsAttribute)));
505     if (!features)
506         return font;
507     CFIndex featureCount = CFArrayGetCount(features.get());
508     RetainPtr<CFMutableArrayRef> newFeatures = adoptCF(CFArrayCreateMutable(kCFAllocatorDefault, featureCount, &kCFTypeArrayCallBacks));
509     for (CFIndex i = 0; i < featureCount; ++i) {
510         CFDictionaryRef feature = static_cast<CFDictionaryRef>(CFArrayGetValueAtIndex(features.get(), i));
511         CFArrayAppendValue(newFeatures.get(), removedFeature(feature, font).get());
512     }
513     CFTypeRef keys[] = { kCTFontFeatureSettingsAttribute };
514     CFTypeRef values[] = { newFeatures.get() };
515     RetainPtr<CFDictionaryRef> attributes = adoptCF(CFDictionaryCreate(kCFAllocatorDefault, keys, values, WTF_ARRAY_LENGTH(keys), &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks));
516     RetainPtr<CTFontDescriptorRef> newDescriptor = adoptCF(CTFontDescriptorCreateWithAttributes(attributes.get()));
517     return adoptCF(CTFontCreateCopyWithAttributes(font, CTFontGetSize(font), nullptr, newDescriptor.get()));
518 }
519
520 RefPtr<Font> Font::createFontWithoutSynthesizableFeatures() const
521 {
522     float size = m_platformData.size();
523     CTFontSymbolicTraits fontTraits = CTFontGetSymbolicTraits(m_platformData.font());
524     RetainPtr<CTFontRef> ctFont = createCTFontWithoutSynthesizableFeatures(m_platformData.font());
525     return createDerivativeFont(ctFont.get(), size, m_platformData.orientation(), fontTraits, m_platformData.syntheticBold(), m_platformData.syntheticOblique());
526 }
527
528 RefPtr<Font> Font::platformCreateScaledFont(const FontDescription&, float scaleFactor) const
529 {
530     float size = m_platformData.size() * scaleFactor;
531     CTFontSymbolicTraits fontTraits = CTFontGetSymbolicTraits(m_platformData.font());
532     RetainPtr<CTFontDescriptorRef> fontDescriptor = adoptCF(CTFontCopyFontDescriptor(m_platformData.font()));
533     RetainPtr<CTFontRef> scaledFont = adoptCF(CTFontCreateWithFontDescriptor(fontDescriptor.get(), size, nullptr));
534
535     return createDerivativeFont(scaledFont.get(), size, m_platformData.orientation(), fontTraits, m_platformData.syntheticBold(), m_platformData.syntheticOblique());
536 }
537
538 static int extractNumber(CFNumberRef number)
539 {
540     int result = 0;
541     if (number)
542         CFNumberGetValue(number, kCFNumberIntType, &result);
543     return result;
544 }
545
546 void Font::determinePitch()
547 {
548     CTFontRef ctFont = m_platformData.font();
549     ASSERT(ctFont);
550
551     // Special case Osaka-Mono.
552     // According to <rdar://problem/3999467>, we should treat Osaka-Mono as fixed pitch.
553     // Note that the AppKit does not report Osaka-Mono as fixed pitch.
554
555     // Special case MS-PGothic.
556     // According to <rdar://problem/4032938>, we should not treat MS-PGothic as fixed pitch.
557     // Note that AppKit does report MS-PGothic as fixed pitch.
558
559     // Special case MonotypeCorsiva
560     // According to <rdar://problem/5454704>, we should not treat MonotypeCorsiva as fixed pitch.
561     // Note that AppKit does report MonotypeCorsiva as fixed pitch.
562
563     auto fullName = adoptCF(CTFontCopyFullName(ctFont));
564     auto familyName = adoptCF(CTFontCopyFamilyName(ctFont));
565
566     int fixedPitch = extractNumber(adoptCF(static_cast<CFNumberRef>(CTFontCopyAttribute(m_platformData.font(), kCTFontFixedAdvanceAttribute))).get());
567     m_treatAsFixedPitch = (CTFontGetSymbolicTraits(ctFont) & kCTFontMonoSpaceTrait) || fixedPitch || (caseInsensitiveCompare(fullName.get(), CFSTR("Osaka-Mono")) || caseInsensitiveCompare(fullName.get(), CFSTR("MS-PGothic")) || caseInsensitiveCompare(fullName.get(), CFSTR("MonotypeCorsiva")));
568 #if PLATFORM(IOS)
569     if (familyName && caseInsensitiveCompare(familyName.get(), CFSTR("Courier New"))) {
570         // Special case Courier New to not be treated as fixed pitch, as this will make use of a hacked space width which is undesireable for iPhone (see rdar://6269783).
571         m_treatAsFixedPitch = false;
572     }
573 #endif
574 }
575
576 FloatRect Font::platformBoundsForGlyph(Glyph glyph) const
577 {
578     FloatRect boundingBox;
579     boundingBox = CTFontGetBoundingRectsForGlyphs(m_platformData.ctFont(), platformData().orientation() == Vertical ? kCTFontOrientationVertical : kCTFontOrientationHorizontal, &glyph, 0, 1);
580     boundingBox.setY(-boundingBox.maxY());
581     if (m_syntheticBoldOffset)
582         boundingBox.setWidth(boundingBox.width() + m_syntheticBoldOffset);
583
584     return boundingBox;
585 }
586
587 #if !((PLATFORM(MAC) && __MAC_OS_X_VERSION_MIN_REQUIRED >= 101200) || PLATFORM(IOS))
588 static inline std::optional<CGSize> advanceForColorBitmapFont(const FontPlatformData& platformData, Glyph glyph)
589 {
590     CTFontRef font = platformData.font();
591     if (!font || !platformData.isColorBitmapFont())
592         return std::nullopt;
593     CGSize advance;
594     CTFontGetAdvancesForGlyphs(font, kCTFontOrientationDefault, &glyph, &advance, 1);
595     return advance;
596 }
597
598 static inline bool canUseFastGlyphAdvanceGetter(const FontPlatformData& platformData, Glyph glyph, CGSize& advance, bool& populatedAdvance)
599 {
600     if (platformData.isEmoji() || platformData.hasCustomTracking() || platformData.textRenderingMode() == OptimizeLegibility)
601         return false;
602     if (auto size = advanceForColorBitmapFont(platformData, glyph)) {
603         populatedAdvance = true;
604         advance = size.value();
605         return false;
606     }
607     return true;
608 }
609 #endif
610
611 float Font::platformWidthForGlyph(Glyph glyph) const
612 {
613     CGSize advance = CGSizeZero;
614     bool horizontal = platformData().orientation() == Horizontal;
615     CGFontRenderingStyle style = kCGFontRenderingStyleAntialiasing | kCGFontRenderingStyleSubpixelPositioning | kCGFontRenderingStyleSubpixelQuantization | kCGFontAntialiasingStyleUnfiltered;
616
617 #if (PLATFORM(MAC) && __MAC_OS_X_VERSION_MIN_REQUIRED >= 101200) || PLATFORM(IOS)
618     if (platformData().size()) {
619         CTFontOrientation orientation = horizontal || m_isBrokenIdeographFallback ? kCTFontOrientationHorizontal : kCTFontOrientationVertical;
620         // FIXME: Remove this special-casing when <rdar://problem/28197291> and <rdar://problem/28662086> are fixed.
621         if (CTFontIsAppleColorEmoji(m_platformData.ctFont()) || m_platformData.hasVariations())
622             CTFontGetAdvancesForGlyphs(m_platformData.ctFont(), orientation, &glyph, &advance, 1);
623         else
624             CTFontGetUnsummedAdvancesForGlyphsAndStyle(m_platformData.ctFont(), orientation, style, &glyph, &advance, 1);
625     }
626
627 #else
628
629     bool populatedAdvance = false;
630     if ((horizontal || m_isBrokenIdeographFallback) && canUseFastGlyphAdvanceGetter(this->platformData(), glyph, advance, populatedAdvance)) {
631         float pointSize = platformData().size();
632         CGAffineTransform m = CGAffineTransformMakeScale(pointSize, pointSize);
633         if (!CGFontGetGlyphAdvancesForStyle(platformData().cgFont(), &m, style, &glyph, 1, &advance)) {
634             RetainPtr<CFStringRef> fullName = adoptCF(CGFontCopyFullName(platformData().cgFont()));
635             LOG_ERROR("Unable to cache glyph widths for %@ %f", fullName.get(), pointSize);
636             advance.width = 0;
637         }
638     } else if (!populatedAdvance && platformData().size())
639         CTFontGetAdvancesForGlyphs(m_platformData.ctFont(), horizontal ? kCTFontOrientationHorizontal : kCTFontOrientationVertical, &glyph, &advance, 1);
640 #endif
641
642     return advance.width + m_syntheticBoldOffset;
643 }
644
645 struct ProviderInfo {
646     const UChar* characters;
647     size_t length;
648     CFDictionaryRef attributes;
649 };
650
651 static const UniChar* provideStringAndAttributes(CFIndex stringIndex, CFIndex* count, CFDictionaryRef* attributes, void* context)
652 {
653     ProviderInfo* info = static_cast<struct ProviderInfo*>(context);
654     if (stringIndex < 0 || static_cast<size_t>(stringIndex) >= info->length)
655         return 0;
656
657     *count = info->length - stringIndex;
658     *attributes = info->attributes;
659     return info->characters + stringIndex;
660 }
661
662 bool Font::canRenderCombiningCharacterSequence(const UChar* characters, size_t length) const
663 {
664     ASSERT(isMainThread());
665
666     if (!m_combiningCharacterSequenceSupport)
667         m_combiningCharacterSequenceSupport = std::make_unique<HashMap<String, bool>>();
668
669     WTF::HashMap<String, bool>::AddResult addResult = m_combiningCharacterSequenceSupport->add(String(characters, length), false);
670     if (!addResult.isNewEntry)
671         return addResult.iterator->value;
672
673     RetainPtr<CFTypeRef> fontEqualityObject = platformData().objectForEqualityCheck();
674
675     ProviderInfo info = { characters, length, getCFStringAttributes(false, platformData().orientation()) };
676     RetainPtr<CTLineRef> line = adoptCF(CTLineCreateWithUniCharProvider(&provideStringAndAttributes, 0, &info));
677
678     CFArrayRef runArray = CTLineGetGlyphRuns(line.get());
679     CFIndex runCount = CFArrayGetCount(runArray);
680
681     for (CFIndex r = 0; r < runCount; r++) {
682         CTRunRef ctRun = static_cast<CTRunRef>(CFArrayGetValueAtIndex(runArray, r));
683         ASSERT(CFGetTypeID(ctRun) == CTRunGetTypeID());
684         CFDictionaryRef runAttributes = CTRunGetAttributes(ctRun);
685         CTFontRef runFont = static_cast<CTFontRef>(CFDictionaryGetValue(runAttributes, kCTFontAttributeName));
686         if (!CFEqual(fontEqualityObject.get(), FontPlatformData::objectForEqualityCheck(runFont).get()))
687             return false;
688     }
689
690     addResult.iterator->value = true;
691     return true;
692 }
693
694 } // namespace WebCore