21a674d08a863c0750c930b72c519b56cf6377d4
[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::Yes:
158         result.add(fontFeatureTag("liga"), 1);
159         result.add(fontFeatureTag("clig"), 1);
160         break;
161     case FontVariantLigatures::No:
162         result.add(fontFeatureTag("liga"), 0);
163         result.add(fontFeatureTag("clig"), 0);
164         break;
165     default:
166         break;
167     }
168
169     switch (variantSettings.discretionaryLigatures) {
170     case FontVariantLigatures::Yes:
171         result.add(fontFeatureTag("dlig"), 1);
172         break;
173     case FontVariantLigatures::No:
174         result.add(fontFeatureTag("dlig"), 0);
175         break;
176     default:
177         break;
178     }
179
180     switch (variantSettings.historicalLigatures) {
181     case FontVariantLigatures::Yes:
182         result.add(fontFeatureTag("hlig"), 1);
183         break;
184     case FontVariantLigatures::No:
185         result.add(fontFeatureTag("hlig"), 0);
186         break;
187     default:
188         break;
189     }
190
191     switch (variantSettings.contextualAlternates) {
192     case FontVariantLigatures::Yes:
193         result.add(fontFeatureTag("calt"), 1);
194         break;
195     case FontVariantLigatures::No:
196         result.add(fontFeatureTag("calt"), 0);
197         break;
198     default:
199         break;
200     }
201
202     switch (variantSettings.position) {
203     case FontVariantPosition::Subscript:
204         result.add(fontFeatureTag("subs"), 1);
205         break;
206     case FontVariantPosition::Superscript:
207         result.add(fontFeatureTag("sups"), 1);
208         break;
209     default:
210         break;
211     }
212
213     switch (variantSettings.caps) {
214     case FontVariantCaps::AllSmall:
215         result.add(fontFeatureTag("c2sc"), 1);
216         FALLTHROUGH;
217     case FontVariantCaps::Small:
218         result.add(fontFeatureTag("smcp"), 1);
219         break;
220     case FontVariantCaps::AllPetite:
221         result.add(fontFeatureTag("c2pc"), 1);
222         FALLTHROUGH;
223     case FontVariantCaps::Petite:
224         result.add(fontFeatureTag("pcap"), 1);
225         break;
226     case FontVariantCaps::Unicase:
227         result.add(fontFeatureTag("unic"), 1);
228         break;
229     case FontVariantCaps::Titling:
230         result.add(fontFeatureTag("titl"), 1);
231         break;
232     default:
233         break;
234     }
235
236     switch (variantSettings.numericFigure) {
237     case FontVariantNumericFigure::LiningNumbers:
238         result.add(fontFeatureTag("lnum"), 1);
239         break;
240     case FontVariantNumericFigure::OldStyleNumbers:
241         result.add(fontFeatureTag("onum"), 1);
242         break;
243     default:
244         break;
245     }
246
247     switch (variantSettings.numericSpacing) {
248     case FontVariantNumericSpacing::ProportionalNumbers:
249         result.add(fontFeatureTag("pnum"), 1);
250         break;
251     case FontVariantNumericSpacing::TabularNumbers:
252         result.add(fontFeatureTag("tnum"), 1);
253         break;
254     default:
255         break;
256     }
257
258     switch (variantSettings.numericFraction) {
259     case FontVariantNumericFraction::DiagonalFractions:
260         result.add(fontFeatureTag("frac"), 1);
261         break;
262     case FontVariantNumericFraction::StackedFractions:
263         result.add(fontFeatureTag("afrc"), 1);
264         break;
265     default:
266         break;
267     }
268
269     if (variantSettings.numericOrdinal == FontVariantNumericOrdinal::Yes)
270         result.add(fontFeatureTag("ordn"), 1);
271     if (variantSettings.numericSlashedZero == FontVariantNumericSlashedZero::Yes)
272         result.add(fontFeatureTag("zero"), 1);
273
274     switch (variantSettings.alternates) {
275     case FontVariantAlternates::HistoricalForms:
276         result.add(fontFeatureTag("hist"), 1);
277         break;
278     default:
279         break;
280     }
281
282     switch (variantSettings.eastAsianVariant) {
283     case FontVariantEastAsianVariant::Jis78:
284         result.add(fontFeatureTag("jp78"), 1);
285         break;
286     case FontVariantEastAsianVariant::Jis83:
287         result.add(fontFeatureTag("jp83"), 1);
288         break;
289     case FontVariantEastAsianVariant::Jis90:
290         result.add(fontFeatureTag("jp90"), 1);
291         break;
292     case FontVariantEastAsianVariant::Jis04:
293         result.add(fontFeatureTag("jp04"), 1);
294         break;
295     case FontVariantEastAsianVariant::Simplified:
296         result.add(fontFeatureTag("smpl"), 1);
297         break;
298     case FontVariantEastAsianVariant::Traditional:
299         result.add(fontFeatureTag("trad"), 1);
300         break;
301     default:
302         break;
303     }
304
305     switch (variantSettings.eastAsianWidth) {
306     case FontVariantEastAsianWidth::FullWidth:
307         result.add(fontFeatureTag("fwid"), 1);
308         break;
309     case FontVariantEastAsianWidth::ProportionalWidth:
310         result.add(fontFeatureTag("pwid"), 1);
311         break;
312     default:
313         break;
314     }
315
316     if (variantSettings.eastAsianRuby == FontVariantEastAsianRuby::Yes)
317         result.add(fontFeatureTag("ruby"), 1);
318
319     return result;
320 }
321
322 RetainPtr<CTFontRef> preparePlatformFont(CTFontRef originalFont, TextRenderingMode textRenderingMode, const FontFeatureSettings& features, const FontVariantSettings& variantSettings)
323 {
324     if (!originalFont || (!features.size() && (textRenderingMode == AutoTextRendering) && variantSettings.isAllNormal()))
325         return originalFont;
326
327     // This algorithm is described at http://www.w3.org/TR/css3-fonts/#feature-precedence
328
329     // Step 1: CoreText handles default features (such as required ligatures).
330
331     // Steps 2-3: Consult with @font-face
332     // FIXME: This is not yet implemented.
333
334     // Step 4: Font-variant
335     auto fontFeatureSettingsFromVariants = computeFeatureSettingsFromVariants(variantSettings);
336
337     // Step 5: Other properties (text-rendering)
338     if (textRenderingMode == OptimizeSpeed) {
339         fontFeatureSettingsFromVariants.set(fontFeatureTag("liga"), 0);
340         fontFeatureSettingsFromVariants.set(fontFeatureTag("clig"), 0);
341         fontFeatureSettingsFromVariants.set(fontFeatureTag("dlig"), 0);
342         fontFeatureSettingsFromVariants.set(fontFeatureTag("hlig"), 0);
343         fontFeatureSettingsFromVariants.set(fontFeatureTag("calt"), 0);
344     }
345
346     // Step 6: Font-feature-settings
347     for (auto& newFeature : features)
348         fontFeatureSettingsFromVariants.set(newFeature.tag(), newFeature.value());
349
350     RetainPtr<CFMutableDictionaryRef> attributes = adoptCF(CFDictionaryCreateMutable(kCFAllocatorDefault, 2, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks));
351     if (fontFeatureSettingsFromVariants.size()) {
352         RetainPtr<CFMutableArrayRef> featureArray = adoptCF(CFArrayCreateMutable(kCFAllocatorDefault, features.size(), &kCFTypeArrayCallBacks));
353         for (auto& p : fontFeatureSettingsFromVariants) {
354             auto feature = FontFeature(p.key, p.value);
355             appendTrueTypeFeature(featureArray.get(), feature);
356             appendOpenTypeFeature(featureArray.get(), feature);
357         }
358         CFDictionaryAddValue(attributes.get(), kCTFontFeatureSettingsAttribute, featureArray.get());
359     }
360     if (textRenderingMode == OptimizeLegibility) {
361         CGFloat size = CTFontGetSize(originalFont);
362         RetainPtr<CFNumberRef> sizeNumber = adoptCF(CFNumberCreate(kCFAllocatorDefault, kCFNumberCGFloatType, &size));
363         CFDictionaryAddValue(attributes.get(), kCTFontOpticalSizeAttribute, sizeNumber.get());
364     }
365     RetainPtr<CTFontDescriptorRef> descriptor = adoptCF(CTFontDescriptorCreateWithAttributes(attributes.get()));
366     return adoptCF(CTFontCreateCopyWithAttributes(originalFont, CTFontGetSize(originalFont), nullptr, descriptor.get()));
367 }
368
369 FontWeight fontWeightFromCoreText(CGFloat weight)
370 {
371     if (weight < -0.6)
372         return FontWeight100;
373     if (weight < -0.365)
374         return FontWeight200;
375     if (weight < -0.115)
376         return FontWeight300;
377     if (weight <  0.130)
378         return FontWeight400;
379     if (weight <  0.235)
380         return FontWeight500;
381     if (weight <  0.350)
382         return FontWeight600;
383     if (weight <  0.500)
384         return FontWeight700;
385     if (weight <  0.700)
386         return FontWeight800;
387     return FontWeight900;
388 }
389
390 static inline FontTraitsMask toTraitsMask(CTFontSymbolicTraits ctFontTraits, CGFloat weight)
391 {
392     FontTraitsMask weightMask;
393     switch (fontWeightFromCoreText(weight)) {
394     case FontWeight100:
395         weightMask = FontWeight100Mask;
396         break;
397     case FontWeight200:
398         weightMask = FontWeight200Mask;
399         break;
400     case FontWeight300:
401         weightMask = FontWeight300Mask;
402         break;
403     case FontWeight400:
404         weightMask = FontWeight400Mask;
405         break;
406     case FontWeight500:
407         weightMask = FontWeight500Mask;
408         break;
409     case FontWeight600:
410         weightMask = FontWeight600Mask;
411         break;
412     case FontWeight700:
413         weightMask = FontWeight700Mask;
414         break;
415     case FontWeight800:
416         weightMask = FontWeight800Mask;
417         break;
418     case FontWeight900:
419         weightMask = FontWeight900Mask;
420         break;
421     }
422     return static_cast<FontTraitsMask>(((ctFontTraits & kCTFontTraitItalic) ? FontStyleItalicMask : FontStyleNormalMask)
423         | FontVariantNormalMask | weightMask);
424 }
425
426 bool isFontWeightBold(FontWeight fontWeight)
427 {
428     return fontWeight >= FontWeight600;
429 }
430
431 uint16_t toCoreTextFontWeight(FontWeight fontWeight)
432 {
433     static const int coreTextFontWeights[] = {
434         100, // FontWeight100
435         200, // FontWeight200
436         300, // FontWeight300
437         400, // FontWeight400
438         500, // FontWeight500
439         600, // FontWeight600
440         700, // FontWeight700
441         800, // FontWeight800
442         900, // FontWeight900
443     };
444     return coreTextFontWeights[fontWeight];
445 }
446
447 RefPtr<Font> FontCache::similarFont(const FontDescription& description, const AtomicString& family)
448 {
449     // Attempt to find an appropriate font using a match based on the presence of keywords in
450     // the requested names. For example, we'll match any name that contains "Arabic" to Geeza Pro.
451     if (family.isEmpty())
452         return nullptr;
453
454 #if PLATFORM(IOS)
455     // Substitute the default monospace font for well-known monospace fonts.
456     static NeverDestroyed<AtomicString> monaco("monaco", AtomicString::ConstructFromLiteral);
457     static NeverDestroyed<AtomicString> menlo("menlo", AtomicString::ConstructFromLiteral);
458     static NeverDestroyed<AtomicString> courier("courier", AtomicString::ConstructFromLiteral);
459     if (equalIgnoringCase(family, monaco) || equalIgnoringCase(family, menlo))
460         return fontForFamily(description, courier);
461
462     // Substitute Verdana for Lucida Grande.
463     static NeverDestroyed<AtomicString> lucidaGrande("lucida grande", AtomicString::ConstructFromLiteral);
464     static NeverDestroyed<AtomicString> verdana("verdana", AtomicString::ConstructFromLiteral);
465     if (equalIgnoringCase(family, lucidaGrande))
466         return fontForFamily(description, verdana);
467 #endif
468
469     static NeverDestroyed<String> arabic(ASCIILiteral("Arabic"));
470     static NeverDestroyed<String> pashto(ASCIILiteral("Pashto"));
471     static NeverDestroyed<String> urdu(ASCIILiteral("Urdu"));
472     static String* matchWords[3] = { &arabic.get(), &pashto.get(), &urdu.get() };
473     static NeverDestroyed<AtomicString> geezaPlain("GeezaPro", AtomicString::ConstructFromLiteral);
474     static NeverDestroyed<AtomicString> geezaBold("GeezaPro-Bold", AtomicString::ConstructFromLiteral);
475     for (String* matchWord : matchWords) {
476         if (family.contains(*matchWord, false))
477             return fontForFamily(description, isFontWeightBold(description.weight()) ? geezaBold : geezaPlain);
478     }
479     return nullptr;
480 }
481
482 Vector<FontTraitsMask> FontCache::getTraitsInFamily(const AtomicString& familyName)
483 {
484     RetainPtr<CFStringRef> familyNameStr = familyName.string().createCFString();
485     CFTypeRef keys[] = { kCTFontFamilyNameAttribute };
486     CFTypeRef values[] = { familyNameStr.get() };
487     RetainPtr<CFDictionaryRef> attributes = adoptCF(CFDictionaryCreate(kCFAllocatorDefault, keys, values, WTF_ARRAY_LENGTH(keys), &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks));
488     RetainPtr<CTFontDescriptorRef> fontDescriptor = adoptCF(CTFontDescriptorCreateWithAttributes(attributes.get()));
489     RetainPtr<CFArrayRef> matchedDescriptors = adoptCF(CTFontDescriptorCreateMatchingFontDescriptors(fontDescriptor.get(), nullptr));
490     if (!matchedDescriptors)
491         return { };
492
493     CFIndex numMatches = CFArrayGetCount(matchedDescriptors.get());
494     if (!numMatches)
495         return { };
496
497     Vector<FontTraitsMask> traitsMasks;
498     traitsMasks.reserveInitialCapacity(numMatches);
499     for (CFIndex i = 0; i < numMatches; ++i) {
500         RetainPtr<CFDictionaryRef> traits = adoptCF((CFDictionaryRef)CTFontDescriptorCopyAttribute((CTFontDescriptorRef)CFArrayGetValueAtIndex(matchedDescriptors.get(), i), kCTFontTraitsAttribute));
501         CFNumberRef resultRef = (CFNumberRef)CFDictionaryGetValue(traits.get(), kCTFontSymbolicTrait);
502         CFNumberRef weightRef = (CFNumberRef)CFDictionaryGetValue(traits.get(), kCTFontWeightTrait);
503         if (resultRef && weightRef) {
504             CTFontSymbolicTraits symbolicTraits;
505             CFNumberGetValue(resultRef, kCFNumberIntType, &symbolicTraits);
506             CGFloat weight = 0;
507             CFNumberGetValue(weightRef, kCFNumberCGFloatType, &weight);
508             traitsMasks.uncheckedAppend(toTraitsMask(symbolicTraits, weight));
509         }
510     }
511     return traitsMasks;
512 }
513
514 static void invalidateFontCache()
515 {
516     if (!isMainThread()) {
517         callOnMainThread([] {
518             invalidateFontCache();
519         });
520         return;
521     }
522
523     FontCache::singleton().invalidate();
524
525     platformInvalidateFontCache();
526 }
527
528 static void fontCacheRegisteredFontsChangedNotificationCallback(CFNotificationCenterRef, void* observer, CFStringRef name, const void *, CFDictionaryRef)
529 {
530     ASSERT_UNUSED(observer, observer == &FontCache::singleton());
531     ASSERT_UNUSED(name, CFEqual(name, kCTFontManagerRegisteredFontsChangedNotification));
532
533     invalidateFontCache();
534 }
535
536 void FontCache::platformInit()
537 {
538     CFNotificationCenterAddObserver(CFNotificationCenterGetLocalCenter(), this, fontCacheRegisteredFontsChangedNotificationCallback, kCTFontManagerRegisteredFontsChangedNotification, 0, CFNotificationSuspensionBehaviorDeliverImmediately);
539 }
540
541 Vector<String> FontCache::systemFontFamilies()
542 {
543     // FIXME: <rdar://problem/21890188>
544     RetainPtr<CFDictionaryRef> attributes = adoptCF(CFDictionaryCreate(kCFAllocatorDefault, nullptr, nullptr, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks));
545     auto emptyFontDescriptor = adoptCF(CTFontDescriptorCreateWithAttributes(attributes.get()));
546     auto matchedDescriptors = adoptCF(CTFontDescriptorCreateMatchingFontDescriptors(emptyFontDescriptor.get(), nullptr));
547     if (!matchedDescriptors)
548         return { };
549
550     CFIndex numMatches = CFArrayGetCount(matchedDescriptors.get());
551     if (!numMatches)
552         return { };
553
554     HashSet<String> visited;
555     for (CFIndex i = 0; i < numMatches; ++i) {
556         auto fontDescriptor = static_cast<CTFontDescriptorRef>(CFArrayGetValueAtIndex(matchedDescriptors.get(), i));
557         if (auto familyName = adoptCF(static_cast<CFStringRef>(CTFontDescriptorCopyAttribute(fontDescriptor, kCTFontFamilyNameAttribute))))
558             visited.add(familyName.get());
559     }
560
561     Vector<String> result;
562     copyToVector(visited, result);
563     return result;
564 }
565
566 static CTFontSymbolicTraits computeTraits(const FontDescription& fontDescription)
567 {
568     CTFontSymbolicTraits traits = 0;
569     if (fontDescription.italic())
570         traits |= kCTFontTraitItalic;
571     if (isFontWeightBold(fontDescription.weight()))
572         traits |= kCTFontTraitBold;
573     return traits;
574 }
575
576 SynthesisPair computeNecessarySynthesis(CTFontRef font, const FontDescription& fontDescription, bool isPlatformFont)
577 {
578 #if PLATFORM(IOS)
579     if (CTFontIsAppleColorEmoji(font))
580         return SynthesisPair(false, false);
581 #endif
582
583     if (isPlatformFont)
584         return SynthesisPair(false, false);
585
586     CTFontSymbolicTraits desiredTraits = computeTraits(fontDescription);
587
588     CTFontSymbolicTraits actualTraits = 0;
589     if (isFontWeightBold(fontDescription.weight()) || fontDescription.italic())
590         actualTraits = CTFontGetSymbolicTraits(font);
591
592     bool needsSyntheticBold = (fontDescription.fontSynthesis() & FontSynthesisWeight) && (desiredTraits & kCTFontTraitBold) && !(actualTraits & kCTFontTraitBold);
593     bool needsSyntheticOblique = (fontDescription.fontSynthesis() & FontSynthesisStyle) && (desiredTraits & kCTFontTraitItalic) && !(actualTraits & kCTFontTraitItalic);
594
595     return SynthesisPair(needsSyntheticBold, needsSyntheticOblique);
596 }
597
598 typedef HashSet<String, CaseFoldingHash> Whitelist;
599 static Whitelist& fontWhitelist()
600 {
601     static NeverDestroyed<Whitelist> whitelist;
602     return whitelist;
603 }
604
605 void FontCache::setFontWhitelist(const Vector<String>& inputWhitelist)
606 {
607     Whitelist& whitelist = fontWhitelist();
608     whitelist.clear();
609     for (auto& item : inputWhitelist)
610         whitelist.add(item);
611 }
612
613 #if ENABLE(PLATFORM_FONT_LOOKUP)
614 static RetainPtr<CTFontRef> platformFontLookupWithFamily(const AtomicString& family, CTFontSymbolicTraits requestedTraits, FontWeight weight, float size)
615 {
616     const auto& whitelist = fontWhitelist();
617     if (whitelist.size() && !whitelist.contains(family))
618         return nullptr;
619
620     return adoptCF(CTFontCreateForCSS(family.string().createCFString().get(), toCoreTextFontWeight(weight), requestedTraits, size));
621 }
622 #endif
623
624 static RetainPtr<CTFontRef> fontWithFamily(const AtomicString& family, CTFontSymbolicTraits desiredTraits, FontWeight weight, const FontFeatureSettings& featureSettings, const FontVariantSettings& variantSettings, const TextRenderingMode& textRenderingMode, float size)
625 {
626     if (family.isEmpty())
627         return nullptr;
628     if (auto specialCase = platformFontWithFamilySpecialCase(family, weight, desiredTraits, size))
629         return specialCase;
630 #if ENABLE(PLATFORM_FONT_LOOKUP)
631     RetainPtr<CTFontRef> foundFont = platformFontLookupWithFamily(family, desiredTraits, weight, size);
632 #else
633     UNUSED_PARAM(featureSettings);
634     UNUSED_PARAM(variantSettings);
635     RetainPtr<CTFontRef> foundFont = platformFontWithFamily(family, desiredTraits, weight, textRenderingMode, size);
636 #endif
637     return preparePlatformFont(foundFont.get(), textRenderingMode, featureSettings, variantSettings);
638 }
639
640 #if PLATFORM(MAC)
641 static bool shouldAutoActivateFontIfNeeded(const AtomicString& family)
642 {
643 #ifndef NDEBUG
644     // This cache is not thread safe so the following assertion is there to
645     // make sure this function is always called from the same thread.
646     static ThreadIdentifier initThreadId = currentThread();
647     ASSERT(currentThread() == initThreadId);
648 #endif
649
650     static NeverDestroyed<HashSet<AtomicString>> knownFamilies;
651     static const unsigned maxCacheSize = 128;
652     ASSERT(knownFamilies.get().size() <= maxCacheSize);
653     if (knownFamilies.get().size() == maxCacheSize)
654         knownFamilies.get().remove(knownFamilies.get().begin());
655
656     // Only attempt to auto-activate fonts once for performance reasons.
657     return knownFamilies.get().add(family).isNewEntry;
658 }
659
660 static void autoActivateFont(const String& name, CGFloat size)
661 {
662     auto fontName = name.createCFString();
663     CFTypeRef keys[] = { kCTFontNameAttribute };
664     CFTypeRef values[] = { fontName.get() };
665     auto attributes = adoptCF(CFDictionaryCreate(kCFAllocatorDefault, keys, values, WTF_ARRAY_LENGTH(keys), &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks));
666     auto descriptor = adoptCF(CTFontDescriptorCreateWithAttributes(attributes.get()));
667     if (auto newFont = CTFontCreateWithFontDescriptor(descriptor.get(), size, nullptr))
668         CFRelease(newFont);
669 }
670 #endif
671
672 std::unique_ptr<FontPlatformData> FontCache::createFontPlatformData(const FontDescription& fontDescription, const AtomicString& family)
673 {
674     CTFontSymbolicTraits traits = computeTraits(fontDescription);
675     float size = fontDescription.computedPixelSize();
676
677     RetainPtr<CTFontRef> font = fontWithFamily(family, traits, fontDescription.weight(), fontDescription.featureSettings(), fontDescription.variantSettings(), fontDescription.textRenderingMode(), size);
678
679 #if PLATFORM(MAC)
680     if (!font) {
681         if (!shouldAutoActivateFontIfNeeded(family))
682             return nullptr;
683
684         // Auto activate the font before looking for it a second time.
685         // Ignore the result because we want to use our own algorithm to actually find the font.
686         autoActivateFont(family.string(), size);
687
688         font = fontWithFamily(family, traits, fontDescription.weight(), fontDescription.featureSettings(), fontDescription.variantSettings(), fontDescription.textRenderingMode(), size);
689     }
690 #endif
691
692     if (!font)
693         return nullptr;
694
695     bool syntheticBold, syntheticOblique;
696     std::tie(syntheticBold, syntheticOblique) = computeNecessarySynthesis(font.get(), fontDescription).boldObliquePair();
697
698     return std::make_unique<FontPlatformData>(font.get(), size, syntheticBold, syntheticOblique, fontDescription.orientation(), fontDescription.widthVariant(), fontDescription.textRenderingMode());
699 }
700
701 typedef HashSet<RetainPtr<CTFontRef>, WTF::RetainPtrObjectHash<CTFontRef>, WTF::RetainPtrObjectHashTraits<CTFontRef>> FallbackDedupSet;
702 static FallbackDedupSet& fallbackDedupSet()
703 {
704     static NeverDestroyed<FallbackDedupSet> dedupSet;
705     return dedupSet.get();
706 }
707
708 void FontCache::platformPurgeInactiveFontData()
709 {
710     Vector<CTFontRef> toRemove;
711     for (auto& font : fallbackDedupSet()) {
712         if (CFGetRetainCount(font.get()) == 1)
713             toRemove.append(font.get());
714     }
715     for (auto& font : toRemove)
716         fallbackDedupSet().remove(font);
717 }
718
719 RefPtr<Font> FontCache::systemFallbackForCharacters(const FontDescription& description, const Font* originalFontData, bool isPlatformFont, const UChar* characters, unsigned length)
720 {
721 #if PLATFORM(IOS)
722     if (length && requiresCustomFallbackFont(*characters)) {
723         auto* fallback = getCustomFallbackFont(*characters, description);
724         if (!fallback)
725             return nullptr;
726         return fontForPlatformData(*fallback);
727     }
728 #endif
729
730     const FontPlatformData& platformData = originalFontData->platformData();
731     // FIXME: Should pass in the locale instead of nullAtom.
732     RetainPtr<CTFontRef> result = platformLookupFallbackFont(platformData.font(), description.weight(), nullAtom, characters, length);
733     result = preparePlatformFont(result.get(), description.textRenderingMode(), description.featureSettings(), description.variantSettings());
734     if (!result)
735         return lastResortFallbackFont(description);
736
737     // FontCascade::drawGlyphBuffer() requires that there are no duplicate Font objects which refer to the same thing. This is enforced in
738     // FontCache::fontForPlatformData(), where our equality check is based on hashing the FontPlatformData, whose hash includes the raw CoreText
739     // font pointer.
740     CTFontRef substituteFont = fallbackDedupSet().add(result).iterator->get();
741
742     bool syntheticBold, syntheticOblique;
743     std::tie(syntheticBold, syntheticOblique) = computeNecessarySynthesis(substituteFont, description, isPlatformFont).boldObliquePair();
744
745     FontPlatformData alternateFont(substituteFont, platformData.size(), syntheticBold, syntheticOblique, platformData.m_orientation, platformData.m_widthVariant, platformData.m_textRenderingMode);
746
747     return fontForPlatformData(alternateFont);
748 }
749
750 }