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