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