Web Inspector: [iOS] Allow inspector to retrieve a list of system fonts
[WebKit-https.git] / Source / WebCore / platform / graphics / ios / FontCacheIOS.mm
1 /*
2  * Copyright (C) 2006, 2007, 2008, 2009 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  *
8  * 1.  Redistributions of source code must retain the above copyright
9  *     notice, this list of conditions and the following disclaimer. 
10  * 2.  Redistributions in binary form must reproduce the above copyright
11  *     notice, this list of conditions and the following disclaimer in the
12  *     documentation and/or other materials provided with the distribution. 
13  * 3.  Neither the name of Apple Inc. ("Apple") nor the names of
14  *     its contributors may be used to endorse or promote products derived
15  *     from this software without specific prior written permission. 
16  *
17  * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
18  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
19  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
20  * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
21  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
22  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
23  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
24  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27  */
28
29 #import "config.h"
30 #import "FontCache.h"
31
32 #import "CoreGraphicsSPI.h"
33 #import "CoreTextSPI.h"
34 #import "FontCascade.h"
35 #import "RenderThemeIOS.h"
36 #import <wtf/HashSet.h>
37 #import <wtf/NeverDestroyed.h>
38 #import <wtf/RetainPtr.h>
39 #import <wtf/text/CString.h>
40
41 namespace WebCore {
42
43 void FontCache::platformInit()
44 {
45 }
46
47 static inline bool isFontWeightBold(NSInteger fontWeight)
48 {
49     return fontWeight >= FontWeight600;
50 }
51
52 static inline bool requiresCustomFallbackFont(const UInt32 character)
53 {
54     return character == AppleLogo || character == blackCircle || character == narrowNonBreakingSpace;
55 }
56
57 #if __IPHONE_OS_VERSION_MIN_REQUIRED < 90000
58 static CFCharacterSetRef copyFontCharacterSet(CFStringRef fontName)
59 {
60     // The size, 10, is arbitrary.
61     RetainPtr<CTFontDescriptorRef> fontDescriptor = adoptCF(CTFontDescriptorCreateWithNameAndSize(fontName, 10));
62     RetainPtr<CTFontRef> font = adoptCF(CTFontCreateWithFontDescriptor(fontDescriptor.get(), 10, nullptr));
63     return (CFCharacterSetRef)CTFontDescriptorCopyAttribute(fontDescriptor.get(), kCTFontCharacterSetAttribute);
64 }
65
66 static CFCharacterSetRef appleColorEmojiCharacterSet()
67 {
68     static CFCharacterSetRef characterSet = copyFontCharacterSet(CFSTR("AppleColorEmoji"));
69     return characterSet;
70 }
71
72 static CFCharacterSetRef phoneFallbackCharacterSet()
73 {
74     static CFCharacterSetRef characterSet = copyFontCharacterSet(CFSTR(".PhoneFallback"));
75     return characterSet;
76 }
77 #endif
78
79 PassRefPtr<Font> FontCache::getSystemFontFallbackForCharacters(const FontDescription& description, const Font* originalFontData, const UChar* characters, unsigned length)
80 {
81     const FontPlatformData& platformData = originalFontData->platformData();
82     CTFontRef ctFont = platformData.font();
83
84     RetainPtr<CFStringRef> localeString;
85 #if __IPHONE_OS_VERSION_MIN_REQUIRED > 90000
86     if (!description.locale().isNull())
87         localeString = description.locale().string().createCFString();
88 #endif
89
90     CFIndex coveredLength = 0;
91     RetainPtr<CTFontRef> substituteFont = adoptCF(CTFontCreatePhysicalFontForCharactersWithLanguage(ctFont, (const UTF16Char*)characters, (CFIndex)length, localeString.get(), &coveredLength));
92     if (!substituteFont)
93         return nullptr;
94
95     substituteFont = applyFontFeatureSettings(substituteFont.get(), description.featureSettings());
96
97     CTFontSymbolicTraits originalTraits = CTFontGetSymbolicTraits(ctFont);
98     CTFontSymbolicTraits actualTraits = 0;
99     if (isFontWeightBold(description.weight()) || description.italic())
100         actualTraits = CTFontGetSymbolicTraits(substituteFont.get());
101
102     bool syntheticBold = (originalTraits & kCTFontTraitBold) && !(actualTraits & kCTFontTraitBold);
103     bool syntheticOblique = (originalTraits & kCTFontTraitItalic) && !(actualTraits & kCTFontTraitItalic);
104
105     FontPlatformData alternateFont(substituteFont.get(), platformData.size(), syntheticBold, syntheticOblique, platformData.m_orientation);
106     alternateFont.setIsEmoji(CTFontIsAppleColorEmoji(substituteFont.get()));
107
108     return fontForPlatformData(alternateFont);
109 }
110
111 #if __IPHONE_OS_VERSION_MIN_REQUIRED < 90000
112 enum class LanguageSpecificFont {
113     None,
114     ChineseJapanese,
115     Korean,
116     Cyrillic,
117     Arabic,
118     Hebrew,
119     Indic,
120     Thai,
121     Lao,
122     Tibetan,
123     CanadianAboriginalSyllabic,
124     Khmer,
125     Emoji,
126 };
127
128 static LanguageSpecificFont languageSpecificFallbackFont(UChar32 c)
129 {
130     static bool isGB18030ComplianceRequired = wkIsGB18030ComplianceRequired();
131
132     // The following ranges are Korean Hangul and should be rendered by AppleSDGothicNeo
133     // U+1100 - U+11FF
134     // U+3130 - U+318F
135     // U+AC00 - U+D7A3
136
137     // These are Cyrillic and should be rendered by Helvetica Neue
138     // U+0400 - U+052F
139
140     if (c < 0x400)
141         return LanguageSpecificFont::None;
142     if (c < 0x530)
143         return LanguageSpecificFont::Cyrillic;
144     if (c < 0x590)
145         return LanguageSpecificFont::None;
146     if (c < 0x600)
147         return LanguageSpecificFont::Hebrew;
148     if (c < 0x700)
149         return LanguageSpecificFont::Arabic;
150     if (c < 0x900)
151         return LanguageSpecificFont::None;
152     if (c < 0xE00)
153         return LanguageSpecificFont::Indic;
154     if (c < 0xE80)
155         return LanguageSpecificFont::Thai;
156     if (c < 0x0F00)
157         return LanguageSpecificFont::Lao;
158     if (c < 0x1000)
159         return LanguageSpecificFont::Tibetan;
160     if (c < 0x1100)
161         return LanguageSpecificFont::None;
162     if (c < 0x1200)
163         return LanguageSpecificFont::Korean;
164     if (c < 0x1400)
165         return LanguageSpecificFont::None;
166     if (c < 0x1780)
167         return LanguageSpecificFont::CanadianAboriginalSyllabic;
168     if (c < 0x1800)
169         return LanguageSpecificFont::Khmer;
170     if (c < 0x2E80)
171         return LanguageSpecificFont::None;
172     if (c < 0x3130)
173         return LanguageSpecificFont::ChineseJapanese;
174     if (c < 0x3190)
175         return LanguageSpecificFont::Korean;
176     if (c < 0xAC00)
177         return LanguageSpecificFont::ChineseJapanese;
178     if (c < 0xD7A4)
179         return LanguageSpecificFont::Korean;
180     if (c < 0xE000)
181         return LanguageSpecificFont::ChineseJapanese;
182     if (c < 0xE600)
183         return isGB18030ComplianceRequired ? LanguageSpecificFont::ChineseJapanese : LanguageSpecificFont::Emoji;
184     if (c < 0xE865 && isGB18030ComplianceRequired)
185         return LanguageSpecificFont::ChineseJapanese;
186     if (c < 0xF900)
187         return LanguageSpecificFont::None;
188     if (c < 0xFB00)
189         return LanguageSpecificFont::ChineseJapanese;
190     if (c < 0xFB50)
191         return LanguageSpecificFont::None;
192     if (c < 0xFE00)
193         return LanguageSpecificFont::Arabic;
194     if (c < 0xFE20)
195         return LanguageSpecificFont::None;
196     if (c < 0xFE70)
197         return LanguageSpecificFont::ChineseJapanese;
198     if (c < 0xFF00)
199         return LanguageSpecificFont::Arabic;
200     if (c < 0xFFF0)
201         return LanguageSpecificFont::ChineseJapanese;
202     if (c < 0x20000)
203         return LanguageSpecificFont::None;
204     if (c < 0x30000)
205         return LanguageSpecificFont::ChineseJapanese;
206     return LanguageSpecificFont::None;
207 }
208 #endif
209
210 RefPtr<Font> FontCache::systemFallbackForCharacters(const FontDescription& description, const Font* originalFontData, bool, const UChar* characters, unsigned length)
211 {
212     // Unlike OS X, our fallback font on iPhone is Arial Unicode, which doesn't have some apple-specific glyphs like F8FF.
213     // Fall back to the Apple Fallback font in this case.
214     if (length && requiresCustomFallbackFont(*characters)) {
215         auto* fallback = getCustomFallbackFont(*characters, description);
216         if (!fallback)
217             return nullptr;
218         return fontForPlatformData(*fallback);
219     }
220
221     UChar32 c = *characters;
222     if (length > 1 && U16_IS_LEAD(c) && U16_IS_TRAIL(characters[1]))
223         c = U16_GET_SUPPLEMENTARY(c, characters[1]);
224
225     // For system fonts we use CoreText fallback mechanism.
226     if (length) {
227         RetainPtr<CTFontDescriptorRef> fontDescriptor = adoptCF(CTFontCopyFontDescriptor(originalFontData->getCTFont()));
228         if (CTFontDescriptorIsSystemUIFont(fontDescriptor.get()))
229             return getSystemFontFallbackForCharacters(description, originalFontData, characters, length);
230     }
231
232     RefPtr<Font> font;
233
234 #if __IPHONE_OS_VERSION_MIN_REQUIRED < 90000
235     LanguageSpecificFont languageSpecificFont = LanguageSpecificFont::None;
236     if (length)
237         languageSpecificFont = languageSpecificFallbackFont(c);
238
239     switch (languageSpecificFont) {
240     case LanguageSpecificFont::ChineseJapanese: {
241         // By default, Chinese font is preferred, fall back on Japanese.
242
243         enum CJKFontVariant {
244             kCJKFontUseHiragino = 0,
245             kCJKFontUseSTHeitiSC,
246             kCJKFontUseSTHeitiTC,
247             kCJKFontUseSTHeitiJ,
248             kCJKFontUseSTHeitiK,
249             kCJKFontsUseHKGPW3UI
250         };
251
252         static NeverDestroyed<AtomicString> plainHiragino("HiraKakuProN-W3", AtomicString::ConstructFromLiteral);
253         static NeverDestroyed<AtomicString> plainSTHeitiSC("STHeitiSC-Light", AtomicString::ConstructFromLiteral);
254         static NeverDestroyed<AtomicString> plainSTHeitiTC("STHeitiTC-Light", AtomicString::ConstructFromLiteral);
255         static NeverDestroyed<AtomicString> plainSTHeitiJ("STHeitiJ-Light", AtomicString::ConstructFromLiteral);
256         static NeverDestroyed<AtomicString> plainSTHeitiK("STHeitiK-Light", AtomicString::ConstructFromLiteral);
257         static NeverDestroyed<AtomicString> plainHKGPW3UI("HKGPW3UI", AtomicString::ConstructFromLiteral);
258         static AtomicString* cjkPlain[] = {     
259             &plainHiragino.get(),
260             &plainSTHeitiSC.get(),
261             &plainSTHeitiTC.get(),
262             &plainSTHeitiJ.get(),
263             &plainSTHeitiK.get(),
264             &plainHKGPW3UI.get(),
265         };
266
267         static NeverDestroyed<AtomicString> boldHiragino("HiraKakuProN-W6", AtomicString::ConstructFromLiteral);
268         static NeverDestroyed<AtomicString> boldSTHeitiSC("STHeitiSC-Medium", AtomicString::ConstructFromLiteral);
269         static NeverDestroyed<AtomicString> boldSTHeitiTC("STHeitiTC-Medium", AtomicString::ConstructFromLiteral);
270         static NeverDestroyed<AtomicString> boldSTHeitiJ("STHeitiJ-Medium", AtomicString::ConstructFromLiteral);
271         static NeverDestroyed<AtomicString> boldSTHeitiK("STHeitiK-Medium", AtomicString::ConstructFromLiteral);
272         static NeverDestroyed<AtomicString> boldHKGPW3UI("HKGPW3UI", AtomicString::ConstructFromLiteral);
273         static AtomicString* cjkBold[] = {  
274             &boldHiragino.get(),
275             &boldSTHeitiSC.get(),
276             &boldSTHeitiTC.get(),
277             &boldSTHeitiJ.get(),
278             &boldSTHeitiK.get(),
279             &boldHKGPW3UI.get(),
280         };
281
282         // Default below is for Simplified Chinese user: zh-Hans - note that Hiragino is the
283         // the secondary font as we want its for Hiragana and Katakana. The other CJK fonts
284         // do not, and should not, contain Hiragana or Katakana glyphs.
285         static CJKFontVariant preferredCJKFont = kCJKFontUseSTHeitiSC;
286         static CJKFontVariant secondaryCJKFont = kCJKFontsUseHKGPW3UI;
287
288         static bool CJKFontInitialized;
289         if (!CJKFontInitialized) {
290             CJKFontInitialized = true;
291             // Testing: languageName = (CFStringRef)@"ja";
292             NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
293             NSArray *languages = [defaults stringArrayForKey:@"AppleLanguages"];
294
295             if (languages) {
296                 for (NSString *language in languages) {
297                     RetainPtr<CFStringRef> languageName = adoptCF(CFLocaleCreateCanonicalLanguageIdentifierFromString(nullptr, (CFStringRef)language));
298                     if (CFEqual(languageName.get(), CFSTR("zh-Hans")))
299                         break; // Simplified Chinese - default settings
300                     else if (CFEqual(languageName.get(), CFSTR("ja"))) {
301                         preferredCJKFont = kCJKFontUseHiragino; // Japanese - prefer Hiragino and STHeiti Japanse Variant
302                         secondaryCJKFont = kCJKFontUseSTHeitiJ;
303                         break;
304                     } else if (CFEqual(languageName.get(), CFSTR("ko"))) {
305                         preferredCJKFont = kCJKFontUseSTHeitiK; // Korean - prefer STHeiti Korean Variant 
306                         break;
307                     } else if (CFEqual(languageName.get(), CFSTR("zh-Hant"))) {
308                         preferredCJKFont = kCJKFontUseSTHeitiTC; // Traditional Chinese - prefer STHeiti Traditional Variant
309                         break;
310                     }
311                 }
312             }
313         }
314
315         font = fontForFamily(description, isFontWeightBold(description.weight()) ? *cjkBold[preferredCJKFont] : *cjkPlain[preferredCJKFont], false);
316         bool useSecondaryFont = true;
317         if (font) {
318             CGGlyph glyphs[2];
319             // CGFontGetGlyphsForUnichars takes UTF-16 buffer. Should only be 1 codepoint but since we may pass in two UTF-16 characters,
320             // make room for 2 glyphs just to be safe.
321             CGFontGetGlyphsForUnichars(font->platformData().cgFont(), characters, glyphs, length);
322
323             useSecondaryFont = (glyphs[0] == 0);
324         }
325
326         if (useSecondaryFont)
327             font = fontForFamily(description, isFontWeightBold(description.weight()) ? *cjkBold[secondaryCJKFont] : *cjkPlain[secondaryCJKFont], false);
328         break;
329     }
330     case LanguageSpecificFont::Korean: {
331         static NeverDestroyed<AtomicString> koreanPlain("AppleSDGothicNeo-Medium", AtomicString::ConstructFromLiteral);
332         static NeverDestroyed<AtomicString> koreanBold("AppleSDGothicNeo-Bold", AtomicString::ConstructFromLiteral);
333         font = fontForFamily(description, isFontWeightBold(description.weight()) ? koreanBold : koreanPlain, false);
334         break;
335     }
336     case LanguageSpecificFont::Cyrillic: {
337         static NeverDestroyed<AtomicString> cyrillicPlain("HelveticaNeue", AtomicString::ConstructFromLiteral);
338         static NeverDestroyed<AtomicString> cyrillicBold("HelveticaNeue-Bold", AtomicString::ConstructFromLiteral);
339         font = fontForFamily(description, isFontWeightBold(description.weight()) ? cyrillicBold : cyrillicPlain, false);
340         break;
341     }
342     case LanguageSpecificFont::Arabic: {
343         static NeverDestroyed<AtomicString> arabicPlain("GeezaPro", AtomicString::ConstructFromLiteral);
344         static NeverDestroyed<AtomicString> arabicBold("GeezaPro-Bold", AtomicString::ConstructFromLiteral);
345         font = fontForFamily(description, isFontWeightBold(description.weight()) ? arabicBold : arabicPlain, false);
346         break;
347     }
348     case LanguageSpecificFont::Hebrew: {
349         static NeverDestroyed<AtomicString> hebrewPlain("ArialHebrew", AtomicString::ConstructFromLiteral);
350         static NeverDestroyed<AtomicString> hebrewBold("ArialHebrew-Bold", AtomicString::ConstructFromLiteral);
351         font = fontForFamily(description, isFontWeightBold(description.weight()) ? hebrewBold : hebrewPlain, false);
352         break;
353     }
354     case LanguageSpecificFont::Indic: {
355         static NeverDestroyed<AtomicString> devanagariFont("KohinoorDevanagari-Book", AtomicString::ConstructFromLiteral);
356         static NeverDestroyed<AtomicString> bengaliFont("BanglaSangamMN", AtomicString::ConstructFromLiteral);
357         static NeverDestroyed<AtomicString> gurmukhiFont("GurmukhiMN", AtomicString::ConstructFromLiteral); // Might be replaced in a future release with a Sangam version.
358         static NeverDestroyed<AtomicString> gujaratiFont("GujaratiSangamMN", AtomicString::ConstructFromLiteral);
359         static NeverDestroyed<AtomicString> oriyaFont("OriyaSangamMN", AtomicString::ConstructFromLiteral);
360         static NeverDestroyed<AtomicString> tamilFont("TamilSangamMN", AtomicString::ConstructFromLiteral);
361         static NeverDestroyed<AtomicString> teluguFont("TeluguSangamMN", AtomicString::ConstructFromLiteral);
362         static NeverDestroyed<AtomicString> kannadaFont("KannadaSangamMN", AtomicString::ConstructFromLiteral);
363         static NeverDestroyed<AtomicString> malayalamFont("MalayalamSangamMN", AtomicString::ConstructFromLiteral);
364         static NeverDestroyed<AtomicString> sinhalaFont("SinhalaSangamMN", AtomicString::ConstructFromLiteral);
365
366         static NeverDestroyed<AtomicString> devanagariFontBold("KohinoorDevanagari-Medium", AtomicString::ConstructFromLiteral);
367         static NeverDestroyed<AtomicString> bengaliFontBold("BanglaSangamMN-Bold", AtomicString::ConstructFromLiteral);
368         static NeverDestroyed<AtomicString> gurmukhiFontBold("GurmukhiMN-Bold", AtomicString::ConstructFromLiteral); // Might be replaced in a future release with a Sangam version.
369         static NeverDestroyed<AtomicString> gujaratiFontBold("GujaratiSangamMN-Bold", AtomicString::ConstructFromLiteral);
370         static NeverDestroyed<AtomicString> oriyaFontBold("OriyaSangamMN-Bold", AtomicString::ConstructFromLiteral);
371         static NeverDestroyed<AtomicString> tamilFontBold("TamilSangamMN-Bold", AtomicString::ConstructFromLiteral);
372         static NeverDestroyed<AtomicString> teluguFontBold("TeluguSangamMN-Bold", AtomicString::ConstructFromLiteral);
373         static NeverDestroyed<AtomicString> kannadaFontBold("KannadaSangamMN-Bold", AtomicString::ConstructFromLiteral);
374         static NeverDestroyed<AtomicString> malayalamFontBold("MalayalamSangamMN-Bold", AtomicString::ConstructFromLiteral);
375         static NeverDestroyed<AtomicString> sinhalaFontBold("SinhalaSangamMN-Bold", AtomicString::ConstructFromLiteral);
376
377         static AtomicString* indicUnicodePageFonts[] = {
378             &devanagariFont.get(),
379             &bengaliFont.get(),
380             &gurmukhiFont.get(),
381             &gujaratiFont.get(),
382             &oriyaFont.get(),
383             &tamilFont.get(),
384             &teluguFont.get(),
385             &kannadaFont.get(),
386             &malayalamFont.get(),
387             &sinhalaFont.get()
388         };
389
390         static AtomicString* indicUnicodePageFontsBold[] = {
391             &devanagariFontBold.get(),
392             &bengaliFontBold.get(),
393             &gurmukhiFontBold.get(),
394             &gujaratiFontBold.get(),
395             &oriyaFontBold.get(),
396             &tamilFontBold.get(),
397             &teluguFontBold.get(),
398             &kannadaFontBold.get(),
399             &malayalamFontBold.get(),
400             &sinhalaFontBold.get()
401         };
402
403         uint32_t indicPageOrderIndex = (c - 0x0900) / 0x0080; // Indic scripts start at 0x0900 in Unicode. Each script is allocalted a block of 0x80 characters.
404         if (indicPageOrderIndex < (sizeof(indicUnicodePageFonts) / sizeof(AtomicString*))) {
405             AtomicString* indicFontString = isFontWeightBold(description.weight()) ? indicUnicodePageFontsBold[indicPageOrderIndex] : indicUnicodePageFonts[indicPageOrderIndex];
406             if (indicFontString)
407                 font = fontForFamily(description, *indicFontString, false);
408         }
409         break;
410     }
411     case LanguageSpecificFont::Thai: {
412         static NeverDestroyed<AtomicString> thaiPlain("Thonburi", AtomicString::ConstructFromLiteral);
413         static NeverDestroyed<AtomicString> thaiBold("Thonburi-Bold", AtomicString::ConstructFromLiteral);
414         font = fontForFamily(description, isFontWeightBold(description.weight()) ? thaiBold : thaiPlain, false);
415         break;
416     }
417     case LanguageSpecificFont::Tibetan: {
418         static NeverDestroyed<AtomicString> tibetanPlain("Kailasa", AtomicString::ConstructFromLiteral);
419         static NeverDestroyed<AtomicString> tibetanBold("Kailasa-Bold", AtomicString::ConstructFromLiteral);
420         font = fontForFamily(description, isFontWeightBold(description.weight()) ? tibetanBold : tibetanPlain, false);
421         break;
422     }
423     case LanguageSpecificFont::CanadianAboriginalSyllabic: {
424         static NeverDestroyed<AtomicString> casPlain("EuphemiaUCAS", AtomicString::ConstructFromLiteral);
425         static NeverDestroyed<AtomicString> casBold("EuphemiaUCAS-Bold", AtomicString::ConstructFromLiteral);
426         font = fontForFamily(description, isFontWeightBold(description.weight()) ? casBold : casPlain, false);
427         break;
428     }
429     case LanguageSpecificFont::Khmer: {
430         static NeverDestroyed<AtomicString> khmer("KhmerSangamMN", AtomicString::ConstructFromLiteral);
431         font = fontForFamily(description, khmer, false);
432         break;
433     }
434     case LanguageSpecificFont::Lao: {
435         static NeverDestroyed<AtomicString> lao("LaoSangamMN", AtomicString::ConstructFromLiteral);
436         font = fontForFamily(description, lao, false);
437         break;
438     }
439     default: {
440         static NeverDestroyed<AtomicString> appleColorEmoji("AppleColorEmoji", AtomicString::ConstructFromLiteral);
441         bool useEmojiFont = languageSpecificFont == LanguageSpecificFont::Emoji;
442         if (!useEmojiFont) {
443             if (!CFCharacterSetIsLongCharacterMember(phoneFallbackCharacterSet(), c))
444                 useEmojiFont = CFCharacterSetIsLongCharacterMember(appleColorEmojiCharacterSet(), c);
445         }
446         if (useEmojiFont)
447             font = fontForFamily(description, appleColorEmoji, false);
448         else {
449             RetainPtr<CTFontRef> fallbackFont = adoptCF(CTFontCreateForCharacters(originalFontData->getCTFont(), characters, length, nullptr));
450             if (RetainPtr<CFStringRef> foundFontName = adoptCF(CTFontCopyPostScriptName(fallbackFont.get())))
451                 font = fontForFamily(description, foundFontName.get(), false);
452         }
453         break;
454     }
455     }
456 #else
457     RetainPtr<CTFontDescriptorRef> fallbackFontDescriptor = adoptCF(CTFontCreatePhysicalFontDescriptorForCharactersWithLanguage(originalFontData->getCTFont(), characters, length, nullptr, nullptr));
458     if (auto foundFontName = adoptCF(static_cast<CFStringRef>(CTFontDescriptorCopyAttribute(fallbackFontDescriptor.get(), kCTFontNameAttribute)))) {
459         if (c >= 0x0600 && c <= 0x06ff) { // Arabic
460             auto familyName = adoptCF(static_cast<CFStringRef>(CTFontDescriptorCopyAttribute(fallbackFontDescriptor.get(), kCTFontFamilyNameAttribute)));
461             if (fontFamilyShouldNotBeUsedForArabic(familyName.get()))
462                 foundFontName = isFontWeightBold(description.weight()) ? CFSTR("GeezaPro-Bold") : CFSTR("GeezaPro");
463         }
464         font = fontForFamily(description, foundFontName.get(), false);
465     }
466 #endif
467
468     if (font)
469         return font.release();
470
471     return lastResortFallbackFont(description);
472 }
473
474 Vector<String> FontCache::systemFontFamilies()
475 {
476     // FIXME: <rdar://problem/21890188>
477     Vector<String> fontFamilies;
478     auto emptyFontDescriptor = adoptCF(CTFontDescriptorCreateWithAttributes((CFDictionaryRef) @{ }));
479     auto matchedDescriptors = adoptCF(CTFontDescriptorCreateMatchingFontDescriptors(emptyFontDescriptor.get(), nullptr));
480     if (!matchedDescriptors)
481         return fontFamilies;
482
483     CFIndex numMatches = CFArrayGetCount(matchedDescriptors.get());
484     if (!numMatches)
485         return fontFamilies;
486
487     HashSet<String> visited;
488     for (CFIndex i = 0; i < numMatches; ++i) {
489         auto fontDescriptor = static_cast<CTFontDescriptorRef>(CFArrayGetValueAtIndex(matchedDescriptors.get(), i));
490         if (auto familyName = adoptCF(static_cast<CFStringRef>(CTFontDescriptorCopyAttribute(fontDescriptor, kCTFontFamilyNameAttribute))))
491             visited.add(familyName.get());
492     }
493
494     copyToVector(visited, fontFamilies);
495     return fontFamilies;
496 }
497
498 RefPtr<Font> FontCache::similarFont(const FontDescription& description)
499 {
500     // Attempt to find an appropriate font using a match based on the presence of keywords in
501     // the requested names. For example, we'll match any name that contains "Arabic" to Geeza Pro.
502     RefPtr<Font> font;
503     for (unsigned i = 0; i < description.familyCount(); ++i) {
504         const AtomicString& family = description.familyAt(i);
505         if (family.isEmpty())
506             continue;
507
508         // Substitute the default monospace font for well-known monospace fonts.
509         static NeverDestroyed<AtomicString> monaco("monaco", AtomicString::ConstructFromLiteral);
510         static NeverDestroyed<AtomicString> menlo("menlo", AtomicString::ConstructFromLiteral);
511         static NeverDestroyed<AtomicString> courier("courier", AtomicString::ConstructFromLiteral);
512         if (equalIgnoringCase(family, monaco) || equalIgnoringCase(family, menlo)) {
513             font = fontForFamily(description, courier);
514             continue;
515         }
516
517         // Substitute Verdana for Lucida Grande.
518         static NeverDestroyed<AtomicString> lucidaGrande("lucida grande", AtomicString::ConstructFromLiteral);
519         static NeverDestroyed<AtomicString> verdana("verdana", AtomicString::ConstructFromLiteral);
520         if (equalIgnoringCase(family, lucidaGrande)) {
521             font = fontForFamily(description, verdana);
522             continue;
523         }
524
525         static NeverDestroyed<String> arabic(ASCIILiteral("Arabic"));
526         static NeverDestroyed<String> pashto(ASCIILiteral("Pashto"));
527         static NeverDestroyed<String> urdu(ASCIILiteral("Urdu"));
528         static String* matchWords[3] = { &arabic.get(), &pashto.get(), &urdu.get() };
529         static NeverDestroyed<AtomicString> geezaPlain("GeezaPro", AtomicString::ConstructFromLiteral);
530         static NeverDestroyed<AtomicString> geezaBold("GeezaPro-Bold", AtomicString::ConstructFromLiteral);
531         for (unsigned j = 0; j < 3 && !font; ++j) {
532             if (family.contains(*matchWords[j], false))
533                 font = fontForFamily(description, isFontWeightBold(description.weight()) ? geezaBold : geezaPlain);
534         }
535     }
536
537     return font.release();
538 }
539
540 Ref<Font> FontCache::lastResortFallbackFont(const FontDescription& fontDescription)
541 {
542     static NeverDestroyed<AtomicString> fallbackFontFamily(".PhoneFallback", AtomicString::ConstructFromLiteral);
543     return *fontForFamily(fontDescription, fallbackFontFamily, false);
544 }
545
546 FontPlatformData* FontCache::getCustomFallbackFont(const UInt32 c, const FontDescription& description)
547 {
548     ASSERT(requiresCustomFallbackFont(c));
549
550     static NeverDestroyed<AtomicString> helveticaFamily("Helvetica Neue", AtomicString::ConstructFromLiteral);
551     static NeverDestroyed<AtomicString> lockClockFamily("LockClock-Light", AtomicString::ConstructFromLiteral);
552     static NeverDestroyed<AtomicString> timesNewRomanPSMTFamily("TimesNewRomanPSMT", AtomicString::ConstructFromLiteral);
553
554     AtomicString* family = nullptr;
555     switch (c) {
556     case AppleLogo:
557         family = &helveticaFamily.get();
558         break;
559     case blackCircle:
560         family = &lockClockFamily.get();
561         break;
562     case narrowNonBreakingSpace:
563         family = &timesNewRomanPSMTFamily.get();
564         break;
565     default:
566         ASSERT_NOT_REACHED();
567         return nullptr;
568     }
569     ASSERT(family);
570     if (!family)
571         return nullptr;
572     return getCachedFontPlatformData(description, *family);
573 }
574
575 static inline FontTraitsMask toTraitsMask(CTFontSymbolicTraits ctFontTraits)
576 {
577     return static_cast<FontTraitsMask>(((ctFontTraits & kCTFontTraitItalic) ? FontStyleItalicMask : FontStyleNormalMask)
578         | FontVariantNormalMask
579         // FontWeight600 or higher is bold for CTFonts, so choose middle values for
580         // bold (600-900) and non-bold (100-500)
581         | ((ctFontTraits & kCTFontTraitBold) ? FontWeight700Mask : FontWeight300Mask));
582 }
583
584 void FontCache::getTraitsInFamily(const AtomicString& familyName, Vector<unsigned>& traitsMasks)
585 {
586     RetainPtr<CFStringRef> familyNameStr = familyName.string().createCFString();
587     NSDictionary *attributes = @{ (id)kCTFontFamilyNameAttribute: (NSString*)familyNameStr.get() };
588     RetainPtr<CTFontDescriptorRef> fontDescriptor = adoptCF(CTFontDescriptorCreateWithAttributes((CFDictionaryRef)attributes));
589     RetainPtr<NSArray> matchedDescriptors = adoptNS((NSArray *)CTFontDescriptorCreateMatchingFontDescriptors(fontDescriptor.get(), nullptr));
590
591     NSInteger numMatches = [matchedDescriptors.get() count];
592     if (!matchedDescriptors || !numMatches)
593         return;
594
595     for (NSInteger i = 0; i < numMatches; ++i) {
596         RetainPtr<CFDictionaryRef> traits = adoptCF((CFDictionaryRef)CTFontDescriptorCopyAttribute((CTFontDescriptorRef)[matchedDescriptors.get() objectAtIndex:i], kCTFontTraitsAttribute));
597         CFNumberRef resultRef = (CFNumberRef)CFDictionaryGetValue(traits.get(), kCTFontSymbolicTrait);
598         if (resultRef) {
599             CTFontSymbolicTraits symbolicTraits;
600             CFNumberGetValue(resultRef, kCFNumberIntType, &symbolicTraits);
601             traitsMasks.append(toTraitsMask(symbolicTraits));
602         }
603     }
604 }
605
606 float FontCache::weightOfCTFont(CTFontRef font)
607 {
608     float result = 0;
609     RetainPtr<CFDictionaryRef> traits = adoptCF(CTFontCopyTraits(font));
610     if (!traits)
611         return result;
612
613     CFNumberRef resultRef = (CFNumberRef)CFDictionaryGetValue(traits.get(), kCTFontWeightTrait);
614     if (resultRef)
615         CFNumberGetValue(resultRef, kCFNumberFloatType, &result);
616
617     return result;
618 }
619
620 static CTFontRef createCTFontWithTextStyle(const String& familyName, CTFontSymbolicTraits traits, CGFloat size)
621 {
622     if (familyName.isNull())
623         return nullptr;
624
625     CTFontSymbolicTraits symbolicTraits = 0;
626     if (traits & kCTFontBoldTrait)
627         symbolicTraits |= kCTFontBoldTrait;
628     if (traits & kCTFontTraitItalic)
629         symbolicTraits |= kCTFontTraitItalic;
630     RetainPtr<CFStringRef> familyNameStr = familyName.createCFString();
631     RetainPtr<CTFontDescriptorRef> fontDescriptor = adoptCF(CTFontDescriptorCreateWithTextStyle(familyNameStr.get(), RenderThemeIOS::contentSizeCategory(), nullptr));
632     if (symbolicTraits)
633         fontDescriptor = adoptCF(CTFontDescriptorCreateCopyWithSymbolicTraits(fontDescriptor.get(), symbolicTraits, symbolicTraits));
634
635     return CTFontCreateWithFontDescriptor(fontDescriptor.get(), size, nullptr);
636 }
637
638 static CTFontRef createCTFontWithFamilyNameAndWeight(const String& familyName, CTFontSymbolicTraits traits, float size, uint16_t weight)
639 {
640     if (familyName.isNull())
641         return nullptr;
642
643     static NeverDestroyed<AtomicString> systemUIFontWithWebKitPrefix("-webkit-system-font", AtomicString::ConstructFromLiteral);
644     static NeverDestroyed<AtomicString> systemUIFontWithApplePrefix("-apple-system", AtomicString::ConstructFromLiteral);
645     static NeverDestroyed<AtomicString> systemUIFontWithAppleAlternatePrefix("-apple-system-font", AtomicString::ConstructFromLiteral);
646     if (equalIgnoringCase(familyName, systemUIFontWithWebKitPrefix) || equalIgnoringCase(familyName, systemUIFontWithApplePrefix) || equalIgnoringCase(familyName, systemUIFontWithAppleAlternatePrefix)) {
647         CTFontUIFontType fontType = kCTFontUIFontSystem;
648         if (weight > 300) {
649             // The comment below has been copied from CoreText/UIFoundation. However, in WebKit we synthesize the oblique,
650             // so we should investigate the result <rdar://problem/14449340>:
651             if (traits & kCTFontTraitBold)
652                 fontType = kCTFontUIFontEmphasizedSystem;
653         } else if (weight > 250)
654             fontType = static_cast<CTFontUIFontType>(kCTFontUIFontSystemLight);
655         else if (weight > 150)
656             fontType = static_cast<CTFontUIFontType>(kCTFontUIFontSystemThin);
657         else
658             fontType = static_cast<CTFontUIFontType>(kCTFontUIFontSystemUltraLight);
659         RetainPtr<CTFontDescriptorRef> fontDescriptor = adoptCF(CTFontDescriptorCreateForUIType(fontType, size, nullptr));
660         if (traits & kCTFontTraitItalic)
661             fontDescriptor = adoptCF(CTFontDescriptorCreateCopyWithSymbolicTraits(fontDescriptor.get(), kCTFontItalicTrait, kCTFontItalicTrait));
662         return CTFontCreateWithFontDescriptor(fontDescriptor.get(), size, nullptr);
663     }
664
665     static NeverDestroyed<AtomicString> systemUIMonospacedNumbersFontWithApplePrefix("-apple-system-monospaced-numbers", AtomicString::ConstructFromLiteral);
666     if (equalIgnoringCase(familyName, systemUIMonospacedNumbersFontWithApplePrefix)) {
667         RetainPtr<CTFontDescriptorRef> systemFontDescriptor = adoptCF(CTFontDescriptorCreateForUIType(kCTFontUIFontSystem, size, nullptr));
668         RetainPtr<CTFontDescriptorRef> monospaceFontDescriptor = adoptCF(CTFontDescriptorCreateCopyWithFeature(systemFontDescriptor.get(), (CFNumberRef)@(kNumberSpacingType), (CFNumberRef)@(kMonospacedNumbersSelector)));
669         return CTFontCreateWithFontDescriptor(monospaceFontDescriptor.get(), size, nullptr);
670     }
671
672
673     RetainPtr<CFStringRef> familyNameStr = familyName.createCFString();
674     CTFontSymbolicTraits requestedTraits = (CTFontSymbolicTraits)(traits & (kCTFontBoldTrait | kCTFontItalicTrait));
675     return CTFontCreateForCSS(familyNameStr.get(), weight, requestedTraits, size);
676 }
677
678 static uint16_t toCTFontWeight(FontWeight fontWeight)
679 {
680     switch (fontWeight) {
681     case FontWeight100:
682         return 100;
683     case FontWeight200:
684         return 200;
685     case FontWeight300:
686         return 300;
687     case FontWeight400:
688         return 400;
689     case FontWeight500:
690         return 500;
691     case FontWeight600:
692         return 600;
693     case FontWeight700:
694         return 700;
695     case FontWeight800:
696         return 800;
697     case FontWeight900:
698         return 900;
699     default:
700         return 400;
701     }
702 }
703
704 std::unique_ptr<FontPlatformData> FontCache::createFontPlatformData(const FontDescription& fontDescription, const AtomicString& family)
705 {
706     CTFontSymbolicTraits traits = 0;
707     if (fontDescription.italic())
708         traits |= kCTFontTraitItalic;
709     if (isFontWeightBold(fontDescription.weight()))
710         traits |= kCTFontTraitBold;
711     float size = fontDescription.computedPixelSize();
712
713     RetainPtr<CTFontRef> ctFont;
714     if (family.startsWith("UICTFontTextStyle"))
715         ctFont = adoptCF(createCTFontWithTextStyle(family, traits, size));
716     else
717         ctFont = adoptCF(createCTFontWithFamilyNameAndWeight(family, traits, size, toCTFontWeight(fontDescription.weight())));
718     if (!ctFont)
719         return nullptr;
720
721     ctFont = applyFontFeatureSettings(ctFont.get(), fontDescription.featureSettings());
722
723     CTFontSymbolicTraits actualTraits = 0;
724     if (isFontWeightBold(fontDescription.weight()) || fontDescription.italic())
725         actualTraits = CTFontGetSymbolicTraits(ctFont.get());
726
727     bool isAppleColorEmoji = CTFontIsAppleColorEmoji(ctFont.get());
728
729     bool syntheticBold = (fontDescription.fontSynthesis() & FontSynthesisWeight) && (traits & kCTFontTraitBold) && !(actualTraits & kCTFontTraitBold) && !isAppleColorEmoji;
730     bool syntheticOblique = (fontDescription.fontSynthesis() & FontSynthesisStyle) && (traits & kCTFontTraitItalic) && !(actualTraits & kCTFontTraitItalic) && !isAppleColorEmoji;
731
732     auto result = std::make_unique<FontPlatformData>(ctFont.get(), size, syntheticBold, syntheticOblique, fontDescription.orientation(), fontDescription.widthVariant());
733     if (isAppleColorEmoji)
734         result->setIsEmoji(true);
735     return result;
736 }
737
738 } // namespace WebCore