1 /*
2 * Copyright 2006 The Android Open Source Project
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
8 #include "include/core/SkTypes.h"
9 #if defined(SK_BUILD_FOR_MAC) || defined(SK_BUILD_FOR_IOS)
10
11 #ifdef SK_BUILD_FOR_MAC
12 #import <ApplicationServices/ApplicationServices.h>
13 #endif
14
15 #ifdef SK_BUILD_FOR_IOS
16 #include <CoreText/CoreText.h>
17 #include <CoreText/CTFontManager.h>
18 #include <CoreGraphics/CoreGraphics.h>
19 #include <CoreFoundation/CoreFoundation.h>
20 #include <dlfcn.h>
21 #endif
22
23 #include "include/core/SkData.h"
24 #include "include/core/SkFontArguments.h"
25 #include "include/core/SkFontMgr.h"
26 #include "include/core/SkFontStyle.h"
27 #include "include/core/SkStream.h"
28 #include "include/core/SkString.h"
29 #include "include/core/SkTypeface.h"
30 #include "include/ports/SkFontMgr_mac_ct.h"
31 #include "include/private/base/SkFixed.h"
32 #include "include/private/base/SkOnce.h"
33 #include "include/private/base/SkTPin.h"
34 #include "include/private/base/SkTemplates.h"
35 #include "include/private/base/SkTo.h"
36 #include "src/base/SkUTF.h"
37 #include "src/core/SkFontDescriptor.h"
38 #include "src/ports/SkTypeface_mac_ct.h"
39
40 #include <string.h>
41 #include <memory>
42
43 using namespace skia_private;
44
make_CFString(const char s[])45 static SkUniqueCFRef<CFStringRef> make_CFString(const char s[]) {
46 return SkUniqueCFRef<CFStringRef>(CFStringCreateWithCString(nullptr, s, kCFStringEncodingUTF8));
47 }
48
49 /** Creates a typeface from a descriptor, searching the cache. */
create_from_desc(CTFontDescriptorRef desc)50 static sk_sp<SkTypeface> create_from_desc(CTFontDescriptorRef desc) {
51 SkUniqueCFRef<CTFontRef> ctFont(CTFontCreateWithFontDescriptor(desc, 0, nullptr));
52 if (!ctFont) {
53 return nullptr;
54 }
55
56 return SkTypeface_Mac::Make(std::move(ctFont), OpszVariation(), nullptr);
57 }
58
create_descriptor(const char familyName[],const SkFontStyle & style)59 static SkUniqueCFRef<CTFontDescriptorRef> create_descriptor(const char familyName[],
60 const SkFontStyle& style) {
61 SkUniqueCFRef<CFMutableDictionaryRef> cfAttributes(
62 CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
63 &kCFTypeDictionaryKeyCallBacks,
64 &kCFTypeDictionaryValueCallBacks));
65
66 SkUniqueCFRef<CFMutableDictionaryRef> cfTraits(
67 CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
68 &kCFTypeDictionaryKeyCallBacks,
69 &kCFTypeDictionaryValueCallBacks));
70
71 if (!cfAttributes || !cfTraits) {
72 return nullptr;
73 }
74
75 // Setting both kCTFontSymbolicTrait and the specific WWS traits may lead to strange outcomes.
76
77 // CTFontTraits (weight)
78 CGFloat ctWeight = SkCTFontCTWeightForCSSWeight(style.weight());
79 SkUniqueCFRef<CFNumberRef> cfFontWeight(
80 CFNumberCreate(kCFAllocatorDefault, kCFNumberCGFloatType, &ctWeight));
81 if (cfFontWeight) {
82 CFDictionaryAddValue(cfTraits.get(), kCTFontWeightTrait, cfFontWeight.get());
83 }
84 // CTFontTraits (width)
85 CGFloat ctWidth = SkCTFontCTWidthForCSSWidth(style.width());
86 SkUniqueCFRef<CFNumberRef> cfFontWidth(
87 CFNumberCreate(kCFAllocatorDefault, kCFNumberCGFloatType, &ctWidth));
88 if (cfFontWidth) {
89 CFDictionaryAddValue(cfTraits.get(), kCTFontWidthTrait, cfFontWidth.get());
90 }
91 // CTFontTraits (slant)
92 // Slope value set to 0.07 based on WebKit's implementation for better font matching
93 static const CGFloat kSystemFontItalicSlope = 0.07;
94 CGFloat ctSlant = style.slant() == SkFontStyle::kUpright_Slant ? 0 : kSystemFontItalicSlope;
95 SkUniqueCFRef<CFNumberRef> cfFontSlant(
96 CFNumberCreate(kCFAllocatorDefault, kCFNumberCGFloatType, &ctSlant));
97 if (cfFontSlant) {
98 CFDictionaryAddValue(cfTraits.get(), kCTFontSlantTrait, cfFontSlant.get());
99 }
100 // CTFontTraits
101 CFDictionaryAddValue(cfAttributes.get(), kCTFontTraitsAttribute, cfTraits.get());
102
103 // CTFontFamilyName
104 if (familyName) {
105 SkUniqueCFRef<CFStringRef> cfFontName = make_CFString(familyName);
106 if (cfFontName) {
107 CFDictionaryAddValue(cfAttributes.get(), kCTFontFamilyNameAttribute, cfFontName.get());
108 }
109 }
110
111 return SkUniqueCFRef<CTFontDescriptorRef>(
112 CTFontDescriptorCreateWithAttributes(cfAttributes.get()));
113 }
114
115 // Same as the above function except style is included so we can
116 // compare whether the created font conforms to the style. If not, we need
117 // to recreate the font with symbolic traits. This is needed due to MacOS 10.11
118 // font creation problem https://bugs.chromium.org/p/skia/issues/detail?id=8447.
create_from_desc_and_style(CTFontDescriptorRef desc,const SkFontStyle & style)119 static sk_sp<SkTypeface> create_from_desc_and_style(CTFontDescriptorRef desc,
120 const SkFontStyle& style) {
121 SkUniqueCFRef<CTFontRef> ctFont(CTFontCreateWithFontDescriptor(desc, 0, nullptr));
122 if (!ctFont) {
123 return nullptr;
124 }
125
126 const CTFontSymbolicTraits traits = CTFontGetSymbolicTraits(ctFont.get());
127 CTFontSymbolicTraits expected_traits = traits;
128 if (style.slant() != SkFontStyle::kUpright_Slant) {
129 expected_traits |= kCTFontItalicTrait;
130 }
131 if (style.weight() >= SkFontStyle::kBold_Weight) {
132 expected_traits |= kCTFontBoldTrait;
133 }
134
135 if (expected_traits != traits) {
136 SkUniqueCFRef<CTFontRef> ctNewFont(CTFontCreateCopyWithSymbolicTraits(
137 ctFont.get(), 0, nullptr, expected_traits, expected_traits));
138 if (ctNewFont) {
139 ctFont = std::move(ctNewFont);
140 }
141 }
142
143 return SkTypeface_Mac::Make(std::move(ctFont), OpszVariation(), nullptr);
144 }
145
146 /** Creates a typeface from a name, searching the cache. */
create_from_name(const char familyName[],const SkFontStyle & style)147 static sk_sp<SkTypeface> create_from_name(const char familyName[], const SkFontStyle& style) {
148 SkUniqueCFRef<CTFontDescriptorRef> desc = create_descriptor(familyName, style);
149 if (!desc) {
150 return nullptr;
151 }
152 return create_from_desc_and_style(desc.get(), style);
153 }
154
map_css_names(const char * name)155 static const char* map_css_names(const char* name) {
156 static const struct {
157 const char* fFrom; // name the caller specified
158 const char* fTo; // "canonical" name we map to
159 } gPairs[] = {
160 { "sans-serif", "Helvetica" },
161 { "serif", "Times" },
162 { "monospace", "Courier" }
163 };
164
165 for (size_t i = 0; i < std::size(gPairs); i++) {
166 if (strcmp(name, gPairs[i].fFrom) == 0) {
167 return gPairs[i].fTo;
168 }
169 }
170 return name; // no change
171 }
172
173 namespace {
174
find_desc_str(CTFontDescriptorRef desc,CFStringRef name,SkString * value)175 static bool find_desc_str(CTFontDescriptorRef desc, CFStringRef name, SkString* value) {
176 SkUniqueCFRef<CFStringRef> ref((CFStringRef)CTFontDescriptorCopyAttribute(desc, name));
177 if (!ref) {
178 return false;
179 }
180 SkStringFromCFString(ref.get(), value);
181 return true;
182 }
183
sqr(int value)184 static inline int sqr(int value) {
185 SkASSERT(SkAbs32(value) < 0x7FFF); // check for overflow
186 return value * value;
187 }
188
189 // We normalize each axis (weight, width, italic) to be base-900
compute_metric(const SkFontStyle & a,const SkFontStyle & b)190 static int compute_metric(const SkFontStyle& a, const SkFontStyle& b) {
191 return sqr(a.weight() - b.weight()) +
192 sqr((a.width() - b.width()) * 100) +
193 sqr((a.slant() != b.slant()) * 900);
194 }
195
name_required()196 static SkUniqueCFRef<CFSetRef> name_required() {
197 CFStringRef set_values[] = {kCTFontFamilyNameAttribute};
198 return SkUniqueCFRef<CFSetRef>(CFSetCreate(kCFAllocatorDefault,
199 reinterpret_cast<const void**>(set_values), std::size(set_values),
200 &kCFTypeSetCallBacks));
201 }
202
203 class SkFontStyleSet_Mac : public SkFontStyleSet {
204 public:
SkFontStyleSet_Mac(CTFontDescriptorRef desc)205 SkFontStyleSet_Mac(CTFontDescriptorRef desc)
206 : fArray(CTFontDescriptorCreateMatchingFontDescriptors(desc, name_required().get()))
207 , fCount(0)
208 {
209 if (!fArray) {
210 fArray.reset(CFArrayCreate(nullptr, nullptr, 0, nullptr));
211 }
212 fCount = SkToInt(CFArrayGetCount(fArray.get()));
213 }
214
count()215 int count() override {
216 return fCount;
217 }
218
getStyle(int index,SkFontStyle * style,SkString * name)219 void getStyle(int index, SkFontStyle* style, SkString* name) override {
220 SkASSERT((unsigned)index < (unsigned)fCount);
221 CTFontDescriptorRef desc = (CTFontDescriptorRef)CFArrayGetValueAtIndex(fArray.get(), index);
222 if (style) {
223 *style = SkCTFontDescriptorGetSkFontStyle(desc, false);
224 }
225 if (name) {
226 if (!find_desc_str(desc, kCTFontStyleNameAttribute, name)) {
227 name->reset();
228 }
229 }
230 }
231
createTypeface(int index)232 sk_sp<SkTypeface> createTypeface(int index) override {
233 SkASSERT((unsigned)index < (unsigned)CFArrayGetCount(fArray.get()));
234 CTFontDescriptorRef desc = (CTFontDescriptorRef)CFArrayGetValueAtIndex(fArray.get(), index);
235
236 return create_from_desc(desc);
237 }
238
matchStyle(const SkFontStyle & pattern)239 sk_sp<SkTypeface> matchStyle(const SkFontStyle& pattern) override {
240 if (0 == fCount) {
241 return nullptr;
242 }
243 return create_from_desc(findMatchingDesc(pattern));
244 }
245
246 private:
247 SkUniqueCFRef<CFArrayRef> fArray;
248 int fCount;
249
findMatchingDesc(const SkFontStyle & pattern) const250 CTFontDescriptorRef findMatchingDesc(const SkFontStyle& pattern) const {
251 int bestMetric = SK_MaxS32;
252 CTFontDescriptorRef bestDesc = nullptr;
253
254 for (int i = 0; i < fCount; ++i) {
255 CTFontDescriptorRef desc = (CTFontDescriptorRef)CFArrayGetValueAtIndex(fArray.get(), i);
256 int metric = compute_metric(pattern, SkCTFontDescriptorGetSkFontStyle(desc, false));
257 if (0 == metric) {
258 return desc;
259 }
260 if (metric < bestMetric) {
261 bestMetric = metric;
262 bestDesc = desc;
263 }
264 }
265 SkASSERT(bestDesc);
266 return bestDesc;
267 }
268 };
269
SkCopyAvailableFontFamilyNames(CTFontCollectionRef collection)270 SkUniqueCFRef<CFArrayRef> SkCopyAvailableFontFamilyNames(CTFontCollectionRef collection) {
271 // Create a CFArray of all available font descriptors.
272 SkUniqueCFRef<CFArrayRef> descriptors(
273 CTFontCollectionCreateMatchingFontDescriptors(collection));
274
275 // Copy the font family names of the font descriptors into a CFSet.
276 auto addDescriptorFamilyNameToSet = [](const void* value, void* context) -> void {
277 CTFontDescriptorRef descriptor = static_cast<CTFontDescriptorRef>(value);
278 CFMutableSetRef familyNameSet = static_cast<CFMutableSetRef>(context);
279 SkUniqueCFRef<CFTypeRef> familyName(
280 CTFontDescriptorCopyAttribute(descriptor, kCTFontFamilyNameAttribute));
281 if (familyName) {
282 CFSetAddValue(familyNameSet, familyName.get());
283 }
284 };
285 SkUniqueCFRef<CFMutableSetRef> familyNameSet(
286 CFSetCreateMutable(kCFAllocatorDefault, 0, &kCFTypeSetCallBacks));
287 CFArrayApplyFunction(descriptors.get(), CFRangeMake(0, CFArrayGetCount(descriptors.get())),
288 addDescriptorFamilyNameToSet, familyNameSet.get());
289
290 // Get the set of family names into an array; this does not retain.
291 CFIndex count = CFSetGetCount(familyNameSet.get());
292 std::unique_ptr<const void*[]> familyNames(new const void*[count]);
293 CFSetGetValues(familyNameSet.get(), familyNames.get());
294
295 // Sort the array of family names (to match CTFontManagerCopyAvailableFontFamilyNames).
296 std::sort(familyNames.get(), familyNames.get() + count, [](const void* a, const void* b){
297 return CFStringCompare((CFStringRef)a, (CFStringRef)b, 0) == kCFCompareLessThan;
298 });
299
300 // Copy family names into a CFArray; this does retain.
301 return SkUniqueCFRef<CFArrayRef>(
302 CFArrayCreate(kCFAllocatorDefault, familyNames.get(), count, &kCFTypeArrayCallBacks));
303 }
304
305 /** Use CTFontManagerCopyAvailableFontFamilyNames if available, simulate if not. */
SkCTFontManagerCopyAvailableFontFamilyNames()306 SkUniqueCFRef<CFArrayRef> SkCTFontManagerCopyAvailableFontFamilyNames() {
307 #ifdef SK_BUILD_FOR_IOS
308 using CTFontManagerCopyAvailableFontFamilyNamesProc = CFArrayRef (*)(void);
309 CTFontManagerCopyAvailableFontFamilyNamesProc ctFontManagerCopyAvailableFontFamilyNames;
310 *(void**)(&ctFontManagerCopyAvailableFontFamilyNames) =
311 dlsym(RTLD_DEFAULT, "CTFontManagerCopyAvailableFontFamilyNames");
312 if (ctFontManagerCopyAvailableFontFamilyNames) {
313 return SkUniqueCFRef<CFArrayRef>(ctFontManagerCopyAvailableFontFamilyNames());
314 }
315 SkUniqueCFRef<CTFontCollectionRef> collection(
316 CTFontCollectionCreateFromAvailableFonts(nullptr));
317 return SkUniqueCFRef<CFArrayRef>(SkCopyAvailableFontFamilyNames(collection.get()));
318 #else
319 return SkUniqueCFRef<CFArrayRef>(CTFontManagerCopyAvailableFontFamilyNames());
320 #endif
321 }
322
323 } // namespace
324
325 class SkFontMgr_Mac : public SkFontMgr {
326 SkUniqueCFRef<CFArrayRef> fNames;
327 int fCount;
328
getFamilyNameAt(int index) const329 CFStringRef getFamilyNameAt(int index) const {
330 SkASSERT((unsigned)index < (unsigned)fCount);
331 return (CFStringRef)CFArrayGetValueAtIndex(fNames.get(), index);
332 }
333
CreateSet(CFStringRef cfFamilyName)334 static sk_sp<SkFontStyleSet> CreateSet(CFStringRef cfFamilyName) {
335 SkUniqueCFRef<CFMutableDictionaryRef> cfAttr(
336 CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
337 &kCFTypeDictionaryKeyCallBacks,
338 &kCFTypeDictionaryValueCallBacks));
339
340 CFDictionaryAddValue(cfAttr.get(), kCTFontFamilyNameAttribute, cfFamilyName);
341
342 SkUniqueCFRef<CTFontDescriptorRef> desc(
343 CTFontDescriptorCreateWithAttributes(cfAttr.get()));
344 return sk_sp<SkFontStyleSet>(new SkFontStyleSet_Mac(desc.get()));
345 }
346
347 public:
348 SkUniqueCFRef<CTFontCollectionRef> fFontCollection;
SkFontMgr_Mac(CTFontCollectionRef fontCollection)349 SkFontMgr_Mac(CTFontCollectionRef fontCollection)
350 : fNames(fontCollection ? SkCopyAvailableFontFamilyNames(fontCollection)
351 : SkCTFontManagerCopyAvailableFontFamilyNames())
352 , fCount(fNames ? SkToInt(CFArrayGetCount(fNames.get())) : 0)
353 , fFontCollection(fontCollection ? (CTFontCollectionRef)CFRetain(fontCollection)
354 : CTFontCollectionCreateFromAvailableFonts(nullptr))
355 {}
356
357 protected:
onCountFamilies() const358 int onCountFamilies() const override {
359 return fCount;
360 }
361
onGetFamilyName(int index,SkString * familyName) const362 void onGetFamilyName(int index, SkString* familyName) const override {
363 if ((unsigned)index < (unsigned)fCount) {
364 SkStringFromCFString(this->getFamilyNameAt(index), familyName);
365 } else {
366 familyName->reset();
367 }
368 }
369
onCreateStyleSet(int index) const370 sk_sp<SkFontStyleSet> onCreateStyleSet(int index) const override {
371 if ((unsigned)index >= (unsigned)fCount) {
372 return nullptr;
373 }
374 return CreateSet(this->getFamilyNameAt(index));
375 }
376
onMatchFamily(const char familyName[]) const377 sk_sp<SkFontStyleSet> onMatchFamily(const char familyName[]) const override {
378 if (!familyName) {
379 return nullptr;
380 }
381 SkUniqueCFRef<CFStringRef> cfName = make_CFString(familyName);
382 return CreateSet(cfName.get());
383 }
384
onMatchFamilyStyle(const char familyName[],const SkFontStyle & style) const385 sk_sp<SkTypeface> onMatchFamilyStyle(const char familyName[],
386 const SkFontStyle& style) const override {
387 SkUniqueCFRef<CTFontDescriptorRef> reqDesc = create_descriptor(familyName, style);
388 if (!familyName) {
389 return create_from_desc(reqDesc.get());
390 }
391 SkUniqueCFRef<CTFontDescriptorRef> resolvedDesc(
392 CTFontDescriptorCreateMatchingFontDescriptor(reqDesc.get(), name_required().get()));
393 if (!resolvedDesc) {
394 return nullptr;
395 }
396 return create_from_desc(resolvedDesc.get());
397 }
398
onMatchFamilyStyleCharacter(const char familyName[],const SkFontStyle & style,const char * bcp47[],int bcp47Count,SkUnichar character) const399 sk_sp<SkTypeface> onMatchFamilyStyleCharacter(const char familyName[],
400 const SkFontStyle& style,
401 const char* bcp47[], int bcp47Count,
402 SkUnichar character) const override {
403 SkUniqueCFRef<CTFontDescriptorRef> desc = create_descriptor(familyName, style);
404 SkUniqueCFRef<CTFontRef> familyFont(CTFontCreateWithFontDescriptor(desc.get(), 0, nullptr));
405
406 // kCFStringEncodingUTF32 is BE unless there is a BOM.
407 // Since there is no machine endian option, explicitly state machine endian.
408 #ifdef SK_CPU_LENDIAN
409 constexpr CFStringEncoding encoding = kCFStringEncodingUTF32LE;
410 #else
411 constexpr CFStringEncoding encoding = kCFStringEncodingUTF32BE;
412 #endif
413 SkUniqueCFRef<CFStringRef> string(CFStringCreateWithBytes(
414 kCFAllocatorDefault, reinterpret_cast<const UInt8 *>(&character), sizeof(character),
415 encoding, false));
416 // If 0xD800 <= codepoint <= 0xDFFF || 0x10FFFF < codepoint 'string' may be nullptr.
417 // No font should be covering such codepoints (even the magic fallback font).
418 if (!string) {
419 return nullptr;
420 }
421 CFRange range = CFRangeMake(0, CFStringGetLength(string.get())); // in UniChar units.
422 SkUniqueCFRef<CTFontRef> fallbackFont(
423 CTFontCreateForString(familyFont.get(), string.get(), range));
424 return SkTypeface_Mac::Make(std::move(fallbackFont), OpszVariation(), nullptr);
425 }
426
onMakeFromData(sk_sp<SkData> data,int ttcIndex) const427 sk_sp<SkTypeface> onMakeFromData(sk_sp<SkData> data, int ttcIndex) const override {
428 return this->makeFromStream(
429 std::unique_ptr<SkStreamAsset>(new SkMemoryStream(std::move(data))), ttcIndex);
430 }
431
onMakeFromStreamIndex(std::unique_ptr<SkStreamAsset> stream,int ttcIndex) const432 sk_sp<SkTypeface> onMakeFromStreamIndex(std::unique_ptr<SkStreamAsset> stream,
433 int ttcIndex) const override {
434 return this->makeFromStream(std::move(stream),
435 SkFontArguments().setCollectionIndex(ttcIndex));
436 }
437
onMakeFromStreamArgs(std::unique_ptr<SkStreamAsset> stream,const SkFontArguments & args) const438 sk_sp<SkTypeface> onMakeFromStreamArgs(std::unique_ptr<SkStreamAsset> stream,
439 const SkFontArguments& args) const override {
440 return SkTypeface_Mac::MakeFromStream(std::move(stream), args);
441 }
442
onMakeFromFile(const char path[],int ttcIndex) const443 sk_sp<SkTypeface> onMakeFromFile(const char path[], int ttcIndex) const override {
444 sk_sp<SkData> data = SkData::MakeFromFileName(path);
445 if (!data) {
446 return nullptr;
447 }
448
449 return this->onMakeFromData(std::move(data), ttcIndex);
450 }
451
onLegacyMakeTypeface(const char familyName[],SkFontStyle style) const452 sk_sp<SkTypeface> onLegacyMakeTypeface(const char familyName[], SkFontStyle style) const override {
453 if (familyName) {
454 familyName = map_css_names(familyName);
455 }
456
457 sk_sp<SkTypeface> face = create_from_name(familyName, style);
458 if (face) {
459 return face;
460 }
461
462 static SkTypeface* gDefaultFace;
463 static SkOnce lookupDefault;
464 static const char FONT_DEFAULT_NAME[] = "Lucida Sans";
465 lookupDefault([]{
466 gDefaultFace = create_from_name(FONT_DEFAULT_NAME, SkFontStyle()).release();
467 });
468 return sk_ref_sp(gDefaultFace);
469 }
470 };
471
SkFontMgr_New_CoreText(CTFontCollectionRef fontCollection)472 sk_sp<SkFontMgr> SkFontMgr_New_CoreText(CTFontCollectionRef fontCollection) {
473 return sk_make_sp<SkFontMgr_Mac>(fontCollection);
474 }
475
476 #endif//defined(SK_BUILD_FOR_MAC) || defined(SK_BUILD_FOR_IOS)
477