Addressing post-review comments on r213163
[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) || PLATFORM(IOS))
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
424 #define WORKAROUND_CORETEXT_VARIATIONS_UNSPECIFIED_VALUE_BUG ((PLATFORM(MAC) && __MAC_OS_X_VERSION_MIN_REQUIRED < 101300) || (PLATFORM(IOS) && __IPHONE_OS_VERSION_MIN_REQUIRED < 110000))
425 #if ENABLE(VARIATION_FONTS) && WORKAROUND_CORETEXT_VARIATIONS_UNSPECIFIED_VALUE_BUG
426 static inline bool fontIsSystemFont(CTFontRef font)
427 {
428     if (CTFontDescriptorIsSystemUIFont(adoptCF(CTFontCopyFontDescriptor(font)).get()))
429         return true;
430     auto name = adoptCF(CTFontCopyPostScriptName(font));
431     return CFStringGetLength(name.get()) > 0 && CFStringGetCharacterAtIndex(name.get(), 0) == '.';
432 }
433 #endif
434
435 RetainPtr<CTFontRef> preparePlatformFont(CTFontRef originalFont, TextRenderingMode textRenderingMode, const FontFeatureSettings* fontFaceFeatures, const FontVariantSettings* fontFaceVariantSettings, const FontFeatureSettings& features, const FontVariantSettings& variantSettings, const FontVariationSettings& variations)
436 {
437     bool alwaysAddVariations = false;
438
439     // FIXME: Remove when <rdar://problem/29859207> is fixed
440 #if ENABLE(VARIATION_FONTS)
441     auto defaultValues = defaultVariationValues(originalFont);
442 #if WORKAROUND_CORETEXT_VARIATIONS_UNSPECIFIED_VALUE_BUG
443     alwaysAddVariations = !defaultValues.isEmpty();
444 #endif
445 #endif
446
447     if (!originalFont || (!features.size() && (!alwaysAddVariations && variations.isEmpty()) && (textRenderingMode == AutoTextRendering) && variantSettings.isAllNormal()
448         && (!fontFaceFeatures || !fontFaceFeatures->size()) && (!fontFaceVariantSettings || fontFaceVariantSettings->isAllNormal())))
449         return originalFont;
450
451     // This algorithm is described at http://www.w3.org/TR/css3-fonts/#feature-precedence
452     FeaturesMap featuresToBeApplied;
453
454     // Step 1: CoreText handles default features (such as required ligatures).
455
456     // Step 2: Consult with font-variant-* inside @font-face
457     if (fontFaceVariantSettings)
458         featuresToBeApplied = computeFeatureSettingsFromVariants(*fontFaceVariantSettings);
459
460     // Step 3: Consult with font-feature-settings inside @font-face
461     if (fontFaceFeatures) {
462         for (auto& fontFaceFeature : *fontFaceFeatures)
463             featuresToBeApplied.set(fontFaceFeature.tag(), fontFaceFeature.value());
464     }
465
466     // Step 4: Font-variant
467     for (auto& newFeature : computeFeatureSettingsFromVariants(variantSettings))
468         featuresToBeApplied.set(newFeature.key, newFeature.value);
469
470     // Step 5: Other properties (text-rendering)
471     if (textRenderingMode == OptimizeSpeed) {
472         featuresToBeApplied.set(fontFeatureTag("liga"), 0);
473         featuresToBeApplied.set(fontFeatureTag("clig"), 0);
474         featuresToBeApplied.set(fontFeatureTag("dlig"), 0);
475         featuresToBeApplied.set(fontFeatureTag("hlig"), 0);
476         featuresToBeApplied.set(fontFeatureTag("calt"), 0);
477     }
478
479     // Step 6: Font-feature-settings
480     for (auto& newFeature : features)
481         featuresToBeApplied.set(newFeature.tag(), newFeature.value());
482
483 #if ENABLE(VARIATION_FONTS)
484     VariationsMap variationsToBeApplied;
485
486     auto applyVariationValue = [&](const FontTag& tag, float value, bool isDefaultValue) {
487         // FIXME: Remove when <rdar://problem/28707822> is fixed
488 #define WORKAROUND_CORETEXT_VARIATIONS_DEFAULT_VALUE_BUG ((PLATFORM(MAC) && __MAC_OS_X_VERSION_MIN_REQUIRED < 101300) || (PLATFORM(IOS) && __IPHONE_OS_VERSION_MIN_REQUIRED < 110000))
489 #if WORKAROUND_CORETEXT_VARIATIONS_DEFAULT_VALUE_BUG
490         if (isDefaultValue)
491             value += 0.0001;
492 #else
493         UNUSED_PARAM(isDefaultValue);
494 #endif
495 #undef WORKAROUND_CORETEXT_VARIATIONS_DEFAULT_VALUE_BUG
496         variationsToBeApplied.set(tag, value);
497     };
498
499     for (auto& newVariation : variations) {
500         auto iterator = defaultValues.find(newVariation.tag());
501         if (iterator == defaultValues.end())
502             continue;
503         float valueToApply = clampTo(newVariation.value(), iterator->value.minimumValue, iterator->value.maximumValue);
504         bool isDefaultValue = valueToApply == iterator->value.defaultValue;
505         applyVariationValue(newVariation.tag(), valueToApply, isDefaultValue);
506     }
507
508 #if WORKAROUND_CORETEXT_VARIATIONS_UNSPECIFIED_VALUE_BUG
509     if (!fontIsSystemFont(originalFont)) {
510         for (auto& defaultValue : defaultValues) {
511             if (!variationsToBeApplied.contains(defaultValue.key))
512                 applyVariationValue(defaultValue.key, defaultValue.value.defaultValue, true);
513         }
514     }
515 #endif
516 #undef WORKAROUND_CORETEXT_VARIATIONS_UNSPECIFIED_VALUE_BUG
517
518 #endif // ENABLE(VARIATION_FONTS)
519
520     auto attributes = adoptCF(CFDictionaryCreateMutable(kCFAllocatorDefault, 2, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks));
521     if (!featuresToBeApplied.isEmpty()) {
522         auto featureArray = adoptCF(CFArrayCreateMutable(kCFAllocatorDefault, features.size(), &kCFTypeArrayCallBacks));
523         for (auto& p : featuresToBeApplied) {
524             auto feature = FontFeature(p.key, p.value);
525             appendTrueTypeFeature(featureArray.get(), feature);
526             appendOpenTypeFeature(featureArray.get(), feature);
527         }
528         CFDictionaryAddValue(attributes.get(), kCTFontFeatureSettingsAttribute, featureArray.get());
529     }
530
531 #if ENABLE(VARIATION_FONTS)
532     if (!variationsToBeApplied.isEmpty()) {
533         auto variationDictionary = adoptCF(CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks));
534         for (auto& p : variationsToBeApplied) {
535             long long bitwiseTag = p.key[0] << 24 | p.key[1] << 16 | p.key[2] << 8 | p.key[3];
536             auto tagNumber = adoptCF(CFNumberCreate(kCFAllocatorDefault, kCFNumberLongLongType, &bitwiseTag));
537             auto valueNumber = adoptCF(CFNumberCreate(kCFAllocatorDefault, kCFNumberFloatType, &p.value));
538             CFDictionarySetValue(variationDictionary.get(), tagNumber.get(), valueNumber.get());
539         }
540         CFDictionaryAddValue(attributes.get(), kCTFontVariationAttribute, variationDictionary.get());
541     }
542 #endif
543
544     if (textRenderingMode == OptimizeLegibility) {
545         CGFloat size = CTFontGetSize(originalFont);
546         auto sizeNumber = adoptCF(CFNumberCreate(kCFAllocatorDefault, kCFNumberCGFloatType, &size));
547         CFDictionaryAddValue(attributes.get(), kCTFontOpticalSizeAttribute, sizeNumber.get());
548     }
549     auto descriptor = adoptCF(CTFontDescriptorCreateWithAttributes(attributes.get()));
550     auto result = adoptCF(CTFontCreateCopyWithAttributes(originalFont, CTFontGetSize(originalFont), nullptr, descriptor.get()));
551     return result;
552 }
553
554 FontWeight fontWeightFromCoreText(CGFloat weight)
555 {
556     if (weight < -0.6)
557         return FontWeight100;
558     if (weight < -0.365)
559         return FontWeight200;
560     if (weight < -0.115)
561         return FontWeight300;
562     if (weight <  0.130)
563         return FontWeight400;
564     if (weight <  0.235)
565         return FontWeight500;
566     if (weight <  0.350)
567         return FontWeight600;
568     if (weight <  0.500)
569         return FontWeight700;
570     if (weight <  0.700)
571         return FontWeight800;
572     return FontWeight900;
573 }
574
575 static inline FontTraitsMask toTraitsMask(CTFontSymbolicTraits ctFontTraits, CGFloat weight)
576 {
577     FontTraitsMask weightMask;
578     switch (fontWeightFromCoreText(weight)) {
579     case FontWeight100:
580         weightMask = FontWeight100Mask;
581         break;
582     case FontWeight200:
583         weightMask = FontWeight200Mask;
584         break;
585     case FontWeight300:
586         weightMask = FontWeight300Mask;
587         break;
588     case FontWeight400:
589         weightMask = FontWeight400Mask;
590         break;
591     case FontWeight500:
592         weightMask = FontWeight500Mask;
593         break;
594     case FontWeight600:
595         weightMask = FontWeight600Mask;
596         break;
597     case FontWeight700:
598         weightMask = FontWeight700Mask;
599         break;
600     case FontWeight800:
601         weightMask = FontWeight800Mask;
602         break;
603     case FontWeight900:
604         weightMask = FontWeight900Mask;
605         break;
606     }
607     return static_cast<FontTraitsMask>(((ctFontTraits & kCTFontTraitItalic) ? FontStyleItalicMask : FontStyleNormalMask) | weightMask);
608 }
609
610 bool isFontWeightBold(FontWeight fontWeight)
611 {
612     return fontWeight >= FontWeight600;
613 }
614
615 uint16_t toCoreTextFontWeight(FontWeight fontWeight)
616 {
617     static const int coreTextFontWeights[] = {
618         100, // FontWeight100
619         200, // FontWeight200
620         300, // FontWeight300
621         400, // FontWeight400
622         500, // FontWeight500
623         600, // FontWeight600
624         700, // FontWeight700
625         800, // FontWeight800
626         900, // FontWeight900
627     };
628     return coreTextFontWeights[fontWeight];
629 }
630
631 RefPtr<Font> FontCache::similarFont(const FontDescription& description, const AtomicString& family)
632 {
633     // Attempt to find an appropriate font using a match based on the presence of keywords in
634     // the requested names. For example, we'll match any name that contains "Arabic" to Geeza Pro.
635     if (family.isEmpty())
636         return nullptr;
637
638 #if PLATFORM(IOS)
639     // Substitute the default monospace font for well-known monospace fonts.
640     if (equalLettersIgnoringASCIICase(family, "monaco") || equalLettersIgnoringASCIICase(family, "menlo")) {
641         static NeverDestroyed<AtomicString> courier("courier", AtomicString::ConstructFromLiteral);
642         return fontForFamily(description, courier);
643     }
644
645     // Substitute Verdana for Lucida Grande.
646     if (equalLettersIgnoringASCIICase(family, "lucida grande")) {
647         static NeverDestroyed<AtomicString> verdana("verdana", AtomicString::ConstructFromLiteral);
648         return fontForFamily(description, verdana);
649     }
650 #endif
651
652     static NeverDestroyed<String> arabic(ASCIILiteral("Arabic"));
653     static NeverDestroyed<String> pashto(ASCIILiteral("Pashto"));
654     static NeverDestroyed<String> urdu(ASCIILiteral("Urdu"));
655     static String* matchWords[3] = { &arabic.get(), &pashto.get(), &urdu.get() };
656     static NeverDestroyed<AtomicString> geezaPlain("GeezaPro", AtomicString::ConstructFromLiteral);
657     static NeverDestroyed<AtomicString> geezaBold("GeezaPro-Bold", AtomicString::ConstructFromLiteral);
658     for (String* matchWord : matchWords) {
659         if (family.contains(*matchWord, false))
660             return fontForFamily(description, isFontWeightBold(description.weight()) ? geezaBold : geezaPlain);
661     }
662     return nullptr;
663 }
664
665 Vector<FontTraitsMask> FontCache::getTraitsInFamily(const AtomicString& familyName)
666 {
667     auto familyNameStr = familyName.string().createCFString();
668     CFTypeRef keys[] = { kCTFontFamilyNameAttribute };
669     CFTypeRef values[] = { familyNameStr.get() };
670     auto attributes = adoptCF(CFDictionaryCreate(kCFAllocatorDefault, keys, values, WTF_ARRAY_LENGTH(keys), &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks));
671     auto fontDescriptor = adoptCF(CTFontDescriptorCreateWithAttributes(attributes.get()));
672     auto matchedDescriptors = adoptCF(CTFontDescriptorCreateMatchingFontDescriptors(fontDescriptor.get(), nullptr));
673     if (!matchedDescriptors)
674         return { };
675
676     CFIndex numMatches = CFArrayGetCount(matchedDescriptors.get());
677     if (!numMatches)
678         return { };
679
680     Vector<FontTraitsMask> traitsMasks;
681     traitsMasks.reserveInitialCapacity(numMatches);
682     for (CFIndex i = 0; i < numMatches; ++i) {
683         auto traits = adoptCF((CFDictionaryRef)CTFontDescriptorCopyAttribute((CTFontDescriptorRef)CFArrayGetValueAtIndex(matchedDescriptors.get(), i), kCTFontTraitsAttribute));
684         CFNumberRef resultRef = (CFNumberRef)CFDictionaryGetValue(traits.get(), kCTFontSymbolicTrait);
685         CFNumberRef weightRef = (CFNumberRef)CFDictionaryGetValue(traits.get(), kCTFontWeightTrait);
686         if (resultRef && weightRef) {
687             CTFontSymbolicTraits symbolicTraits;
688             CFNumberGetValue(resultRef, kCFNumberIntType, &symbolicTraits);
689             CGFloat weight = 0;
690             CFNumberGetValue(weightRef, kCFNumberCGFloatType, &weight);
691             traitsMasks.uncheckedAppend(toTraitsMask(symbolicTraits, weight));
692         }
693     }
694     return traitsMasks;
695 }
696
697 static void invalidateFontCache();
698
699 static void fontCacheRegisteredFontsChangedNotificationCallback(CFNotificationCenterRef, void* observer, CFStringRef name, const void *, CFDictionaryRef)
700 {
701     ASSERT_UNUSED(observer, observer == &FontCache::singleton());
702     ASSERT_UNUSED(name, CFEqual(name, kCTFontManagerRegisteredFontsChangedNotification));
703
704     invalidateFontCache();
705 }
706
707 void FontCache::platformInit()
708 {
709     CFNotificationCenterAddObserver(CFNotificationCenterGetLocalCenter(), this, fontCacheRegisteredFontsChangedNotificationCallback, kCTFontManagerRegisteredFontsChangedNotification, 0, CFNotificationSuspensionBehaviorDeliverImmediately);
710 }
711
712 Vector<String> FontCache::systemFontFamilies()
713 {
714     // FIXME: <rdar://problem/21890188>
715     auto attributes = adoptCF(CFDictionaryCreate(kCFAllocatorDefault, nullptr, nullptr, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks));
716     auto emptyFontDescriptor = adoptCF(CTFontDescriptorCreateWithAttributes(attributes.get()));
717     auto matchedDescriptors = adoptCF(CTFontDescriptorCreateMatchingFontDescriptors(emptyFontDescriptor.get(), nullptr));
718     if (!matchedDescriptors)
719         return { };
720
721     CFIndex numMatches = CFArrayGetCount(matchedDescriptors.get());
722     if (!numMatches)
723         return { };
724
725     HashSet<String> visited;
726     for (CFIndex i = 0; i < numMatches; ++i) {
727         auto fontDescriptor = static_cast<CTFontDescriptorRef>(CFArrayGetValueAtIndex(matchedDescriptors.get(), i));
728         if (auto familyName = adoptCF(static_cast<CFStringRef>(CTFontDescriptorCopyAttribute(fontDescriptor, kCTFontFamilyNameAttribute))))
729             visited.add(familyName.get());
730     }
731
732     Vector<String> result;
733     copyToVector(visited, result);
734     return result;
735 }
736
737 static CTFontSymbolicTraits computeTraits(const FontDescription& fontDescription)
738 {
739     CTFontSymbolicTraits traits = 0;
740     if (fontDescription.italic())
741         traits |= kCTFontTraitItalic;
742     if (isFontWeightBold(fontDescription.weight()))
743         traits |= kCTFontTraitBold;
744     return traits;
745 }
746
747 SynthesisPair computeNecessarySynthesis(CTFontRef font, const FontDescription& fontDescription, bool isPlatformFont)
748 {
749 #if PLATFORM(IOS)
750     if (CTFontIsAppleColorEmoji(font))
751         return SynthesisPair(false, false);
752 #endif
753
754     if (isPlatformFont)
755         return SynthesisPair(false, false);
756
757     CTFontSymbolicTraits desiredTraits = computeTraits(fontDescription);
758
759     CTFontSymbolicTraits actualTraits = 0;
760     if (isFontWeightBold(fontDescription.weight()) || fontDescription.italic())
761         actualTraits = CTFontGetSymbolicTraits(font);
762
763     bool needsSyntheticBold = (fontDescription.fontSynthesis() & FontSynthesisWeight) && (desiredTraits & kCTFontTraitBold) && !(actualTraits & kCTFontTraitBold);
764     bool needsSyntheticOblique = (fontDescription.fontSynthesis() & FontSynthesisStyle) && (desiredTraits & kCTFontTraitItalic) && !(actualTraits & kCTFontTraitItalic);
765
766     return SynthesisPair(needsSyntheticBold, needsSyntheticOblique);
767 }
768
769 typedef HashSet<String, ASCIICaseInsensitiveHash> Whitelist;
770 static Whitelist& fontWhitelist()
771 {
772     static NeverDestroyed<Whitelist> whitelist;
773     return whitelist;
774 }
775
776 void FontCache::setFontWhitelist(const Vector<String>& inputWhitelist)
777 {
778     Whitelist& whitelist = fontWhitelist();
779     whitelist.clear();
780     for (auto& item : inputWhitelist)
781         whitelist.add(item);
782 }
783
784 static inline bool isSystemFont(const AtomicString& family)
785 {
786     // AtomicString's operator[] handles out-of-bounds by returning 0.
787     return family[0] == '.';
788 }
789
790 #if !SHOULD_USE_CORE_TEXT_FONT_LOOKUP
791
792 constexpr float italicThreshold = 20;
793 constexpr float weightThreshold = 500;
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     // [Inclusive, Inclusive]
807     struct Range {
808         Range()
809         {
810             ASSERT(!isValid());
811         }
812
813         Range(float minimum, float maximum)
814             : minimum(minimum)
815             , maximum(maximum)
816         {
817             ASSERT(isValid());
818         }
819
820         bool isValid() const
821         {
822             return minimum <= maximum;
823         }
824
825         void expand(const Range& other)
826         {
827             ASSERT(other.isValid());
828             if (!isValid())
829                 *this = other;
830             else {
831                 minimum = std::min(minimum, other.minimum);
832                 maximum = std::max(maximum, other.maximum);
833             }
834             ASSERT(isValid());
835         }
836
837         bool includes(float target) const
838         {
839             return target >= minimum && target <= maximum;
840         }
841
842         float minimum { 1 };
843         float maximum { 0 };
844     };
845
846     struct InstalledFont {
847         InstalledFont() = default;
848
849         InstalledFont(CTFontDescriptorRef fontDescriptor)
850             : fontDescriptor(fontDescriptor)
851         {
852             if (!fontDescriptor)
853                 return;
854
855             auto traits = adoptCF(static_cast<CFDictionaryRef>(CTFontDescriptorCopyAttribute(fontDescriptor, kCTFontTraitsAttribute)));
856             float width = 0;
857             float slant = 0;
858             float weight = 0;
859             if (traits) {
860                 auto widthNumber = static_cast<CFNumberRef>(CFDictionaryGetValue(traits.get(), kCTFontWidthTrait));
861                 if (widthNumber) {
862                     // FIXME: Normalize this from Core Text's [-1, 1] range to CSS's [50%, 200%] range.
863                     auto success = CFNumberGetValue(widthNumber, kCFNumberFloatType, &width);
864                     ASSERT_UNUSED(success, success);
865                 }
866
867                 auto symbolicTraitsNumber = static_cast<CFNumberRef>(CFDictionaryGetValue(traits.get(), kCTFontSymbolicTrait));
868                 if (symbolicTraitsNumber) {
869                     int32_t symbolicTraits;
870                     auto success = CFNumberGetValue(symbolicTraitsNumber, kCFNumberSInt32Type, &symbolicTraits);
871                     ASSERT_UNUSED(success, success);
872                     slant = symbolicTraits & kCTFontTraitItalic ? italicThreshold : 0;
873                 }
874             }
875
876             auto weightNumber = adoptCF(static_cast<CFNumberRef>(CTFontDescriptorCopyAttribute(fontDescriptor, kCTFontCSSWeightAttribute)));
877             if (weightNumber) {
878                 auto success = CFNumberGetValue(weightNumber.get(), kCFNumberFloatType, &weight);
879                 ASSERT_UNUSED(success, success);
880             }
881             
882             stretch = Range(width, width);
883             style = Range(slant, slant);
884             this->weight = Range(weight, weight);
885         }
886
887         RetainPtr<CTFontDescriptorRef> fontDescriptor;
888         Range stretch;
889         Range style;
890         Range weight;
891     };
892
893     struct InstalledFontFamily {
894         InstalledFontFamily() = default;
895
896         explicit InstalledFontFamily(Vector<InstalledFont>&& installedFonts)
897             : installedFonts(WTFMove(installedFonts))
898         {
899             for (auto& font : this->installedFonts)
900                 expand(font);
901         }
902
903         void expand(const InstalledFont& installedFont)
904         {
905             stretchBounds.expand(installedFont.stretch);
906             styleBounds.expand(installedFont.style);
907             weightBounds.expand(installedFont.weight);
908         }
909
910         bool isEmpty() const
911         {
912             return installedFonts.isEmpty();
913         }
914
915         size_t size() const
916         {
917             return installedFonts.size();
918         }
919
920         Vector<InstalledFont> installedFonts;
921         Range stretchBounds;
922         Range styleBounds;
923         Range weightBounds;
924     };
925
926     const InstalledFontFamily& collectionForFamily(const String& familyName)
927     {
928         auto folded = familyName.foldCase();
929         return m_familyNameToFontDescriptors.ensure(folded, [&] {
930             auto familyNameString = folded.createCFString();
931             CFTypeRef keys[] = { kCTFontFamilyNameAttribute };
932             CFTypeRef values[] = { familyNameString.get() };
933             auto attributes = adoptCF(CFDictionaryCreate(kCFAllocatorDefault, keys, values, WTF_ARRAY_LENGTH(keys), &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks));
934             auto fontDescriptorToMatch = adoptCF(CTFontDescriptorCreateWithAttributes(attributes.get()));
935             if (auto matches = adoptCF(CTFontDescriptorCreateMatchingFontDescriptors(fontDescriptorToMatch.get(), nullptr))) {
936                 auto count = CFArrayGetCount(matches.get());
937                 Vector<InstalledFont> result;
938                 result.reserveInitialCapacity(count);
939                 for (CFIndex i = 0; i < count; ++i) {
940                     InstalledFont installedFont(static_cast<CTFontDescriptorRef>(CFArrayGetValueAtIndex(matches.get(), i)));
941                     result.uncheckedAppend(WTFMove(installedFont));
942                 }
943                 return InstalledFontFamily(WTFMove(result));
944             }
945             return InstalledFontFamily();
946         }).iterator->value;
947     }
948
949     const InstalledFont& fontForPostScriptName(const AtomicString& postScriptName)
950     {
951         auto folded = postScriptName.string().foldCase();
952         return m_postScriptNameToFontDescriptors.ensure(folded, [&] {
953             auto postScriptNameString = folded.createCFString();
954             CFTypeRef keys[] = { kCTFontNameAttribute };
955             CFTypeRef values[] = { postScriptNameString.get() };
956             auto attributes = adoptCF(CFDictionaryCreate(kCFAllocatorDefault, keys, values, WTF_ARRAY_LENGTH(keys), &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks));
957             auto fontDescriptorToMatch = adoptCF(CTFontDescriptorCreateWithAttributes(attributes.get()));
958             auto match = adoptCF(static_cast<CTFontDescriptorRef>(CTFontDescriptorCreateMatchingFontDescriptor(fontDescriptorToMatch.get(), nullptr)));
959             return InstalledFont(match.get());
960         }).iterator->value;
961     }
962
963     void clear()
964     {
965         m_familyNameToFontDescriptors.clear();
966         m_postScriptNameToFontDescriptors.clear();
967     }
968
969 private:
970     friend class NeverDestroyed<FontDatabase>;
971
972     FontDatabase() = default;
973
974     HashMap<String, InstalledFontFamily> m_familyNameToFontDescriptors;
975     HashMap<String, InstalledFont> m_postScriptNameToFontDescriptors;
976 };
977
978 template <typename T>
979 using IterateActiveFontsWithReturnCallback = std::function<std::optional<T>(const FontDatabase::InstalledFont&, size_t)>;
980
981 template <typename T>
982 inline std::optional<T> iterateActiveFontsWithReturn(const FontDatabase::InstalledFontFamily& installedFonts, const std::unique_ptr<bool[]>& filter, IterateActiveFontsWithReturnCallback<T> callback)
983 {
984     for (size_t i = 0; i < installedFonts.size(); ++i) {
985         if (!filter[i])
986             continue;
987         if (auto result = callback(installedFonts.installedFonts[i], i))
988             return result;
989     }
990     return std::nullopt;
991 }
992
993 template <typename T>
994 inline void iterateActiveFonts(const FontDatabase::InstalledFontFamily& installedFonts, const std::unique_ptr<bool[]>& filter, T callback)
995 {
996     iterateActiveFontsWithReturn<int>(installedFonts, filter, [&](const FontDatabase::InstalledFont& font, size_t i) -> std::optional<int> {
997         callback(font, i);
998         return std::nullopt;
999     });
1000 }
1001
1002 static inline std::optional<float> findClosestStretch(float, const FontDatabase::InstalledFontFamily&, const std::unique_ptr<bool[]>&)
1003 {
1004     // FIXME: Implement this.
1005     return 0;
1006 }
1007
1008 static inline void filterStretch(float, const FontDatabase::InstalledFontFamily&, std::unique_ptr<bool[]>&)
1009 {
1010     // FIXME: Implement this.
1011 }
1012
1013 static inline std::optional<float> findClosestStyle(float targetStyle, const FontDatabase::InstalledFontFamily& installedFonts, const std::unique_ptr<bool[]>& filter)
1014 {
1015     std::function<float(const FontDatabase::InstalledFont&)> computeScore;
1016
1017     if (targetStyle >= italicThreshold) {
1018         float threshold = std::max(targetStyle, installedFonts.styleBounds.maximum);
1019         computeScore = [&, threshold](const FontDatabase::InstalledFont& font) -> float {
1020             ASSERT(font.style.isValid());
1021             if (font.style.includes(targetStyle))
1022                 return 0;
1023             ASSERT(font.style.minimum > targetStyle || font.style.maximum < targetStyle);
1024             if (font.style.minimum > targetStyle)
1025                 return font.style.minimum - targetStyle;
1026             ASSERT(targetStyle > font.style.maximum);
1027             return threshold - font.style.maximum;
1028         };
1029     } else if (targetStyle >= 0) {
1030         float threshold = std::max(targetStyle, installedFonts.styleBounds.maximum);
1031         computeScore = [&, threshold](const FontDatabase::InstalledFont& font) -> float {
1032             ASSERT(font.style.isValid());
1033             if (font.style.includes(targetStyle))
1034                 return 0;
1035             ASSERT(font.style.minimum > targetStyle || font.style.maximum < targetStyle);
1036             if (font.style.maximum >= 0 && font.style.maximum < targetStyle)
1037                 return targetStyle - font.style.maximum;
1038             if (font.style.minimum > targetStyle)
1039                 return font.style.minimum;
1040             ASSERT(font.style.maximum < 0);
1041             return threshold - font.style.maximum;
1042         };
1043     } else if (targetStyle > -italicThreshold) {
1044         float threshold = std::min(targetStyle, installedFonts.styleBounds.minimum);
1045         computeScore = [&, threshold](const FontDatabase::InstalledFont& font) -> float {
1046             ASSERT(font.style.isValid());
1047             if (font.style.includes(targetStyle))
1048                 return 0;
1049             ASSERT(font.style.minimum > targetStyle || font.style.maximum < targetStyle);
1050             if (font.style.minimum > targetStyle && font.style.minimum <= 0)
1051                 return font.style.minimum - targetStyle;
1052             if (font.style.maximum < targetStyle)
1053                 return -font.style.maximum;
1054             ASSERT(font.style.minimum > 0);
1055             return font.style.minimum - threshold;
1056         };
1057     } else {
1058         ASSERT(targetStyle <= -italicThreshold);
1059         float threshold = std::min(targetStyle, installedFonts.styleBounds.minimum);
1060         computeScore = [&, threshold](const FontDatabase::InstalledFont& font) -> float {
1061             ASSERT(font.style.isValid());
1062             if (font.style.includes(targetStyle))
1063                 return 0;
1064             ASSERT(font.style.minimum > targetStyle || font.style.maximum < targetStyle);
1065             if (font.style.maximum < targetStyle)
1066                 return targetStyle - font.style.maximum;
1067             ASSERT(font.style.minimum > targetStyle);
1068             return font.style.minimum - threshold;
1069         };
1070     }
1071
1072     size_t closestIndex = 0;
1073     std::optional<float> minimumScore;
1074     iterateActiveFonts(installedFonts, filter, [&](auto& installedFont, size_t i) {
1075         auto score = computeScore(installedFont);
1076         if (!minimumScore || score < minimumScore.value()) {
1077             minimumScore = score;
1078             closestIndex = i;
1079         }
1080     });
1081
1082     if (!minimumScore)
1083         return std::nullopt;
1084     auto& winner = installedFonts.installedFonts[closestIndex];
1085     if (winner.style.includes(targetStyle))
1086         return targetStyle;
1087     if (winner.style.minimum > targetStyle)
1088         return winner.style.minimum;
1089     ASSERT(winner.style.maximum < targetStyle);
1090     return winner.style.maximum;
1091 }
1092
1093 static inline void filterStyle(float target, const FontDatabase::InstalledFontFamily& installedFonts, std::unique_ptr<bool[]>& filter)
1094 {
1095     iterateActiveFonts(installedFonts, filter, [&](auto& installedFont, size_t i) {
1096         if (!installedFont.style.includes(target))
1097             filter[i] = false;
1098     });
1099 }
1100
1101 static inline std::optional<float> findClosestWeight(float targetWeight, const FontDatabase::InstalledFontFamily& installedFonts, const std::unique_ptr<bool[]>& filter)
1102 {
1103     {
1104         // The spec states: "If the desired weight is 400, 500 is checked first ... If the desired weight is 500, 400 is checked first"
1105         IterateActiveFontsWithReturnCallback<float> searchFor400 = [&](const FontDatabase::InstalledFont& font, size_t) -> std::optional<float> {
1106             if (font.weight.includes(400))
1107                 return 400;
1108             return std::nullopt;
1109         };
1110         IterateActiveFontsWithReturnCallback<float> searchFor500 = [&](const FontDatabase::InstalledFont& font, size_t) -> std::optional<float> {
1111             if (font.weight.includes(500))
1112                 return 500;
1113             return std::nullopt;
1114         };
1115         if (targetWeight == 400) {
1116             if (auto result = iterateActiveFontsWithReturn(installedFonts, filter, searchFor400))
1117                 return result;
1118             if (auto result = iterateActiveFontsWithReturn(installedFonts, filter, searchFor500))
1119                 return result;
1120         } else if (targetWeight == 500) {
1121             if (auto result = iterateActiveFontsWithReturn(installedFonts, filter, searchFor500))
1122                 return result;
1123             if (auto result = iterateActiveFontsWithReturn(installedFonts, filter, searchFor400))
1124                 return result;
1125         }
1126     }
1127
1128     std::function<float(const FontDatabase::InstalledFont&)> computeScore;
1129     if (targetWeight <= weightThreshold) {
1130         float threshold = std::min(targetWeight, installedFonts.weightBounds.minimum);
1131         computeScore = [&, threshold](const FontDatabase::InstalledFont& font) -> float {
1132             if (font.weight.includes(targetWeight))
1133                 return 0;
1134             ASSERT(font.weight.minimum > targetWeight || font.weight.maximum < targetWeight);
1135             if (font.weight.maximum < targetWeight)
1136                 return targetWeight - font.weight.maximum;
1137             ASSERT(font.weight.minimum > targetWeight);
1138             return font.weight.minimum - threshold;
1139         };
1140     } else {
1141         ASSERT(targetWeight > weightThreshold);
1142         float threshold = std::max(targetWeight, installedFonts.weightBounds.maximum);
1143         computeScore = [&, threshold](const FontDatabase::InstalledFont& font) -> float {
1144             if (font.weight.includes(targetWeight))
1145                 return 0;
1146             ASSERT(font.weight.minimum > targetWeight || font.weight.maximum < targetWeight);
1147             if (font.weight.minimum > targetWeight)
1148                 return font.weight.minimum - targetWeight;
1149             ASSERT(font.weight.maximum < targetWeight);
1150             return threshold - font.weight.maximum;
1151         };
1152     }
1153
1154     size_t closestIndex = 0;
1155     std::optional<float> minimumScore;
1156     iterateActiveFonts(installedFonts, filter, [&](auto& installedFont, size_t i) {
1157         auto score = computeScore(installedFont);
1158         if (!minimumScore || score < minimumScore.value()) {
1159             minimumScore = score;
1160             closestIndex = i;
1161         }
1162     });
1163
1164     if (!minimumScore)
1165         return std::nullopt;
1166     auto& winner = installedFonts.installedFonts[closestIndex];
1167     if (winner.weight.includes(targetWeight))
1168         return targetWeight;
1169     if (winner.weight.minimum > targetWeight)
1170         return winner.weight.minimum;
1171     ASSERT(winner.weight.maximum < targetWeight);
1172     return winner.weight.maximum;
1173 }
1174
1175 static inline void filterWeight(float target, const FontDatabase::InstalledFontFamily& installedFonts, std::unique_ptr<bool[]>& filter)
1176 {
1177     iterateActiveFonts(installedFonts, filter, [&](auto& installedFont, size_t i) {
1178         if (!installedFont.weight.includes(target))
1179             filter[i] = false;
1180     });
1181 }
1182
1183 static inline float computeTargetWeight(FontWeight weight)
1184 {
1185     switch (weight) {
1186     case FontWeight100:
1187         return 100;
1188     case FontWeight200:
1189         return 200;
1190     case FontWeight300:
1191         return 300;
1192     case FontWeight400:
1193         return 400;
1194     case FontWeight500:
1195         return 500;
1196     case FontWeight600:
1197         return 600;
1198     case FontWeight700:
1199         return 700;
1200     case FontWeight800:
1201         return 800;
1202     case FontWeight900:
1203         return 900;
1204     default:
1205         ASSERT_NOT_REACHED();
1206         return 400;
1207     }
1208 }
1209
1210 static const FontDatabase::InstalledFont* findClosestFont(const FontDatabase::InstalledFontFamily& familyFonts, CTFontSymbolicTraits requestedTraits, FontWeight weight)
1211 {
1212     ASSERT(!familyFonts.isEmpty());
1213
1214     // Parallel to familyFonts.
1215     std::unique_ptr<bool[]> filter { new bool[familyFonts.size()] };
1216     for (size_t i = 0; i < familyFonts.size(); ++i)
1217         filter[i] = true;
1218
1219     // FIXME: Implement this.
1220     float targetStretch = 0;
1221     if (auto closestStretch = findClosestStretch(targetStretch, familyFonts, filter))
1222         filterStretch(closestStretch.value(), familyFonts, filter);
1223     else
1224         return nullptr;
1225
1226     float targetStyle = requestedTraits & kCTFontTraitItalic ? italicThreshold : 0;
1227     if (auto closestStyle = findClosestStyle(targetStyle, familyFonts, filter))
1228         filterStyle(closestStyle.value(), familyFonts, filter);
1229     else
1230         return nullptr;
1231
1232     float targetWeight = computeTargetWeight(weight);
1233     if (auto closestWeight = findClosestWeight(targetWeight, familyFonts, filter))
1234         filterWeight(closestWeight.value(), familyFonts, filter);
1235     else
1236         return nullptr;
1237
1238     return iterateActiveFontsWithReturn<const FontDatabase::InstalledFont*>(familyFonts, filter, [](const FontDatabase::InstalledFont& font, size_t) {
1239         return &font;
1240     }).value_or(nullptr);
1241 }
1242
1243 #endif // !SHOULD_USE_CORE_TEXT_FONT_LOOKUP
1244
1245 static RetainPtr<CTFontRef> platformFontLookupWithFamily(const AtomicString& family, CTFontSymbolicTraits requestedTraits, FontWeight weight, float size)
1246 {
1247     const auto& whitelist = fontWhitelist();
1248     if (!isSystemFont(family) && whitelist.size() && !whitelist.contains(family))
1249         return nullptr;
1250
1251
1252 #if SHOULD_USE_CORE_TEXT_FONT_LOOKUP
1253     return adoptCF(CTFontCreateForCSS(family.string().createCFString().get(), toCoreTextFontWeight(weight), requestedTraits, size));
1254 #else
1255     const auto& familyFonts = FontDatabase::singleton().collectionForFamily(family.string());
1256     if (familyFonts.isEmpty()) {
1257         // The CSS spec states that font-family only accepts a name of an actual font family. However, in WebKit, we claim to also
1258         // support supplying a PostScript name instead. However, this creates problems when the other properties (font-weight,
1259         // font-style) disagree with the traits of the PostScript-named font. The solution we have come up with is, when the default
1260         // values for font-weight and font-style are supplied, honor the PostScript name, but if font-weight specifies bold or
1261         // font-style specifies italic, then we run the regular matching algorithm on the family of the PostScript font. This way,
1262         // if content simply states "font-family: PostScriptName;" without specifying the other font properties, it will be honored,
1263         // but if a <b> appears as a descendent element, it will be honored too.
1264         const auto& postScriptFont = FontDatabase::singleton().fontForPostScriptName(family);
1265         if (!postScriptFont.fontDescriptor)
1266             return nullptr;
1267         if (((requestedTraits & kCTFontTraitItalic) && postScriptFont.style.maximum < italicThreshold) || (weight >= FontWeight600 && postScriptFont.weight.maximum < 600)) {
1268             auto postScriptFamilyName = adoptCF(static_cast<CFStringRef>(CTFontDescriptorCopyAttribute(postScriptFont.fontDescriptor.get(), kCTFontFamilyNameAttribute)));
1269             if (!postScriptFamilyName)
1270                 return nullptr;
1271             const auto& familyFonts = FontDatabase::singleton().collectionForFamily(String(postScriptFamilyName.get()));
1272             if (familyFonts.isEmpty())
1273                 return nullptr;
1274             if (const auto* installedFont = findClosestFont(familyFonts, requestedTraits, weight)) {
1275                 if (!installedFont->fontDescriptor)
1276                     return nullptr;
1277                 return adoptCF(CTFontCreateWithFontDescriptor(installedFont->fontDescriptor.get(), size, nullptr));
1278             }
1279             return nullptr;
1280         }
1281         return adoptCF(CTFontCreateWithFontDescriptor(postScriptFont.fontDescriptor.get(), size, nullptr));
1282     }
1283
1284     if (const auto* installedFont = findClosestFont(familyFonts, requestedTraits, weight))
1285         return adoptCF(CTFontCreateWithFontDescriptor(installedFont->fontDescriptor.get(), size, nullptr));
1286
1287     return nullptr;
1288 #endif
1289 }
1290
1291 static void invalidateFontCache()
1292 {
1293     if (!isMainThread()) {
1294         callOnMainThread([] {
1295             invalidateFontCache();
1296         });
1297         return;
1298     }
1299
1300 #if !SHOULD_USE_CORE_TEXT_FONT_LOOKUP
1301     FontDatabase::singleton().clear();
1302 #endif
1303
1304     FontCache::singleton().invalidate();
1305 }
1306
1307 static RetainPtr<CTFontRef> fontWithFamily(const AtomicString& family, CTFontSymbolicTraits desiredTraits, FontWeight weight, const FontFeatureSettings& featureSettings, const FontVariantSettings& variantSettings, const FontVariationSettings& variationSettings, const FontFeatureSettings* fontFaceFeatures, const FontVariantSettings* fontFaceVariantSettings, const TextRenderingMode& textRenderingMode, float size)
1308 {
1309     if (family.isEmpty())
1310         return nullptr;
1311
1312     auto foundFont = platformFontWithFamilySpecialCase(family, weight, desiredTraits, size);
1313     if (!foundFont)
1314         foundFont = platformFontLookupWithFamily(family, desiredTraits, weight, size);
1315     return preparePlatformFont(foundFont.get(), textRenderingMode, fontFaceFeatures, fontFaceVariantSettings, featureSettings, variantSettings, variationSettings);
1316 }
1317
1318 #if PLATFORM(MAC)
1319 static bool shouldAutoActivateFontIfNeeded(const AtomicString& family)
1320 {
1321 #ifndef NDEBUG
1322     // This cache is not thread safe so the following assertion is there to
1323     // make sure this function is always called from the same thread.
1324     static ThreadIdentifier initThreadId = currentThread();
1325     ASSERT(currentThread() == initThreadId);
1326 #endif
1327
1328     static NeverDestroyed<HashSet<AtomicString>> knownFamilies;
1329     static const unsigned maxCacheSize = 128;
1330     ASSERT(knownFamilies.get().size() <= maxCacheSize);
1331     if (knownFamilies.get().size() == maxCacheSize)
1332         knownFamilies.get().remove(knownFamilies.get().begin());
1333
1334     // Only attempt to auto-activate fonts once for performance reasons.
1335     return knownFamilies.get().add(family).isNewEntry;
1336 }
1337
1338 static void autoActivateFont(const String& name, CGFloat size)
1339 {
1340     auto fontName = name.createCFString();
1341     CFTypeRef keys[] = { kCTFontNameAttribute };
1342     CFTypeRef values[] = { fontName.get() };
1343     auto attributes = adoptCF(CFDictionaryCreate(kCFAllocatorDefault, keys, values, WTF_ARRAY_LENGTH(keys), &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks));
1344     auto descriptor = adoptCF(CTFontDescriptorCreateWithAttributes(attributes.get()));
1345     if (auto newFont = CTFontCreateWithFontDescriptor(descriptor.get(), size, nullptr))
1346         CFRelease(newFont);
1347 }
1348 #endif
1349
1350 std::unique_ptr<FontPlatformData> FontCache::createFontPlatformData(const FontDescription& fontDescription, const AtomicString& family, const FontFeatureSettings* fontFaceFeatures, const FontVariantSettings* fontFaceVariantSettings)
1351 {
1352     CTFontSymbolicTraits traits = computeTraits(fontDescription);
1353     float size = fontDescription.computedPixelSize();
1354
1355     auto font = fontWithFamily(family, traits, fontDescription.weight(), fontDescription.featureSettings(), fontDescription.variantSettings(), fontDescription.variationSettings(), fontFaceFeatures, fontFaceVariantSettings, fontDescription.textRenderingMode(), size);
1356
1357 #if PLATFORM(MAC)
1358     if (!font) {
1359         if (!shouldAutoActivateFontIfNeeded(family))
1360             return nullptr;
1361
1362         // Auto activate the font before looking for it a second time.
1363         // Ignore the result because we want to use our own algorithm to actually find the font.
1364         autoActivateFont(family.string(), size);
1365
1366         font = fontWithFamily(family, traits, fontDescription.weight(), fontDescription.featureSettings(), fontDescription.variantSettings(), fontDescription.variationSettings(), fontFaceFeatures, fontFaceVariantSettings, fontDescription.textRenderingMode(), size);
1367     }
1368 #endif
1369
1370     if (!font)
1371         return nullptr;
1372
1373     bool syntheticBold, syntheticOblique;
1374     std::tie(syntheticBold, syntheticOblique) = computeNecessarySynthesis(font.get(), fontDescription).boldObliquePair();
1375
1376     return std::make_unique<FontPlatformData>(font.get(), size, syntheticBold, syntheticOblique, fontDescription.orientation(), fontDescription.widthVariant(), fontDescription.textRenderingMode());
1377 }
1378
1379 typedef HashSet<RetainPtr<CTFontRef>, WTF::RetainPtrObjectHash<CTFontRef>, WTF::RetainPtrObjectHashTraits<CTFontRef>> FallbackDedupSet;
1380 static FallbackDedupSet& fallbackDedupSet()
1381 {
1382     static NeverDestroyed<FallbackDedupSet> dedupSet;
1383     return dedupSet.get();
1384 }
1385
1386 void FontCache::platformPurgeInactiveFontData()
1387 {
1388     Vector<CTFontRef> toRemove;
1389     for (auto& font : fallbackDedupSet()) {
1390         if (CFGetRetainCount(font.get()) == 1)
1391             toRemove.append(font.get());
1392     }
1393     for (auto& font : toRemove)
1394         fallbackDedupSet().remove(font);
1395 }
1396
1397 #if PLATFORM(IOS)
1398 static inline bool isArabicCharacter(UChar character)
1399 {
1400     return character >= 0x0600 && character <= 0x06FF;
1401 }
1402 #endif
1403
1404 static RetainPtr<CTFontRef> lookupFallbackFont(CTFontRef font, FontWeight fontWeight, const AtomicString& locale, const UChar* characters, unsigned length)
1405 {
1406     ASSERT(length > 0);
1407
1408     RetainPtr<CFStringRef> localeString;
1409 #if (PLATFORM(IOS) && __IPHONE_OS_VERSION_MIN_REQUIRED >= 100000) || (PLATFORM(MAC) && __MAC_OS_X_VERSION_MIN_REQUIRED >= 101200)
1410     if (!locale.isNull())
1411         localeString = locale.string().createCFString();
1412 #else
1413     UNUSED_PARAM(locale);
1414 #endif
1415
1416     CFIndex coveredLength = 0;
1417     auto result = adoptCF(CTFontCreateForCharactersWithLanguage(font, characters, length, localeString.get(), &coveredLength));
1418
1419 #if PLATFORM(IOS)
1420     // Callers of this function won't include multiple code points. "Length" is to know how many code units
1421     // are in the code point.
1422     UChar firstCharacter = characters[0];
1423     if (isArabicCharacter(firstCharacter)) {
1424         auto familyName = adoptCF(static_cast<CFStringRef>(CTFontCopyAttribute(result.get(), kCTFontFamilyNameAttribute)));
1425         if (fontFamilyShouldNotBeUsedForArabic(familyName.get())) {
1426             CFStringRef newFamilyName = isFontWeightBold(fontWeight) ? CFSTR("GeezaPro-Bold") : CFSTR("GeezaPro");
1427             CFTypeRef keys[] = { kCTFontNameAttribute };
1428             CFTypeRef values[] = { newFamilyName };
1429             auto attributes = adoptCF(CFDictionaryCreate(kCFAllocatorDefault, keys, values, WTF_ARRAY_LENGTH(keys), &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks));
1430             auto modification = adoptCF(CTFontDescriptorCreateWithAttributes(attributes.get()));
1431             result = adoptCF(CTFontCreateCopyWithAttributes(result.get(), CTFontGetSize(result.get()), nullptr, modification.get()));
1432         }
1433     }
1434 #else
1435     UNUSED_PARAM(fontWeight);
1436 #endif
1437
1438     return result;
1439 }
1440
1441 RefPtr<Font> FontCache::systemFallbackForCharacters(const FontDescription& description, const Font* originalFontData, bool isPlatformFont, const UChar* characters, unsigned length)
1442 {
1443 #if PLATFORM(IOS)
1444     if (length && requiresCustomFallbackFont(*characters)) {
1445         auto* fallback = getCustomFallbackFont(*characters, description);
1446         if (!fallback)
1447             return nullptr;
1448         return fontForPlatformData(*fallback);
1449     }
1450 #endif
1451
1452     const FontPlatformData& platformData = originalFontData->platformData();
1453     auto result = lookupFallbackFont(platformData.font(), description.weight(), description.locale(), characters, length);
1454     result = preparePlatformFont(result.get(), description.textRenderingMode(), nullptr, nullptr, description.featureSettings(), description.variantSettings(), description.variationSettings());
1455     if (!result)
1456         return lastResortFallbackFont(description);
1457
1458     // FontCascade::drawGlyphBuffer() requires that there are no duplicate Font objects which refer to the same thing. This is enforced in
1459     // FontCache::fontForPlatformData(), where our equality check is based on hashing the FontPlatformData, whose hash includes the raw CoreText
1460     // font pointer.
1461     CTFontRef substituteFont = fallbackDedupSet().add(result).iterator->get();
1462
1463     bool syntheticBold, syntheticOblique;
1464     std::tie(syntheticBold, syntheticOblique) = computeNecessarySynthesis(substituteFont, description, isPlatformFont).boldObliquePair();
1465
1466     FontPlatformData alternateFont(substituteFont, platformData.size(), syntheticBold, syntheticOblique, platformData.orientation(), platformData.widthVariant(), platformData.textRenderingMode());
1467
1468     return fontForPlatformData(alternateFont);
1469 }
1470
1471 const AtomicString& FontCache::platformAlternateFamilyName(const AtomicString& familyName)
1472 {
1473     static const UChar heitiString[] = { 0x9ed1, 0x4f53 };
1474     static const UChar songtiString[] = { 0x5b8b, 0x4f53 };
1475     static const UChar weiruanXinXiMingTi[] = { 0x5fae, 0x8edf, 0x65b0, 0x7d30, 0x660e, 0x9ad4 };
1476     static const UChar weiruanYaHeiString[] = { 0x5fae, 0x8f6f, 0x96c5, 0x9ed1 };
1477     static const UChar weiruanZhengHeitiString[] = { 0x5fae, 0x8edf, 0x6b63, 0x9ed1, 0x9ad4 };
1478
1479     static NeverDestroyed<AtomicString> songtiSC("Songti SC", AtomicString::ConstructFromLiteral);
1480     static NeverDestroyed<AtomicString> songtiTC("Songti TC", AtomicString::ConstructFromLiteral);
1481 #if PLATFORM(MAC) && __MAC_OS_X_VERSION_MIN_REQUIRED < 101100
1482     static NeverDestroyed<AtomicString> heitiSCReplacement("Heiti SC", AtomicString::ConstructFromLiteral);
1483     static NeverDestroyed<AtomicString> heitiTCReplacement("Heiti TC", AtomicString::ConstructFromLiteral);
1484 #else
1485     static NeverDestroyed<AtomicString> heitiSCReplacement("PingFang SC", AtomicString::ConstructFromLiteral);
1486     static NeverDestroyed<AtomicString> heitiTCReplacement("PingFang TC", AtomicString::ConstructFromLiteral);
1487 #endif
1488
1489     switch (familyName.length()) {
1490     case 2:
1491         if (equal(familyName, songtiString))
1492             return songtiSC;
1493         if (equal(familyName, heitiString))
1494             return heitiSCReplacement;
1495         break;
1496     case 4:
1497         if (equal(familyName, weiruanYaHeiString))
1498             return heitiSCReplacement;
1499         break;
1500     case 5:
1501         if (equal(familyName, weiruanZhengHeitiString))
1502             return heitiTCReplacement;
1503         break;
1504     case 6:
1505         if (equalLettersIgnoringASCIICase(familyName, "simsun"))
1506             return songtiSC;
1507         if (equal(familyName, weiruanXinXiMingTi))
1508             return songtiTC;
1509         break;
1510     case 10:
1511         if (equalLettersIgnoringASCIICase(familyName, "ms mingliu"))
1512             return songtiTC;
1513         if (equalIgnoringASCIICase(familyName, "\\5b8b\\4f53"))
1514             return songtiSC;
1515         break;
1516 #if PLATFORM(MAC) && __MAC_OS_X_VERSION_MIN_REQUIRED < 101100
1517     case 15:
1518         if (equalLettersIgnoringASCIICase(familyName, "microsoft yahei"))
1519             return heitiSCReplacement;
1520         break;
1521 #endif
1522     case 18:
1523         if (equalLettersIgnoringASCIICase(familyName, "microsoft jhenghei"))
1524             return heitiTCReplacement;
1525         break;
1526     }
1527
1528     return nullAtom;
1529 }
1530
1531 }