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