2 * Copyright (C) 2006, 2007, 2008, 2009 Apple Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
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.
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.
32 #import "CoreGraphicsSPI.h"
33 #import "CoreTextSPI.h"
34 #import "FontCascade.h"
35 #import "RenderThemeIOS.h"
36 #import <wtf/NeverDestroyed.h>
37 #import <wtf/RetainPtr.h>
38 #import <wtf/text/CString.h>
42 void FontCache::platformInit()
46 static inline bool isFontWeightBold(NSInteger fontWeight)
48 return fontWeight >= FontWeight600;
51 static inline bool requiresCustomFallbackFont(const UInt32 character)
53 return character == AppleLogo || character == blackCircle || character == narrowNonBreakingSpace;
56 #if __IPHONE_OS_VERSION_MIN_REQUIRED < 90000
57 static CFCharacterSetRef copyFontCharacterSet(CFStringRef fontName)
59 // The size, 10, is arbitrary.
60 RetainPtr<CTFontDescriptorRef> fontDescriptor = adoptCF(CTFontDescriptorCreateWithNameAndSize(fontName, 10));
61 RetainPtr<CTFontRef> font = adoptCF(CTFontCreateWithFontDescriptor(fontDescriptor.get(), 10, nullptr));
62 return (CFCharacterSetRef)CTFontDescriptorCopyAttribute(fontDescriptor.get(), kCTFontCharacterSetAttribute);
65 static CFCharacterSetRef appleColorEmojiCharacterSet()
67 static CFCharacterSetRef characterSet = copyFontCharacterSet(CFSTR("AppleColorEmoji"));
71 static CFCharacterSetRef phoneFallbackCharacterSet()
73 static CFCharacterSetRef characterSet = copyFontCharacterSet(CFSTR(".PhoneFallback"));
78 PassRefPtr<Font> FontCache::getSystemFontFallbackForCharacters(const FontDescription& description, const Font* originalFontData, const UChar* characters, unsigned length)
80 const FontPlatformData& platformData = originalFontData->platformData();
81 CTFontRef ctFont = platformData.font();
83 CFIndex coveredLength = 0;
84 RetainPtr<CTFontRef> substituteFont = adoptCF(CTFontCreatePhysicalFontForCharactersWithLanguage(ctFont, (const UTF16Char*)characters, (CFIndex)length, 0, &coveredLength));
88 CTFontSymbolicTraits originalTraits = CTFontGetSymbolicTraits(ctFont);
89 CTFontSymbolicTraits actualTraits = 0;
90 if (isFontWeightBold(description.weight()) || description.italic())
91 actualTraits = CTFontGetSymbolicTraits(substituteFont.get());
93 bool syntheticBold = (originalTraits & kCTFontTraitBold) && !(actualTraits & kCTFontTraitBold);
94 bool syntheticOblique = (originalTraits & kCTFontTraitItalic) && !(actualTraits & kCTFontTraitItalic);
96 FontPlatformData alternateFont(substituteFont.get(), platformData.size(), syntheticBold, syntheticOblique, platformData.m_orientation);
97 alternateFont.m_isEmoji = CTFontIsAppleColorEmoji(substituteFont.get());
99 return fontForPlatformData(alternateFont);
102 #if __IPHONE_OS_VERSION_MIN_REQUIRED < 90000
103 enum class LanguageSpecificFont {
114 CanadianAboriginalSyllabic,
119 static LanguageSpecificFont languageSpecificFallbackFont(UChar32 c)
121 static bool isGB18030ComplianceRequired = wkIsGB18030ComplianceRequired();
123 // The following ranges are Korean Hangul and should be rendered by AppleSDGothicNeo
128 // These are Cyrillic and should be rendered by Helvetica Neue
132 return LanguageSpecificFont::None;
134 return LanguageSpecificFont::Cyrillic;
136 return LanguageSpecificFont::None;
138 return LanguageSpecificFont::Hebrew;
140 return LanguageSpecificFont::Arabic;
142 return LanguageSpecificFont::None;
144 return LanguageSpecificFont::Indic;
146 return LanguageSpecificFont::Thai;
148 return LanguageSpecificFont::Lao;
150 return LanguageSpecificFont::Tibetan;
152 return LanguageSpecificFont::None;
154 return LanguageSpecificFont::Korean;
156 return LanguageSpecificFont::None;
158 return LanguageSpecificFont::CanadianAboriginalSyllabic;
160 return LanguageSpecificFont::Khmer;
162 return LanguageSpecificFont::None;
164 return LanguageSpecificFont::ChineseJapanese;
166 return LanguageSpecificFont::Korean;
168 return LanguageSpecificFont::ChineseJapanese;
170 return LanguageSpecificFont::Korean;
172 return LanguageSpecificFont::ChineseJapanese;
174 return isGB18030ComplianceRequired ? LanguageSpecificFont::ChineseJapanese : LanguageSpecificFont::Emoji;
175 if (c < 0xE865 && isGB18030ComplianceRequired)
176 return LanguageSpecificFont::ChineseJapanese;
178 return LanguageSpecificFont::None;
180 return LanguageSpecificFont::ChineseJapanese;
182 return LanguageSpecificFont::None;
184 return LanguageSpecificFont::Arabic;
186 return LanguageSpecificFont::None;
188 return LanguageSpecificFont::ChineseJapanese;
190 return LanguageSpecificFont::Arabic;
192 return LanguageSpecificFont::ChineseJapanese;
194 return LanguageSpecificFont::None;
196 return LanguageSpecificFont::ChineseJapanese;
197 return LanguageSpecificFont::None;
201 RefPtr<Font> FontCache::systemFallbackForCharacters(const FontDescription& description, const Font* originalFontData, bool, const UChar* characters, unsigned length)
203 // Unlike OS X, our fallback font on iPhone is Arial Unicode, which doesn't have some apple-specific glyphs like F8FF.
204 // Fall back to the Apple Fallback font in this case.
205 if (length && requiresCustomFallbackFont(*characters)) {
206 auto* fallback = getCustomFallbackFont(*characters, description);
209 return fontForPlatformData(*fallback);
212 UChar32 c = *characters;
213 if (length > 1 && U16_IS_LEAD(c) && U16_IS_TRAIL(characters[1]))
214 c = U16_GET_SUPPLEMENTARY(c, characters[1]);
216 // For system fonts we use CoreText fallback mechanism.
218 RetainPtr<CTFontDescriptorRef> fontDescriptor = adoptCF(CTFontCopyFontDescriptor(originalFontData->getCTFont()));
219 if (CTFontDescriptorIsSystemUIFont(fontDescriptor.get()))
220 return getSystemFontFallbackForCharacters(description, originalFontData, characters, length);
225 #if __IPHONE_OS_VERSION_MIN_REQUIRED < 90000
226 LanguageSpecificFont languageSpecificFont = LanguageSpecificFont::None;
228 languageSpecificFont = languageSpecificFallbackFont(c);
230 switch (languageSpecificFont) {
231 case LanguageSpecificFont::ChineseJapanese: {
232 // By default, Chinese font is preferred, fall back on Japanese.
234 enum CJKFontVariant {
235 kCJKFontUseHiragino = 0,
236 kCJKFontUseSTHeitiSC,
237 kCJKFontUseSTHeitiTC,
243 static NeverDestroyed<AtomicString> plainHiragino("HiraKakuProN-W3", AtomicString::ConstructFromLiteral);
244 static NeverDestroyed<AtomicString> plainSTHeitiSC("STHeitiSC-Light", AtomicString::ConstructFromLiteral);
245 static NeverDestroyed<AtomicString> plainSTHeitiTC("STHeitiTC-Light", AtomicString::ConstructFromLiteral);
246 static NeverDestroyed<AtomicString> plainSTHeitiJ("STHeitiJ-Light", AtomicString::ConstructFromLiteral);
247 static NeverDestroyed<AtomicString> plainSTHeitiK("STHeitiK-Light", AtomicString::ConstructFromLiteral);
248 static NeverDestroyed<AtomicString> plainHKGPW3UI("HKGPW3UI", AtomicString::ConstructFromLiteral);
249 static AtomicString* cjkPlain[] = {
250 &plainHiragino.get(),
251 &plainSTHeitiSC.get(),
252 &plainSTHeitiTC.get(),
253 &plainSTHeitiJ.get(),
254 &plainSTHeitiK.get(),
255 &plainHKGPW3UI.get(),
258 static NeverDestroyed<AtomicString> boldHiragino("HiraKakuProN-W6", AtomicString::ConstructFromLiteral);
259 static NeverDestroyed<AtomicString> boldSTHeitiSC("STHeitiSC-Medium", AtomicString::ConstructFromLiteral);
260 static NeverDestroyed<AtomicString> boldSTHeitiTC("STHeitiTC-Medium", AtomicString::ConstructFromLiteral);
261 static NeverDestroyed<AtomicString> boldSTHeitiJ("STHeitiJ-Medium", AtomicString::ConstructFromLiteral);
262 static NeverDestroyed<AtomicString> boldSTHeitiK("STHeitiK-Medium", AtomicString::ConstructFromLiteral);
263 static NeverDestroyed<AtomicString> boldHKGPW3UI("HKGPW3UI", AtomicString::ConstructFromLiteral);
264 static AtomicString* cjkBold[] = {
266 &boldSTHeitiSC.get(),
267 &boldSTHeitiTC.get(),
273 // Default below is for Simplified Chinese user: zh-Hans - note that Hiragino is the
274 // the secondary font as we want its for Hiragana and Katakana. The other CJK fonts
275 // do not, and should not, contain Hiragana or Katakana glyphs.
276 static CJKFontVariant preferredCJKFont = kCJKFontUseSTHeitiSC;
277 static CJKFontVariant secondaryCJKFont = kCJKFontsUseHKGPW3UI;
279 static bool CJKFontInitialized;
280 if (!CJKFontInitialized) {
281 CJKFontInitialized = true;
282 // Testing: languageName = (CFStringRef)@"ja";
283 NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
284 NSArray *languages = [defaults stringArrayForKey:@"AppleLanguages"];
287 for (NSString *language in languages) {
288 RetainPtr<CFStringRef> languageName = adoptCF(CFLocaleCreateCanonicalLanguageIdentifierFromString(nullptr, (CFStringRef)language));
289 if (CFEqual(languageName.get(), CFSTR("zh-Hans")))
290 break; // Simplified Chinese - default settings
291 else if (CFEqual(languageName.get(), CFSTR("ja"))) {
292 preferredCJKFont = kCJKFontUseHiragino; // Japanese - prefer Hiragino and STHeiti Japanse Variant
293 secondaryCJKFont = kCJKFontUseSTHeitiJ;
295 } else if (CFEqual(languageName.get(), CFSTR("ko"))) {
296 preferredCJKFont = kCJKFontUseSTHeitiK; // Korean - prefer STHeiti Korean Variant
298 } else if (CFEqual(languageName.get(), CFSTR("zh-Hant"))) {
299 preferredCJKFont = kCJKFontUseSTHeitiTC; // Traditional Chinese - prefer STHeiti Traditional Variant
306 font = fontForFamily(description, isFontWeightBold(description.weight()) ? *cjkBold[preferredCJKFont] : *cjkPlain[preferredCJKFont], false);
307 bool useSecondaryFont = true;
310 // CGFontGetGlyphsForUnichars takes UTF-16 buffer. Should only be 1 codepoint but since we may pass in two UTF-16 characters,
311 // make room for 2 glyphs just to be safe.
312 CGFontGetGlyphsForUnichars(font->platformData().cgFont(), characters, glyphs, length);
314 useSecondaryFont = (glyphs[0] == 0);
317 if (useSecondaryFont)
318 font = fontForFamily(description, isFontWeightBold(description.weight()) ? *cjkBold[secondaryCJKFont] : *cjkPlain[secondaryCJKFont], false);
321 case LanguageSpecificFont::Korean: {
322 static NeverDestroyed<AtomicString> koreanPlain("AppleSDGothicNeo-Medium", AtomicString::ConstructFromLiteral);
323 static NeverDestroyed<AtomicString> koreanBold("AppleSDGothicNeo-Bold", AtomicString::ConstructFromLiteral);
324 font = fontForFamily(description, isFontWeightBold(description.weight()) ? koreanBold : koreanPlain, false);
327 case LanguageSpecificFont::Cyrillic: {
328 static NeverDestroyed<AtomicString> cyrillicPlain("HelveticaNeue", AtomicString::ConstructFromLiteral);
329 static NeverDestroyed<AtomicString> cyrillicBold("HelveticaNeue-Bold", AtomicString::ConstructFromLiteral);
330 font = fontForFamily(description, isFontWeightBold(description.weight()) ? cyrillicBold : cyrillicPlain, false);
333 case LanguageSpecificFont::Arabic: {
334 static NeverDestroyed<AtomicString> arabicPlain("GeezaPro", AtomicString::ConstructFromLiteral);
335 static NeverDestroyed<AtomicString> arabicBold("GeezaPro-Bold", AtomicString::ConstructFromLiteral);
336 font = fontForFamily(description, isFontWeightBold(description.weight()) ? arabicBold : arabicPlain, false);
339 case LanguageSpecificFont::Hebrew: {
340 static NeverDestroyed<AtomicString> hebrewPlain("ArialHebrew", AtomicString::ConstructFromLiteral);
341 static NeverDestroyed<AtomicString> hebrewBold("ArialHebrew-Bold", AtomicString::ConstructFromLiteral);
342 font = fontForFamily(description, isFontWeightBold(description.weight()) ? hebrewBold : hebrewPlain, false);
345 case LanguageSpecificFont::Indic: {
346 static NeverDestroyed<AtomicString> devanagariFont("KohinoorDevanagari-Book", AtomicString::ConstructFromLiteral);
347 static NeverDestroyed<AtomicString> bengaliFont("BanglaSangamMN", AtomicString::ConstructFromLiteral);
348 static NeverDestroyed<AtomicString> gurmukhiFont("GurmukhiMN", AtomicString::ConstructFromLiteral); // Might be replaced in a future release with a Sangam version.
349 static NeverDestroyed<AtomicString> gujaratiFont("GujaratiSangamMN", AtomicString::ConstructFromLiteral);
350 static NeverDestroyed<AtomicString> oriyaFont("OriyaSangamMN", AtomicString::ConstructFromLiteral);
351 static NeverDestroyed<AtomicString> tamilFont("TamilSangamMN", AtomicString::ConstructFromLiteral);
352 static NeverDestroyed<AtomicString> teluguFont("TeluguSangamMN", AtomicString::ConstructFromLiteral);
353 static NeverDestroyed<AtomicString> kannadaFont("KannadaSangamMN", AtomicString::ConstructFromLiteral);
354 static NeverDestroyed<AtomicString> malayalamFont("MalayalamSangamMN", AtomicString::ConstructFromLiteral);
355 static NeverDestroyed<AtomicString> sinhalaFont("SinhalaSangamMN", AtomicString::ConstructFromLiteral);
357 static NeverDestroyed<AtomicString> devanagariFontBold("KohinoorDevanagari-Medium", AtomicString::ConstructFromLiteral);
358 static NeverDestroyed<AtomicString> bengaliFontBold("BanglaSangamMN-Bold", AtomicString::ConstructFromLiteral);
359 static NeverDestroyed<AtomicString> gurmukhiFontBold("GurmukhiMN-Bold", AtomicString::ConstructFromLiteral); // Might be replaced in a future release with a Sangam version.
360 static NeverDestroyed<AtomicString> gujaratiFontBold("GujaratiSangamMN-Bold", AtomicString::ConstructFromLiteral);
361 static NeverDestroyed<AtomicString> oriyaFontBold("OriyaSangamMN-Bold", AtomicString::ConstructFromLiteral);
362 static NeverDestroyed<AtomicString> tamilFontBold("TamilSangamMN-Bold", AtomicString::ConstructFromLiteral);
363 static NeverDestroyed<AtomicString> teluguFontBold("TeluguSangamMN-Bold", AtomicString::ConstructFromLiteral);
364 static NeverDestroyed<AtomicString> kannadaFontBold("KannadaSangamMN-Bold", AtomicString::ConstructFromLiteral);
365 static NeverDestroyed<AtomicString> malayalamFontBold("MalayalamSangamMN-Bold", AtomicString::ConstructFromLiteral);
366 static NeverDestroyed<AtomicString> sinhalaFontBold("SinhalaSangamMN-Bold", AtomicString::ConstructFromLiteral);
368 static AtomicString* indicUnicodePageFonts[] = {
369 &devanagariFont.get(),
377 &malayalamFont.get(),
381 static AtomicString* indicUnicodePageFontsBold[] = {
382 &devanagariFontBold.get(),
383 &bengaliFontBold.get(),
384 &gurmukhiFontBold.get(),
385 &gujaratiFontBold.get(),
386 &oriyaFontBold.get(),
387 &tamilFontBold.get(),
388 &teluguFontBold.get(),
389 &kannadaFontBold.get(),
390 &malayalamFontBold.get(),
391 &sinhalaFontBold.get()
394 uint32_t indicPageOrderIndex = (c - 0x0900) / 0x0080; // Indic scripts start at 0x0900 in Unicode. Each script is allocalted a block of 0x80 characters.
395 if (indicPageOrderIndex < (sizeof(indicUnicodePageFonts) / sizeof(AtomicString*))) {
396 AtomicString* indicFontString = isFontWeightBold(description.weight()) ? indicUnicodePageFontsBold[indicPageOrderIndex] : indicUnicodePageFonts[indicPageOrderIndex];
398 font = fontForFamily(description, *indicFontString, false);
402 case LanguageSpecificFont::Thai: {
403 static NeverDestroyed<AtomicString> thaiPlain("Thonburi", AtomicString::ConstructFromLiteral);
404 static NeverDestroyed<AtomicString> thaiBold("Thonburi-Bold", AtomicString::ConstructFromLiteral);
405 font = fontForFamily(description, isFontWeightBold(description.weight()) ? thaiBold : thaiPlain, false);
408 case LanguageSpecificFont::Tibetan: {
409 static NeverDestroyed<AtomicString> tibetanPlain("Kailasa", AtomicString::ConstructFromLiteral);
410 static NeverDestroyed<AtomicString> tibetanBold("Kailasa-Bold", AtomicString::ConstructFromLiteral);
411 font = fontForFamily(description, isFontWeightBold(description.weight()) ? tibetanBold : tibetanPlain, false);
414 case LanguageSpecificFont::CanadianAboriginalSyllabic: {
415 static NeverDestroyed<AtomicString> casPlain("EuphemiaUCAS", AtomicString::ConstructFromLiteral);
416 static NeverDestroyed<AtomicString> casBold("EuphemiaUCAS-Bold", AtomicString::ConstructFromLiteral);
417 font = fontForFamily(description, isFontWeightBold(description.weight()) ? casBold : casPlain, false);
420 case LanguageSpecificFont::Khmer: {
421 static NeverDestroyed<AtomicString> khmer("KhmerSangamMN", AtomicString::ConstructFromLiteral);
422 font = fontForFamily(description, khmer, false);
425 case LanguageSpecificFont::Lao: {
426 static NeverDestroyed<AtomicString> lao("LaoSangamMN", AtomicString::ConstructFromLiteral);
427 font = fontForFamily(description, lao, false);
431 static NeverDestroyed<AtomicString> appleColorEmoji("AppleColorEmoji", AtomicString::ConstructFromLiteral);
432 bool useEmojiFont = languageSpecificFont == LanguageSpecificFont::Emoji;
434 if (!CFCharacterSetIsLongCharacterMember(phoneFallbackCharacterSet(), c))
435 useEmojiFont = CFCharacterSetIsLongCharacterMember(appleColorEmojiCharacterSet(), c);
438 font = fontForFamily(description, appleColorEmoji, false);
440 RetainPtr<CTFontRef> fallbackFont = adoptCF(CTFontCreateForCharacters(originalFontData->getCTFont(), characters, length, nullptr));
441 if (RetainPtr<CFStringRef> foundFontName = adoptCF(CTFontCopyPostScriptName(fallbackFont.get())))
442 font = fontForFamily(description, foundFontName.get(), false);
448 RetainPtr<CTFontDescriptorRef> fallbackFontDescriptor = adoptCF(CTFontCreatePhysicalFontDescriptorForCharactersWithLanguage(originalFontData->getCTFont(), characters, length, nullptr, nullptr));
449 if (RetainPtr<CFStringRef> foundFontName = adoptCF(static_cast<CFStringRef>(CTFontDescriptorCopyAttribute(fallbackFontDescriptor.get(), kCTFontNameAttribute))))
450 font = fontForFamily(description, foundFontName.get(), false);
454 return font.release();
456 return lastResortFallbackFont(description);
459 RefPtr<Font> FontCache::similarFont(const FontDescription& description)
461 // Attempt to find an appropriate font using a match based on the presence of keywords in
462 // the requested names. For example, we'll match any name that contains "Arabic" to Geeza Pro.
464 for (unsigned i = 0; i < description.familyCount(); ++i) {
465 const AtomicString& family = description.familyAt(i);
466 if (family.isEmpty())
469 // Substitute the default monospace font for well-known monospace fonts.
470 static NeverDestroyed<AtomicString> monaco("monaco", AtomicString::ConstructFromLiteral);
471 static NeverDestroyed<AtomicString> menlo("menlo", AtomicString::ConstructFromLiteral);
472 static NeverDestroyed<AtomicString> courier("courier", AtomicString::ConstructFromLiteral);
473 if (equalIgnoringCase(family, monaco) || equalIgnoringCase(family, menlo)) {
474 font = fontForFamily(description, courier);
478 // Substitute Verdana for Lucida Grande.
479 static NeverDestroyed<AtomicString> lucidaGrande("lucida grande", AtomicString::ConstructFromLiteral);
480 static NeverDestroyed<AtomicString> verdana("verdana", AtomicString::ConstructFromLiteral);
481 if (equalIgnoringCase(family, lucidaGrande)) {
482 font = fontForFamily(description, verdana);
486 static NeverDestroyed<String> arabic(ASCIILiteral("Arabic"));
487 static NeverDestroyed<String> pashto(ASCIILiteral("Pashto"));
488 static NeverDestroyed<String> urdu(ASCIILiteral("Urdu"));
489 static String* matchWords[3] = { &arabic.get(), &pashto.get(), &urdu.get() };
490 static NeverDestroyed<AtomicString> geezaPlain("GeezaPro", AtomicString::ConstructFromLiteral);
491 static NeverDestroyed<AtomicString> geezaBold("GeezaPro-Bold", AtomicString::ConstructFromLiteral);
492 for (unsigned j = 0; j < 3 && !font; ++j) {
493 if (family.contains(*matchWords[j], false))
494 font = fontForFamily(description, isFontWeightBold(description.weight()) ? geezaBold : geezaPlain);
498 return font.release();
501 Ref<Font> FontCache::lastResortFallbackFont(const FontDescription& fontDescription)
503 static NeverDestroyed<AtomicString> fallbackFontFamily(".PhoneFallback", AtomicString::ConstructFromLiteral);
504 return *fontForFamily(fontDescription, fallbackFontFamily, false);
507 FontPlatformData* FontCache::getCustomFallbackFont(const UInt32 c, const FontDescription& description)
509 ASSERT(requiresCustomFallbackFont(c));
511 static NeverDestroyed<AtomicString> helveticaFamily("Helvetica Neue", AtomicString::ConstructFromLiteral);
512 static NeverDestroyed<AtomicString> lockClockFamily("LockClock-Light", AtomicString::ConstructFromLiteral);
513 static NeverDestroyed<AtomicString> timesNewRomanPSMTFamily("TimesNewRomanPSMT", AtomicString::ConstructFromLiteral);
515 AtomicString* family = nullptr;
518 family = &helveticaFamily.get();
521 family = &lockClockFamily.get();
523 case narrowNonBreakingSpace:
524 family = ×NewRomanPSMTFamily.get();
527 ASSERT_NOT_REACHED();
533 return getCachedFontPlatformData(description, *family);
536 static inline FontTraitsMask toTraitsMask(CTFontSymbolicTraits ctFontTraits)
538 return static_cast<FontTraitsMask>(((ctFontTraits & kCTFontTraitItalic) ? FontStyleItalicMask : FontStyleNormalMask)
539 | FontVariantNormalMask
540 // FontWeight600 or higher is bold for CTFonts, so choose middle values for
541 // bold (600-900) and non-bold (100-500)
542 | ((ctFontTraits & kCTFontTraitBold) ? FontWeight700Mask : FontWeight300Mask));
545 void FontCache::getTraitsInFamily(const AtomicString& familyName, Vector<unsigned>& traitsMasks)
547 RetainPtr<CFStringRef> familyNameStr = familyName.string().createCFString();
548 NSDictionary *attributes = @{ (id)kCTFontFamilyNameAttribute: (NSString*)familyNameStr.get() };
549 RetainPtr<CTFontDescriptorRef> fontDescriptor = adoptCF(CTFontDescriptorCreateWithAttributes((CFDictionaryRef)attributes));
550 RetainPtr<NSArray> matchedDescriptors = adoptNS((NSArray *)CTFontDescriptorCreateMatchingFontDescriptors(fontDescriptor.get(), nullptr));
552 NSInteger numMatches = [matchedDescriptors.get() count];
553 if (!matchedDescriptors || !numMatches)
556 for (NSInteger i = 0; i < numMatches; ++i) {
557 RetainPtr<CFDictionaryRef> traits = adoptCF((CFDictionaryRef)CTFontDescriptorCopyAttribute((CTFontDescriptorRef)[matchedDescriptors.get() objectAtIndex:i], kCTFontTraitsAttribute));
558 CFNumberRef resultRef = (CFNumberRef)CFDictionaryGetValue(traits.get(), kCTFontSymbolicTrait);
560 CTFontSymbolicTraits symbolicTraits;
561 CFNumberGetValue(resultRef, kCFNumberIntType, &symbolicTraits);
562 traitsMasks.append(toTraitsMask(symbolicTraits));
567 float FontCache::weightOfCTFont(CTFontRef font)
570 RetainPtr<CFDictionaryRef> traits = adoptCF(CTFontCopyTraits(font));
574 CFNumberRef resultRef = (CFNumberRef)CFDictionaryGetValue(traits.get(), kCTFontWeightTrait);
576 CFNumberGetValue(resultRef, kCFNumberFloatType, &result);
581 static CTFontRef createCTFontWithTextStyle(const String& familyName, CTFontSymbolicTraits traits, CGFloat size)
583 if (familyName.isNull())
586 CTFontSymbolicTraits symbolicTraits = 0;
587 if (traits & kCTFontBoldTrait)
588 symbolicTraits |= kCTFontBoldTrait;
589 if (traits & kCTFontTraitItalic)
590 symbolicTraits |= kCTFontTraitItalic;
591 RetainPtr<CFStringRef> familyNameStr = familyName.createCFString();
592 RetainPtr<CTFontDescriptorRef> fontDescriptor = adoptCF(CTFontDescriptorCreateWithTextStyle(familyNameStr.get(), RenderThemeIOS::contentSizeCategory(), nullptr));
594 fontDescriptor = adoptCF(CTFontDescriptorCreateCopyWithSymbolicTraits(fontDescriptor.get(), symbolicTraits, symbolicTraits));
596 return CTFontCreateWithFontDescriptor(fontDescriptor.get(), size, nullptr);
599 static CTFontRef createCTFontWithFamilyNameAndWeight(const String& familyName, CTFontSymbolicTraits traits, float size, uint16_t weight)
601 if (familyName.isNull())
604 static NeverDestroyed<AtomicString> systemUIFontWithWebKitPrefix("-webkit-system-font", AtomicString::ConstructFromLiteral);
605 static NeverDestroyed<AtomicString> systemUIFontWithApplePrefix("-apple-system-font", AtomicString::ConstructFromLiteral);
606 if (equalIgnoringCase(familyName, systemUIFontWithWebKitPrefix) || equalIgnoringCase(familyName, systemUIFontWithApplePrefix)) {
607 CTFontUIFontType fontType = kCTFontUIFontSystem;
609 // The comment below has been copied from CoreText/UIFoundation. However, in WebKit we synthesize the oblique,
610 // so we should investigate the result <rdar://problem/14449340>:
611 // 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.
612 if (traits & kCTFontTraitBold)
613 fontType = kCTFontUIFontEmphasizedSystem;
614 else if (traits & kCTFontTraitItalic)
615 fontType = static_cast<CTFontUIFontType>(kCTFontUIFontSystemItalic);
616 } else if (weight > 250)
617 fontType = static_cast<CTFontUIFontType>(kCTFontUIFontSystemLight);
618 else if (weight > 150)
619 fontType = static_cast<CTFontUIFontType>(kCTFontUIFontSystemThin);
621 fontType = static_cast<CTFontUIFontType>(kCTFontUIFontSystemUltraLight);
622 RetainPtr<CTFontDescriptorRef> fontDescriptor = adoptCF(CTFontDescriptorCreateForUIType(fontType, size, nullptr));
623 return CTFontCreateWithFontDescriptor(fontDescriptor.get(), size, nullptr);
626 static NeverDestroyed<AtomicString> systemUIMonospacedNumbersFontWithApplePrefix("-apple-system-font-monospaced-numbers", AtomicString::ConstructFromLiteral);
627 if (equalIgnoringCase(familyName, systemUIMonospacedNumbersFontWithApplePrefix)) {
628 RetainPtr<CTFontDescriptorRef> systemFontDescriptor = adoptCF(CTFontDescriptorCreateForUIType(kCTFontUIFontSystem, size, nullptr));
629 RetainPtr<CTFontDescriptorRef> monospaceFontDescriptor = adoptCF(CTFontDescriptorCreateCopyWithFeature(systemFontDescriptor.get(), (CFNumberRef)@(kNumberSpacingType), (CFNumberRef)@(kMonospacedNumbersSelector)));
630 return CTFontCreateWithFontDescriptor(monospaceFontDescriptor.get(), size, nullptr);
634 RetainPtr<CFStringRef> familyNameStr = familyName.createCFString();
635 CTFontSymbolicTraits requestedTraits = (CTFontSymbolicTraits)(traits & (kCTFontBoldTrait | kCTFontItalicTrait));
636 return CTFontCreateForCSS(familyNameStr.get(), weight, requestedTraits, size);
639 static uint16_t toCTFontWeight(FontWeight fontWeight)
641 switch (fontWeight) {
665 std::unique_ptr<FontPlatformData> FontCache::createFontPlatformData(const FontDescription& fontDescription, const AtomicString& family)
667 // Special case for "Courier" font. We used to have only an oblique variant on iOS, so prior to
668 // iOS 6.0, we disallowed its use here. We'll fall back on "Courier New". <rdar://problem/5116477&10850227>
669 static NeverDestroyed<AtomicString> courier("Courier", AtomicString::ConstructFromLiteral);
670 static bool shouldDisallowCourier = !iosExecutableWasLinkedOnOrAfterVersion(wkIOSSystemVersion_6_0);
671 if (shouldDisallowCourier && equalIgnoringCase(family, courier))
674 CTFontSymbolicTraits traits = 0;
675 if (fontDescription.italic())
676 traits |= kCTFontTraitItalic;
677 if (isFontWeightBold(fontDescription.weight()))
678 traits |= kCTFontTraitBold;
679 float size = fontDescription.computedPixelSize();
681 RetainPtr<CTFontRef> ctFont;
682 if (family.startsWith("UICTFontTextStyle"))
683 ctFont = adoptCF(createCTFontWithTextStyle(family, traits, size));
685 ctFont = adoptCF(createCTFontWithFamilyNameAndWeight(family, traits, size, toCTFontWeight(fontDescription.weight())));
689 CTFontSymbolicTraits actualTraits = 0;
690 if (isFontWeightBold(fontDescription.weight()) || fontDescription.italic())
691 actualTraits = CTFontGetSymbolicTraits(ctFont.get());
693 bool isAppleColorEmoji = CTFontIsAppleColorEmoji(ctFont.get());
695 bool syntheticBold = (fontDescription.fontSynthesis() & FontSynthesisWeight) && (traits & kCTFontTraitBold) && !(actualTraits & kCTFontTraitBold) && !isAppleColorEmoji;
696 bool syntheticOblique = (fontDescription.fontSynthesis() & FontSynthesisStyle) && (traits & kCTFontTraitItalic) && !(actualTraits & kCTFontTraitItalic) && !isAppleColorEmoji;
698 auto result = std::make_unique<FontPlatformData>(ctFont.get(), size, syntheticBold, syntheticOblique, fontDescription.orientation(), fontDescription.widthVariant());
699 if (isAppleColorEmoji)
700 result->m_isEmoji = true;
704 } // namespace WebCore