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