36e3d2b0523a6b6f1a5ed76a53556331c074deb8
[WebKit-https.git] / Source / WebCore / platform / graphics / cocoa / FontCacheCoreText.cpp
1 /*
2  * Copyright (C) 2015-2017 Apple Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
14  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23  * THE POSSIBILITY OF SUCH DAMAGE.
24  */
25
26 #include "config.h"
27 #include "FontCache.h"
28
29 #include "CoreTextSPI.h"
30 #include "Font.h"
31
32 #include <CoreText/SFNTLayoutTypes.h>
33
34 #include <wtf/HashSet.h>
35 #include <wtf/MainThread.h>
36 #include <wtf/NeverDestroyed.h>
37
38 #define SHOULD_USE_CORE_TEXT_FONT_LOOKUP (PLATFORM(MAC) && __MAC_OS_X_VERSION_MIN_REQUIRED < 101200)
39 #define HAS_CORE_TEXT_WIDTH_ATTRIBUTE ((PLATFORM(MAC) && __MAC_OS_X_VERSION_MIN_REQUIRED >= 101300) || (PLATFORM(IOS) && __IPHONE_OS_VERSION_MIN_REQUIRED >= 110000))
40
41 namespace WebCore {
42
43 static inline void appendRawTrueTypeFeature(CFMutableArrayRef features, int type, int selector)
44 {
45     auto typeNumber = adoptCF(CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &type));
46     auto selectorNumber = adoptCF(CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &selector));
47     CFTypeRef featureKeys[] = { kCTFontFeatureTypeIdentifierKey, kCTFontFeatureSelectorIdentifierKey };
48     CFTypeRef featureValues[] = { typeNumber.get(), selectorNumber.get() };
49     auto feature = adoptCF(CFDictionaryCreate(kCFAllocatorDefault, featureKeys, featureValues, WTF_ARRAY_LENGTH(featureKeys), &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks));
50     CFArrayAppendValue(features, feature.get());
51 }
52
53 static inline bool tagEquals(FontTag tag, const char comparison[4])
54 {
55     return equalIgnoringASCIICase(tag.data(), comparison, 4);
56 }
57
58 static inline void appendTrueTypeFeature(CFMutableArrayRef features, const FontFeature& feature)
59 {
60     if (tagEquals(feature.tag(), "liga") || tagEquals(feature.tag(), "clig")) {
61         if (feature.enabled()) {
62             appendRawTrueTypeFeature(features, kLigaturesType, kCommonLigaturesOnSelector);
63             appendRawTrueTypeFeature(features, kLigaturesType, kContextualLigaturesOnSelector);
64         } else {
65             appendRawTrueTypeFeature(features, kLigaturesType, kCommonLigaturesOffSelector);
66             appendRawTrueTypeFeature(features, kLigaturesType, kContextualLigaturesOffSelector);
67         }
68     } else if (tagEquals(feature.tag(), "dlig")) {
69         if (feature.enabled())
70             appendRawTrueTypeFeature(features, kLigaturesType, kRareLigaturesOnSelector);
71         else
72             appendRawTrueTypeFeature(features, kLigaturesType, kRareLigaturesOffSelector);
73     } else if (tagEquals(feature.tag(), "hlig")) {
74         if (feature.enabled())
75             appendRawTrueTypeFeature(features, kLigaturesType, kHistoricalLigaturesOnSelector);
76         else
77             appendRawTrueTypeFeature(features, kLigaturesType, kHistoricalLigaturesOffSelector);
78     } else if (tagEquals(feature.tag(), "calt")) {
79         if (feature.enabled())
80             appendRawTrueTypeFeature(features, kContextualAlternatesType, kContextualAlternatesOnSelector);
81         else
82             appendRawTrueTypeFeature(features, kContextualAlternatesType, kContextualAlternatesOffSelector);
83     } else if (tagEquals(feature.tag(), "subs") && feature.enabled())
84         appendRawTrueTypeFeature(features, kVerticalPositionType, kInferiorsSelector);
85     else if (tagEquals(feature.tag(), "sups") && feature.enabled())
86         appendRawTrueTypeFeature(features, kVerticalPositionType, kSuperiorsSelector);
87     else if (tagEquals(feature.tag(), "smcp") && feature.enabled())
88         appendRawTrueTypeFeature(features, kLowerCaseType, kLowerCaseSmallCapsSelector);
89     else if (tagEquals(feature.tag(), "c2sc") && feature.enabled())
90         appendRawTrueTypeFeature(features, kUpperCaseType, kUpperCaseSmallCapsSelector);
91     else if (tagEquals(feature.tag(), "pcap") && feature.enabled())
92         appendRawTrueTypeFeature(features, kLowerCaseType, kLowerCasePetiteCapsSelector);
93     else if (tagEquals(feature.tag(), "c2pc") && feature.enabled())
94         appendRawTrueTypeFeature(features, kUpperCaseType, kUpperCasePetiteCapsSelector);
95     else if (tagEquals(feature.tag(), "unic") && feature.enabled())
96         appendRawTrueTypeFeature(features, kLetterCaseType, 14);
97     else if (tagEquals(feature.tag(), "titl") && feature.enabled())
98         appendRawTrueTypeFeature(features, kStyleOptionsType, kTitlingCapsSelector);
99     else if (tagEquals(feature.tag(), "lnum") && feature.enabled())
100         appendRawTrueTypeFeature(features, kNumberCaseType, kUpperCaseNumbersSelector);
101     else if (tagEquals(feature.tag(), "onum") && feature.enabled())
102         appendRawTrueTypeFeature(features, kNumberCaseType, kLowerCaseNumbersSelector);
103     else if (tagEquals(feature.tag(), "pnum") && feature.enabled())
104         appendRawTrueTypeFeature(features, kNumberSpacingType, kProportionalNumbersSelector);
105     else if (tagEquals(feature.tag(), "tnum") && feature.enabled())
106         appendRawTrueTypeFeature(features, kNumberSpacingType, kMonospacedNumbersSelector);
107     else if (tagEquals(feature.tag(), "frac") && feature.enabled())
108         appendRawTrueTypeFeature(features, kFractionsType, kDiagonalFractionsSelector);
109     else if (tagEquals(feature.tag(), "afrc") && feature.enabled())
110         appendRawTrueTypeFeature(features, kFractionsType, kVerticalFractionsSelector);
111     else if (tagEquals(feature.tag(), "ordn") && feature.enabled())
112         appendRawTrueTypeFeature(features, kVerticalPositionType, kOrdinalsSelector);
113     else if (tagEquals(feature.tag(), "zero") && feature.enabled())
114         appendRawTrueTypeFeature(features, kTypographicExtrasType, kSlashedZeroOnSelector);
115     else if (tagEquals(feature.tag(), "hist") && feature.enabled())
116         appendRawTrueTypeFeature(features, kLigaturesType, kHistoricalLigaturesOnSelector);
117     else if (tagEquals(feature.tag(), "jp78") && feature.enabled())
118         appendRawTrueTypeFeature(features, kCharacterShapeType, kJIS1978CharactersSelector);
119     else if (tagEquals(feature.tag(), "jp83") && feature.enabled())
120         appendRawTrueTypeFeature(features, kCharacterShapeType, kJIS1983CharactersSelector);
121     else if (tagEquals(feature.tag(), "jp90") && feature.enabled())
122         appendRawTrueTypeFeature(features, kCharacterShapeType, kJIS1990CharactersSelector);
123     else if (tagEquals(feature.tag(), "jp04") && feature.enabled())
124         appendRawTrueTypeFeature(features, kCharacterShapeType, kJIS2004CharactersSelector);
125     else if (tagEquals(feature.tag(), "smpl") && feature.enabled())
126         appendRawTrueTypeFeature(features, kCharacterShapeType, kSimplifiedCharactersSelector);
127     else if (tagEquals(feature.tag(), "trad") && feature.enabled())
128         appendRawTrueTypeFeature(features, kCharacterShapeType, kTraditionalCharactersSelector);
129     else if (tagEquals(feature.tag(), "fwid") && feature.enabled())
130         appendRawTrueTypeFeature(features, kTextSpacingType, kMonospacedTextSelector);
131     else if (tagEquals(feature.tag(), "pwid") && feature.enabled())
132         appendRawTrueTypeFeature(features, kTextSpacingType, kProportionalTextSelector);
133     else if (tagEquals(feature.tag(), "ruby") && feature.enabled())
134         appendRawTrueTypeFeature(features, kRubyKanaType, kRubyKanaOnSelector);
135 }
136
137 static inline void appendOpenTypeFeature(CFMutableArrayRef features, const FontFeature& feature)
138 {
139     auto featureKey = adoptCF(CFStringCreateWithBytes(kCFAllocatorDefault, reinterpret_cast<const UInt8*>(feature.tag().data()), feature.tag().size() * sizeof(FontTag::value_type), kCFStringEncodingASCII, false));
140     int rawFeatureValue = feature.value();
141     auto featureValue = adoptCF(CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &rawFeatureValue));
142     CFTypeRef featureDictionaryKeys[] = { kCTFontOpenTypeFeatureTag, kCTFontOpenTypeFeatureValue };
143     CFTypeRef featureDictionaryValues[] = { featureKey.get(), featureValue.get() };
144     auto featureDictionary = adoptCF(CFDictionaryCreate(kCFAllocatorDefault, featureDictionaryKeys, featureDictionaryValues, WTF_ARRAY_LENGTH(featureDictionaryValues), &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks));
145     CFArrayAppendValue(features, featureDictionary.get());
146 }
147
148 typedef HashMap<FontTag, int, FourCharacterTagHash, FourCharacterTagHashTraits> FeaturesMap;
149 #if ENABLE(VARIATION_FONTS)
150 typedef HashMap<FontTag, float, FourCharacterTagHash, FourCharacterTagHashTraits> VariationsMap;
151 #endif
152
153 static FeaturesMap computeFeatureSettingsFromVariants(const FontVariantSettings& variantSettings)
154 {
155     FeaturesMap result;
156
157     switch (variantSettings.commonLigatures) {
158     case FontVariantLigatures::Normal:
159         break;
160     case FontVariantLigatures::Yes:
161         result.add(fontFeatureTag("liga"), 1);
162         result.add(fontFeatureTag("clig"), 1);
163         break;
164     case FontVariantLigatures::No:
165         result.add(fontFeatureTag("liga"), 0);
166         result.add(fontFeatureTag("clig"), 0);
167         break;
168     default:
169         ASSERT_NOT_REACHED();
170     }
171
172     switch (variantSettings.discretionaryLigatures) {
173     case FontVariantLigatures::Normal:
174         break;
175     case FontVariantLigatures::Yes:
176         result.add(fontFeatureTag("dlig"), 1);
177         break;
178     case FontVariantLigatures::No:
179         result.add(fontFeatureTag("dlig"), 0);
180         break;
181     default:
182         ASSERT_NOT_REACHED();
183     }
184
185     switch (variantSettings.historicalLigatures) {
186     case FontVariantLigatures::Normal:
187         break;
188     case FontVariantLigatures::Yes:
189         result.add(fontFeatureTag("hlig"), 1);
190         break;
191     case FontVariantLigatures::No:
192         result.add(fontFeatureTag("hlig"), 0);
193         break;
194     default:
195         ASSERT_NOT_REACHED();
196     }
197
198     switch (variantSettings.contextualAlternates) {
199     case FontVariantLigatures::Normal:
200         break;
201     case FontVariantLigatures::Yes:
202         result.add(fontFeatureTag("calt"), 1);
203         break;
204     case FontVariantLigatures::No:
205         result.add(fontFeatureTag("calt"), 0);
206         break;
207     default:
208         ASSERT_NOT_REACHED();
209     }
210
211     switch (variantSettings.position) {
212     case FontVariantPosition::Normal:
213         break;
214     case FontVariantPosition::Subscript:
215         result.add(fontFeatureTag("subs"), 1);
216         break;
217     case FontVariantPosition::Superscript:
218         result.add(fontFeatureTag("sups"), 1);
219         break;
220     default:
221         ASSERT_NOT_REACHED();
222     }
223
224     switch (variantSettings.caps) {
225     case FontVariantCaps::Normal:
226         break;
227     case FontVariantCaps::AllSmall:
228         result.add(fontFeatureTag("c2sc"), 1);
229         FALLTHROUGH;
230     case FontVariantCaps::Small:
231         result.add(fontFeatureTag("smcp"), 1);
232         break;
233     case FontVariantCaps::AllPetite:
234         result.add(fontFeatureTag("c2pc"), 1);
235         FALLTHROUGH;
236     case FontVariantCaps::Petite:
237         result.add(fontFeatureTag("pcap"), 1);
238         break;
239     case FontVariantCaps::Unicase:
240         result.add(fontFeatureTag("unic"), 1);
241         break;
242     case FontVariantCaps::Titling:
243         result.add(fontFeatureTag("titl"), 1);
244         break;
245     default:
246         ASSERT_NOT_REACHED();
247     }
248
249     switch (variantSettings.numericFigure) {
250     case FontVariantNumericFigure::Normal:
251         break;
252     case FontVariantNumericFigure::LiningNumbers:
253         result.add(fontFeatureTag("lnum"), 1);
254         break;
255     case FontVariantNumericFigure::OldStyleNumbers:
256         result.add(fontFeatureTag("onum"), 1);
257         break;
258     default:
259         ASSERT_NOT_REACHED();
260     }
261
262     switch (variantSettings.numericSpacing) {
263     case FontVariantNumericSpacing::Normal:
264         break;
265     case FontVariantNumericSpacing::ProportionalNumbers:
266         result.add(fontFeatureTag("pnum"), 1);
267         break;
268     case FontVariantNumericSpacing::TabularNumbers:
269         result.add(fontFeatureTag("tnum"), 1);
270         break;
271     default:
272         ASSERT_NOT_REACHED();
273     }
274
275     switch (variantSettings.numericFraction) {
276     case FontVariantNumericFraction::Normal:
277         break;
278     case FontVariantNumericFraction::DiagonalFractions:
279         result.add(fontFeatureTag("frac"), 1);
280         break;
281     case FontVariantNumericFraction::StackedFractions:
282         result.add(fontFeatureTag("afrc"), 1);
283         break;
284     default:
285         ASSERT_NOT_REACHED();
286     }
287
288     switch (variantSettings.numericOrdinal) {
289     case FontVariantNumericOrdinal::Normal:
290         break;
291     case FontVariantNumericOrdinal::Yes:
292         result.add(fontFeatureTag("ordn"), 1);
293         break;
294     default:
295         ASSERT_NOT_REACHED();
296     }
297
298     switch (variantSettings.numericSlashedZero) {
299     case FontVariantNumericSlashedZero::Normal:
300         break;
301     case FontVariantNumericSlashedZero::Yes:
302         result.add(fontFeatureTag("zero"), 1);
303         break;
304     default:
305         ASSERT_NOT_REACHED();
306     }
307
308     switch (variantSettings.alternates) {
309     case FontVariantAlternates::Normal:
310         break;
311     case FontVariantAlternates::HistoricalForms:
312         result.add(fontFeatureTag("hist"), 1);
313         break;
314     default:
315         ASSERT_NOT_REACHED();
316     }
317
318     switch (variantSettings.eastAsianVariant) {
319     case FontVariantEastAsianVariant::Normal:
320         break;
321     case FontVariantEastAsianVariant::Jis78:
322         result.add(fontFeatureTag("jp78"), 1);
323         break;
324     case FontVariantEastAsianVariant::Jis83:
325         result.add(fontFeatureTag("jp83"), 1);
326         break;
327     case FontVariantEastAsianVariant::Jis90:
328         result.add(fontFeatureTag("jp90"), 1);
329         break;
330     case FontVariantEastAsianVariant::Jis04:
331         result.add(fontFeatureTag("jp04"), 1);
332         break;
333     case FontVariantEastAsianVariant::Simplified:
334         result.add(fontFeatureTag("smpl"), 1);
335         break;
336     case FontVariantEastAsianVariant::Traditional:
337         result.add(fontFeatureTag("trad"), 1);
338         break;
339     default:
340         ASSERT_NOT_REACHED();
341     }
342
343     switch (variantSettings.eastAsianWidth) {
344     case FontVariantEastAsianWidth::Normal:
345         break;
346     case FontVariantEastAsianWidth::Full:
347         result.add(fontFeatureTag("fwid"), 1);
348         break;
349     case FontVariantEastAsianWidth::Proportional:
350         result.add(fontFeatureTag("pwid"), 1);
351         break;
352     default:
353         ASSERT_NOT_REACHED();
354     }
355
356     switch (variantSettings.eastAsianRuby) {
357     case FontVariantEastAsianRuby::Normal:
358         break;
359     case FontVariantEastAsianRuby::Yes:
360         result.add(fontFeatureTag("ruby"), 1);
361         break;
362     default:
363         ASSERT_NOT_REACHED();
364     }
365
366     return result;
367 }
368
369 #if ENABLE(VARIATION_FONTS)
370 struct VariationDefaults {
371     float defaultValue;
372     float minimumValue;
373     float maximumValue;
374 };
375
376 typedef HashMap<FontTag, VariationDefaults, FourCharacterTagHash, FourCharacterTagHashTraits> VariationDefaultsMap;
377
378 static VariationDefaultsMap defaultVariationValues(CTFontRef font)
379 {
380     VariationDefaultsMap result;
381     auto axes = adoptCF(CTFontCopyVariationAxes(font));
382     if (!axes)
383         return result;
384     auto size = CFArrayGetCount(axes.get());
385     for (CFIndex i = 0; i < size; ++i) {
386         CFDictionaryRef axis = static_cast<CFDictionaryRef>(CFArrayGetValueAtIndex(axes.get(), i));
387         CFNumberRef axisIdentifier = static_cast<CFNumberRef>(CFDictionaryGetValue(axis, kCTFontVariationAxisIdentifierKey));
388         CFNumberRef defaultValue = static_cast<CFNumberRef>(CFDictionaryGetValue(axis, kCTFontVariationAxisDefaultValueKey));
389         CFNumberRef minimumValue = static_cast<CFNumberRef>(CFDictionaryGetValue(axis, kCTFontVariationAxisMinimumValueKey));
390         CFNumberRef maximumValue = static_cast<CFNumberRef>(CFDictionaryGetValue(axis, kCTFontVariationAxisMaximumValueKey));
391         uint32_t rawAxisIdentifier = 0;
392         Boolean success = CFNumberGetValue(axisIdentifier, kCFNumberSInt32Type, &rawAxisIdentifier);
393         ASSERT_UNUSED(success, success);
394         float rawDefaultValue = 0;
395         float rawMinimumValue = 0;
396         float rawMaximumValue = 0;
397         CFNumberGetValue(defaultValue, kCFNumberFloatType, &rawDefaultValue);
398         CFNumberGetValue(minimumValue, kCFNumberFloatType, &rawMinimumValue);
399         CFNumberGetValue(maximumValue, kCFNumberFloatType, &rawMaximumValue);
400
401         if (rawMinimumValue > rawMaximumValue)
402             std::swap(rawMinimumValue, rawMaximumValue);
403
404         auto b1 = rawAxisIdentifier >> 24;
405         auto b2 = (rawAxisIdentifier & 0xFF0000) >> 16;
406         auto b3 = (rawAxisIdentifier & 0xFF00) >> 8;
407         auto b4 = rawAxisIdentifier & 0xFF;
408         FontTag resultKey = {{ static_cast<char>(b1), static_cast<char>(b2), static_cast<char>(b3), static_cast<char>(b4) }};
409         VariationDefaults resultValues = { rawDefaultValue, rawMinimumValue, rawMaximumValue };
410         result.set(resultKey, resultValues);
411     }
412     return result;
413 }
414
415 static inline bool fontIsSystemFont(CTFontRef font)
416 {
417     if (CTFontDescriptorIsSystemUIFont(adoptCF(CTFontCopyFontDescriptor(font)).get()))
418         return true;
419     auto name = adoptCF(CTFontCopyPostScriptName(font));
420     return CFStringGetLength(name.get()) > 0 && CFStringGetCharacterAtIndex(name.get(), 0) == '.';
421 }
422
423 // These values were calculated by performing a linear regression on the CSS weights/widths/slopes and Core Text weights/widths/slopes of San Francisco.
424 // FIXME: <rdar://problem/31312602> Get the real values from Core Text.
425 static inline float normalizeWeight(float value)
426 {
427     return 523.7 * value - 109.3;
428 }
429
430 static inline float normalizeSlope(float value)
431 {
432     return value * 300;
433 }
434
435 static inline float denormalizeWeight(float value)
436 {
437     return (value + 109.3) / 523.7;
438 }
439
440 static inline float denormalizeSlope(float value)
441 {
442     return value / 300;
443 }
444
445 static inline float denormalizeVariationWidth(float value)
446 {
447     if (value <= 125)
448         return value / 100;
449     if (value <= 150)
450         return (value + 125) / 200;
451     return (value + 400) / 400;
452 }
453 #endif
454
455 #if ENABLE(VARIATION_FONTS) || !HAS_CORE_TEXT_WIDTH_ATTRIBUTE
456 static inline float normalizeVariationWidth(float value)
457 {
458     if (value <= 1.25)
459         return value * 100;
460     if (value <= 1.375)
461         return value * 200 - 125;
462     return value * 400 - 400;
463 }
464 #endif
465
466 #if !HAS_CORE_TEXT_WIDTH_ATTRIBUTE
467 static inline float normalizeWidth(float value)
468 {
469     return normalizeVariationWidth(value + 1);
470 }
471 #endif
472
473 struct FontType {
474     FontType(CTFontRef font)
475     {
476         auto tables = adoptCF(CTFontCopyAvailableTables(font, kCTFontTableOptionNoOptions));
477         if (!tables)
478             return;
479         auto size = CFArrayGetCount(tables.get());
480         for (CFIndex i = 0; i < size; ++i) {
481             // This is so yucky.
482             // https://developer.apple.com/reference/coretext/1510774-ctfontcopyavailabletables
483             // "The returned set will contain unboxed values, which can be extracted like so:"
484             // "CTFontTableTag tag = (CTFontTableTag)(uintptr_t)CFArrayGetValueAtIndex(tags, index);"
485             CTFontTableTag tableTag = static_cast<CTFontTableTag>(reinterpret_cast<uintptr_t>(CFArrayGetValueAtIndex(tables.get(), i)));
486             switch (tableTag) {
487             case 'fvar':
488                 if (variationType == VariationType::NotVariable)
489                     variationType = VariationType::TrueTypeGX;
490                 break;
491             case 'STAT':
492                 variationType = VariationType::OpenType18;
493                 break;
494             case 'morx':
495             case 'mort':
496                 aatShaping = true;
497                 break;
498             case 'GPOS':
499             case 'GSUB':
500                 openTypeShaping = true;
501                 break;
502             }
503         }
504     }
505
506     enum class VariationType {
507         NotVariable,
508         TrueTypeGX,
509         OpenType18
510     };
511     VariationType variationType { VariationType::NotVariable };
512     bool openTypeShaping { false };
513     bool aatShaping { false };
514 };
515
516 RetainPtr<CTFontRef> preparePlatformFont(CTFontRef originalFont, const FontDescription& fontDescription, const FontFeatureSettings* fontFaceFeatures, const FontVariantSettings* fontFaceVariantSettings, FontSelectionSpecifiedCapabilities fontFaceCapabilities, float size, bool applyWeightWidthSlopeVariations)
517 {
518     bool alwaysAddVariations = false;
519
520     // FIXME: Remove when <rdar://problem/29859207> is fixed
521 #if ENABLE(VARIATION_FONTS)
522     auto defaultValues = defaultVariationValues(originalFont);
523     alwaysAddVariations = !defaultValues.isEmpty();
524
525     auto fontSelectionRequest = fontDescription.fontSelectionRequest();
526     auto fontOpticalSizing = fontDescription.opticalSizing();
527     auto fontStyleAxis = fontDescription.fontStyleAxis();
528 #else
529     UNUSED_PARAM(fontFaceCapabilities);
530     UNUSED_PARAM(size);
531     UNUSED_PARAM(applyWeightWidthSlopeVariations);
532 #endif
533
534     const auto& features = fontDescription.featureSettings();
535     const auto& variantSettings = fontDescription.variantSettings();
536     const auto& variations = fontDescription.variationSettings();
537     auto textRenderingMode = fontDescription.textRenderingMode();
538
539     if (!originalFont || (!features.size() && (!alwaysAddVariations && variations.isEmpty()) && (textRenderingMode == AutoTextRendering) && variantSettings.isAllNormal()
540         && (!fontFaceFeatures || !fontFaceFeatures->size()) && (!fontFaceVariantSettings || fontFaceVariantSettings->isAllNormal())))
541         return originalFont;
542
543     // This algorithm is described at http://www.w3.org/TR/css3-fonts/#feature-precedence
544     FeaturesMap featuresToBeApplied;
545
546     // Step 1: CoreText handles default features (such as required ligatures).
547
548     // Step 2: Consult with font-variant-* inside @font-face
549     if (fontFaceVariantSettings)
550         featuresToBeApplied = computeFeatureSettingsFromVariants(*fontFaceVariantSettings);
551
552     // Step 3: Consult with font-feature-settings inside @font-face
553     if (fontFaceFeatures) {
554         for (auto& fontFaceFeature : *fontFaceFeatures)
555             featuresToBeApplied.set(fontFaceFeature.tag(), fontFaceFeature.value());
556     }
557
558     // Step 4: Font-variant
559     for (auto& newFeature : computeFeatureSettingsFromVariants(variantSettings))
560         featuresToBeApplied.set(newFeature.key, newFeature.value);
561
562     // Step 5: Other properties (text-rendering)
563     if (textRenderingMode == OptimizeSpeed) {
564         featuresToBeApplied.set(fontFeatureTag("liga"), 0);
565         featuresToBeApplied.set(fontFeatureTag("clig"), 0);
566         featuresToBeApplied.set(fontFeatureTag("dlig"), 0);
567         featuresToBeApplied.set(fontFeatureTag("hlig"), 0);
568         featuresToBeApplied.set(fontFeatureTag("calt"), 0);
569     }
570
571     // Step 6: Font-feature-settings
572     for (auto& newFeature : features)
573         featuresToBeApplied.set(newFeature.tag(), newFeature.value());
574
575     FontType fontType(originalFont);
576
577 #if ENABLE(VARIATION_FONTS)
578     VariationsMap variationsToBeApplied;
579
580     bool needsConversion = fontType.variationType == FontType::VariationType::TrueTypeGX;
581
582     auto applyVariation = [&](const FontTag& tag, float value) {
583         auto iterator = defaultValues.find(tag);
584         if (iterator == defaultValues.end())
585             return;
586         float valueToApply = clampTo(value, iterator->value.minimumValue, iterator->value.maximumValue);
587         variationsToBeApplied.set(tag, valueToApply);
588     };
589
590     // The system font is somewhat magical. Don't mess with its variations.
591     if (applyWeightWidthSlopeVariations && !fontIsSystemFont(originalFont)) {
592         float weight = fontSelectionRequest.weight;
593         float width = fontSelectionRequest.width;
594         float slope = fontSelectionRequest.slope;
595         if (auto weightValue = fontFaceCapabilities.weight)
596             weight = std::max(std::min(weight, static_cast<float>(weightValue->maximum)), static_cast<float>(weightValue->minimum));
597         if (auto widthValue = fontFaceCapabilities.width)
598             width = std::max(std::min(width, static_cast<float>(widthValue->maximum)), static_cast<float>(widthValue->minimum));
599         if (auto slopeValue = fontFaceCapabilities.weight)
600             slope = std::max(std::min(slope, static_cast<float>(slopeValue->maximum)), static_cast<float>(slopeValue->minimum));
601         if (needsConversion) {
602             weight = denormalizeWeight(weight);
603             width = denormalizeVariationWidth(width);
604             slope = denormalizeSlope(slope);
605         }
606         applyVariation({{'w', 'g', 'h', 't'}}, weight);
607         applyVariation({{'w', 'd', 't', 'h'}}, width);
608         if (fontStyleAxis == FontStyleAxis::ital)
609             applyVariation({{'i', 't', 'a', 'l'}}, 1);
610         else
611             applyVariation({{'s', 'l', 'n', 't'}}, slope);
612     }
613
614     if (fontOpticalSizing == FontOpticalSizing::Enabled) {
615         const float pxToPtRatio = 3.0f / 4;
616         applyVariation({{'o', 'p', 's', 'z'}}, size * pxToPtRatio);
617     }
618
619     for (auto& newVariation : variations)
620         applyVariation(newVariation.tag(), newVariation.value());
621
622 #endif // ENABLE(VARIATION_FONTS)
623
624     auto attributes = adoptCF(CFDictionaryCreateMutable(kCFAllocatorDefault, 2, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks));
625     if (!featuresToBeApplied.isEmpty()) {
626         auto featureArray = adoptCF(CFArrayCreateMutable(kCFAllocatorDefault, features.size(), &kCFTypeArrayCallBacks));
627         for (auto& p : featuresToBeApplied) {
628             auto feature = FontFeature(p.key, p.value);
629             if (fontType.aatShaping)
630                 appendTrueTypeFeature(featureArray.get(), feature);
631             if (fontType.openTypeShaping)
632                 appendOpenTypeFeature(featureArray.get(), feature);
633         }
634         CFDictionaryAddValue(attributes.get(), kCTFontFeatureSettingsAttribute, featureArray.get());
635     }
636
637 #if ENABLE(VARIATION_FONTS)
638     if (!variationsToBeApplied.isEmpty()) {
639         auto variationDictionary = adoptCF(CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks));
640         for (auto& p : variationsToBeApplied) {
641             long long bitwiseTag = p.key[0] << 24 | p.key[1] << 16 | p.key[2] << 8 | p.key[3];
642             auto tagNumber = adoptCF(CFNumberCreate(kCFAllocatorDefault, kCFNumberLongLongType, &bitwiseTag));
643             auto valueNumber = adoptCF(CFNumberCreate(kCFAllocatorDefault, kCFNumberFloatType, &p.value));
644             CFDictionarySetValue(variationDictionary.get(), tagNumber.get(), valueNumber.get());
645         }
646         CFDictionaryAddValue(attributes.get(), kCTFontVariationAttribute, variationDictionary.get());
647     }
648 #endif
649
650     if (textRenderingMode == OptimizeLegibility) {
651         CGFloat size = CTFontGetSize(originalFont);
652         auto sizeNumber = adoptCF(CFNumberCreate(kCFAllocatorDefault, kCFNumberCGFloatType, &size));
653         CFDictionaryAddValue(attributes.get(), kCTFontOpticalSizeAttribute, sizeNumber.get());
654     }
655     auto descriptor = adoptCF(CTFontDescriptorCreateWithAttributes(attributes.get()));
656     auto result = adoptCF(CTFontCreateCopyWithAttributes(originalFont, CTFontGetSize(originalFont), nullptr, descriptor.get()));
657     return result;
658 }
659
660 RefPtr<Font> FontCache::similarFont(const FontDescription& description, const AtomicString& family)
661 {
662     // Attempt to find an appropriate font using a match based on the presence of keywords in
663     // the requested names. For example, we'll match any name that contains "Arabic" to Geeza Pro.
664     if (family.isEmpty())
665         return nullptr;
666
667 #if PLATFORM(IOS)
668     // Substitute the default monospace font for well-known monospace fonts.
669     if (equalLettersIgnoringASCIICase(family, "monaco") || equalLettersIgnoringASCIICase(family, "menlo")) {
670         static NeverDestroyed<AtomicString> courier("courier", AtomicString::ConstructFromLiteral);
671         return fontForFamily(description, courier);
672     }
673
674     // Substitute Verdana for Lucida Grande.
675     if (equalLettersIgnoringASCIICase(family, "lucida grande")) {
676         static NeverDestroyed<AtomicString> verdana("verdana", AtomicString::ConstructFromLiteral);
677         return fontForFamily(description, verdana);
678     }
679 #endif
680
681     static NeverDestroyed<String> arabic(MAKE_STATIC_STRING_IMPL("Arabic"));
682     static NeverDestroyed<String> pashto(MAKE_STATIC_STRING_IMPL("Pashto"));
683     static NeverDestroyed<String> urdu(MAKE_STATIC_STRING_IMPL("Urdu"));
684     static String* matchWords[3] = { &arabic.get(), &pashto.get(), &urdu.get() };
685     static NeverDestroyed<AtomicString> geezaPlain("GeezaPro", AtomicString::ConstructFromLiteral);
686     static NeverDestroyed<AtomicString> geezaBold("GeezaPro-Bold", AtomicString::ConstructFromLiteral);
687     for (String* matchWord : matchWords) {
688         if (family.contains(*matchWord, false))
689             return fontForFamily(description, isFontWeightBold(description.weight()) ? geezaBold : geezaPlain);
690     }
691     return nullptr;
692 }
693
694 #if !HAS_CORE_TEXT_WIDTH_ATTRIBUTE
695 static float stretchFromCoreTextTraits(CFDictionaryRef traits)
696 {
697     auto widthNumber = static_cast<CFNumberRef>(CFDictionaryGetValue(traits, kCTFontWidthTrait));
698     if (!widthNumber)
699         return normalStretchValue();
700
701     float ctWidth;
702     auto success = CFNumberGetValue(widthNumber, kCFNumberFloatType, &ctWidth);
703     ASSERT_UNUSED(success, success);
704     return normalizeWidth(ctWidth);
705 }
706 #endif
707
708 static void invalidateFontCache();
709
710 static void fontCacheRegisteredFontsChangedNotificationCallback(CFNotificationCenterRef, void* observer, CFStringRef, const void *, CFDictionaryRef)
711 {
712     ASSERT_UNUSED(observer, observer == &FontCache::singleton());
713
714     invalidateFontCache();
715 }
716
717 void FontCache::platformInit()
718 {
719     CFNotificationCenterAddObserver(CFNotificationCenterGetLocalCenter(), this, &fontCacheRegisteredFontsChangedNotificationCallback, kCTFontManagerRegisteredFontsChangedNotification, nullptr, CFNotificationSuspensionBehaviorDeliverImmediately);
720
721 #if PLATFORM(MAC)
722     CFNotificationCenterRef center = CFNotificationCenterGetLocalCenter();
723     const CFStringRef notificationName = kCFLocaleCurrentLocaleDidChangeNotification;
724 #else
725     CFNotificationCenterRef center = CFNotificationCenterGetDarwinNotifyCenter();
726     const CFStringRef notificationName = CFSTR("com.apple.language.changed");
727 #endif
728     CFNotificationCenterAddObserver(center, this, &fontCacheRegisteredFontsChangedNotificationCallback, notificationName, nullptr, CFNotificationSuspensionBehaviorDeliverImmediately);
729 }
730
731 Vector<String> FontCache::systemFontFamilies()
732 {
733     // FIXME: <rdar://problem/21890188>
734     auto attributes = adoptCF(CFDictionaryCreate(kCFAllocatorDefault, nullptr, nullptr, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks));
735     auto emptyFontDescriptor = adoptCF(CTFontDescriptorCreateWithAttributes(attributes.get()));
736     auto matchedDescriptors = adoptCF(CTFontDescriptorCreateMatchingFontDescriptors(emptyFontDescriptor.get(), nullptr));
737     if (!matchedDescriptors)
738         return { };
739
740     CFIndex numMatches = CFArrayGetCount(matchedDescriptors.get());
741     if (!numMatches)
742         return { };
743
744     HashSet<String> visited;
745     for (CFIndex i = 0; i < numMatches; ++i) {
746         auto fontDescriptor = static_cast<CTFontDescriptorRef>(CFArrayGetValueAtIndex(matchedDescriptors.get(), i));
747         if (auto familyName = adoptCF(static_cast<CFStringRef>(CTFontDescriptorCopyAttribute(fontDescriptor, kCTFontFamilyNameAttribute))))
748             visited.add(familyName.get());
749     }
750
751     Vector<String> result;
752     copyToVector(visited, result);
753     return result;
754 }
755
756 static CTFontSymbolicTraits computeTraits(const FontDescription& fontDescription)
757 {
758     CTFontSymbolicTraits traits = 0;
759     if (fontDescription.italic())
760         traits |= kCTFontTraitItalic;
761     if (isFontWeightBold(fontDescription.weight()))
762         traits |= kCTFontTraitBold;
763     return traits;
764 }
765
766 SynthesisPair computeNecessarySynthesis(CTFontRef font, const FontDescription& fontDescription, bool isPlatformFont)
767 {
768 #if PLATFORM(IOS)
769     if (CTFontIsAppleColorEmoji(font))
770         return SynthesisPair(false, false);
771 #endif
772
773     if (isPlatformFont)
774         return SynthesisPair(false, false);
775
776     CTFontSymbolicTraits desiredTraits = computeTraits(fontDescription);
777
778     CTFontSymbolicTraits actualTraits = 0;
779     if (isFontWeightBold(fontDescription.weight()) || isItalic(fontDescription.italic()))
780         actualTraits = CTFontGetSymbolicTraits(font);
781
782     bool needsSyntheticBold = (fontDescription.fontSynthesis() & FontSynthesisWeight) && (desiredTraits & kCTFontTraitBold) && !(actualTraits & kCTFontTraitBold);
783     bool needsSyntheticOblique = (fontDescription.fontSynthesis() & FontSynthesisStyle) && (desiredTraits & kCTFontTraitItalic) && !(actualTraits & kCTFontTraitItalic);
784
785     return SynthesisPair(needsSyntheticBold, needsSyntheticOblique);
786 }
787
788 typedef HashSet<String, ASCIICaseInsensitiveHash> Whitelist;
789 static Whitelist& fontWhitelist()
790 {
791     static NeverDestroyed<Whitelist> whitelist;
792     return whitelist;
793 }
794
795 void FontCache::setFontWhitelist(const Vector<String>& inputWhitelist)
796 {
797     Whitelist& whitelist = fontWhitelist();
798     whitelist.clear();
799     for (auto& item : inputWhitelist)
800         whitelist.add(item);
801 }
802
803 static inline bool isSystemFont(const AtomicString& family)
804 {
805     // AtomicString's operator[] handles out-of-bounds by returning 0.
806     return family[0] == '.';
807 }
808
809 #if SHOULD_USE_CORE_TEXT_FONT_LOOKUP
810 static float fontWeightFromCoreText(CGFloat weight)
811 {
812     if (weight < -0.6)
813         return 100;
814     if (weight < -0.365)
815         return 200;
816     if (weight < -0.115)
817         return 300;
818     if (weight <  0.130)
819         return 400;
820     if (weight <  0.235)
821         return 500;
822     if (weight <  0.350)
823         return 600;
824     if (weight <  0.500)
825         return 700;
826     if (weight <  0.700)
827         return 800;
828     return 900;
829 }
830 #endif
831
832 class FontDatabase {
833 public:
834     static FontDatabase& singleton()
835     {
836         static NeverDestroyed<FontDatabase> database;
837         return database;
838     }
839
840     FontDatabase(const FontDatabase&) = delete;
841     FontDatabase& operator=(const FontDatabase&) = delete;
842
843     struct InstalledFont {
844         InstalledFont() = default;
845
846         InstalledFont(CTFontDescriptorRef fontDescriptor)
847             : fontDescriptor(fontDescriptor)
848             , capabilities(capabilitiesForFontDescriptor(fontDescriptor))
849         {
850         }
851
852         RetainPtr<CTFontDescriptorRef> fontDescriptor;
853         FontSelectionCapabilities capabilities;
854     };
855
856     struct InstalledFontFamily {
857         InstalledFontFamily() = default;
858
859         explicit InstalledFontFamily(Vector<InstalledFont>&& installedFonts)
860             : installedFonts(WTFMove(installedFonts))
861         {
862             for (auto& font : this->installedFonts)
863                 expand(font);
864         }
865
866         void expand(const InstalledFont& installedFont)
867         {
868             capabilities.expand(installedFont.capabilities);
869         }
870
871         bool isEmpty() const
872         {
873             return installedFonts.isEmpty();
874         }
875
876         size_t size() const
877         {
878             return installedFonts.size();
879         }
880
881         Vector<InstalledFont> installedFonts;
882         FontSelectionCapabilities capabilities;
883     };
884
885     const InstalledFontFamily& collectionForFamily(const String& familyName)
886     {
887         auto folded = familyName.foldCase();
888         return m_familyNameToFontDescriptors.ensure(folded, [&] {
889             auto familyNameString = folded.createCFString();
890             CFTypeRef keys[] = { kCTFontFamilyNameAttribute };
891             CFTypeRef values[] = { familyNameString.get() };
892             auto attributes = adoptCF(CFDictionaryCreate(kCFAllocatorDefault, keys, values, WTF_ARRAY_LENGTH(keys), &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks));
893             auto fontDescriptorToMatch = adoptCF(CTFontDescriptorCreateWithAttributes(attributes.get()));
894             if (auto matches = adoptCF(CTFontDescriptorCreateMatchingFontDescriptors(fontDescriptorToMatch.get(), nullptr))) {
895                 auto count = CFArrayGetCount(matches.get());
896                 Vector<InstalledFont> result;
897                 result.reserveInitialCapacity(count);
898                 for (CFIndex i = 0; i < count; ++i) {
899                     InstalledFont installedFont(static_cast<CTFontDescriptorRef>(CFArrayGetValueAtIndex(matches.get(), i)));
900                     result.uncheckedAppend(WTFMove(installedFont));
901                 }
902                 return InstalledFontFamily(WTFMove(result));
903             }
904             return InstalledFontFamily();
905         }).iterator->value;
906     }
907
908     const InstalledFont& fontForPostScriptName(const AtomicString& postScriptName)
909     {
910         auto folded = postScriptName.string().foldCase();
911         return m_postScriptNameToFontDescriptors.ensure(folded, [&] {
912             auto postScriptNameString = folded.createCFString();
913             CFTypeRef keys[] = { kCTFontEnabledAttribute, kCTFontNameAttribute };
914             CFTypeRef values[] = { kCFBooleanTrue, postScriptNameString.get() };
915             auto attributes = adoptCF(CFDictionaryCreate(kCFAllocatorDefault, keys, values, WTF_ARRAY_LENGTH(keys), &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks));
916             auto fontDescriptorToMatch = adoptCF(CTFontDescriptorCreateWithAttributes(attributes.get()));
917             auto match = adoptCF(static_cast<CTFontDescriptorRef>(CTFontDescriptorCreateMatchingFontDescriptor(fontDescriptorToMatch.get(), nullptr)));
918             return InstalledFont(match.get());
919         }).iterator->value;
920     }
921
922     void clear()
923     {
924         m_familyNameToFontDescriptors.clear();
925         m_postScriptNameToFontDescriptors.clear();
926     }
927
928 private:
929     friend class NeverDestroyed<FontDatabase>;
930
931     FontDatabase() = default;
932
933     HashMap<String, InstalledFontFamily> m_familyNameToFontDescriptors;
934     HashMap<String, InstalledFont> m_postScriptNameToFontDescriptors;
935 };
936
937 // Because this struct holds intermediate values which may be in the compressed -1 - 1 GX range, we don't want to use the relatively large
938 // quantization of FontSelectionValue. Instead, do this logic with floats.
939 struct MinMax {
940     float minimum;
941     float maximum;
942 };
943
944 struct VariationCapabilities {
945     std::optional<MinMax> weight;
946     std::optional<MinMax> width;
947     std::optional<MinMax> slope;
948 };
949
950 #if ENABLE(VARIATION_FONTS)
951 static std::optional<MinMax> extractVariationBounds(CFDictionaryRef axis)
952 {
953     CFNumberRef minimumValue = static_cast<CFNumberRef>(CFDictionaryGetValue(axis, kCTFontVariationAxisMinimumValueKey));
954     CFNumberRef maximumValue = static_cast<CFNumberRef>(CFDictionaryGetValue(axis, kCTFontVariationAxisMaximumValueKey));
955     float rawMinimumValue = 0;
956     float rawMaximumValue = 0;
957     CFNumberGetValue(minimumValue, kCFNumberFloatType, &rawMinimumValue);
958     CFNumberGetValue(maximumValue, kCFNumberFloatType, &rawMaximumValue);
959     if (rawMinimumValue < rawMaximumValue)
960         return {{ rawMinimumValue, rawMaximumValue }};
961     return std::nullopt;
962 }
963 #endif
964
965 static VariationCapabilities variationCapabilitiesForFontDescriptor(CTFontDescriptorRef fontDescriptor)
966 {
967     VariationCapabilities result;
968
969 #if ENABLE(VARIATION_FONTS)
970     if (!adoptCF(CTFontDescriptorCopyAttribute(fontDescriptor, kCTFontVariationAttribute)))
971         return result;
972
973     auto font = adoptCF(CTFontCreateWithFontDescriptor(fontDescriptor, 0, nullptr));
974     auto variations = adoptCF(CTFontCopyVariationAxes(font.get()));
975     if (!variations)
976         return result;
977
978     auto axisCount = CFArrayGetCount(variations.get());
979     if (!axisCount)
980         return result;
981
982     for (CFIndex i = 0; i < axisCount; ++i) {
983         CFDictionaryRef axis = static_cast<CFDictionaryRef>(CFArrayGetValueAtIndex(variations.get(), i));
984         CFNumberRef axisIdentifier = static_cast<CFNumberRef>(CFDictionaryGetValue(axis, kCTFontVariationAxisIdentifierKey));
985         uint32_t rawAxisIdentifier = 0;
986         Boolean success = CFNumberGetValue(axisIdentifier, kCFNumberSInt32Type, &rawAxisIdentifier);
987         ASSERT_UNUSED(success, success);
988         if (rawAxisIdentifier == 0x77676874) // 'wght'
989             result.weight = extractVariationBounds(axis);
990         else if (rawAxisIdentifier == 0x77647468) // 'wdth'
991             result.width = extractVariationBounds(axis);
992         else if (rawAxisIdentifier == 0x736C6E74) // 'slnt'
993             result.slope = extractVariationBounds(axis);
994     }
995
996     if (FontType(font.get()).variationType == FontType::VariationType::TrueTypeGX) {
997         if (result.weight)
998             result.weight = {{ normalizeWeight(result.weight.value().minimum), normalizeWeight(result.weight.value().maximum) }};
999         if (result.width)
1000             result.width = {{ normalizeVariationWidth(result.width.value().minimum), normalizeVariationWidth(result.width.value().maximum) }};
1001         if (result.slope)
1002             result.slope = {{ normalizeSlope(result.slope.value().minimum), normalizeSlope(result.slope.value().maximum) }};
1003     }
1004 #else
1005     UNUSED_PARAM(fontDescriptor);
1006 #endif
1007
1008     return result;
1009 }
1010
1011 #if !SHOULD_USE_CORE_TEXT_FONT_LOOKUP || HAS_CORE_TEXT_WIDTH_ATTRIBUTE
1012 static float getCSSAttribute(CTFontDescriptorRef fontDescriptor, const CFStringRef attribute, float fallback)
1013 {
1014     auto number = adoptCF(static_cast<CFNumberRef>(CTFontDescriptorCopyAttribute(fontDescriptor, attribute)));
1015     if (!number)
1016         return fallback;
1017     float cssValue;
1018     auto success = CFNumberGetValue(number.get(), kCFNumberFloatType, &cssValue);
1019     ASSERT_UNUSED(success, success);
1020     return cssValue;
1021 }
1022 #endif
1023
1024 FontSelectionCapabilities capabilitiesForFontDescriptor(CTFontDescriptorRef fontDescriptor)
1025 {
1026     if (!fontDescriptor)
1027         return { };
1028
1029     VariationCapabilities variationCapabilities = variationCapabilitiesForFontDescriptor(fontDescriptor);
1030
1031 #if SHOULD_USE_CORE_TEXT_FONT_LOOKUP || !HAS_CORE_TEXT_WIDTH_ATTRIBUTE
1032     bool weightOrWidthComeFromTraits = !variationCapabilities.weight || !variationCapabilities.width;
1033 #else
1034     bool weightOrWidthComeFromTraits = false;
1035 #endif
1036
1037     if (!variationCapabilities.slope || weightOrWidthComeFromTraits) {
1038         auto traits = adoptCF(static_cast<CFDictionaryRef>(CTFontDescriptorCopyAttribute(fontDescriptor, kCTFontTraitsAttribute)));
1039         if (traits) {
1040 #if !HAS_CORE_TEXT_WIDTH_ATTRIBUTE
1041             if (!variationCapabilities.width) {
1042                 auto widthValue = stretchFromCoreTextTraits(traits.get());
1043                 variationCapabilities.width = {{ widthValue, widthValue }};
1044             }
1045 #endif
1046
1047             if (!variationCapabilities.slope) {
1048                 auto symbolicTraitsNumber = static_cast<CFNumberRef>(CFDictionaryGetValue(traits.get(), kCTFontSymbolicTrait));
1049                 if (symbolicTraitsNumber) {
1050                     int32_t symbolicTraits;
1051                     auto success = CFNumberGetValue(symbolicTraitsNumber, kCFNumberSInt32Type, &symbolicTraits);
1052                     ASSERT_UNUSED(success, success);
1053                     auto slopeValue = static_cast<float>(symbolicTraits & kCTFontTraitItalic ? italicValue() : normalItalicValue());
1054                     variationCapabilities.slope = {{ slopeValue, slopeValue }};
1055                 } else
1056                     variationCapabilities.slope = {{ static_cast<float>(normalItalicValue()), static_cast<float>(normalItalicValue()) }};
1057             }
1058
1059 #if SHOULD_USE_CORE_TEXT_FONT_LOOKUP
1060             if (!variationCapabilities.weight) {
1061                 auto weightNumber = static_cast<CFNumberRef>(CFDictionaryGetValue(traits.get(), kCTFontWeightTrait));
1062                 if (weightNumber) {
1063                     CGFloat ctWeight;
1064                     auto success = CFNumberGetValue(weightNumber, kCFNumberCGFloatType, &ctWeight);
1065                     ASSERT_UNUSED(success, success);
1066                     auto weightValue = fontWeightFromCoreText(ctWeight);
1067                     variationCapabilities.weight = {{ weightValue, weightValue }};
1068                 } else
1069                     variationCapabilities.weight = {{ static_cast<float>(normalWeightValue()), static_cast<float>(normalWeightValue()) }};
1070             }
1071 #endif
1072         }
1073     }
1074
1075 #if !SHOULD_USE_CORE_TEXT_FONT_LOOKUP
1076     if (!variationCapabilities.weight) {
1077         auto value = getCSSAttribute(fontDescriptor, kCTFontCSSWeightAttribute, static_cast<float>(normalWeightValue()));
1078         variationCapabilities.weight = {{ value, value }};
1079     }
1080 #endif
1081
1082 #if HAS_CORE_TEXT_WIDTH_ATTRIBUTE
1083     if (!variationCapabilities.width) {
1084         auto value = getCSSAttribute(fontDescriptor, kCTFontCSSWidthAttribute, static_cast<float>(normalStretchValue()));
1085         variationCapabilities.width = {{ value, value }};
1086     }
1087 #endif
1088
1089     return {{ FontSelectionValue(variationCapabilities.weight.value().minimum), FontSelectionValue(variationCapabilities.weight.value().maximum) },
1090         { FontSelectionValue(variationCapabilities.width.value().minimum), FontSelectionValue(variationCapabilities.width.value().maximum) },
1091         { FontSelectionValue(variationCapabilities.slope.value().minimum), FontSelectionValue(variationCapabilities.slope.value().maximum) }};
1092 }
1093
1094 #if !SHOULD_USE_CORE_TEXT_FONT_LOOKUP
1095 static const FontDatabase::InstalledFont* findClosestFont(const FontDatabase::InstalledFontFamily& familyFonts, FontSelectionRequest fontSelectionRequest)
1096 {
1097     Vector<FontSelectionCapabilities> capabilities;
1098     capabilities.reserveInitialCapacity(familyFonts.size());
1099     for (auto& font : familyFonts.installedFonts)
1100         capabilities.uncheckedAppend(font.capabilities);
1101     FontSelectionAlgorithm fontSelectionAlgorithm(fontSelectionRequest, capabilities, familyFonts.capabilities);
1102     return &familyFonts.installedFonts[fontSelectionAlgorithm.indexOfBestCapabilities()];
1103 }
1104 #endif
1105
1106 Vector<FontSelectionCapabilities> FontCache::getFontSelectionCapabilitiesInFamily(const AtomicString& familyName)
1107 {
1108     const auto& fonts = FontDatabase::singleton().collectionForFamily(familyName.string());
1109     if (fonts.isEmpty())
1110         return { };
1111
1112     Vector<FontSelectionCapabilities> result;
1113     result.reserveInitialCapacity(fonts.size());
1114     for (const auto& font : fonts.installedFonts)
1115         result.uncheckedAppend(font.capabilities);
1116     return result;
1117 }
1118
1119 struct FontLookup {
1120     RetainPtr<CTFontRef> result;
1121     bool createdFromPostScriptName { false };
1122 };
1123
1124 static FontLookup platformFontLookupWithFamily(const AtomicString& family, FontSelectionRequest request, float size)
1125 {
1126     const auto& whitelist = fontWhitelist();
1127     if (!isSystemFont(family) && whitelist.size() && !whitelist.contains(family))
1128         return { nullptr };
1129
1130 #if SHOULD_USE_CORE_TEXT_FONT_LOOKUP
1131     CTFontSymbolicTraits traits = (isFontWeightBold(request.weight) ? kCTFontTraitBold : 0) | (isItalic(request.slope) ? kCTFontTraitItalic : 0);
1132     return { adoptCF(CTFontCreateForCSS(family.string().createCFString().get(), static_cast<float>(request.weight), traits, size)) };
1133 #else
1134     const auto& familyFonts = FontDatabase::singleton().collectionForFamily(family.string());
1135     if (familyFonts.isEmpty()) {
1136         // The CSS spec states that font-family only accepts a name of an actual font family. However, in WebKit, we claim to also
1137         // support supplying a PostScript name instead. However, this creates problems when the other properties (font-weight,
1138         // font-style) disagree with the traits of the PostScript-named font. The solution we have come up with is, when the default
1139         // values for font-weight and font-style are supplied, honor the PostScript name, but if font-weight specifies bold or
1140         // font-style specifies italic, then we run the regular matching algorithm on the family of the PostScript font. This way,
1141         // if content simply states "font-family: PostScriptName;" without specifying the other font properties, it will be honored,
1142         // but if a <b> appears as a descendent element, it will be honored too.
1143         const auto& postScriptFont = FontDatabase::singleton().fontForPostScriptName(family);
1144         if (!postScriptFont.fontDescriptor)
1145             return { nullptr };
1146         if ((isItalic(request.slope) && !isItalic(postScriptFont.capabilities.slope.maximum))
1147             || (isFontWeightBold(request.weight) && !isFontWeightBold(postScriptFont.capabilities.weight.maximum))) {
1148             auto postScriptFamilyName = adoptCF(static_cast<CFStringRef>(CTFontDescriptorCopyAttribute(postScriptFont.fontDescriptor.get(), kCTFontFamilyNameAttribute)));
1149             if (!postScriptFamilyName)
1150                 return { nullptr };
1151             const auto& familyFonts = FontDatabase::singleton().collectionForFamily(String(postScriptFamilyName.get()));
1152             if (familyFonts.isEmpty())
1153                 return { nullptr };
1154             if (const auto* installedFont = findClosestFont(familyFonts, request)) {
1155                 if (!installedFont->fontDescriptor)
1156                     return { nullptr };
1157                 return { adoptCF(CTFontCreateWithFontDescriptor(installedFont->fontDescriptor.get(), size, nullptr)), true };
1158             }
1159             return { nullptr };
1160         }
1161         return { adoptCF(CTFontCreateWithFontDescriptor(postScriptFont.fontDescriptor.get(), size, nullptr)), true };
1162     }
1163
1164     if (const auto* installedFont = findClosestFont(familyFonts, request))
1165         return { adoptCF(CTFontCreateWithFontDescriptor(installedFont->fontDescriptor.get(), size, nullptr)), false };
1166
1167     return { nullptr };
1168 #endif
1169 }
1170
1171 static void invalidateFontCache()
1172 {
1173     if (!isMainThread()) {
1174         callOnMainThread([] {
1175             invalidateFontCache();
1176         });
1177         return;
1178     }
1179
1180     FontDescription::invalidateCaches();
1181
1182     FontDatabase::singleton().clear();
1183
1184     FontCache::singleton().invalidate();
1185 }
1186
1187 static RetainPtr<CTFontRef> fontWithFamily(const AtomicString& family, const FontDescription& fontDescription, const FontFeatureSettings* fontFaceFeatures, const FontVariantSettings* fontFaceVariantSettings, FontSelectionSpecifiedCapabilities fontFaceCapabilities, float size)
1188 {
1189     if (family.isEmpty())
1190         return nullptr;
1191
1192     const auto& request = fontDescription.fontSelectionRequest();
1193     FontLookup fontLookup;
1194     fontLookup.result = platformFontWithFamilySpecialCase(family, request, size);
1195     if (!fontLookup.result)
1196         fontLookup = platformFontLookupWithFamily(family, request, size);
1197     return preparePlatformFont(fontLookup.result.get(), fontDescription, fontFaceFeatures, fontFaceVariantSettings, fontFaceCapabilities, size, !fontLookup.createdFromPostScriptName);
1198 }
1199
1200 #if PLATFORM(MAC)
1201 static bool shouldAutoActivateFontIfNeeded(const AtomicString& family)
1202 {
1203 #ifndef NDEBUG
1204     // This cache is not thread safe so the following assertion is there to
1205     // make sure this function is always called from the same thread.
1206     static ThreadIdentifier initThreadId = currentThread();
1207     ASSERT(currentThread() == initThreadId);
1208 #endif
1209
1210     static NeverDestroyed<HashSet<AtomicString>> knownFamilies;
1211     static const unsigned maxCacheSize = 128;
1212     ASSERT(knownFamilies.get().size() <= maxCacheSize);
1213     if (knownFamilies.get().size() == maxCacheSize)
1214         knownFamilies.get().remove(knownFamilies.get().begin());
1215
1216     // Only attempt to auto-activate fonts once for performance reasons.
1217     return knownFamilies.get().add(family).isNewEntry;
1218 }
1219
1220 static void autoActivateFont(const String& name, CGFloat size)
1221 {
1222     auto fontName = name.createCFString();
1223     CFTypeRef keys[] = { kCTFontNameAttribute };
1224     CFTypeRef values[] = { fontName.get() };
1225     auto attributes = adoptCF(CFDictionaryCreate(kCFAllocatorDefault, keys, values, WTF_ARRAY_LENGTH(keys), &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks));
1226     auto descriptor = adoptCF(CTFontDescriptorCreateWithAttributes(attributes.get()));
1227     if (auto newFont = CTFontCreateWithFontDescriptor(descriptor.get(), size, nullptr))
1228         CFRelease(newFont);
1229 }
1230 #endif
1231
1232 std::unique_ptr<FontPlatformData> FontCache::createFontPlatformData(const FontDescription& fontDescription, const AtomicString& family, const FontFeatureSettings* fontFaceFeatures, const FontVariantSettings* fontFaceVariantSettings, FontSelectionSpecifiedCapabilities fontFaceCapabilities)
1233 {
1234     float size = fontDescription.computedPixelSize();
1235
1236     auto font = fontWithFamily(family, fontDescription, fontFaceFeatures, fontFaceVariantSettings, fontFaceCapabilities, size);
1237
1238 #if PLATFORM(MAC)
1239     if (!font) {
1240         if (!shouldAutoActivateFontIfNeeded(family))
1241             return nullptr;
1242
1243         // Auto activate the font before looking for it a second time.
1244         // Ignore the result because we want to use our own algorithm to actually find the font.
1245         autoActivateFont(family.string(), size);
1246
1247         font = fontWithFamily(family, fontDescription, fontFaceFeatures, fontFaceVariantSettings, fontFaceCapabilities, size);
1248     }
1249 #endif
1250
1251     if (!font)
1252         return nullptr;
1253
1254     bool syntheticBold, syntheticOblique;
1255     std::tie(syntheticBold, syntheticOblique) = computeNecessarySynthesis(font.get(), fontDescription).boldObliquePair();
1256
1257     return std::make_unique<FontPlatformData>(font.get(), size, syntheticBold, syntheticOblique, fontDescription.orientation(), fontDescription.widthVariant(), fontDescription.textRenderingMode());
1258 }
1259
1260 typedef HashSet<RetainPtr<CTFontRef>, WTF::RetainPtrObjectHash<CTFontRef>, WTF::RetainPtrObjectHashTraits<CTFontRef>> FallbackDedupSet;
1261 static FallbackDedupSet& fallbackDedupSet()
1262 {
1263     static NeverDestroyed<FallbackDedupSet> dedupSet;
1264     return dedupSet.get();
1265 }
1266
1267 void FontCache::platformPurgeInactiveFontData()
1268 {
1269     Vector<CTFontRef> toRemove;
1270     for (auto& font : fallbackDedupSet()) {
1271         if (CFGetRetainCount(font.get()) == 1)
1272             toRemove.append(font.get());
1273     }
1274     for (auto& font : toRemove)
1275         fallbackDedupSet().remove(font);
1276 }
1277
1278 #if PLATFORM(IOS)
1279 static inline bool isArabicCharacter(UChar character)
1280 {
1281     return character >= 0x0600 && character <= 0x06FF;
1282 }
1283 #endif
1284
1285 static RetainPtr<CTFontRef> lookupFallbackFont(CTFontRef font, FontSelectionValue fontWeight, const AtomicString& locale, const UChar* characters, unsigned length)
1286 {
1287     ASSERT(length > 0);
1288
1289     RetainPtr<CFStringRef> localeString;
1290 #if (PLATFORM(IOS) && TARGET_OS_IOS) || (PLATFORM(MAC) && __MAC_OS_X_VERSION_MIN_REQUIRED >= 101200)
1291     if (!locale.isNull())
1292         localeString = locale.string().createCFString();
1293 #else
1294     UNUSED_PARAM(locale);
1295 #endif
1296
1297     CFIndex coveredLength = 0;
1298     RetainPtr<CTFontRef> result;
1299 #if !USE_PLATFORM_SYSTEM_FALLBACK_LIST && ((PLATFORM(MAC) && __MAC_OS_X_VERSION_MIN_REQUIRED >= 101200) || (PLATFORM(IOS) && TARGET_OS_IOS))
1300     result = adoptCF(CTFontCreatePhysicalFontForCharactersWithLanguage(font, characters, length, localeString.get(), &coveredLength));
1301 #else
1302     result = adoptCF(CTFontCreateForCharactersWithLanguage(font, characters, length, localeString.get(), &coveredLength));
1303 #endif
1304
1305 #if PLATFORM(IOS)
1306     // Callers of this function won't include multiple code points. "Length" is to know how many code units
1307     // are in the code point.
1308     UChar firstCharacter = characters[0];
1309     if (isArabicCharacter(firstCharacter)) {
1310         auto familyName = adoptCF(static_cast<CFStringRef>(CTFontCopyAttribute(result.get(), kCTFontFamilyNameAttribute)));
1311         if (fontFamilyShouldNotBeUsedForArabic(familyName.get())) {
1312             CFStringRef newFamilyName = isFontWeightBold(fontWeight) ? CFSTR("GeezaPro-Bold") : CFSTR("GeezaPro");
1313             CFTypeRef keys[] = { kCTFontNameAttribute };
1314             CFTypeRef values[] = { newFamilyName };
1315             auto attributes = adoptCF(CFDictionaryCreate(kCFAllocatorDefault, keys, values, WTF_ARRAY_LENGTH(keys), &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks));
1316             auto modification = adoptCF(CTFontDescriptorCreateWithAttributes(attributes.get()));
1317             result = adoptCF(CTFontCreateCopyWithAttributes(result.get(), CTFontGetSize(result.get()), nullptr, modification.get()));
1318         }
1319     }
1320 #else
1321     UNUSED_PARAM(fontWeight);
1322 #endif
1323
1324     return result;
1325 }
1326
1327 RefPtr<Font> FontCache::systemFallbackForCharacters(const FontDescription& description, const Font* originalFontData, bool isPlatformFont, const UChar* characters, unsigned length)
1328 {
1329 #if PLATFORM(IOS)
1330     if (length && requiresCustomFallbackFont(*characters)) {
1331         auto* fallback = getCustomFallbackFont(*characters, description);
1332         if (!fallback)
1333             return nullptr;
1334         return fontForPlatformData(*fallback);
1335     }
1336 #endif
1337
1338     const FontPlatformData& platformData = originalFontData->platformData();
1339     auto result = lookupFallbackFont(platformData.font(), description.weight(), description.locale(), characters, length);
1340     result = preparePlatformFont(result.get(), description, nullptr, nullptr, { }, description.computedSize());
1341     if (!result)
1342         return lastResortFallbackFont(description);
1343
1344     // FontCascade::drawGlyphBuffer() requires that there are no duplicate Font objects which refer to the same thing. This is enforced in
1345     // FontCache::fontForPlatformData(), where our equality check is based on hashing the FontPlatformData, whose hash includes the raw CoreText
1346     // font pointer.
1347     CTFontRef substituteFont = fallbackDedupSet().add(result).iterator->get();
1348
1349     bool syntheticBold, syntheticOblique;
1350     std::tie(syntheticBold, syntheticOblique) = computeNecessarySynthesis(substituteFont, description, isPlatformFont).boldObliquePair();
1351
1352     FontPlatformData alternateFont(substituteFont, platformData.size(), syntheticBold, syntheticOblique, platformData.orientation(), platformData.widthVariant(), platformData.textRenderingMode());
1353
1354     return fontForPlatformData(alternateFont);
1355 }
1356
1357 const AtomicString& FontCache::platformAlternateFamilyName(const AtomicString& familyName)
1358 {
1359     static const UChar heitiString[] = { 0x9ed1, 0x4f53 };
1360     static const UChar songtiString[] = { 0x5b8b, 0x4f53 };
1361     static const UChar weiruanXinXiMingTi[] = { 0x5fae, 0x8edf, 0x65b0, 0x7d30, 0x660e, 0x9ad4 };
1362     static const UChar weiruanYaHeiString[] = { 0x5fae, 0x8f6f, 0x96c5, 0x9ed1 };
1363     static const UChar weiruanZhengHeitiString[] = { 0x5fae, 0x8edf, 0x6b63, 0x9ed1, 0x9ad4 };
1364
1365     static NeverDestroyed<AtomicString> songtiSC("Songti SC", AtomicString::ConstructFromLiteral);
1366     static NeverDestroyed<AtomicString> songtiTC("Songti TC", AtomicString::ConstructFromLiteral);
1367     static NeverDestroyed<AtomicString> heitiSCReplacement("PingFang SC", AtomicString::ConstructFromLiteral);
1368     static NeverDestroyed<AtomicString> heitiTCReplacement("PingFang TC", AtomicString::ConstructFromLiteral);
1369
1370     switch (familyName.length()) {
1371     case 2:
1372         if (equal(familyName, songtiString))
1373             return songtiSC;
1374         if (equal(familyName, heitiString))
1375             return heitiSCReplacement;
1376         break;
1377     case 4:
1378         if (equal(familyName, weiruanYaHeiString))
1379             return heitiSCReplacement;
1380         break;
1381     case 5:
1382         if (equal(familyName, weiruanZhengHeitiString))
1383             return heitiTCReplacement;
1384         break;
1385     case 6:
1386         if (equalLettersIgnoringASCIICase(familyName, "simsun"))
1387             return songtiSC;
1388         if (equal(familyName, weiruanXinXiMingTi))
1389             return songtiTC;
1390         break;
1391     case 10:
1392         if (equalLettersIgnoringASCIICase(familyName, "ms mingliu"))
1393             return songtiTC;
1394         if (equalIgnoringASCIICase(familyName, "\\5b8b\\4f53"))
1395             return songtiSC;
1396         break;
1397     case 18:
1398         if (equalLettersIgnoringASCIICase(familyName, "microsoft jhenghei"))
1399             return heitiTCReplacement;
1400         break;
1401     }
1402
1403     return nullAtom;
1404 }
1405
1406 }