[Cocoa] Make system-ui obey the user-installed-font policy
[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 #if PLATFORM(IOS)
33
34 #import "FontCascade.h"
35 #import "RenderThemeIOS.h"
36 #import <pal/spi/cg/CoreGraphicsSPI.h>
37 #import <pal/spi/cocoa/CoreTextSPI.h>
38 #import <wtf/HashSet.h>
39 #import <wtf/NeverDestroyed.h>
40 #import <wtf/RetainPtr.h>
41 #import <wtf/SoftLinking.h>
42 #import <wtf/text/CString.h>
43
44 namespace WebCore {
45
46 bool requiresCustomFallbackFont(UChar32 character)
47 {
48     return character == AppleLogo || character == blackCircle || character == narrowNonBreakingSpace;
49 }
50
51 FontPlatformData* FontCache::getCustomFallbackFont(const UInt32 c, const FontDescription& description)
52 {
53     ASSERT(requiresCustomFallbackFont(c));
54
55     static NeverDestroyed<AtomicString> helveticaFamily("Helvetica Neue", AtomicString::ConstructFromLiteral);
56     static NeverDestroyed<AtomicString> lockClockFamily("LockClock-Light", AtomicString::ConstructFromLiteral);
57     static NeverDestroyed<AtomicString> timesNewRomanPSMTFamily("TimesNewRomanPSMT", AtomicString::ConstructFromLiteral);
58
59     AtomicString* family = nullptr;
60     switch (c) {
61     case AppleLogo:
62         family = &helveticaFamily.get();
63         break;
64     case blackCircle:
65         family = &lockClockFamily.get();
66         break;
67     case narrowNonBreakingSpace:
68         family = &timesNewRomanPSMTFamily.get();
69         break;
70     default:
71         ASSERT_NOT_REACHED();
72         return nullptr;
73     }
74     ASSERT(family);
75     if (!family)
76         return nullptr;
77     return getCachedFontPlatformData(description, *family);
78 }
79
80 static RetainPtr<CTFontDescriptorRef> baseSystemFontDescriptor(FontSelectionValue weight, bool bold, float size)
81 {
82     CTFontUIFontType fontType = kCTFontUIFontSystem;
83     if (weight >= FontSelectionValue(350)) {
84         if (bold)
85             fontType = kCTFontUIFontEmphasizedSystem;
86     } else if (weight >= FontSelectionValue(250))
87         fontType = static_cast<CTFontUIFontType>(kCTFontUIFontSystemLight);
88     else if (weight >= FontSelectionValue(150))
89         fontType = static_cast<CTFontUIFontType>(kCTFontUIFontSystemThin);
90     else
91         fontType = static_cast<CTFontUIFontType>(kCTFontUIFontSystemUltraLight);
92     return adoptCF(CTFontDescriptorCreateForUIType(fontType, size, nullptr));
93 }
94
95 static RetainPtr<NSDictionary> systemFontModificationAttributes(FontSelectionValue weight, bool italic)
96 {
97     RetainPtr<NSMutableDictionary> traitsDictionary = adoptNS([[NSMutableDictionary alloc] init]);
98
99     float ctWeight = kCTFontWeightRegular;
100     if (weight < FontSelectionValue(150))
101         ctWeight = kCTFontWeightUltraLight;
102     else if (weight < FontSelectionValue(250))
103         ctWeight = kCTFontWeightThin;
104     else if (weight < FontSelectionValue(350))
105         ctWeight = kCTFontWeightLight;
106     else if (weight < FontSelectionValue(450))
107         ctWeight = kCTFontWeightRegular;
108     else if (weight < FontSelectionValue(550))
109         ctWeight = kCTFontWeightMedium;
110     else if (weight < FontSelectionValue(650))
111         ctWeight = kCTFontWeightSemibold;
112     else if (weight < FontSelectionValue(750))
113         ctWeight = kCTFontWeightBold;
114     else if (weight < FontSelectionValue(850))
115         ctWeight = kCTFontWeightHeavy;
116     else
117         ctWeight = kCTFontWeightBlack;
118     [traitsDictionary setObject:[NSNumber numberWithFloat:ctWeight] forKey:static_cast<NSString *>(kCTFontWeightTrait)];
119
120     [traitsDictionary setObject:@YES forKey:static_cast<NSString *>(kCTFontUIFontDesignTrait)];
121
122     if (italic)
123         [traitsDictionary setObject:[NSNumber numberWithInt:kCTFontItalicTrait] forKey:static_cast<NSString *>(kCTFontSymbolicTrait)];
124
125     return @{ static_cast<NSString *>(kCTFontTraitsAttribute) : traitsDictionary.get() };
126 }
127
128 static RetainPtr<CTFontDescriptorRef> systemFontDescriptor(FontSelectionValue weight, bool bold, bool italic, float size)
129 {
130     RetainPtr<CTFontDescriptorRef> fontDescriptor = baseSystemFontDescriptor(weight, bold, size);
131     RetainPtr<NSDictionary> attributes = systemFontModificationAttributes(weight, italic);
132     return adoptCF(CTFontDescriptorCreateCopyWithAttributes(fontDescriptor.get(), static_cast<CFDictionaryRef>(attributes.get())));
133 }
134
135 RetainPtr<CTFontRef> platformFontWithFamilySpecialCase(const AtomicString& family, FontSelectionRequest request, float size, AllowUserInstalledFonts allowUserInstalledFonts)
136 {
137     // FIXME: See comment in FontCascadeDescription::effectiveFamilyAt() in FontDescriptionCocoa.cpp
138     if (family.startsWith("UICTFontTextStyle")) {
139         CTFontSymbolicTraits traits = (isFontWeightBold(request.weight) || FontCache::singleton().shouldMockBoldSystemFontForAccessibility() ? kCTFontTraitBold : 0) | (isItalic(request.slope) ? kCTFontTraitItalic : 0);
140         RetainPtr<CFStringRef> familyNameStr = family.string().createCFString();
141         RetainPtr<CTFontDescriptorRef> fontDescriptor = adoptCF(CTFontDescriptorCreateWithTextStyle(familyNameStr.get(), RenderThemeIOS::contentSizeCategory(), nullptr));
142         if (traits)
143             fontDescriptor = adoptCF(CTFontDescriptorCreateCopyWithSymbolicTraits(fontDescriptor.get(), traits, traits));
144         return createFontForInstalledFonts(fontDescriptor.get(), size, allowUserInstalledFonts);
145     }
146
147     if (equalLettersIgnoringASCIICase(family, "-webkit-system-font") || equalLettersIgnoringASCIICase(family, "-apple-system") || equalLettersIgnoringASCIICase(family, "-apple-system-font") || equalLettersIgnoringASCIICase(family, "system-ui")) {
148         auto fontDescriptor = systemFontDescriptor(request.weight, isFontWeightBold(request.weight), isItalic(request.slope), size);
149         return createFontForInstalledFonts(fontDescriptor.get(), size, allowUserInstalledFonts);
150     }
151
152     if (equalLettersIgnoringASCIICase(family, "-apple-system-monospaced-numbers")) {
153         RetainPtr<CTFontDescriptorRef> systemFontDescriptor = adoptCF(CTFontDescriptorCreateForUIType(kCTFontUIFontSystem, size, nullptr));
154         RetainPtr<CTFontDescriptorRef> monospaceFontDescriptor = adoptCF(CTFontDescriptorCreateCopyWithFeature(systemFontDescriptor.get(), (CFNumberRef)@(kNumberSpacingType), (CFNumberRef)@(kMonospacedNumbersSelector)));
155         return createFontForInstalledFonts(monospaceFontDescriptor.get(), size, allowUserInstalledFonts);
156     }
157
158     if (equalLettersIgnoringASCIICase(family, "lastresort")) {
159 #if __IPHONE_OS_VERSION_MIN_REQUIRED >= 110000
160         static const CTFontDescriptorRef lastResort = CTFontDescriptorCreateLastResort();
161         return adoptCF(CTFontCreateWithFontDescriptor(lastResort, size, nullptr));
162 #else
163         // LastResort is special, so it's important to look this exact string up, and not some case-folded version.
164         // We handle this here so any caching and case folding we do in our general text codepath is bypassed.
165         return adoptCF(CTFontCreateWithName(CFSTR("LastResort"), size, nullptr));
166 #endif
167     }
168
169     return nullptr;
170 }
171
172 } // namespace WebCore
173
174 #endif // PLATFORM(IOS)