All Indic text is rendered as boxes on iOS
[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 "Font.h"
33 #import "RenderThemeIOS.h"
34 #import <CoreGraphics/CGFontUnicodeSupport.h>
35 #import <CoreText/CTFontDescriptorPriv.h>
36 #import <CoreText/CTFontPriv.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     wkSetUpFontCache();
46 }
47
48 static inline bool isFontWeightBold(NSInteger fontWeight)
49 {
50     return fontWeight >= FontWeight600;
51 }
52
53 static inline bool requiresCustomFallbackFont(const UInt32 character)
54 {
55     return character == AppleLogo || character == blackCircle;
56 }
57
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
78 PassRefPtr<SimpleFontData> FontCache::getSystemFontFallbackForCharacters(const FontDescription& description, const SimpleFontData* originalFontData, const UChar* characters, int length)
79 {
80     const FontPlatformData& platformData = originalFontData->platformData();
81     CTFontRef ctFont = platformData.font();
82
83     CFIndex coveredLength = 0;
84     RetainPtr<CTFontRef> substituteFont = adoptCF(CTFontCreatePhysicalFontForCharactersWithLanguage(ctFont, (const UTF16Char*)characters, (CFIndex)length, 0, &coveredLength));
85     if (!substituteFont)
86         return 0;
87
88     CTFontSymbolicTraits originalTraits = CTFontGetSymbolicTraits(ctFont);
89     CTFontSymbolicTraits actualTraits = 0;
90     if (isFontWeightBold(description.weight()) || description.italic())
91         actualTraits = CTFontGetSymbolicTraits(substituteFont.get());
92
93     bool syntheticBold = (originalTraits & kCTFontTraitBold) && !(actualTraits & kCTFontTraitBold);
94     bool syntheticOblique = (originalTraits & kCTFontTraitItalic) && !(actualTraits & kCTFontTraitItalic);
95
96     FontPlatformData alternateFont(substituteFont.get(), platformData.size(), platformData.isPrinterFont(), syntheticBold, syntheticOblique, platformData.m_orientation);
97     alternateFont.m_isEmoji = CTFontIsAppleColorEmoji(substituteFont.get());
98
99     return getCachedFontData(&alternateFont, DoNotRetain);
100 }
101
102 enum class LanguageSpecificFont {
103     None,
104     ChineseJapanese,
105     Korean,
106     Cyrillic,
107     Arabic,
108     Hebrew,
109     Indic,
110     Thai,
111     Lao,
112     Tibetan,
113     CanadianAboriginalSyllabic,
114     Khmer,
115     Emoji,
116 };
117
118 static LanguageSpecificFont languageSpecificFallbackFont(UChar32 c)
119 {
120     static bool isGB18030ComplianceRequired = wkIsGB18030ComplianceRequired();
121
122     // The following ranges are Korean Hangul and should be rendered by AppleSDGothicNeo
123     // U+1100 - U+11FF
124     // U+3130 - U+318F
125     // U+AC00 - U+D7A3
126
127     // These are Cyrillic and should be rendered by Helvetica Neue
128     // U+0400 - U+052F
129
130     if (c < 0x400)
131         return LanguageSpecificFont::None;
132     if (c < 0x530)
133         return LanguageSpecificFont::Cyrillic;
134     if (c < 0x590)
135         return LanguageSpecificFont::None;
136     if (c < 0x600)
137         return LanguageSpecificFont::Hebrew;
138     if (c < 0x700)
139         return LanguageSpecificFont::Arabic;
140     if (c < 0x900)
141         return LanguageSpecificFont::None;
142     if (c < 0xE00)
143         return LanguageSpecificFont::Indic;
144     if (c < 0xE80)
145         return LanguageSpecificFont::Thai;
146     if (c < 0x0F00)
147         return LanguageSpecificFont::Lao;
148     if (c < 0x1000)
149         return LanguageSpecificFont::Tibetan;
150     if (c < 0x1100)
151         return LanguageSpecificFont::None;
152     if (c < 0x1200)
153         return LanguageSpecificFont::Korean;
154     if (c < 0x1401)
155         return LanguageSpecificFont::Khmer; // FIXME: These codepoints don't belong to Khmer
156     if (c < 0x1780)
157         return LanguageSpecificFont::CanadianAboriginalSyllabic;
158     if (c < 0x1800)
159         return LanguageSpecificFont::Khmer;
160     if (c < 0x2E80)
161         return LanguageSpecificFont::None;
162     if (c < 0x3130)
163         return LanguageSpecificFont::ChineseJapanese;
164     if (c < 0x3190)
165         return LanguageSpecificFont::Korean;
166     if (c < 0xAC00)
167         return LanguageSpecificFont::ChineseJapanese;
168     if (c < 0xD7A4)
169         return LanguageSpecificFont::Korean;
170     if (c < 0xE000)
171         return LanguageSpecificFont::ChineseJapanese;
172     if (c < 0xE600)
173         return isGB18030ComplianceRequired ? LanguageSpecificFont::ChineseJapanese : LanguageSpecificFont::Emoji;
174     if (c < 0xE865 && isGB18030ComplianceRequired)
175         return LanguageSpecificFont::ChineseJapanese;
176     if (c < 0xF900)
177         return LanguageSpecificFont::None;
178     if (c < 0xFB00)
179         return LanguageSpecificFont::ChineseJapanese;
180     if (c < 0xFB50)
181         return LanguageSpecificFont::None;
182     if (c < 0xFE00)
183         return LanguageSpecificFont::Arabic;
184     if (c < 0xFE20)
185         return LanguageSpecificFont::None;
186     if (c < 0xFE70)
187         return LanguageSpecificFont::ChineseJapanese;
188     if (c < 0xFF00)
189         return LanguageSpecificFont::Arabic;
190     if (c < 0xFFF0)
191         return LanguageSpecificFont::ChineseJapanese;
192     if (c < 0x20000)
193         return LanguageSpecificFont::None;
194     if (c < 0x30000)
195         return LanguageSpecificFont::ChineseJapanese;
196     return LanguageSpecificFont::None;
197 }
198
199 PassRefPtr<SimpleFontData> FontCache::systemFallbackForCharacters(const FontDescription& description, const SimpleFontData* originalFontData, bool, const UChar* characters, int length)
200 {
201     // Unlike OS X, our fallback font on iPhone is Arial Unicode, which doesn't have some apple-specific glyphs like F8FF.
202     // Fall back to the Apple Fallback font in this case.
203     if (length > 0 && requiresCustomFallbackFont(*characters))
204         return getCachedFontData(getCustomFallbackFont(*characters, description), DoNotRetain);
205
206     UChar32 c = *characters;
207     if (length > 1 && U16_IS_LEAD(c) && U16_IS_TRAIL(characters[1]))
208         c = U16_GET_SUPPLEMENTARY(c, characters[1]);
209
210     // For system fonts we use CoreText fallback mechanism.
211     if (length) {
212         RetainPtr<CTFontDescriptorRef> fontDescriptor = adoptCF(CTFontCopyFontDescriptor(originalFontData->getCTFont()));
213         if (CTFontDescriptorIsSystemUIFont(fontDescriptor.get()))
214             return getSystemFontFallbackForCharacters(description, originalFontData, characters, length);
215     }
216
217     LanguageSpecificFont languageSpecificFont = LanguageSpecificFont::None;
218     if (length > 0)
219         languageSpecificFont = languageSpecificFallbackFont(c);
220
221     RefPtr<SimpleFontData> simpleFontData;
222
223     switch (languageSpecificFont) {
224     case LanguageSpecificFont::ChineseJapanese: {
225         // By default, Chinese font is preferred, fall back on Japanese.
226
227         enum CJKFontVariant {
228             kCJKFontUseHiragino = 0,
229             kCJKFontUseSTHeitiSC,
230             kCJKFontUseSTHeitiTC,
231             kCJKFontUseSTHeitiJ,
232             kCJKFontUseSTHeitiK,
233             kCJKFontsUseHKGPW3UI
234         };
235
236         static NeverDestroyed<AtomicString> plainHiragino("HiraKakuProN-W3", AtomicString::ConstructFromLiteral);
237         static NeverDestroyed<AtomicString> plainSTHeitiSC("STHeitiSC-Light", AtomicString::ConstructFromLiteral);
238         static NeverDestroyed<AtomicString> plainSTHeitiTC("STHeitiTC-Light", AtomicString::ConstructFromLiteral);
239         static NeverDestroyed<AtomicString> plainSTHeitiJ("STHeitiJ-Light", AtomicString::ConstructFromLiteral);
240         static NeverDestroyed<AtomicString> plainSTHeitiK("STHeitiK-Light", AtomicString::ConstructFromLiteral);
241         static NeverDestroyed<AtomicString> plainHKGPW3UI("HKGPW3UI", AtomicString::ConstructFromLiteral);
242         static AtomicString* cjkPlain[] = {     
243             &plainHiragino.get(),
244             &plainSTHeitiSC.get(),
245             &plainSTHeitiTC.get(),
246             &plainSTHeitiJ.get(),
247             &plainSTHeitiK.get(),
248             &plainHKGPW3UI.get(),
249         };
250
251         static NeverDestroyed<AtomicString> boldHiragino("HiraKakuProN-W6", AtomicString::ConstructFromLiteral);
252         static NeverDestroyed<AtomicString> boldSTHeitiSC("STHeitiSC-Medium", AtomicString::ConstructFromLiteral);
253         static NeverDestroyed<AtomicString> boldSTHeitiTC("STHeitiTC-Medium", AtomicString::ConstructFromLiteral);
254         static NeverDestroyed<AtomicString> boldSTHeitiJ("STHeitiJ-Medium", AtomicString::ConstructFromLiteral);
255         static NeverDestroyed<AtomicString> boldSTHeitiK("STHeitiK-Medium", AtomicString::ConstructFromLiteral);
256         static NeverDestroyed<AtomicString> boldHKGPW3UI("HKGPW3UI", AtomicString::ConstructFromLiteral);
257         static AtomicString* cjkBold[] = {  
258             &boldHiragino.get(),
259             &boldSTHeitiSC.get(),
260             &boldSTHeitiTC.get(),
261             &boldSTHeitiJ.get(),
262             &boldSTHeitiK.get(),
263             &boldHKGPW3UI.get(),
264         };
265
266         // Default below is for Simplified Chinese user: zh-Hans - note that Hiragino is the
267         // the secondary font as we want its for Hiragana and Katakana. The other CJK fonts
268         // do not, and should not, contain Hiragana or Katakana glyphs.
269         static CJKFontVariant preferredCJKFont = kCJKFontUseSTHeitiSC;
270         static CJKFontVariant secondaryCJKFont = kCJKFontsUseHKGPW3UI;
271
272         static bool CJKFontInitialized;
273         if (!CJKFontInitialized) {
274             CJKFontInitialized = true;
275             // Testing: languageName = (CFStringRef)@"ja";
276             NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
277             NSArray *languages = [defaults stringArrayForKey:@"AppleLanguages"];
278
279             if (languages) {
280                 for (NSString *language in languages) {
281                     RetainPtr<CFStringRef> languageName = adoptCF(CFLocaleCreateCanonicalLanguageIdentifierFromString(nullptr, (CFStringRef)language));
282                     if (CFEqual(languageName.get(), CFSTR("zh-Hans")))
283                         break; // Simplified Chinese - default settings
284                     else if (CFEqual(languageName.get(), CFSTR("ja"))) {
285                         preferredCJKFont = kCJKFontUseHiragino; // Japanese - prefer Hiragino and STHeiti Japanse Variant
286                         secondaryCJKFont = kCJKFontUseSTHeitiJ;
287                         break;
288                     } else if (CFEqual(languageName.get(), CFSTR("ko"))) {
289                         preferredCJKFont = kCJKFontUseSTHeitiK; // Korean - prefer STHeiti Korean Variant 
290                         break;
291                     } else if (CFEqual(languageName.get(), CFSTR("zh-Hant"))) {
292                         preferredCJKFont = kCJKFontUseSTHeitiTC; // Traditional Chinese - prefer STHeiti Traditional Variant
293                         break;
294                     }
295                 }
296             }
297         }
298
299         simpleFontData = getCachedFontData(description, isFontWeightBold(description.weight()) ? *cjkBold[preferredCJKFont] : *cjkPlain[preferredCJKFont], false, DoNotRetain);
300         bool useSecondaryFont = true;
301         if (simpleFontData) {
302             CGGlyph glyphs[2];
303             // CGFontGetGlyphsForUnichars takes UTF-16 buffer. Should only be 1 codepoint but since we may pass in two UTF-16 characters,
304             // make room for 2 glyphs just to be safe.
305             CGFontGetGlyphsForUnichars(simpleFontData->platformData().cgFont(), characters, glyphs, length);
306
307             useSecondaryFont = (glyphs[0] == 0);
308         }
309
310         if (useSecondaryFont)
311             simpleFontData = getCachedFontData(description, isFontWeightBold(description.weight()) ? *cjkBold[secondaryCJKFont] : *cjkPlain[secondaryCJKFont], false, DoNotRetain);
312         break;
313     }
314     case LanguageSpecificFont::Korean: {
315         static NeverDestroyed<AtomicString> koreanPlain("AppleSDGothicNeo-Medium", AtomicString::ConstructFromLiteral);
316         static NeverDestroyed<AtomicString> koreanBold("AppleSDGothicNeo-Bold", AtomicString::ConstructFromLiteral);
317         simpleFontData = getCachedFontData(description, isFontWeightBold(description.weight()) ? koreanBold : koreanPlain, false, DoNotRetain);
318         break;
319     }
320     case LanguageSpecificFont::Cyrillic: {
321         static NeverDestroyed<AtomicString> cyrillicPlain("HelveticaNeue", AtomicString::ConstructFromLiteral);
322         static NeverDestroyed<AtomicString> cyrillicBold("HelveticaNeue-Bold", AtomicString::ConstructFromLiteral);
323         simpleFontData = getCachedFontData(description, isFontWeightBold(description.weight()) ? cyrillicBold : cyrillicPlain, false, DoNotRetain);
324         break;
325     }
326     case LanguageSpecificFont::Arabic: {
327         static NeverDestroyed<AtomicString> arabicPlain("GeezaPro", AtomicString::ConstructFromLiteral);
328         static NeverDestroyed<AtomicString> arabicBold("GeezaPro-Bold", AtomicString::ConstructFromLiteral);
329         simpleFontData = getCachedFontData(description, isFontWeightBold(description.weight()) ? arabicBold : arabicPlain, false, DoNotRetain);
330         break;
331     }
332     case LanguageSpecificFont::Hebrew: {
333         static NeverDestroyed<AtomicString> hebrewPlain("ArialHebrew", AtomicString::ConstructFromLiteral);
334         static NeverDestroyed<AtomicString> hebrewBold("ArialHebrew-Bold", AtomicString::ConstructFromLiteral);
335         simpleFontData = getCachedFontData(description, isFontWeightBold(description.weight()) ? hebrewBold : hebrewPlain, false, DoNotRetain);
336         break;
337     }
338     case LanguageSpecificFont::Indic: {
339         static NeverDestroyed<AtomicString> devanagariFont("KohinoorDevanagari-Book", AtomicString::ConstructFromLiteral);
340         static NeverDestroyed<AtomicString> bengaliFont("BanglaSangamMN", AtomicString::ConstructFromLiteral);
341         static NeverDestroyed<AtomicString> gurmukhiFont("GurmukhiMN", AtomicString::ConstructFromLiteral); // Might be replaced in a future release with a Sangam version.
342         static NeverDestroyed<AtomicString> gujaratiFont("GujaratiSangamMN", AtomicString::ConstructFromLiteral);
343         static NeverDestroyed<AtomicString> oriyaFont("OriyaSangamMN", AtomicString::ConstructFromLiteral);
344         static NeverDestroyed<AtomicString> tamilFont("TamilSangamMN", AtomicString::ConstructFromLiteral);
345         static NeverDestroyed<AtomicString> teluguFont("TeluguSangamMN", AtomicString::ConstructFromLiteral);
346         static NeverDestroyed<AtomicString> kannadaFont("KannadaSangamMN", AtomicString::ConstructFromLiteral);
347         static NeverDestroyed<AtomicString> malayalamFont("MalayalamSangamMN", AtomicString::ConstructFromLiteral);
348         static NeverDestroyed<AtomicString> sinhalaFont("SinhalaSangamMN", AtomicString::ConstructFromLiteral);
349
350         static NeverDestroyed<AtomicString> devanagariFontBold("KohinoorDevanagari-Medium", AtomicString::ConstructFromLiteral);
351         static NeverDestroyed<AtomicString> bengaliFontBold("BanglaSangamMN-Bold", AtomicString::ConstructFromLiteral);
352         static NeverDestroyed<AtomicString> gurmukhiFontBold("GurmukhiMN-Bold", AtomicString::ConstructFromLiteral); // Might be replaced in a future release with a Sangam version.
353         static NeverDestroyed<AtomicString> gujaratiFontBold("GujaratiSangamMN-Bold", AtomicString::ConstructFromLiteral);
354         static NeverDestroyed<AtomicString> oriyaFontBold("OriyaSangamMN-Bold", AtomicString::ConstructFromLiteral);
355         static NeverDestroyed<AtomicString> tamilFontBold("TamilSangamMN-Bold", AtomicString::ConstructFromLiteral);
356         static NeverDestroyed<AtomicString> teluguFontBold("TeluguSangamMN-Bold", AtomicString::ConstructFromLiteral);
357         static NeverDestroyed<AtomicString> kannadaFontBold("KannadaSangamMN-Bold", AtomicString::ConstructFromLiteral);
358         static NeverDestroyed<AtomicString> malayalamFontBold("MalayalamSangamMN-Bold", AtomicString::ConstructFromLiteral);
359         static NeverDestroyed<AtomicString> sinhalaFontBold("SinhalaSangamMN-Bold", AtomicString::ConstructFromLiteral);
360
361         static AtomicString* indicUnicodePageFonts[] = {
362             &devanagariFont.get(),
363             &bengaliFont.get(),
364             &gurmukhiFont.get(),
365             &gujaratiFont.get(),
366             &oriyaFont.get(),
367             &tamilFont.get(),
368             &teluguFont.get(),
369             &kannadaFont.get(),
370             &malayalamFont.get(),
371             &sinhalaFont.get()
372         };
373
374         static AtomicString* indicUnicodePageFontsBold[] = {
375             &devanagariFontBold.get(),
376             &bengaliFontBold.get(),
377             &gurmukhiFontBold.get(),
378             &gujaratiFontBold.get(),
379             &oriyaFontBold.get(),
380             &tamilFontBold.get(),
381             &teluguFontBold.get(),
382             &kannadaFontBold.get(),
383             &malayalamFontBold.get(),
384             &sinhalaFontBold.get()
385         };
386
387         uint32_t indicPageOrderIndex = (c - 0x0900) / 0x0080; // Indic scripts start at 0x0900 in Unicode. Each script is allocalted a block of 0x80 characters.
388         if (indicPageOrderIndex < (sizeof(indicUnicodePageFonts) / sizeof(AtomicString*))) {
389             AtomicString* indicFontString = isFontWeightBold(description.weight()) ? indicUnicodePageFontsBold[indicPageOrderIndex] : indicUnicodePageFonts[indicPageOrderIndex];
390             if (indicFontString)
391                 simpleFontData = getCachedFontData(description, *indicFontString, false, DoNotRetain);
392         }
393         break;
394     }
395     case LanguageSpecificFont::Thai: {
396         static NeverDestroyed<AtomicString> thaiPlain("Thonburi", AtomicString::ConstructFromLiteral);
397         static NeverDestroyed<AtomicString> thaiBold("Thonburi-Bold", AtomicString::ConstructFromLiteral);
398         simpleFontData = getCachedFontData(description, isFontWeightBold(description.weight()) ? thaiBold : thaiPlain, false, DoNotRetain);
399         break;
400     }
401     case LanguageSpecificFont::Tibetan: {
402         static NeverDestroyed<AtomicString> tibetanPlain("Kailasa", AtomicString::ConstructFromLiteral);
403         static NeverDestroyed<AtomicString> tibetanBold("Kailasa-Bold", AtomicString::ConstructFromLiteral);
404         simpleFontData = getCachedFontData(description, isFontWeightBold(description.weight()) ? tibetanBold : tibetanPlain, false, DoNotRetain);
405         break;
406     }
407     case LanguageSpecificFont::CanadianAboriginalSyllabic: {
408         static NeverDestroyed<AtomicString> casPlain("EuphemiaUCAS", AtomicString::ConstructFromLiteral);
409         static NeverDestroyed<AtomicString> casBold("EuphemiaUCAS-Bold", AtomicString::ConstructFromLiteral);
410         simpleFontData = getCachedFontData(description, isFontWeightBold(description.weight()) ? casBold : casPlain, false, DoNotRetain);
411         break;
412     }
413     case LanguageSpecificFont::Khmer: {
414         static NeverDestroyed<AtomicString> khmer("KhmerSangamMN", AtomicString::ConstructFromLiteral);
415         simpleFontData = getCachedFontData(description, khmer, false, DoNotRetain);
416         break;
417     }
418     case LanguageSpecificFont::Lao: {
419         static NeverDestroyed<AtomicString> lao("LaoSangamMN", AtomicString::ConstructFromLiteral);
420         simpleFontData = getCachedFontData(description, lao, false, DoNotRetain);
421         break;
422     }
423     default: {
424         static NeverDestroyed<AtomicString> appleColorEmoji("AppleColorEmoji", AtomicString::ConstructFromLiteral);
425         bool useEmojiFont = languageSpecificFont == LanguageSpecificFont::Emoji;
426         if (!useEmojiFont) {
427             if (!CFCharacterSetIsLongCharacterMember(phoneFallbackCharacterSet(), c))
428                 useEmojiFont = CFCharacterSetIsLongCharacterMember(appleColorEmojiCharacterSet(), c);
429         }
430         if (useEmojiFont)
431             simpleFontData = getCachedFontData(description, appleColorEmoji, false, DoNotRetain);
432         break;
433     }
434     }
435
436     if (simpleFontData)
437         return simpleFontData.release();
438
439     return getNonRetainedLastResortFallbackFont(description);
440 }
441
442 PassRefPtr<SimpleFontData> FontCache::similarFontPlatformData(const FontDescription& description)
443 {
444     // Attempt to find an appropriate font using a match based on the presence of keywords in
445     // the requested names. For example, we'll match any name that contains "Arabic" to Geeza Pro.
446     RefPtr<SimpleFontData> simpleFontData;
447     for (unsigned i = 0; i < description.familyCount(); ++i) {
448         const AtomicString& family = description.familyAt(i);
449         if (family.isEmpty())
450             continue;
451
452         // Substitute the default monospace font for well-known monospace fonts.
453         static NeverDestroyed<AtomicString> monaco("monaco", AtomicString::ConstructFromLiteral);
454         static NeverDestroyed<AtomicString> menlo("menlo", AtomicString::ConstructFromLiteral);
455         static NeverDestroyed<AtomicString> courier("courier", AtomicString::ConstructFromLiteral);
456         if (equalIgnoringCase(family, monaco) || equalIgnoringCase(family, menlo)) {
457             simpleFontData = getCachedFontData(description, courier);
458             continue;
459         }
460
461         // Substitute Verdana for Lucida Grande.
462         static NeverDestroyed<AtomicString> lucidaGrande("lucida grande", AtomicString::ConstructFromLiteral);
463         static NeverDestroyed<AtomicString> verdana("verdana", AtomicString::ConstructFromLiteral);
464         if (equalIgnoringCase(family, lucidaGrande)) {
465             simpleFontData = getCachedFontData(description, verdana);
466             continue;
467         }
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 (int j = 0; j < 3 && !simpleFontData; ++j)
476             if (family.contains(*matchWords[j], false))
477                 simpleFontData = getCachedFontData(description, isFontWeightBold(description.weight()) ? geezaBold : geezaPlain);
478     }
479
480     return simpleFontData.release();
481 }
482
483 PassRefPtr<SimpleFontData> FontCache::getLastResortFallbackFont(const FontDescription& fontDescription, ShouldRetain shouldRetain)
484 {
485     static NeverDestroyed<AtomicString> fallbackFontFamily(".PhoneFallback", AtomicString::ConstructFromLiteral);
486     return getCachedFontData(fontDescription, fallbackFontFamily, false, shouldRetain);
487 }
488
489 FontPlatformData* FontCache::getCustomFallbackFont(const UInt32 c, const FontDescription& description)
490 {
491     ASSERT(requiresCustomFallbackFont(c));
492     if (c == AppleLogo) {
493         static NeverDestroyed<AtomicString> helveticaFamily("Helvetica Neue", AtomicString::ConstructFromLiteral);
494         return getCachedFontPlatformData(description, helveticaFamily);
495     }
496     if (c == blackCircle) {
497         static NeverDestroyed<AtomicString> lockClockFamily("LockClock-Light", AtomicString::ConstructFromLiteral);
498         return getCachedFontPlatformData(description, lockClockFamily);
499     }
500     return nullptr;
501 }
502
503 static inline FontTraitsMask toTraitsMask(CTFontSymbolicTraits ctFontTraits)
504 {
505     return static_cast<FontTraitsMask>(((ctFontTraits & kCTFontTraitItalic) ? FontStyleItalicMask : FontStyleNormalMask)
506         | FontVariantNormalMask
507         // FontWeight600 or higher is bold for CTFonts, so choose middle values for
508         // bold (600-900) and non-bold (100-500)
509         | ((ctFontTraits & kCTFontTraitBold) ? FontWeight700Mask : FontWeight300Mask));
510 }
511
512 void FontCache::getTraitsInFamily(const AtomicString& familyName, Vector<unsigned>& traitsMasks)
513 {
514     RetainPtr<CFStringRef> familyNameStr = familyName.string().createCFString();
515     NSDictionary *attributes = @{ (id)kCTFontFamilyNameAttribute: (NSString*)familyNameStr.get() };
516     RetainPtr<CTFontDescriptorRef> fontDescriptor = adoptCF(CTFontDescriptorCreateWithAttributes((CFDictionaryRef)attributes));
517     RetainPtr<NSArray> matchedDescriptors = adoptNS((NSArray *)CTFontDescriptorCreateMatchingFontDescriptors(fontDescriptor.get(), nullptr));
518
519     NSInteger numMatches = [matchedDescriptors.get() count];
520     if (!matchedDescriptors || !numMatches)
521         return;
522
523     for (NSInteger i = 0; i < numMatches; ++i) {
524         RetainPtr<CFDictionaryRef> traits = adoptCF((CFDictionaryRef)CTFontDescriptorCopyAttribute((CTFontDescriptorRef)[matchedDescriptors.get() objectAtIndex:i], kCTFontTraitsAttribute));
525         CFNumberRef resultRef = (CFNumberRef)CFDictionaryGetValue(traits.get(), kCTFontSymbolicTrait);
526         if (resultRef) {
527             CTFontSymbolicTraits symbolicTraits;
528             CFNumberGetValue(resultRef, kCFNumberIntType, &symbolicTraits);
529             traitsMasks.append(toTraitsMask(symbolicTraits));
530         }
531     }
532 }
533
534 float FontCache::weightOfCTFont(CTFontRef font)
535 {
536     float result = 0;
537     RetainPtr<CFDictionaryRef> traits = adoptCF(CTFontCopyTraits(font));
538     if (!traits)
539         return result;
540
541     CFNumberRef resultRef = (CFNumberRef)CFDictionaryGetValue(traits.get(), kCTFontWeightTrait);
542     if (resultRef)
543         CFNumberGetValue(resultRef, kCFNumberFloatType, &result);
544
545     return result;
546 }
547
548 static CTFontRef createCTFontWithTextStyle(const String& familyName, CTFontSymbolicTraits traits, CGFloat size)
549 {
550     if (familyName.isNull())
551         return nullptr;
552
553     CTFontSymbolicTraits symbolicTraits = 0;
554     if (traits & kCTFontBoldTrait)
555         symbolicTraits |= kCTFontBoldTrait;
556     if (traits & kCTFontTraitItalic)
557         symbolicTraits |= kCTFontTraitItalic;
558     RetainPtr<CFStringRef> familyNameStr = familyName.createCFString();
559     RetainPtr<CTFontDescriptorRef> fontDescriptor = adoptCF(CTFontDescriptorCreateWithTextStyle(familyNameStr.get(), RenderThemeIOS::contentSizeCategory(), nullptr));
560     if (symbolicTraits)
561         fontDescriptor = adoptCF(CTFontDescriptorCreateCopyWithSymbolicTraits(fontDescriptor.get(), symbolicTraits, symbolicTraits));
562
563     return CTFontCreateWithFontDescriptor(fontDescriptor.get(), size, nullptr);
564 }
565
566 static CTFontRef createCTFontWithFamilyNameAndWeight(const String& familyName, CTFontSymbolicTraits traits, float size, uint16_t weight)
567 {
568     if (familyName.isNull())
569         return nullptr;
570
571     static NeverDestroyed<AtomicString> systemUIFontWithWebKitPrefix("-webkit-system-font", AtomicString::ConstructFromLiteral);
572     static NeverDestroyed<AtomicString> systemUIFontWithApplePrefix("-apple-system-font", AtomicString::ConstructFromLiteral);
573     if (equalIgnoringCase(familyName, systemUIFontWithWebKitPrefix) || equalIgnoringCase(familyName, systemUIFontWithApplePrefix)) {
574         CTFontUIFontType fontType = kCTFontUIFontSystem;
575         if (weight > 300) {
576             // The comment below has been copied from CoreText/UIFoundation. However, in WebKit we synthesize the oblique,
577             // so we should investigate the result <rdar://problem/14449340>:
578             // We don't do bold-italic for system fonts. If you ask for it, we'll assume that you're just kidding and that you really want bold. This is a feature.
579             if (traits & kCTFontTraitBold)
580                 fontType = kCTFontUIFontEmphasizedSystem;
581             else if (traits & kCTFontTraitItalic)
582                 fontType = static_cast<CTFontUIFontType>(kCTFontUIFontSystemItalic);
583         } else if (weight > 250)
584             fontType = static_cast<CTFontUIFontType>(kCTFontUIFontSystemLight);
585         else if (weight > 150)
586             fontType = static_cast<CTFontUIFontType>(kCTFontUIFontSystemThin);
587         else
588             fontType = static_cast<CTFontUIFontType>(kCTFontUIFontSystemUltraLight);
589         RetainPtr<CTFontDescriptorRef> fontDescriptor = adoptCF(CTFontDescriptorCreateForUIType(fontType, size, nullptr));
590         return CTFontCreateWithFontDescriptor(fontDescriptor.get(), size, nullptr);
591     }
592
593     RetainPtr<CFStringRef> familyNameStr = familyName.createCFString();
594     CTFontSymbolicTraits requestedTraits = (CTFontSymbolicTraits)(traits & (kCTFontBoldTrait | kCTFontItalicTrait));
595     return CTFontCreateForCSS(familyNameStr.get(), weight, requestedTraits, size);
596 }
597
598 static uint16_t toCTFontWeight(FontWeight fontWeight)
599 {
600     switch (fontWeight) {
601     case FontWeight100:
602         return 100;
603     case FontWeight200:
604         return 200;
605     case FontWeight300:
606         return 300;
607     case FontWeight400:
608         return 400;
609     case FontWeight500:
610         return 500;
611     case FontWeight600:
612         return 600;
613     case FontWeight700:
614         return 700;
615     case FontWeight800:
616         return 800;
617     case FontWeight900:
618         return 900;
619     default:
620         return 400;
621     }
622 }
623
624 PassOwnPtr<FontPlatformData> FontCache::createFontPlatformData(const FontDescription& fontDescription, const AtomicString& family)
625 {
626     // Special case for "Courier" font. We used to have only an oblique variant on iOS, so prior to
627     // iOS 6.0, we disallowed its use here. We'll fall back on "Courier New". <rdar://problem/5116477&10850227>
628     static NeverDestroyed<AtomicString> courier("Courier", AtomicString::ConstructFromLiteral);
629     static bool shouldDisallowCourier = !iosExecutableWasLinkedOnOrAfterVersion(wkIOSSystemVersion_6_0);
630     if (shouldDisallowCourier && equalIgnoringCase(family, courier))
631         return nullptr;
632
633     CTFontSymbolicTraits traits = 0;
634     if (fontDescription.italic())
635         traits |= kCTFontTraitItalic;
636     if (isFontWeightBold(fontDescription.weight()))
637         traits |= kCTFontTraitBold;
638     float size = fontDescription.computedPixelSize();
639
640     RetainPtr<CTFontRef> ctFont;
641     if (family.startsWith("UICTFontTextStyle"))
642         ctFont = adoptCF(createCTFontWithTextStyle(family, traits, size));
643     else
644         ctFont = adoptCF(createCTFontWithFamilyNameAndWeight(family, traits, size, toCTFontWeight(fontDescription.weight())));
645     if (!ctFont)
646         return nullptr;
647
648     CTFontSymbolicTraits actualTraits = 0;
649     if (isFontWeightBold(fontDescription.weight()) || fontDescription.italic())
650         actualTraits = CTFontGetSymbolicTraits(ctFont.get());
651
652     bool isAppleColorEmoji = CTFontIsAppleColorEmoji(ctFont.get());
653
654     bool syntheticBold = (traits & kCTFontTraitBold) && !(actualTraits & kCTFontTraitBold) && !isAppleColorEmoji;
655     bool syntheticOblique = (traits & kCTFontTraitItalic) && !(actualTraits & kCTFontTraitItalic) && !isAppleColorEmoji;
656
657     FontPlatformData* result = new FontPlatformData(ctFont.get(), size, fontDescription.usePrinterFont(), syntheticBold, syntheticOblique, fontDescription.orientation(), fontDescription.widthVariant());
658     if (isAppleColorEmoji)
659         result->m_isEmoji = true;
660     return adoptPtr(result);
661 }
662
663 } // namespace WebCore