font-variant-* properties in @font-face declarations should be honored
[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 namespace WebCore {
39
40 static inline void appendRawTrueTypeFeature(CFMutableArrayRef features, int type, int selector)
41 {
42     RetainPtr<CFNumberRef> typeNumber = adoptCF(CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &type));
43     RetainPtr<CFNumberRef> selectorNumber = adoptCF(CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &selector));
44     CFTypeRef featureKeys[] = { kCTFontFeatureTypeIdentifierKey, kCTFontFeatureSelectorIdentifierKey };
45     CFTypeRef featureValues[] = { typeNumber.get(), selectorNumber.get() };
46     RetainPtr<CFDictionaryRef> feature = adoptCF(CFDictionaryCreate(kCFAllocatorDefault, featureKeys, featureValues, WTF_ARRAY_LENGTH(featureKeys), &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks));
47     CFArrayAppendValue(features, feature.get());
48 }
49
50 static inline bool tagEquals(FontFeatureTag tag, const char comparison[4])
51 {
52     return equalIgnoringASCIICase(tag.data(), comparison, 4);
53 }
54
55 static inline void appendTrueTypeFeature(CFMutableArrayRef features, const FontFeature& feature)
56 {
57     if (tagEquals(feature.tag(), "liga") || tagEquals(feature.tag(), "clig")) {
58         if (feature.enabled()) {
59             appendRawTrueTypeFeature(features, kLigaturesType, kCommonLigaturesOnSelector);
60             appendRawTrueTypeFeature(features, kLigaturesType, kContextualLigaturesOnSelector);
61         } else {
62             appendRawTrueTypeFeature(features, kLigaturesType, kCommonLigaturesOffSelector);
63             appendRawTrueTypeFeature(features, kLigaturesType, kContextualLigaturesOffSelector);
64         }
65     } else if (tagEquals(feature.tag(), "dlig")) {
66         if (feature.enabled())
67             appendRawTrueTypeFeature(features, kLigaturesType, kRareLigaturesOnSelector);
68         else
69             appendRawTrueTypeFeature(features, kLigaturesType, kRareLigaturesOffSelector);
70     } else if (tagEquals(feature.tag(), "hlig")) {
71         if (feature.enabled())
72             appendRawTrueTypeFeature(features, kLigaturesType, kHistoricalLigaturesOnSelector);
73         else
74             appendRawTrueTypeFeature(features, kLigaturesType, kHistoricalLigaturesOffSelector);
75     } else if (tagEquals(feature.tag(), "calt")) {
76         if (feature.enabled())
77             appendRawTrueTypeFeature(features, kContextualAlternatesType, kContextualAlternatesOnSelector);
78         else
79             appendRawTrueTypeFeature(features, kContextualAlternatesType, kContextualAlternatesOffSelector);
80     } else if (tagEquals(feature.tag(), "subs") && feature.enabled())
81         appendRawTrueTypeFeature(features, kVerticalPositionType, kInferiorsSelector);
82     else if (tagEquals(feature.tag(), "sups") && feature.enabled())
83         appendRawTrueTypeFeature(features, kVerticalPositionType, kSuperiorsSelector);
84     else if (tagEquals(feature.tag(), "smcp") && feature.enabled())
85         appendRawTrueTypeFeature(features, kLowerCaseType, kLowerCaseSmallCapsSelector);
86     else if (tagEquals(feature.tag(), "c2sc") && feature.enabled())
87         appendRawTrueTypeFeature(features, kUpperCaseType, kUpperCaseSmallCapsSelector);
88     else if (tagEquals(feature.tag(), "pcap") && feature.enabled())
89         appendRawTrueTypeFeature(features, kLowerCaseType, kLowerCasePetiteCapsSelector);
90     else if (tagEquals(feature.tag(), "c2pc") && feature.enabled())
91         appendRawTrueTypeFeature(features, kUpperCaseType, kUpperCasePetiteCapsSelector);
92     else if (tagEquals(feature.tag(), "unic") && feature.enabled())
93         appendRawTrueTypeFeature(features, kLetterCaseType, 14);
94     else if (tagEquals(feature.tag(), "titl") && feature.enabled())
95         appendRawTrueTypeFeature(features, kStyleOptionsType, kTitlingCapsSelector);
96     else if (tagEquals(feature.tag(), "lnum") && feature.enabled())
97         appendRawTrueTypeFeature(features, kNumberCaseType, kUpperCaseNumbersSelector);
98     else if (tagEquals(feature.tag(), "onum") && feature.enabled())
99         appendRawTrueTypeFeature(features, kNumberCaseType, kLowerCaseNumbersSelector);
100     else if (tagEquals(feature.tag(), "pnum") && feature.enabled())
101         appendRawTrueTypeFeature(features, kNumberSpacingType, kProportionalNumbersSelector);
102     else if (tagEquals(feature.tag(), "tnum") && feature.enabled())
103         appendRawTrueTypeFeature(features, kNumberSpacingType, kMonospacedNumbersSelector);
104     else if (tagEquals(feature.tag(), "frac") && feature.enabled())
105         appendRawTrueTypeFeature(features, kFractionsType, kDiagonalFractionsSelector);
106     else if (tagEquals(feature.tag(), "afrc") && feature.enabled())
107         appendRawTrueTypeFeature(features, kFractionsType, kVerticalFractionsSelector);
108     else if (tagEquals(feature.tag(), "ordn") && feature.enabled())
109         appendRawTrueTypeFeature(features, kVerticalPositionType, kOrdinalsSelector);
110     else if (tagEquals(feature.tag(), "zero") && feature.enabled())
111         appendRawTrueTypeFeature(features, kTypographicExtrasType, kSlashedZeroOnSelector);
112     else if (tagEquals(feature.tag(), "hist") && feature.enabled())
113         appendRawTrueTypeFeature(features, kLigaturesType, kHistoricalLigaturesOnSelector);
114     else if (tagEquals(feature.tag(), "jp78") && feature.enabled())
115         appendRawTrueTypeFeature(features, kCharacterShapeType, kJIS1978CharactersSelector);
116     else if (tagEquals(feature.tag(), "jp83") && feature.enabled())
117         appendRawTrueTypeFeature(features, kCharacterShapeType, kJIS1983CharactersSelector);
118     else if (tagEquals(feature.tag(), "jp90") && feature.enabled())
119         appendRawTrueTypeFeature(features, kCharacterShapeType, kJIS1990CharactersSelector);
120     else if (tagEquals(feature.tag(), "jp04") && feature.enabled())
121         appendRawTrueTypeFeature(features, kCharacterShapeType, kJIS2004CharactersSelector);
122     else if (tagEquals(feature.tag(), "smpl") && feature.enabled())
123         appendRawTrueTypeFeature(features, kCharacterShapeType, kSimplifiedCharactersSelector);
124     else if (tagEquals(feature.tag(), "trad") && feature.enabled())
125         appendRawTrueTypeFeature(features, kCharacterShapeType, kTraditionalCharactersSelector);
126     else if (tagEquals(feature.tag(), "fwid") && feature.enabled())
127         appendRawTrueTypeFeature(features, kTextSpacingType, kMonospacedTextSelector);
128     else if (tagEquals(feature.tag(), "pwid") && feature.enabled())
129         appendRawTrueTypeFeature(features, kTextSpacingType, kProportionalTextSelector);
130     else if (tagEquals(feature.tag(), "ruby") && feature.enabled())
131         appendRawTrueTypeFeature(features, kRubyKanaType, kRubyKanaOnSelector);
132 }
133
134 static inline void appendOpenTypeFeature(CFMutableArrayRef features, const FontFeature& feature)
135 {
136 #if (PLATFORM(MAC) && __MAC_OS_X_VERSION_MIN_REQUIRED >= 101000) || PLATFORM(IOS)
137     RetainPtr<CFStringRef> featureKey = adoptCF(CFStringCreateWithBytes(kCFAllocatorDefault, reinterpret_cast<const UInt8*>(feature.tag().data()), feature.tag().size() * sizeof(FontFeatureTag::value_type), kCFStringEncodingASCII, false));
138     int rawFeatureValue = feature.value();
139     RetainPtr<CFNumberRef> featureValue = adoptCF(CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &rawFeatureValue));
140     CFTypeRef featureDictionaryKeys[] = { kCTFontOpenTypeFeatureTag, kCTFontOpenTypeFeatureValue };
141     CFTypeRef featureDictionaryValues[] = { featureKey.get(), featureValue.get() };
142     RetainPtr<CFDictionaryRef> featureDictionary = adoptCF(CFDictionaryCreate(kCFAllocatorDefault, featureDictionaryKeys, featureDictionaryValues, WTF_ARRAY_LENGTH(featureDictionaryValues), &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks));
143     CFArrayAppendValue(features, featureDictionary.get());
144 #else
145     UNUSED_PARAM(features);
146     UNUSED_PARAM(feature);
147 #endif
148 }
149
150 typedef HashMap<FontFeatureTag, int, FontFeatureTagHash, FontFeatureTagHashTraits> FeaturesMap;
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::FullWidth:
346         result.add(fontFeatureTag("fwid"), 1);
347         break;
348     case FontVariantEastAsianWidth::ProportionalWidth:
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 RetainPtr<CTFontRef> preparePlatformFont(CTFontRef originalFont, TextRenderingMode textRenderingMode, const FontFeatureSettings* fontFaceFeatures, const FontVariantSettings* fontFaceVariantSettings, const FontFeatureSettings& features, const FontVariantSettings& variantSettings)
369 {
370     if (!originalFont || (!features.size() && (textRenderingMode == AutoTextRendering) && variantSettings.isAllNormal()
371         && (!fontFaceFeatures || !fontFaceFeatures->size()) && (!fontFaceVariantSettings || fontFaceVariantSettings->isAllNormal())))
372         return originalFont;
373
374     // This algorithm is described at http://www.w3.org/TR/css3-fonts/#feature-precedence
375     FeaturesMap featuresToBeApplied;
376
377     // Step 1: CoreText handles default features (such as required ligatures).
378
379     // Step 2: Consult with font-variant-* inside @font-face
380     if (fontFaceVariantSettings)
381         featuresToBeApplied = computeFeatureSettingsFromVariants(*fontFaceVariantSettings);
382
383     // Step 3: Consult with font-feature-settings inside @font-face
384     if (fontFaceFeatures) {
385         for (auto& fontFaceFeature : *fontFaceFeatures)
386             featuresToBeApplied.set(fontFaceFeature.tag(), fontFaceFeature.value());
387     }
388
389     // Step 4: Font-variant
390     for (auto& newFeature : computeFeatureSettingsFromVariants(variantSettings))
391         featuresToBeApplied.set(newFeature.key, newFeature.value);
392
393     // Step 5: Other properties (text-rendering)
394     if (textRenderingMode == OptimizeSpeed) {
395         featuresToBeApplied.set(fontFeatureTag("liga"), 0);
396         featuresToBeApplied.set(fontFeatureTag("clig"), 0);
397         featuresToBeApplied.set(fontFeatureTag("dlig"), 0);
398         featuresToBeApplied.set(fontFeatureTag("hlig"), 0);
399         featuresToBeApplied.set(fontFeatureTag("calt"), 0);
400     }
401
402     // Step 6: Font-feature-settings
403     for (auto& newFeature : features)
404         featuresToBeApplied.set(newFeature.tag(), newFeature.value());
405
406     RetainPtr<CFMutableDictionaryRef> attributes = adoptCF(CFDictionaryCreateMutable(kCFAllocatorDefault, 2, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks));
407     if (featuresToBeApplied.size()) {
408         RetainPtr<CFMutableArrayRef> featureArray = adoptCF(CFArrayCreateMutable(kCFAllocatorDefault, features.size(), &kCFTypeArrayCallBacks));
409         for (auto& p : featuresToBeApplied) {
410             auto feature = FontFeature(p.key, p.value);
411             appendTrueTypeFeature(featureArray.get(), feature);
412             appendOpenTypeFeature(featureArray.get(), feature);
413         }
414         CFDictionaryAddValue(attributes.get(), kCTFontFeatureSettingsAttribute, featureArray.get());
415     }
416     if (textRenderingMode == OptimizeLegibility) {
417         CGFloat size = CTFontGetSize(originalFont);
418         RetainPtr<CFNumberRef> sizeNumber = adoptCF(CFNumberCreate(kCFAllocatorDefault, kCFNumberCGFloatType, &size));
419         CFDictionaryAddValue(attributes.get(), kCTFontOpticalSizeAttribute, sizeNumber.get());
420     }
421     RetainPtr<CTFontDescriptorRef> descriptor = adoptCF(CTFontDescriptorCreateWithAttributes(attributes.get()));
422     auto result = adoptCF(CTFontCreateCopyWithAttributes(originalFont, CTFontGetSize(originalFont), nullptr, descriptor.get()));
423     return result;
424 }
425
426 FontWeight fontWeightFromCoreText(CGFloat weight)
427 {
428     if (weight < -0.6)
429         return FontWeight100;
430     if (weight < -0.365)
431         return FontWeight200;
432     if (weight < -0.115)
433         return FontWeight300;
434     if (weight <  0.130)
435         return FontWeight400;
436     if (weight <  0.235)
437         return FontWeight500;
438     if (weight <  0.350)
439         return FontWeight600;
440     if (weight <  0.500)
441         return FontWeight700;
442     if (weight <  0.700)
443         return FontWeight800;
444     return FontWeight900;
445 }
446
447 static inline FontTraitsMask toTraitsMask(CTFontSymbolicTraits ctFontTraits, CGFloat weight)
448 {
449     FontTraitsMask weightMask;
450     switch (fontWeightFromCoreText(weight)) {
451     case FontWeight100:
452         weightMask = FontWeight100Mask;
453         break;
454     case FontWeight200:
455         weightMask = FontWeight200Mask;
456         break;
457     case FontWeight300:
458         weightMask = FontWeight300Mask;
459         break;
460     case FontWeight400:
461         weightMask = FontWeight400Mask;
462         break;
463     case FontWeight500:
464         weightMask = FontWeight500Mask;
465         break;
466     case FontWeight600:
467         weightMask = FontWeight600Mask;
468         break;
469     case FontWeight700:
470         weightMask = FontWeight700Mask;
471         break;
472     case FontWeight800:
473         weightMask = FontWeight800Mask;
474         break;
475     case FontWeight900:
476         weightMask = FontWeight900Mask;
477         break;
478     }
479     return static_cast<FontTraitsMask>(((ctFontTraits & kCTFontTraitItalic) ? FontStyleItalicMask : FontStyleNormalMask)
480         | FontVariantNormalMask | weightMask);
481 }
482
483 bool isFontWeightBold(FontWeight fontWeight)
484 {
485     return fontWeight >= FontWeight600;
486 }
487
488 uint16_t toCoreTextFontWeight(FontWeight fontWeight)
489 {
490     static const int coreTextFontWeights[] = {
491         100, // FontWeight100
492         200, // FontWeight200
493         300, // FontWeight300
494         400, // FontWeight400
495         500, // FontWeight500
496         600, // FontWeight600
497         700, // FontWeight700
498         800, // FontWeight800
499         900, // FontWeight900
500     };
501     return coreTextFontWeights[fontWeight];
502 }
503
504 RefPtr<Font> FontCache::similarFont(const FontDescription& description, const AtomicString& family)
505 {
506     // Attempt to find an appropriate font using a match based on the presence of keywords in
507     // the requested names. For example, we'll match any name that contains "Arabic" to Geeza Pro.
508     if (family.isEmpty())
509         return nullptr;
510
511 #if PLATFORM(IOS)
512     // Substitute the default monospace font for well-known monospace fonts.
513     static NeverDestroyed<AtomicString> monaco("monaco", AtomicString::ConstructFromLiteral);
514     static NeverDestroyed<AtomicString> menlo("menlo", AtomicString::ConstructFromLiteral);
515     static NeverDestroyed<AtomicString> courier("courier", AtomicString::ConstructFromLiteral);
516     if (equalIgnoringCase(family, monaco) || equalIgnoringCase(family, menlo))
517         return fontForFamily(description, courier);
518
519     // Substitute Verdana for Lucida Grande.
520     static NeverDestroyed<AtomicString> lucidaGrande("lucida grande", AtomicString::ConstructFromLiteral);
521     static NeverDestroyed<AtomicString> verdana("verdana", AtomicString::ConstructFromLiteral);
522     if (equalIgnoringCase(family, lucidaGrande))
523         return fontForFamily(description, verdana);
524 #endif
525
526     static NeverDestroyed<String> arabic(ASCIILiteral("Arabic"));
527     static NeverDestroyed<String> pashto(ASCIILiteral("Pashto"));
528     static NeverDestroyed<String> urdu(ASCIILiteral("Urdu"));
529     static String* matchWords[3] = { &arabic.get(), &pashto.get(), &urdu.get() };
530     static NeverDestroyed<AtomicString> geezaPlain("GeezaPro", AtomicString::ConstructFromLiteral);
531     static NeverDestroyed<AtomicString> geezaBold("GeezaPro-Bold", AtomicString::ConstructFromLiteral);
532     for (String* matchWord : matchWords) {
533         if (family.contains(*matchWord, false))
534             return fontForFamily(description, isFontWeightBold(description.weight()) ? geezaBold : geezaPlain);
535     }
536     return nullptr;
537 }
538
539 Vector<FontTraitsMask> FontCache::getTraitsInFamily(const AtomicString& familyName)
540 {
541     RetainPtr<CFStringRef> familyNameStr = familyName.string().createCFString();
542     CFTypeRef keys[] = { kCTFontFamilyNameAttribute };
543     CFTypeRef values[] = { familyNameStr.get() };
544     RetainPtr<CFDictionaryRef> attributes = adoptCF(CFDictionaryCreate(kCFAllocatorDefault, keys, values, WTF_ARRAY_LENGTH(keys), &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks));
545     RetainPtr<CTFontDescriptorRef> fontDescriptor = adoptCF(CTFontDescriptorCreateWithAttributes(attributes.get()));
546     RetainPtr<CFArrayRef> matchedDescriptors = adoptCF(CTFontDescriptorCreateMatchingFontDescriptors(fontDescriptor.get(), nullptr));
547     if (!matchedDescriptors)
548         return { };
549
550     CFIndex numMatches = CFArrayGetCount(matchedDescriptors.get());
551     if (!numMatches)
552         return { };
553
554     Vector<FontTraitsMask> traitsMasks;
555     traitsMasks.reserveInitialCapacity(numMatches);
556     for (CFIndex i = 0; i < numMatches; ++i) {
557         RetainPtr<CFDictionaryRef> traits = adoptCF((CFDictionaryRef)CTFontDescriptorCopyAttribute((CTFontDescriptorRef)CFArrayGetValueAtIndex(matchedDescriptors.get(), i), kCTFontTraitsAttribute));
558         CFNumberRef resultRef = (CFNumberRef)CFDictionaryGetValue(traits.get(), kCTFontSymbolicTrait);
559         CFNumberRef weightRef = (CFNumberRef)CFDictionaryGetValue(traits.get(), kCTFontWeightTrait);
560         if (resultRef && weightRef) {
561             CTFontSymbolicTraits symbolicTraits;
562             CFNumberGetValue(resultRef, kCFNumberIntType, &symbolicTraits);
563             CGFloat weight = 0;
564             CFNumberGetValue(weightRef, kCFNumberCGFloatType, &weight);
565             traitsMasks.uncheckedAppend(toTraitsMask(symbolicTraits, weight));
566         }
567     }
568     return traitsMasks;
569 }
570
571 static void invalidateFontCache()
572 {
573     if (!isMainThread()) {
574         callOnMainThread([] {
575             invalidateFontCache();
576         });
577         return;
578     }
579
580     FontCache::singleton().invalidate();
581
582     platformInvalidateFontCache();
583 }
584
585 static void fontCacheRegisteredFontsChangedNotificationCallback(CFNotificationCenterRef, void* observer, CFStringRef name, const void *, CFDictionaryRef)
586 {
587     ASSERT_UNUSED(observer, observer == &FontCache::singleton());
588     ASSERT_UNUSED(name, CFEqual(name, kCTFontManagerRegisteredFontsChangedNotification));
589
590     invalidateFontCache();
591 }
592
593 void FontCache::platformInit()
594 {
595     CFNotificationCenterAddObserver(CFNotificationCenterGetLocalCenter(), this, fontCacheRegisteredFontsChangedNotificationCallback, kCTFontManagerRegisteredFontsChangedNotification, 0, CFNotificationSuspensionBehaviorDeliverImmediately);
596 }
597
598 Vector<String> FontCache::systemFontFamilies()
599 {
600     // FIXME: <rdar://problem/21890188>
601     RetainPtr<CFDictionaryRef> attributes = adoptCF(CFDictionaryCreate(kCFAllocatorDefault, nullptr, nullptr, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks));
602     auto emptyFontDescriptor = adoptCF(CTFontDescriptorCreateWithAttributes(attributes.get()));
603     auto matchedDescriptors = adoptCF(CTFontDescriptorCreateMatchingFontDescriptors(emptyFontDescriptor.get(), nullptr));
604     if (!matchedDescriptors)
605         return { };
606
607     CFIndex numMatches = CFArrayGetCount(matchedDescriptors.get());
608     if (!numMatches)
609         return { };
610
611     HashSet<String> visited;
612     for (CFIndex i = 0; i < numMatches; ++i) {
613         auto fontDescriptor = static_cast<CTFontDescriptorRef>(CFArrayGetValueAtIndex(matchedDescriptors.get(), i));
614         if (auto familyName = adoptCF(static_cast<CFStringRef>(CTFontDescriptorCopyAttribute(fontDescriptor, kCTFontFamilyNameAttribute))))
615             visited.add(familyName.get());
616     }
617
618     Vector<String> result;
619     copyToVector(visited, result);
620     return result;
621 }
622
623 static CTFontSymbolicTraits computeTraits(const FontDescription& fontDescription)
624 {
625     CTFontSymbolicTraits traits = 0;
626     if (fontDescription.italic())
627         traits |= kCTFontTraitItalic;
628     if (isFontWeightBold(fontDescription.weight()))
629         traits |= kCTFontTraitBold;
630     return traits;
631 }
632
633 SynthesisPair computeNecessarySynthesis(CTFontRef font, const FontDescription& fontDescription, bool isPlatformFont)
634 {
635 #if PLATFORM(IOS)
636     if (CTFontIsAppleColorEmoji(font))
637         return SynthesisPair(false, false);
638 #endif
639
640     if (isPlatformFont)
641         return SynthesisPair(false, false);
642
643     CTFontSymbolicTraits desiredTraits = computeTraits(fontDescription);
644
645     CTFontSymbolicTraits actualTraits = 0;
646     if (isFontWeightBold(fontDescription.weight()) || fontDescription.italic())
647         actualTraits = CTFontGetSymbolicTraits(font);
648
649     bool needsSyntheticBold = (fontDescription.fontSynthesis() & FontSynthesisWeight) && (desiredTraits & kCTFontTraitBold) && !(actualTraits & kCTFontTraitBold);
650     bool needsSyntheticOblique = (fontDescription.fontSynthesis() & FontSynthesisStyle) && (desiredTraits & kCTFontTraitItalic) && !(actualTraits & kCTFontTraitItalic);
651
652     return SynthesisPair(needsSyntheticBold, needsSyntheticOblique);
653 }
654
655 typedef HashSet<String, CaseFoldingHash> Whitelist;
656 static Whitelist& fontWhitelist()
657 {
658     static NeverDestroyed<Whitelist> whitelist;
659     return whitelist;
660 }
661
662 void FontCache::setFontWhitelist(const Vector<String>& inputWhitelist)
663 {
664     Whitelist& whitelist = fontWhitelist();
665     whitelist.clear();
666     for (auto& item : inputWhitelist)
667         whitelist.add(item);
668 }
669
670 #if ENABLE(PLATFORM_FONT_LOOKUP)
671 static RetainPtr<CTFontRef> platformFontLookupWithFamily(const AtomicString& family, CTFontSymbolicTraits requestedTraits, FontWeight weight, float size)
672 {
673     const auto& whitelist = fontWhitelist();
674     if (whitelist.size() && !whitelist.contains(family))
675         return nullptr;
676
677     return adoptCF(CTFontCreateForCSS(family.string().createCFString().get(), toCoreTextFontWeight(weight), requestedTraits, size));
678 }
679 #endif
680
681 static RetainPtr<CTFontRef> fontWithFamily(const AtomicString& family, CTFontSymbolicTraits desiredTraits, FontWeight weight, const FontFeatureSettings& featureSettings, const FontVariantSettings& variantSettings, const TextRenderingMode& textRenderingMode, float size)
682 {
683     if (family.isEmpty())
684         return nullptr;
685     if (auto specialCase = platformFontWithFamilySpecialCase(family, weight, desiredTraits, size))
686         return specialCase;
687 #if ENABLE(PLATFORM_FONT_LOOKUP)
688     RetainPtr<CTFontRef> foundFont = platformFontLookupWithFamily(family, desiredTraits, weight, size);
689 #else
690     UNUSED_PARAM(featureSettings);
691     UNUSED_PARAM(variantSettings);
692     RetainPtr<CTFontRef> foundFont = platformFontWithFamily(family, desiredTraits, weight, textRenderingMode, size);
693 #endif
694     return preparePlatformFont(foundFont.get(), textRenderingMode, nullptr, nullptr, featureSettings, variantSettings);
695 }
696
697 #if PLATFORM(MAC)
698 static bool shouldAutoActivateFontIfNeeded(const AtomicString& family)
699 {
700 #ifndef NDEBUG
701     // This cache is not thread safe so the following assertion is there to
702     // make sure this function is always called from the same thread.
703     static ThreadIdentifier initThreadId = currentThread();
704     ASSERT(currentThread() == initThreadId);
705 #endif
706
707     static NeverDestroyed<HashSet<AtomicString>> knownFamilies;
708     static const unsigned maxCacheSize = 128;
709     ASSERT(knownFamilies.get().size() <= maxCacheSize);
710     if (knownFamilies.get().size() == maxCacheSize)
711         knownFamilies.get().remove(knownFamilies.get().begin());
712
713     // Only attempt to auto-activate fonts once for performance reasons.
714     return knownFamilies.get().add(family).isNewEntry;
715 }
716
717 static void autoActivateFont(const String& name, CGFloat size)
718 {
719     auto fontName = name.createCFString();
720     CFTypeRef keys[] = { kCTFontNameAttribute };
721     CFTypeRef values[] = { fontName.get() };
722     auto attributes = adoptCF(CFDictionaryCreate(kCFAllocatorDefault, keys, values, WTF_ARRAY_LENGTH(keys), &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks));
723     auto descriptor = adoptCF(CTFontDescriptorCreateWithAttributes(attributes.get()));
724     if (auto newFont = CTFontCreateWithFontDescriptor(descriptor.get(), size, nullptr))
725         CFRelease(newFont);
726 }
727 #endif
728
729 std::unique_ptr<FontPlatformData> FontCache::createFontPlatformData(const FontDescription& fontDescription, const AtomicString& family)
730 {
731     CTFontSymbolicTraits traits = computeTraits(fontDescription);
732     float size = fontDescription.computedPixelSize();
733
734     RetainPtr<CTFontRef> font = fontWithFamily(family, traits, fontDescription.weight(), fontDescription.featureSettings(), fontDescription.variantSettings(), fontDescription.textRenderingMode(), size);
735
736 #if PLATFORM(MAC)
737     if (!font) {
738         if (!shouldAutoActivateFontIfNeeded(family))
739             return nullptr;
740
741         // Auto activate the font before looking for it a second time.
742         // Ignore the result because we want to use our own algorithm to actually find the font.
743         autoActivateFont(family.string(), size);
744
745         font = fontWithFamily(family, traits, fontDescription.weight(), fontDescription.featureSettings(), fontDescription.variantSettings(), fontDescription.textRenderingMode(), size);
746     }
747 #endif
748
749     if (!font)
750         return nullptr;
751
752     bool syntheticBold, syntheticOblique;
753     std::tie(syntheticBold, syntheticOblique) = computeNecessarySynthesis(font.get(), fontDescription).boldObliquePair();
754
755     return std::make_unique<FontPlatformData>(font.get(), size, syntheticBold, syntheticOblique, fontDescription.orientation(), fontDescription.widthVariant(), fontDescription.textRenderingMode());
756 }
757
758 typedef HashSet<RetainPtr<CTFontRef>, WTF::RetainPtrObjectHash<CTFontRef>, WTF::RetainPtrObjectHashTraits<CTFontRef>> FallbackDedupSet;
759 static FallbackDedupSet& fallbackDedupSet()
760 {
761     static NeverDestroyed<FallbackDedupSet> dedupSet;
762     return dedupSet.get();
763 }
764
765 void FontCache::platformPurgeInactiveFontData()
766 {
767     Vector<CTFontRef> toRemove;
768     for (auto& font : fallbackDedupSet()) {
769         if (CFGetRetainCount(font.get()) == 1)
770             toRemove.append(font.get());
771     }
772     for (auto& font : toRemove)
773         fallbackDedupSet().remove(font);
774 }
775
776 RefPtr<Font> FontCache::systemFallbackForCharacters(const FontDescription& description, const Font* originalFontData, bool isPlatformFont, const UChar* characters, unsigned length)
777 {
778 #if PLATFORM(IOS)
779     if (length && requiresCustomFallbackFont(*characters)) {
780         auto* fallback = getCustomFallbackFont(*characters, description);
781         if (!fallback)
782             return nullptr;
783         return fontForPlatformData(*fallback);
784     }
785 #endif
786
787     const FontPlatformData& platformData = originalFontData->platformData();
788     // FIXME: Should pass in the locale instead of nullAtom.
789     RetainPtr<CTFontRef> result = platformLookupFallbackFont(platformData.font(), description.weight(), nullAtom, characters, length);
790     result = preparePlatformFont(result.get(), description.textRenderingMode(), nullptr, nullptr, description.featureSettings(), description.variantSettings());
791     if (!result)
792         return lastResortFallbackFont(description);
793
794     // FontCascade::drawGlyphBuffer() requires that there are no duplicate Font objects which refer to the same thing. This is enforced in
795     // FontCache::fontForPlatformData(), where our equality check is based on hashing the FontPlatformData, whose hash includes the raw CoreText
796     // font pointer.
797     CTFontRef substituteFont = fallbackDedupSet().add(result).iterator->get();
798
799     bool syntheticBold, syntheticOblique;
800     std::tie(syntheticBold, syntheticOblique) = computeNecessarySynthesis(substituteFont, description, isPlatformFont).boldObliquePair();
801
802     FontPlatformData alternateFont(substituteFont, platformData.size(), syntheticBold, syntheticOblique, platformData.m_orientation, platformData.m_widthVariant, platformData.m_textRenderingMode);
803
804     return fontForPlatformData(alternateFont);
805 }
806
807 }