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