xref: /aosp_15_r20/external/skia/src/utils/mac/SkCTFontCreateExactCopy.cpp (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
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 #include "src/utils/mac/SkCTFontCreateExactCopy.h"
12 
13 #include "src/ports/SkTypeface_mac_ct.h"
14 #include "src/utils/mac/SkUniqueCFRef.h"
15 
16 // In macOS 10.12 and later any variation on the CGFont which has default axis value will be
17 // dropped when creating the CTFont. Unfortunately, in macOS 10.15 the priority of setting
18 // the optical size (and opsz variation) is
19 // 1. the value of kCTFontOpticalSizeAttribute in the CTFontDescriptor (undocumented)
20 // 2. the opsz axis default value if kCTFontOpticalSizeAttribute is 'none' (undocumented)
21 // 3. the opsz variation on the nascent CTFont from the CGFont (was dropped if default)
22 // 4. the opsz variation in kCTFontVariationAttribute in CTFontDescriptor (crashes 10.10)
23 // 5. the size requested (can fudge in SkTypeface but not SkScalerContext)
24 // The first one which is found will be used to set the opsz variation (after clamping).
add_opsz_attr(CFMutableDictionaryRef attr,double opsz)25 static void add_opsz_attr(CFMutableDictionaryRef attr, double opsz) {
26     SkUniqueCFRef<CFNumberRef> opszValueNumber(
27         CFNumberCreate(kCFAllocatorDefault, kCFNumberDoubleType, &opsz));
28     // Avoid using kCTFontOpticalSizeAttribute directly
29     CFStringRef SkCTFontOpticalSizeAttribute = CFSTR("NSCTFontOpticalSizeAttribute");
30     CFDictionarySetValue(attr, SkCTFontOpticalSizeAttribute, opszValueNumber.get());
31 }
32 
33 // This turns off application of the 'trak' table to advances, but also all other tracking.
add_notrak_attr(CFMutableDictionaryRef attr)34 static void add_notrak_attr(CFMutableDictionaryRef attr) {
35     int zero = 0;
36     SkUniqueCFRef<CFNumberRef> unscaledTrackingNumber(
37         CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &zero));
38     CFStringRef SkCTFontUnscaledTrackingAttribute = CFSTR("NSCTFontUnscaledTrackingAttribute");
39     CFDictionarySetValue(attr, SkCTFontUnscaledTrackingAttribute, unscaledTrackingNumber.get());
40 }
41 
SkCTFontCreateExactCopy(CTFontRef baseFont,CGFloat textSize,OpszVariation opszVariation)42 SkUniqueCFRef<CTFontRef> SkCTFontCreateExactCopy(CTFontRef baseFont, CGFloat textSize,
43                                                  OpszVariation opszVariation)
44 {
45     SkUniqueCFRef<CFMutableDictionaryRef> attr(
46     CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
47                               &kCFTypeDictionaryKeyCallBacks,
48                               &kCFTypeDictionaryValueCallBacks));
49 
50     if (opszVariation.isSet) {
51         add_opsz_attr(attr.get(), opszVariation.value);
52     } else {
53         // On (at least) 10.10 though 10.14 the default system font was SFNSText/SFNSDisplay.
54         // The CTFont is backed by both; optical size < 20 means SFNSText else SFNSDisplay.
55         // On at least 10.11 the glyph ids in these fonts became non-interchangable.
56         // To keep glyph ids stable over size changes, preserve the optical size.
57         // In 10.15 this was replaced with use of variable fonts with an opsz axis.
58         // A CTFont backed by multiple fonts picked by opsz where the multiple backing fonts are
59         // variable fonts with opsz axis and non-interchangeable glyph ids would break the
60         // opsz.isSet branch above, but hopefully that never happens.
61         // See https://crbug.com/524646 .
62         CFStringRef SkCTFontOpticalSizeAttribute = CFSTR("NSCTFontOpticalSizeAttribute");
63         SkUniqueCFRef<CFTypeRef> opsz(CTFontCopyAttribute(baseFont, SkCTFontOpticalSizeAttribute));
64         double opsz_val;
65         if (!opsz ||
66             CFGetTypeID(opsz.get()) != CFNumberGetTypeID() ||
67             !CFNumberGetValue(static_cast<CFNumberRef>(opsz.get()),kCFNumberDoubleType,&opsz_val) ||
68             opsz_val <= 0)
69         {
70             opsz_val = CTFontGetSize(baseFont);
71         }
72         add_opsz_attr(attr.get(), opsz_val);
73     }
74     add_notrak_attr(attr.get());
75 
76     SkUniqueCFRef<CTFontDescriptorRef> desc(CTFontDescriptorCreateWithAttributes(attr.get()));
77 
78     return SkUniqueCFRef<CTFontRef>(
79             CTFontCreateCopyWithAttributes(baseFont, textSize, nullptr, desc.get()));
80 }
81 
82 #endif
83 
84