• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 #endif
21 
22 #include "include/core/SkColor.h"
23 #include "include/core/SkData.h"
24 #include "include/core/SkFontArguments.h"
25 #include "include/core/SkFontParameters.h"
26 #include "include/core/SkFontStyle.h"
27 #include "include/core/SkFontTypes.h"
28 #include "include/core/SkRect.h"
29 #include "include/core/SkRefCnt.h"
30 #include "include/core/SkScalar.h"
31 #include "include/core/SkStream.h"
32 #include "include/core/SkString.h"
33 #include "include/core/SkTypeface.h"
34 #include "include/ports/SkTypeface_mac.h"
35 #include "include/private/base/SkFixed.h"
36 #include "include/private/base/SkTDArray.h"
37 #include "include/private/base/SkTPin.h"
38 #include "include/private/base/SkTemplates.h"
39 #include "include/private/base/SkMalloc.h"
40 #include "include/private/base/SkMutex.h"
41 #include "include/private/base/SkOnce.h"
42 #include "include/private/base/SkTo.h"
43 #include "src/base/SkUTF.h"
44 #include "src/core/SkAdvancedTypefaceMetrics.h"
45 #include "src/core/SkEndian.h"
46 #include "src/core/SkFontDescriptor.h"
47 #include "src/core/SkMask.h"
48 #include "src/core/SkScalerContext.h"
49 #include "src/core/SkTypefaceCache.h"
50 #include "src/ports/SkScalerContext_mac_ct.h"
51 #include "src/ports/SkTypeface_mac_ct.h"
52 #include "src/sfnt/SkOTTableTypes.h"
53 #include "src/sfnt/SkOTTable_OS_2.h"
54 #include "src/sfnt/SkOTTable_OS_2_V4.h"
55 #include "src/sfnt/SkOTUtils.h"
56 #include "src/sfnt/SkSFNTHeader.h"
57 #include "src/utils/mac/SkCGBase.h"
58 #include "src/utils/mac/SkCGGeometry.h"
59 #include "src/utils/mac/SkCTFont.h"
60 #include "src/utils/mac/SkUniqueCFRef.h"
61 
62 #include <dlfcn.h>
63 #include <limits.h>
64 #include <string.h>
65 #include <memory>
66 
67 using namespace skia_private;
68 
69 /** Assumes src and dst are not nullptr. */
SkStringFromCFString(CFStringRef src,SkString * dst)70 void SkStringFromCFString(CFStringRef src, SkString* dst) {
71     // Reserve enough room for the worst-case string,
72     // plus 1 byte for the trailing null.
73     CFIndex length = CFStringGetMaximumSizeForEncoding(CFStringGetLength(src),
74                                                        kCFStringEncodingUTF8) + 1;
75     dst->resize(length);
76     CFStringGetCString(src, dst->data(), length, kCFStringEncodingUTF8);
77     // Resize to the actual UTF-8 length used, stripping the null character.
78     dst->resize(strlen(dst->c_str()));
79 }
80 
SkCFTypeIDDescription(CFTypeID id)81 SkString SkCFTypeIDDescription(CFTypeID id) {
82     SkUniqueCFRef<CFStringRef> typeDescription(CFCopyTypeIDDescription(id));
83     SkString skTypeDescription;
84     SkStringFromCFString(typeDescription.get(), &skTypeDescription);
85     return skTypeDescription;
86 }
87 
88 template<typename CF> CFTypeID SkCFGetTypeID();
89 #define SK_GETCFTYPEID(cf) \
90 template<> CFTypeID SkCFGetTypeID<cf##Ref>() { return cf##GetTypeID(); }
91 SK_GETCFTYPEID(CFBoolean);
92 SK_GETCFTYPEID(CFDictionary);
93 SK_GETCFTYPEID(CFNumber);
94 
95 /* Checked dynamic downcast of CFTypeRef.
96  *
97  * @param cf the ref to downcast.
98  * @param cfAsCF if cf can be cast to the type CF, receives the downcast ref.
99  * @param name if non-nullptr the cast is expected to succeed and failures will be logged.
100  * @return true if the cast succeeds, false otherwise.
101  */
102 template <typename CF>
SkCFDynamicCast(CFTypeRef cf,CF * cfAsCF,char const * name)103 static bool SkCFDynamicCast(CFTypeRef cf, CF* cfAsCF, char const* name) {
104     //SkDEBUGF("SkCFDynamicCast '%s' of type %s to type %s\n", name ? name : "<annon>",
105     //         SkCFTypeIDDescription(  CFGetTypeID(cf)  ).c_str()
106     //         SkCFTypeIDDescription(SkCFGetTypeID<CF>()).c_str());
107     if (!cf) {
108         if (name) {
109             SkDEBUGF("%s not present\n", name);
110         }
111         return false;
112     }
113     if (CFGetTypeID(cf) != SkCFGetTypeID<CF>()) {
114         if (name) {
115             SkDEBUGF("%s is a %s but expected a %s\n", name,
116                      SkCFTypeIDDescription(  CFGetTypeID(cf)  ).c_str(),
117                      SkCFTypeIDDescription(SkCFGetTypeID<CF>()).c_str());
118         }
119         return false;
120     }
121     *cfAsCF = static_cast<CF>(cf);
122     return true;
123 }
124 
125 template<typename T> struct SkCFNumberTypeFor {};
126 #define SK_CFNUMBERTYPE_FOR(c, cf) \
127 template<> struct SkCFNumberTypeFor<c> : std::integral_constant<CFNumberType, cf> {};
128 SK_CFNUMBERTYPE_FOR(char     , kCFNumberCharType    );
129 SK_CFNUMBERTYPE_FOR(short    , kCFNumberShortType   );
130 SK_CFNUMBERTYPE_FOR(int      , kCFNumberIntType     );
131 SK_CFNUMBERTYPE_FOR(long     , kCFNumberLongType    );
132 SK_CFNUMBERTYPE_FOR(long long, kCFNumberLongLongType);
133 SK_CFNUMBERTYPE_FOR(float    , kCFNumberFloatType   );
134 SK_CFNUMBERTYPE_FOR(double   , kCFNumberDoubleType  );
135 
136 template <typename T>
SkCFNumberDynamicCast(CFTypeRef cf,T * number,CFNumberRef * cfNumber,char const * name)137 static bool SkCFNumberDynamicCast(CFTypeRef cf, T* number, CFNumberRef* cfNumber, char const* name){
138     CFNumberRef cfAsCFNumber;
139     if (!SkCFDynamicCast(cf, &cfAsCFNumber, name)) {
140         return false;
141     }
142     if (!CFNumberGetValue(cfAsCFNumber, SkCFNumberTypeFor<T>::value, number)) {
143         if (name) {
144             SkDEBUGF("%s CFNumber not extractable\n", name);
145         }
146         return false;
147     }
148     if (cfNumber) {
149         *cfNumber = cfAsCFNumber;
150     }
151     return true;
152 }
153 
154 // In macOS 10.12 and later any variation on the CGFont which has default axis value will be
155 // dropped when creating the CTFont. Unfortunately, in macOS 10.15 the priority of setting
156 // the optical size (and opsz variation) is
157 // 1. the value of kCTFontOpticalSizeAttribute in the CTFontDescriptor (undocumented)
158 // 2. the opsz axis default value if kCTFontOpticalSizeAttribute is 'none' (undocumented)
159 // 3. the opsz variation on the nascent CTFont from the CGFont (was dropped if default)
160 // 4. the opsz variation in kCTFontVariationAttribute in CTFontDescriptor (crashes 10.10)
161 // 5. the size requested (can fudge in SkTypeface but not SkScalerContext)
162 // The first one which is found will be used to set the opsz variation (after clamping).
add_opsz_attr(CFMutableDictionaryRef attr,double opsz)163 static void add_opsz_attr(CFMutableDictionaryRef attr, double opsz) {
164     SkUniqueCFRef<CFNumberRef> opszValueNumber(
165         CFNumberCreate(kCFAllocatorDefault, kCFNumberDoubleType, &opsz));
166     // Avoid using kCTFontOpticalSizeAttribute directly
167     CFStringRef SkCTFontOpticalSizeAttribute = CFSTR("NSCTFontOpticalSizeAttribute");
168     CFDictionarySetValue(attr, SkCTFontOpticalSizeAttribute, opszValueNumber.get());
169 }
170 
171 // This turns off application of the 'trak' table to advances, but also all other tracking.
add_notrak_attr(CFMutableDictionaryRef attr)172 static void add_notrak_attr(CFMutableDictionaryRef attr) {
173     int zero = 0;
174     SkUniqueCFRef<CFNumberRef> unscaledTrackingNumber(
175         CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &zero));
176     CFStringRef SkCTFontUnscaledTrackingAttribute = CFSTR("NSCTFontUnscaledTrackingAttribute");
177     CFDictionarySetValue(attr, SkCTFontUnscaledTrackingAttribute, unscaledTrackingNumber.get());
178 }
179 
SkCTFontCreateExactCopy(CTFontRef baseFont,CGFloat textSize,OpszVariation opszVariation)180 SkUniqueCFRef<CTFontRef> SkCTFontCreateExactCopy(CTFontRef baseFont, CGFloat textSize,
181                                                  OpszVariation opszVariation)
182 {
183     SkUniqueCFRef<CFMutableDictionaryRef> attr(
184     CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
185                               &kCFTypeDictionaryKeyCallBacks,
186                               &kCFTypeDictionaryValueCallBacks));
187 
188     if (opszVariation.isSet) {
189         add_opsz_attr(attr.get(), opszVariation.value);
190     } else {
191         // On (at least) 10.10 though 10.14 the default system font was SFNSText/SFNSDisplay.
192         // The CTFont is backed by both; optical size < 20 means SFNSText else SFNSDisplay.
193         // On at least 10.11 the glyph ids in these fonts became non-interchangable.
194         // To keep glyph ids stable over size changes, preserve the optical size.
195         // In 10.15 this was replaced with use of variable fonts with an opsz axis.
196         // A CTFont backed by multiple fonts picked by opsz where the multiple backing fonts are
197         // variable fonts with opsz axis and non-interchangeable glyph ids would break the
198         // opsz.isSet branch above, but hopefully that never happens.
199         // See https://crbug.com/524646 .
200         CFStringRef SkCTFontOpticalSizeAttribute = CFSTR("NSCTFontOpticalSizeAttribute");
201         SkUniqueCFRef<CFTypeRef> opsz(CTFontCopyAttribute(baseFont, SkCTFontOpticalSizeAttribute));
202         double opsz_val;
203         if (!opsz ||
204             CFGetTypeID(opsz.get()) != CFNumberGetTypeID() ||
205             !CFNumberGetValue(static_cast<CFNumberRef>(opsz.get()),kCFNumberDoubleType,&opsz_val) ||
206             opsz_val <= 0)
207         {
208             opsz_val = CTFontGetSize(baseFont);
209         }
210         add_opsz_attr(attr.get(), opsz_val);
211     }
212     add_notrak_attr(attr.get());
213 
214     SkUniqueCFRef<CTFontDescriptorRef> desc(CTFontDescriptorCreateWithAttributes(attr.get()));
215 
216     return SkUniqueCFRef<CTFontRef>(
217             CTFontCreateCopyWithAttributes(baseFont, textSize, nullptr, desc.get()));
218 }
219 
SkTypeface_GetCTFontRef(const SkTypeface * face)220 CTFontRef SkTypeface_GetCTFontRef(const SkTypeface* face) {
221     return face ? (CTFontRef)face->internal_private_getCTFontRef() : nullptr;
222 }
223 
find_by_CTFontRef(SkTypeface * cached,void * context)224 static bool find_by_CTFontRef(SkTypeface* cached, void* context) {
225     CTFontRef self = (CTFontRef)context;
226     CTFontRef other = (CTFontRef)cached->internal_private_getCTFontRef();
227 
228     return CFEqual(self, other);
229 }
230 
231 /** Creates a typeface, searching the cache if providedData is nullptr. */
Make(SkUniqueCFRef<CTFontRef> font,OpszVariation opszVariation,std::unique_ptr<SkStreamAsset> providedData)232 sk_sp<SkTypeface> SkTypeface_Mac::Make(SkUniqueCFRef<CTFontRef> font,
233                                        OpszVariation opszVariation,
234                                        std::unique_ptr<SkStreamAsset> providedData) {
235     static SkMutex gTFCacheMutex;
236     static SkTypefaceCache gTFCache;
237 
238     SkASSERT(font);
239     const bool isFromStream(providedData);
240 
241     auto makeTypeface = [&]() {
242         SkUniqueCFRef<CTFontDescriptorRef> desc(CTFontCopyFontDescriptor(font.get()));
243         SkFontStyle style = SkCTFontDescriptorGetSkFontStyle(desc.get(), isFromStream);
244         CTFontSymbolicTraits traits = CTFontGetSymbolicTraits(font.get());
245         bool isFixedPitch = SkToBool(traits & kCTFontMonoSpaceTrait);
246 
247         return sk_sp<SkTypeface>(new SkTypeface_Mac(std::move(font), style, isFixedPitch,
248                                                     opszVariation, std::move(providedData)));
249     };
250 
251     if (isFromStream) {
252         return makeTypeface();
253     }
254 
255     SkAutoMutexExclusive ama(gTFCacheMutex);
256     sk_sp<SkTypeface> face = gTFCache.findByProcAndRef(find_by_CTFontRef, (void*)font.get());
257     if (!face) {
258         face = makeTypeface();
259         if (face) {
260             gTFCache.add(face);
261         }
262     }
263     return face;
264 }
265 
266 /*  This function is visible on the outside. It first searches the cache, and if
267  *  not found, returns a new entry (after adding it to the cache).
268  */
SkMakeTypefaceFromCTFont(CTFontRef font)269 sk_sp<SkTypeface> SkMakeTypefaceFromCTFont(CTFontRef font) {
270     CFRetain(font);
271     return SkTypeface_Mac::Make(SkUniqueCFRef<CTFontRef>(font),
272                                 OpszVariation(),
273                                 nullptr);
274 }
275 
find_dict_CGFloat(CFDictionaryRef dict,CFStringRef name,CGFloat * value)276 static bool find_dict_CGFloat(CFDictionaryRef dict, CFStringRef name, CGFloat* value) {
277     CFNumberRef num;
278     return CFDictionaryGetValueIfPresent(dict, name, (const void**)&num)
279         && CFNumberIsFloatType(num)
280         && CFNumberGetValue(num, kCFNumberCGFloatType, value);
281 }
282 
283 template <typename S, typename D, typename C> struct LinearInterpolater {
284     struct Mapping {
285         S src_val;
286         D dst_val;
287     };
LinearInterpolaterLinearInterpolater288     constexpr LinearInterpolater(Mapping const mapping[], int mappingCount)
289         : fMapping(mapping), fMappingCount(mappingCount) {}
290 
mapLinearInterpolater291     static D map(S value, S src_min, S src_max, D dst_min, D dst_max) {
292         SkASSERT(src_min < src_max);
293         SkASSERT(dst_min <= dst_max);
294         return C()(dst_min + (((value - src_min) * (dst_max - dst_min)) / (src_max - src_min)));
295     }
296 
mapLinearInterpolater297     D map(S val) const {
298         // -Inf to [0]
299         if (val < fMapping[0].src_val) {
300             return fMapping[0].dst_val;
301         }
302 
303         // Linear from [i] to [i+1]
304         for (int i = 0; i < fMappingCount - 1; ++i) {
305             if (val < fMapping[i+1].src_val) {
306                 return map(val, fMapping[i].src_val, fMapping[i+1].src_val,
307                                 fMapping[i].dst_val, fMapping[i+1].dst_val);
308             }
309         }
310 
311         // From [n] to +Inf
312         // if (fcweight < Inf)
313         return fMapping[fMappingCount - 1].dst_val;
314     }
315 
316     Mapping const * fMapping;
317     int fMappingCount;
318 };
319 
320 struct RoundCGFloatToInt {
operator ()RoundCGFloatToInt321     int operator()(CGFloat s) { return s + 0.5; }
322 };
323 struct CGFloatIdentity {
operator ()CGFloatIdentity324     CGFloat operator()(CGFloat s) { return s; }
325 };
326 
327 /** Convert the [0, 1000] CSS weight to [-1, 1] CTFontDescriptor weight (for system fonts).
328  *
329  *  The -1 to 1 weights reported by CTFontDescriptors have different mappings depending on if the
330  *  CTFont is native or created from a CGDataProvider.
331  */
SkCTFontCTWeightForCSSWeight(int fontstyleWeight)332 CGFloat SkCTFontCTWeightForCSSWeight(int fontstyleWeight) {
333     using Interpolator = LinearInterpolater<int, CGFloat, CGFloatIdentity>;
334 
335     // Note that Mac supports the old OS2 version A so 0 through 10 are as if multiplied by 100.
336     // However, on this end we can't tell, so this is ignored.
337 
338     static Interpolator::Mapping nativeWeightMappings[11];
339     static SkOnce once;
340     once([&] {
341         const CGFloat(&nsFontWeights)[11] = SkCTFontGetNSFontWeightMapping();
342         for (int i = 0; i < 11; ++i) {
343             nativeWeightMappings[i].src_val = i * 100;
344             nativeWeightMappings[i].dst_val = nsFontWeights[i];
345         }
346     });
347     static constexpr Interpolator nativeInterpolator(
348             nativeWeightMappings, std::size(nativeWeightMappings));
349 
350     return nativeInterpolator.map(fontstyleWeight);
351 }
352 
353 /** Convert the [-1, 1] CTFontDescriptor weight to [0, 1000] CSS weight.
354  *
355  *  The -1 to 1 weights reported by CTFontDescriptors have different mappings depending on if the
356  *  CTFont is native or created from a CGDataProvider.
357  */
ct_weight_to_fontstyle(CGFloat cgWeight,bool fromDataProvider)358 static int ct_weight_to_fontstyle(CGFloat cgWeight, bool fromDataProvider) {
359     using Interpolator = LinearInterpolater<CGFloat, int, RoundCGFloatToInt>;
360 
361     // Note that Mac supports the old OS2 version A so 0 through 10 are as if multiplied by 100.
362     // However, on this end we can't tell, so this is ignored.
363 
364     static Interpolator::Mapping nativeWeightMappings[11];
365     static Interpolator::Mapping dataProviderWeightMappings[11];
366     static SkOnce once;
367     once([&] {
368         const CGFloat(&nsFontWeights)[11] = SkCTFontGetNSFontWeightMapping();
369         const CGFloat(&userFontWeights)[11] = SkCTFontGetDataFontWeightMapping();
370         for (int i = 0; i < 11; ++i) {
371             nativeWeightMappings[i].src_val = nsFontWeights[i];
372             nativeWeightMappings[i].dst_val = i * 100;
373 
374             dataProviderWeightMappings[i].src_val = userFontWeights[i];
375             dataProviderWeightMappings[i].dst_val = i * 100;
376         }
377     });
378     static constexpr Interpolator nativeInterpolator(
379             nativeWeightMappings, std::size(nativeWeightMappings));
380     static constexpr Interpolator dataProviderInterpolator(
381             dataProviderWeightMappings, std::size(dataProviderWeightMappings));
382 
383     return fromDataProvider ? dataProviderInterpolator.map(cgWeight)
384                             : nativeInterpolator.map(cgWeight);
385 }
386 
387 /** Convert the [0, 10] CSS weight to [-1, 1] CTFontDescriptor width. */
SkCTFontCTWidthForCSSWidth(int fontstyleWidth)388 CGFloat SkCTFontCTWidthForCSSWidth(int fontstyleWidth) {
389     using Interpolator = LinearInterpolater<int, CGFloat, CGFloatIdentity>;
390 
391     // Values determined by creating font data with every width, creating a CTFont,
392     // and asking the CTFont for its width. See TypefaceStyle test for basics.
393     static constexpr Interpolator::Mapping widthMappings[] = {
394         {  0, -0.5 },
395         { 10,  0.5 },
396     };
397     static constexpr Interpolator interpolator(widthMappings, std::size(widthMappings));
398     return interpolator.map(fontstyleWidth);
399 }
400 
401 /** Convert the [-1, 1] CTFontDescriptor width to [0, 10] CSS weight. */
ct_width_to_fontstyle(CGFloat cgWidth)402 static int ct_width_to_fontstyle(CGFloat cgWidth) {
403     using Interpolator = LinearInterpolater<CGFloat, int, RoundCGFloatToInt>;
404 
405     // Values determined by creating font data with every width, creating a CTFont,
406     // and asking the CTFont for its width. See TypefaceStyle test for basics.
407     static constexpr Interpolator::Mapping widthMappings[] = {
408         { -0.5,  0 },
409         {  0.5, 10 },
410     };
411     static constexpr Interpolator interpolator(widthMappings, std::size(widthMappings));
412     return interpolator.map(cgWidth);
413 }
414 
SkCTFontDescriptorGetSkFontStyle(CTFontDescriptorRef desc,bool fromDataProvider)415 SkFontStyle SkCTFontDescriptorGetSkFontStyle(CTFontDescriptorRef desc, bool fromDataProvider) {
416     SkUniqueCFRef<CFTypeRef> traits(CTFontDescriptorCopyAttribute(desc, kCTFontTraitsAttribute));
417     CFDictionaryRef fontTraitsDict;
418     if (!SkCFDynamicCast(traits.get(), &fontTraitsDict, "Font traits")) {
419         return SkFontStyle();
420     }
421 
422     CGFloat weight, width, slant;
423     if (!find_dict_CGFloat(fontTraitsDict, kCTFontWeightTrait, &weight)) {
424         weight = 0;
425     }
426     if (!find_dict_CGFloat(fontTraitsDict, kCTFontWidthTrait, &width)) {
427         width = 0;
428     }
429     if (!find_dict_CGFloat(fontTraitsDict, kCTFontSlantTrait, &slant)) {
430         slant = 0;
431     }
432 
433     return SkFontStyle(ct_weight_to_fontstyle(weight, fromDataProvider),
434                        ct_width_to_fontstyle(width),
435                        slant ? SkFontStyle::kItalic_Slant
436                              : SkFontStyle::kUpright_Slant);
437 }
438 
439 
440 // Web fonts added to the CTFont registry do not return their character set.
441 // Iterate through the font in this case. The existing caller caches the result,
442 // so the performance impact isn't too bad.
populate_glyph_to_unicode_slow(CTFontRef ctFont,CFIndex glyphCount,SkUnichar * out)443 static void populate_glyph_to_unicode_slow(CTFontRef ctFont, CFIndex glyphCount,
444                                            SkUnichar* out) {
445     sk_bzero(out, glyphCount * sizeof(SkUnichar));
446     UniChar unichar = 0;
447     while (glyphCount > 0) {
448         CGGlyph glyph;
449         if (CTFontGetGlyphsForCharacters(ctFont, &unichar, &glyph, 1)) {
450             if (out[glyph] == 0) {
451                 out[glyph] = unichar;
452                 --glyphCount;
453             }
454         }
455         if (++unichar == 0) {
456             break;
457         }
458     }
459 }
460 
461 static constexpr uint16_t kPlaneSize = 1 << 13;
462 
get_plane_glyph_map(const uint8_t * bits,CTFontRef ctFont,CFIndex glyphCount,SkUnichar * glyphToUnicode,uint8_t planeIndex)463 static void get_plane_glyph_map(const uint8_t* bits,
464                                 CTFontRef ctFont,
465                                 CFIndex glyphCount,
466                                 SkUnichar* glyphToUnicode,
467                                 uint8_t planeIndex) {
468     SkUnichar planeOrigin = (SkUnichar)planeIndex << 16; // top half of codepoint.
469     for (uint16_t i = 0; i < kPlaneSize; i++) {
470         uint8_t mask = bits[i];
471         if (!mask) {
472             continue;
473         }
474         for (uint8_t j = 0; j < 8; j++) {
475             if (0 == (mask & ((uint8_t)1 << j))) {
476                 continue;
477             }
478             uint16_t planeOffset = (i << 3) | j;
479             SkUnichar codepoint = planeOrigin | (SkUnichar)planeOffset;
480             uint16_t utf16[2] = {planeOffset, 0};
481             size_t count = 1;
482             if (planeOrigin != 0) {
483                 count = SkUTF::ToUTF16(codepoint, utf16);
484             }
485             CGGlyph glyphs[2] = {0, 0};
486             if (CTFontGetGlyphsForCharacters(ctFont, utf16, glyphs, count)) {
487                 SkASSERT(glyphs[1] == 0);
488                 SkASSERT(glyphs[0] < glyphCount);
489                 // CTFontCopyCharacterSet and CTFontGetGlyphsForCharacters seem to add 'support'
490                 // for characters 0x9, 0xA, and 0xD mapping them to the glyph for character 0x20?
491                 // Prefer mappings to codepoints at or above 0x20.
492                 if (glyphToUnicode[glyphs[0]] < 0x20) {
493                     glyphToUnicode[glyphs[0]] = codepoint;
494                 }
495             }
496         }
497     }
498 }
499 // Construct Glyph to Unicode table.
populate_glyph_to_unicode(CTFontRef ctFont,CFIndex glyphCount,SkUnichar * glyphToUnicode)500 static void populate_glyph_to_unicode(CTFontRef ctFont, CFIndex glyphCount,
501                                       SkUnichar* glyphToUnicode) {
502     sk_bzero(glyphToUnicode, sizeof(SkUnichar) * glyphCount);
503     SkUniqueCFRef<CFCharacterSetRef> charSet(CTFontCopyCharacterSet(ctFont));
504     if (!charSet) {
505         populate_glyph_to_unicode_slow(ctFont, glyphCount, glyphToUnicode);
506         return;
507     }
508 
509     SkUniqueCFRef<CFDataRef> bitmap(
510             CFCharacterSetCreateBitmapRepresentation(nullptr, charSet.get()));
511     if (!bitmap) {
512         return;
513     }
514     CFIndex dataLength = CFDataGetLength(bitmap.get());
515     if (!dataLength) {
516         return;
517     }
518     SkASSERT(dataLength >= kPlaneSize);
519     const UInt8* bits = CFDataGetBytePtr(bitmap.get());
520 
521     get_plane_glyph_map(bits, ctFont, glyphCount, glyphToUnicode, 0);
522     /*
523     A CFData object that specifies the bitmap representation of the Unicode
524     character points the for the new character set. The bitmap representation could
525     contain all the Unicode character range starting from BMP to Plane 16. The
526     first 8KiB (8192 bytes) of the data represent the BMP range. The BMP range 8KiB
527     can be followed by zero to sixteen 8KiB bitmaps, each prepended with the plane
528     index byte. For example, the bitmap representing the BMP and Plane 2 has the
529     size of 16385 bytes (8KiB for BMP, 1 byte index, and a 8KiB bitmap for Plane
530     2). The plane index byte, in this case, contains the integer value two.
531     */
532 
533     if (dataLength <= kPlaneSize) {
534         return;
535     }
536     int extraPlaneCount = (dataLength - kPlaneSize) / (1 + kPlaneSize);
537     SkASSERT(dataLength == kPlaneSize + extraPlaneCount * (1 + kPlaneSize));
538     while (extraPlaneCount-- > 0) {
539         bits += kPlaneSize;
540         uint8_t planeIndex = *bits++;
541         SkASSERT(planeIndex >= 1);
542         SkASSERT(planeIndex <= 16);
543         get_plane_glyph_map(bits, ctFont, glyphCount, glyphToUnicode, planeIndex);
544     }
545 }
546 
getGlyphToUnicodeMap(SkUnichar * dstArray) const547 void SkTypeface_Mac::getGlyphToUnicodeMap(SkUnichar* dstArray) const {
548     SkUniqueCFRef<CTFontRef> ctFont =
549             SkCTFontCreateExactCopy(fFontRef.get(), CTFontGetUnitsPerEm(fFontRef.get()),
550                                     fOpszVariation);
551     CFIndex glyphCount = CTFontGetGlyphCount(ctFont.get());
552     populate_glyph_to_unicode(ctFont.get(), glyphCount, dstArray);
553 }
554 
onGetAdvancedMetrics() const555 std::unique_ptr<SkAdvancedTypefaceMetrics> SkTypeface_Mac::onGetAdvancedMetrics() const {
556 
557     SkUniqueCFRef<CTFontRef> ctFont =
558             SkCTFontCreateExactCopy(fFontRef.get(), CTFontGetUnitsPerEm(fFontRef.get()),
559                                     fOpszVariation);
560 
561     std::unique_ptr<SkAdvancedTypefaceMetrics> info(new SkAdvancedTypefaceMetrics);
562 
563     {
564         SkUniqueCFRef<CFStringRef> fontName(CTFontCopyPostScriptName(ctFont.get()));
565         if (fontName.get()) {
566             SkStringFromCFString(fontName.get(), &info->fPostScriptName);
567             info->fFontName = info->fPostScriptName;
568         }
569     }
570 
571     CFArrayRef ctAxes = this->getVariationAxes();
572     if (ctAxes && CFArrayGetCount(ctAxes) > 0) {
573         info->fFlags |= SkAdvancedTypefaceMetrics::kVariable_FontFlag;
574     }
575 
576     SkOTTableOS2_V4::Type fsType;
577     if (sizeof(fsType) == this->getTableData(SkTEndian_SwapBE32(SkOTTableOS2::TAG),
578                                              offsetof(SkOTTableOS2_V4, fsType),
579                                              sizeof(fsType),
580                                              &fsType)) {
581         SkOTUtils::SetAdvancedTypefaceFlags(fsType, info.get());
582     }
583 
584     // If it's not a truetype font, mark it as 'other'. Assume that TrueType
585     // fonts always have both glyf and loca tables. At the least, this is what
586     // sfntly needs to subset the font. CTFontCopyAttribute() does not always
587     // succeed in determining this directly.
588     if (!this->getTableSize(SkSetFourByteTag('g','l','y','f')) ||
589         !this->getTableSize(SkSetFourByteTag('l','o','c','a')))
590     {
591         return info;
592     }
593 
594     info->fType = SkAdvancedTypefaceMetrics::kTrueType_Font;
595     CTFontSymbolicTraits symbolicTraits = CTFontGetSymbolicTraits(ctFont.get());
596     if (symbolicTraits & kCTFontMonoSpaceTrait) {
597         info->fStyle |= SkAdvancedTypefaceMetrics::kFixedPitch_Style;
598     }
599     if (symbolicTraits & kCTFontItalicTrait) {
600         info->fStyle |= SkAdvancedTypefaceMetrics::kItalic_Style;
601     }
602     CTFontStylisticClass stylisticClass = symbolicTraits & kCTFontClassMaskTrait;
603     if (stylisticClass >= kCTFontOldStyleSerifsClass && stylisticClass <= kCTFontSlabSerifsClass) {
604         info->fStyle |= SkAdvancedTypefaceMetrics::kSerif_Style;
605     } else if (stylisticClass & kCTFontScriptsClass) {
606         info->fStyle |= SkAdvancedTypefaceMetrics::kScript_Style;
607     }
608     info->fItalicAngle = (int16_t) CTFontGetSlantAngle(ctFont.get());
609     info->fAscent = (int16_t) CTFontGetAscent(ctFont.get());
610     info->fDescent = (int16_t) CTFontGetDescent(ctFont.get());
611     info->fCapHeight = (int16_t) CTFontGetCapHeight(ctFont.get());
612     CGRect bbox = CTFontGetBoundingBox(ctFont.get());
613 
614     SkRect r;
615     r.setLTRB(SkScalarFromCGFloat(SkCGRectGetMinX(bbox)),   // Left
616               SkScalarFromCGFloat(SkCGRectGetMaxY(bbox)),   // Top
617               SkScalarFromCGFloat(SkCGRectGetMaxX(bbox)),   // Right
618               SkScalarFromCGFloat(SkCGRectGetMinY(bbox)));  // Bottom
619 
620     r.roundOut(&(info->fBBox));
621 
622     // Figure out a good guess for StemV - Min width of i, I, !, 1.
623     // This probably isn't very good with an italic font.
624     int16_t min_width = SHRT_MAX;
625     info->fStemV = 0;
626     static const UniChar stem_chars[] = {'i', 'I', '!', '1'};
627     const size_t count = sizeof(stem_chars) / sizeof(stem_chars[0]);
628     CGGlyph glyphs[count];
629     CGRect boundingRects[count];
630     if (CTFontGetGlyphsForCharacters(ctFont.get(), stem_chars, glyphs, count)) {
631         CTFontGetBoundingRectsForGlyphs(ctFont.get(), kCTFontOrientationHorizontal,
632                                         glyphs, boundingRects, count);
633         for (size_t i = 0; i < count; i++) {
634             int16_t width = (int16_t) boundingRects[i].size.width;
635             if (width > 0 && width < min_width) {
636                 min_width = width;
637                 info->fStemV = min_width;
638             }
639         }
640     }
641     return info;
642 }
643 
get_font_type_tag(CTFontRef ctFont)644 static SK_SFNT_ULONG get_font_type_tag(CTFontRef ctFont) {
645     SkUniqueCFRef<CFNumberRef> fontFormatRef(
646             static_cast<CFNumberRef>(CTFontCopyAttribute(ctFont, kCTFontFormatAttribute)));
647     if (!fontFormatRef) {
648         return 0;
649     }
650 
651     SInt32 fontFormatValue;
652     if (!CFNumberGetValue(fontFormatRef.get(), kCFNumberSInt32Type, &fontFormatValue)) {
653         return 0;
654     }
655 
656     switch (fontFormatValue) {
657         case kCTFontFormatOpenTypePostScript:
658             return SkSFNTHeader::fontType_OpenTypeCFF::TAG;
659         case kCTFontFormatOpenTypeTrueType:
660             return SkSFNTHeader::fontType_WindowsTrueType::TAG;
661         case kCTFontFormatTrueType:
662             return SkSFNTHeader::fontType_MacTrueType::TAG;
663         case kCTFontFormatPostScript:
664             return SkSFNTHeader::fontType_PostScript::TAG;
665         case kCTFontFormatBitmap:
666             return SkSFNTHeader::fontType_MacTrueType::TAG;
667         case kCTFontFormatUnrecognized:
668         default:
669             return 0;
670     }
671 }
672 
onOpenStream(int * ttcIndex) const673 std::unique_ptr<SkStreamAsset> SkTypeface_Mac::onOpenStream(int* ttcIndex) const {
674     *ttcIndex = 0;
675 
676     fInitStream([this]{
677     if (fStream) {
678         return;
679     }
680 
681     SK_SFNT_ULONG fontType = get_font_type_tag(fFontRef.get());
682 
683     // get table tags
684     int numTables = this->countTables();
685     SkTDArray<SkFontTableTag> tableTags;
686     tableTags.resize(numTables);
687     this->getTableTags(tableTags.begin());
688 
689     // CT seems to be unreliable in being able to obtain the type,
690     // even if all we want is the first four bytes of the font resource.
691     // Just the presence of the FontForge 'FFTM' table seems to throw it off.
692     if (fontType == 0) {
693         fontType = SkSFNTHeader::fontType_WindowsTrueType::TAG;
694 
695         // see https://skbug.com/7630#c7
696         bool couldBeCFF = false;
697         constexpr SkFontTableTag CFFTag = SkSetFourByteTag('C', 'F', 'F', ' ');
698         constexpr SkFontTableTag CFF2Tag = SkSetFourByteTag('C', 'F', 'F', '2');
699         for (int tableIndex = 0; tableIndex < numTables; ++tableIndex) {
700             if (CFFTag == tableTags[tableIndex] || CFF2Tag == tableTags[tableIndex]) {
701                 couldBeCFF = true;
702             }
703         }
704         if (couldBeCFF) {
705             fontType = SkSFNTHeader::fontType_OpenTypeCFF::TAG;
706         }
707     }
708 
709     // Sometimes CoreGraphics incorrectly thinks a font is kCTFontFormatPostScript.
710     // It is exceedingly unlikely that this is the case, so double check
711     // (see https://crbug.com/809763 ).
712     if (fontType == SkSFNTHeader::fontType_PostScript::TAG) {
713         // see if there are any required 'typ1' tables (see Adobe Technical Note #5180)
714         bool couldBeTyp1 = false;
715         constexpr SkFontTableTag TYPE1Tag = SkSetFourByteTag('T', 'Y', 'P', '1');
716         constexpr SkFontTableTag CIDTag = SkSetFourByteTag('C', 'I', 'D', ' ');
717         for (int tableIndex = 0; tableIndex < numTables; ++tableIndex) {
718             if (TYPE1Tag == tableTags[tableIndex] || CIDTag == tableTags[tableIndex]) {
719                 couldBeTyp1 = true;
720             }
721         }
722         if (!couldBeTyp1) {
723             fontType = SkSFNTHeader::fontType_OpenTypeCFF::TAG;
724         }
725     }
726 
727     // get the table sizes and accumulate the total size of the font
728     SkTDArray<size_t> tableSizes;
729     size_t totalSize = sizeof(SkSFNTHeader) + sizeof(SkSFNTHeader::TableDirectoryEntry) * numTables;
730     for (int tableIndex = 0; tableIndex < numTables; ++tableIndex) {
731         size_t tableSize = this->getTableSize(tableTags[tableIndex]);
732         totalSize += (tableSize + 3) & ~3;
733         *tableSizes.append() = tableSize;
734     }
735 
736     // reserve memory for stream, and zero it (tables must be zero padded)
737     fStream = std::make_unique<SkMemoryStream>(totalSize);
738     char* dataStart = (char*)fStream->getMemoryBase();
739     sk_bzero(dataStart, totalSize);
740     char* dataPtr = dataStart;
741 
742     // compute font header entries
743     uint16_t entrySelector = 0;
744     uint16_t searchRange = 1;
745     while (searchRange < numTables >> 1) {
746         entrySelector++;
747         searchRange <<= 1;
748     }
749     searchRange <<= 4;
750     uint16_t rangeShift = (numTables << 4) - searchRange;
751 
752     // write font header
753     SkSFNTHeader* header = (SkSFNTHeader*)dataPtr;
754     header->fontType = fontType;
755     header->numTables = SkEndian_SwapBE16(numTables);
756     header->searchRange = SkEndian_SwapBE16(searchRange);
757     header->entrySelector = SkEndian_SwapBE16(entrySelector);
758     header->rangeShift = SkEndian_SwapBE16(rangeShift);
759     dataPtr += sizeof(SkSFNTHeader);
760 
761     // write tables
762     SkSFNTHeader::TableDirectoryEntry* entry = (SkSFNTHeader::TableDirectoryEntry*)dataPtr;
763     dataPtr += sizeof(SkSFNTHeader::TableDirectoryEntry) * numTables;
764     for (int tableIndex = 0; tableIndex < numTables; ++tableIndex) {
765         size_t tableSize = tableSizes[tableIndex];
766         this->getTableData(tableTags[tableIndex], 0, tableSize, dataPtr);
767         entry->tag = SkEndian_SwapBE32(tableTags[tableIndex]);
768         entry->checksum = SkEndian_SwapBE32(SkOTUtils::CalcTableChecksum((SK_OT_ULONG*)dataPtr,
769                                                                          tableSize));
770         entry->offset = SkEndian_SwapBE32(SkToU32(dataPtr - dataStart));
771         entry->logicalLength = SkEndian_SwapBE32(SkToU32(tableSize));
772 
773         dataPtr += (tableSize + 3) & ~3;
774         ++entry;
775     }
776     });
777     return fStream->duplicate();
778 }
779 
onOpenExistingStream(int * ttcIndex) const780 std::unique_ptr<SkStreamAsset> SkTypeface_Mac::onOpenExistingStream(int* ttcIndex) const {
781     *ttcIndex = 0;
782     return fStream ? fStream->duplicate() : nullptr;
783 }
784 
onGlyphMaskNeedsCurrentColor() const785 bool SkTypeface_Mac::onGlyphMaskNeedsCurrentColor() const {
786     // `CPAL` (`COLR` and `SVG`) fonts may need the current color.
787     // However, even `sbix` fonts can have glyphs which need the current color.
788     // These may be glyphs with paths but no `sbix` entries, which are impossible to distinguish.
789     return this->fHasColorGlyphs;
790 }
791 
getVariationAxes() const792 CFArrayRef SkTypeface_Mac::getVariationAxes() const {
793     fInitVariationAxes([this]{
794         fVariationAxes.reset(CTFontCopyVariationAxes(fFontRef.get()));
795     });
796     return fVariationAxes.get();
797 }
798 
onGetVariationDesignPosition(SkFontArguments::VariationPosition::Coordinate coordinates[],int coordinateCount) const799 int SkTypeface_Mac::onGetVariationDesignPosition(
800         SkFontArguments::VariationPosition::Coordinate coordinates[], int coordinateCount) const
801 {
802     CFArrayRef ctAxes = this->getVariationAxes();
803     if (!ctAxes) {
804         return -1;
805     }
806     CFIndex axisCount = CFArrayGetCount(ctAxes);
807     if (!coordinates || coordinateCount < axisCount) {
808         return axisCount;
809     }
810 
811     // On 10.12 and later, this only returns non-default variations.
812     SkUniqueCFRef<CFDictionaryRef> ctVariation(CTFontCopyVariation(fFontRef.get()));
813     if (!ctVariation) {
814         return -1;
815     }
816 
817     for (int i = 0; i < axisCount; ++i) {
818         CFDictionaryRef axisInfoDict;
819         if (!SkCFDynamicCast(CFArrayGetValueAtIndex(ctAxes, i), &axisInfoDict, "Axis")) {
820             return -1;
821         }
822 
823         int64_t tagLong;
824         CFNumberRef tagNumber;
825         CFTypeRef tag = CFDictionaryGetValue(axisInfoDict, kCTFontVariationAxisIdentifierKey);
826         if (!SkCFNumberDynamicCast(tag, &tagLong, &tagNumber, "Axis tag")) {
827             return -1;
828         }
829         coordinates[i].axis = tagLong;
830 
831         CGFloat valueCGFloat;
832         CFTypeRef value = CFDictionaryGetValue(ctVariation.get(), tagNumber);
833         if (value) {
834             if (!SkCFNumberDynamicCast(value, &valueCGFloat, nullptr, "Variation value")) {
835                 return -1;
836             }
837         } else {
838             CFTypeRef def = CFDictionaryGetValue(axisInfoDict, kCTFontVariationAxisDefaultValueKey);
839             if (!SkCFNumberDynamicCast(def, &valueCGFloat, nullptr, "Axis default value")) {
840                 return -1;
841             }
842         }
843         coordinates[i].value = SkScalarFromCGFloat(valueCGFloat);
844     }
845     return axisCount;
846 }
847 
onGetUPEM() const848 int SkTypeface_Mac::onGetUPEM() const {
849     SkUniqueCFRef<CGFontRef> cgFont(CTFontCopyGraphicsFont(fFontRef.get(), nullptr));
850     return CGFontGetUnitsPerEm(cgFont.get());
851 }
852 
onCreateFamilyNameIterator() const853 SkTypeface::LocalizedStrings* SkTypeface_Mac::onCreateFamilyNameIterator() const {
854     sk_sp<SkTypeface::LocalizedStrings> nameIter =
855             SkOTUtils::LocalizedStrings_NameTable::MakeForFamilyNames(*this);
856     if (!nameIter) {
857         CFStringRef cfLanguageRaw;
858         SkUniqueCFRef<CFStringRef> cfFamilyName(
859                 CTFontCopyLocalizedName(fFontRef.get(), kCTFontFamilyNameKey, &cfLanguageRaw));
860         SkUniqueCFRef<CFStringRef> cfLanguage(cfLanguageRaw);
861 
862         SkString skLanguage;
863         SkString skFamilyName;
864         if (cfLanguage) {
865             SkStringFromCFString(cfLanguage.get(), &skLanguage);
866         } else {
867             skLanguage = "und"; //undetermined
868         }
869         if (cfFamilyName) {
870             SkStringFromCFString(cfFamilyName.get(), &skFamilyName);
871         }
872 
873         nameIter = sk_make_sp<SkOTUtils::LocalizedStrings_SingleName>(skFamilyName, skLanguage);
874     }
875     return nameIter.release();
876 }
877 
onGetTableTags(SkFontTableTag tags[]) const878 int SkTypeface_Mac::onGetTableTags(SkFontTableTag tags[]) const {
879     SkUniqueCFRef<CFArrayRef> cfArray(
880             CTFontCopyAvailableTables(fFontRef.get(), kCTFontTableOptionNoOptions));
881     if (!cfArray) {
882         return 0;
883     }
884     CFIndex count = CFArrayGetCount(cfArray.get());
885     if (tags) {
886         for (CFIndex i = 0; i < count; ++i) {
887             uintptr_t fontTag = reinterpret_cast<uintptr_t>(
888                 CFArrayGetValueAtIndex(cfArray.get(), i));
889             tags[i] = static_cast<SkFontTableTag>(fontTag);
890         }
891     }
892     return count;
893 }
894 
895 // If, as is the case with web fonts, the CTFont data isn't available,
896 // the CGFont data may work. While the CGFont may always provide the
897 // right result, leave the CTFont code path to minimize disruption.
copy_table_from_font(CTFontRef ctFont,SkFontTableTag tag)898 static SkUniqueCFRef<CFDataRef> copy_table_from_font(CTFontRef ctFont, SkFontTableTag tag) {
899     SkUniqueCFRef<CFDataRef> data(CTFontCopyTable(ctFont, (CTFontTableTag) tag,
900                                                   kCTFontTableOptionNoOptions));
901     if (!data) {
902         SkUniqueCFRef<CGFontRef> cgFont(CTFontCopyGraphicsFont(ctFont, nullptr));
903         data.reset(CGFontCopyTableForTag(cgFont.get(), tag));
904     }
905     return data;
906 }
907 
onGetTableData(SkFontTableTag tag,size_t offset,size_t length,void * dstData) const908 size_t SkTypeface_Mac::onGetTableData(SkFontTableTag tag, size_t offset,
909                                       size_t length, void* dstData) const {
910     SkUniqueCFRef<CFDataRef> srcData = copy_table_from_font(fFontRef.get(), tag);
911     if (!srcData) {
912         return 0;
913     }
914 
915     size_t srcSize = CFDataGetLength(srcData.get());
916     if (offset >= srcSize) {
917         return 0;
918     }
919     if (length > srcSize - offset) {
920         length = srcSize - offset;
921     }
922     if (dstData) {
923         memcpy(dstData, CFDataGetBytePtr(srcData.get()) + offset, length);
924     }
925     return length;
926 }
927 
onCopyTableData(SkFontTableTag tag) const928 sk_sp<SkData> SkTypeface_Mac::onCopyTableData(SkFontTableTag tag) const {
929     SkUniqueCFRef<CFDataRef> srcData = copy_table_from_font(fFontRef.get(), tag);
930     if (!srcData) {
931         return nullptr;
932     }
933     const UInt8* data = CFDataGetBytePtr(srcData.get());
934     CFIndex length = CFDataGetLength(srcData.get());
935     return SkData::MakeWithProc(data, length,
936                                 [](const void*, void* ctx) {
937                                     CFRelease((CFDataRef)ctx);
938                                 }, (void*)srcData.release());
939 }
940 
onCreateScalerContext(const SkScalerContextEffects & effects,const SkDescriptor * desc) const941 std::unique_ptr<SkScalerContext> SkTypeface_Mac::onCreateScalerContext(
942     const SkScalerContextEffects& effects, const SkDescriptor* desc) const
943 {
944     return std::make_unique<SkScalerContext_Mac>(
945             sk_ref_sp(const_cast<SkTypeface_Mac*>(this)), effects, desc);
946 }
947 
onFilterRec(SkScalerContextRec * rec) const948 void SkTypeface_Mac::onFilterRec(SkScalerContextRec* rec) const {
949     if (rec->fFlags & SkScalerContext::kLCD_BGROrder_Flag ||
950         rec->fFlags & SkScalerContext::kLCD_Vertical_Flag)
951     {
952         rec->fMaskFormat = SkMask::kA8_Format;
953         // Render the glyphs as close as possible to what was requested.
954         // The above turns off subpixel rendering, but the user requested it.
955         // Normal hinting will cause the A8 masks to be generated from CoreGraphics subpixel masks.
956         // See comments below for more details.
957         rec->setHinting(SkFontHinting::kNormal);
958     }
959 
960     unsigned flagsWeDontSupport = SkScalerContext::kForceAutohinting_Flag  |
961                                   SkScalerContext::kLCD_BGROrder_Flag |
962                                   SkScalerContext::kLCD_Vertical_Flag;
963 
964     rec->fFlags &= ~flagsWeDontSupport;
965 
966     const SkCTFontSmoothBehavior smoothBehavior = SkCTFontGetSmoothBehavior();
967 
968     // Only two levels of hinting are supported.
969     // kNo_Hinting means avoid CoreGraphics outline dilation (smoothing).
970     // kNormal_Hinting means CoreGraphics outline dilation (smoothing) is allowed.
971     if (rec->getHinting() != SkFontHinting::kNone) {
972         rec->setHinting(SkFontHinting::kNormal);
973     }
974     // If smoothing has no effect, don't request it.
975     if (smoothBehavior == SkCTFontSmoothBehavior::none) {
976         rec->setHinting(SkFontHinting::kNone);
977     }
978 
979     // FIXME: lcd smoothed un-hinted rasterization unsupported.
980     // Tracked by http://code.google.com/p/skia/issues/detail?id=915 .
981     // There is no current means to honor a request for unhinted lcd,
982     // so arbitrarilly ignore the hinting request and honor lcd.
983 
984     // Hinting and smoothing should be orthogonal, but currently they are not.
985     // CoreGraphics has no API to influence hinting. However, its lcd smoothed
986     // output is drawn from auto-dilated outlines (the amount of which is
987     // determined by AppleFontSmoothing). Its regular anti-aliased output is
988     // drawn from un-dilated outlines.
989 
990     // The behavior of Skia is as follows:
991     // [AA][no-hint]: generate AA using CoreGraphic's AA output.
992     // [AA][yes-hint]: use CoreGraphic's LCD output and reduce it to a single
993     // channel. This matches [LCD][yes-hint] in weight.
994     // [LCD][no-hint]: currently unable to honor, and must pick which to respect.
995     // Currently side with LCD, effectively ignoring the hinting setting.
996     // [LCD][yes-hint]: generate LCD using CoreGraphic's LCD output.
997     if (rec->fMaskFormat == SkMask::kLCD16_Format) {
998         if (smoothBehavior == SkCTFontSmoothBehavior::subpixel) {
999             //CoreGraphics creates 555 masks for smoothed text anyway.
1000             rec->fMaskFormat = SkMask::kLCD16_Format;
1001             rec->setHinting(SkFontHinting::kNormal);
1002         } else {
1003             rec->fMaskFormat = SkMask::kA8_Format;
1004             if (smoothBehavior != SkCTFontSmoothBehavior::none) {
1005                 rec->setHinting(SkFontHinting::kNormal);
1006             }
1007         }
1008     }
1009 
1010     // CoreText provides no information as to whether a glyph will be color or not.
1011     // Fonts may mix outlines and bitmaps, so information is needed on a glyph by glyph basis.
1012     // If a font contains an 'sbix' table, consider it to be a color font, and disable lcd.
1013     if (fHasColorGlyphs) {
1014         rec->fMaskFormat = SkMask::kARGB32_Format;
1015     }
1016 
1017     // Unhinted A8 masks (those not derived from LCD masks) must respect SK_GAMMA_APPLY_TO_A8.
1018     // All other masks can use regular gamma.
1019     if (SkMask::kA8_Format == rec->fMaskFormat && SkFontHinting::kNone == rec->getHinting()) {
1020 #ifndef SK_GAMMA_APPLY_TO_A8
1021         // SRGBTODO: Is this correct? Do we want contrast boost?
1022         rec->ignorePreBlend();
1023 #endif
1024     } else {
1025         SkColor color = rec->getLuminanceColor();
1026         if (smoothBehavior == SkCTFontSmoothBehavior::some) {
1027             // CoreGraphics smoothed text without subpixel coverage blitting goes from a gamma of
1028             // 2.0 for black foreground to a gamma of 1.0 for white foreground. Emulate this
1029             // through the mask gamma by reducing the color values to 1/2.
1030             color = SkColorSetRGB(SkColorGetR(color) * 1/2,
1031                                   SkColorGetG(color) * 1/2,
1032                                   SkColorGetB(color) * 1/2);
1033         } else if (smoothBehavior == SkCTFontSmoothBehavior::subpixel) {
1034             // CoreGraphics smoothed text with subpixel coverage blitting goes from a gamma of
1035             // 2.0 for black foreground to a gamma of ~1.4? for white foreground. Emulate this
1036             // through the mask gamma by reducing the color values to 3/4.
1037             color = SkColorSetRGB(SkColorGetR(color) * 3/4,
1038                                   SkColorGetG(color) * 3/4,
1039                                   SkColorGetB(color) * 3/4);
1040         }
1041         rec->setLuminanceColor(color);
1042 
1043         // CoreGraphics dialates smoothed text to provide contrast.
1044         rec->setContrast(0);
1045     }
1046 }
1047 
1048 /** Takes ownership of the CFStringRef. */
get_str(CFStringRef ref,SkString * str)1049 static const char* get_str(CFStringRef ref, SkString* str) {
1050     if (nullptr == ref) {
1051         return nullptr;
1052     }
1053     SkStringFromCFString(ref, str);
1054     CFRelease(ref);
1055     return str->c_str();
1056 }
1057 
onGetFamilyName(SkString * familyName) const1058 void SkTypeface_Mac::onGetFamilyName(SkString* familyName) const {
1059     get_str(CTFontCopyFamilyName(fFontRef.get()), familyName);
1060 }
1061 
onGetPostScriptName(SkString * skPostScriptName) const1062 bool SkTypeface_Mac::onGetPostScriptName(SkString* skPostScriptName) const {
1063     SkUniqueCFRef<CFStringRef> ctPostScriptName(CTFontCopyPostScriptName(fFontRef.get()));
1064     if (!ctPostScriptName) {
1065         return false;
1066     }
1067     if (skPostScriptName) {
1068         SkStringFromCFString(ctPostScriptName.get(), skPostScriptName);
1069     }
1070     return true;
1071 }
1072 
onGetFontDescriptor(SkFontDescriptor * desc,bool * isLocalStream) const1073 void SkTypeface_Mac::onGetFontDescriptor(SkFontDescriptor* desc,
1074                                          bool* isLocalStream) const {
1075     SkString tmpStr;
1076 
1077     desc->setFamilyName(get_str(CTFontCopyFamilyName(fFontRef.get()), &tmpStr));
1078     desc->setFullName(get_str(CTFontCopyFullName(fFontRef.get()), &tmpStr));
1079     desc->setPostscriptName(get_str(CTFontCopyPostScriptName(fFontRef.get()), &tmpStr));
1080     desc->setStyle(this->fontStyle());
1081     desc->setFactoryId(FactoryId);
1082     *isLocalStream = fIsFromStream;
1083 }
1084 
onCharsToGlyphs(const SkUnichar uni[],int count,SkGlyphID glyphs[]) const1085 void SkTypeface_Mac::onCharsToGlyphs(const SkUnichar uni[], int count, SkGlyphID glyphs[]) const {
1086     // Undocumented behavior of CTFontGetGlyphsForCharacters with non-bmp code points:
1087     // When a surrogate pair is detected, the glyph index used is the index of the high surrogate.
1088     // It is documented that if a mapping is unavailable, the glyph will be set to 0.
1089 
1090     AutoSTMalloc<1024, UniChar> charStorage;
1091     const UniChar* src; // UniChar is a UTF-16 16-bit code unit.
1092     int srcCount;
1093     const SkUnichar* utf32 = reinterpret_cast<const SkUnichar*>(uni);
1094     UniChar* utf16 = charStorage.reset(2 * count);
1095     src = utf16;
1096     for (int i = 0; i < count; ++i) {
1097         utf16 += SkUTF::ToUTF16(utf32[i], utf16);
1098     }
1099     srcCount = SkToInt(utf16 - src);
1100 
1101     // If there are any non-bmp code points, the provided 'glyphs' storage will be inadequate.
1102     AutoSTMalloc<1024, uint16_t> glyphStorage;
1103     uint16_t* macGlyphs = glyphs;
1104     if (srcCount > count) {
1105         macGlyphs = glyphStorage.reset(srcCount);
1106     }
1107 
1108     CTFontGetGlyphsForCharacters(fFontRef.get(), src, macGlyphs, srcCount);
1109 
1110     // If there were any non-bmp, then copy and compact.
1111     // If all are bmp, 'glyphs' already contains the compact glyphs.
1112     // If some are non-bmp, copy and compact into 'glyphs'.
1113     if (srcCount > count) {
1114         SkASSERT(glyphs != macGlyphs);
1115         int extra = 0;
1116         for (int i = 0; i < count; ++i) {
1117             glyphs[i] = macGlyphs[i + extra];
1118             if (SkUTF::IsLeadingSurrogateUTF16(src[i + extra])) {
1119                 ++extra;
1120             }
1121         }
1122     } else {
1123         SkASSERT(glyphs == macGlyphs);
1124     }
1125 }
1126 
onCountGlyphs() const1127 int SkTypeface_Mac::onCountGlyphs() const {
1128     return SkToInt(CTFontGetGlyphCount(fFontRef.get()));
1129 }
1130 
1131 /** Creates a dictionary suitable for setting the axes on a CTFont. */
ctvariation_from_SkFontArguments(CTFontRef ct,CFArrayRef ctAxes,const SkFontArguments & args)1132 static CTFontVariation ctvariation_from_SkFontArguments(CTFontRef ct, CFArrayRef ctAxes,
1133                                                         const SkFontArguments& args) {
1134     OpszVariation opsz;
1135     constexpr const SkFourByteTag opszTag = SkSetFourByteTag('o','p','s','z');
1136 
1137     if (!ctAxes) {
1138         return CTFontVariation();
1139     }
1140     CFIndex axisCount = CFArrayGetCount(ctAxes);
1141 
1142     // On 10.12 and later, this only returns non-default variations.
1143     SkUniqueCFRef<CFDictionaryRef> oldCtVariation(CTFontCopyVariation(ct));
1144 
1145     const SkFontArguments::VariationPosition position = args.getVariationDesignPosition();
1146 
1147     SkUniqueCFRef<CFMutableDictionaryRef> newCtVariation(
1148             CFDictionaryCreateMutable(kCFAllocatorDefault, axisCount,
1149                                       &kCFTypeDictionaryKeyCallBacks,
1150                                       &kCFTypeDictionaryValueCallBacks));
1151     SkUniqueCFRef<CFMutableDictionaryRef> wrongOpszVariation;
1152 
1153     for (int i = 0; i < axisCount; ++i) {
1154         CFDictionaryRef axisInfoDict;
1155         if (!SkCFDynamicCast(CFArrayGetValueAtIndex(ctAxes, i), &axisInfoDict, "Axis")) {
1156             return CTFontVariation();
1157         }
1158 
1159         int64_t tagLong;
1160         CFNumberRef tagNumber;
1161         CFTypeRef tag = CFDictionaryGetValue(axisInfoDict, kCTFontVariationAxisIdentifierKey);
1162         if (!SkCFNumberDynamicCast(tag, &tagLong, &tagNumber, "Axis tag")) {
1163             return CTFontVariation();
1164         }
1165 
1166         // The variation axes can be set to any value, but cg will effectively pin them.
1167         // Pin them here to normalize.
1168         double minDouble;
1169         double maxDouble;
1170         double defDouble;
1171         CFTypeRef min = CFDictionaryGetValue(axisInfoDict, kCTFontVariationAxisMinimumValueKey);
1172         CFTypeRef max = CFDictionaryGetValue(axisInfoDict, kCTFontVariationAxisMaximumValueKey);
1173         CFTypeRef def = CFDictionaryGetValue(axisInfoDict, kCTFontVariationAxisDefaultValueKey);
1174         if (!SkCFNumberDynamicCast(min, &minDouble, nullptr, "Axis min") ||
1175             !SkCFNumberDynamicCast(max, &maxDouble, nullptr, "Axis max") ||
1176             !SkCFNumberDynamicCast(def, &defDouble, nullptr, "Axis def"))
1177         {
1178             return CTFontVariation();
1179         }
1180 
1181         // Start with the default value.
1182         double value = defDouble;
1183 
1184         // Then the current value.
1185         bool haveCurrentDouble = false;
1186         double currentDouble = 0;
1187         if (oldCtVariation) {
1188             CFTypeRef currentNumber = CFDictionaryGetValue(oldCtVariation.get(), tagNumber);
1189             if (currentNumber) {
1190                 if (!SkCFNumberDynamicCast(currentNumber, &value, nullptr, "Variation value")) {
1191                     return CTFontVariation();
1192                 }
1193                 currentDouble = value;
1194                 haveCurrentDouble = true;
1195             }
1196         }
1197 
1198         // Then the requested value.
1199         // The position may be over specified. If there are multiple values for a given axis,
1200         // use the last one since that's what css-fonts-4 requires.
1201         for (int j = position.coordinateCount; j --> 0;) {
1202             if (position.coordinates[j].axis == tagLong) {
1203                 value = SkTPin<double>(position.coordinates[j].value, minDouble, maxDouble);
1204                 if (tagLong == opszTag) {
1205                     opsz.isSet = true;
1206                 }
1207                 break;
1208             }
1209         }
1210         if (tagLong == opszTag) {
1211             opsz.value = value;
1212             if (haveCurrentDouble && value == currentDouble) {
1213                 // Calculate a value strictly in range but different from currentValue.
1214                 double wrongOpszDouble = ((maxDouble - minDouble) / 2.0) + minDouble;
1215                 if (wrongOpszDouble == currentDouble) {
1216                     wrongOpszDouble = ((maxDouble - minDouble) / 4.0) + minDouble;
1217                 }
1218                 wrongOpszVariation.reset(
1219                     CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
1220                                               &kCFTypeDictionaryKeyCallBacks,
1221                                               &kCFTypeDictionaryValueCallBacks));
1222                 SkUniqueCFRef<CFNumberRef> wrongOpszNumber(
1223                     CFNumberCreate(kCFAllocatorDefault, kCFNumberDoubleType, &wrongOpszDouble));
1224                 CFDictionarySetValue(wrongOpszVariation.get(), tagNumber, wrongOpszNumber.get());
1225             }
1226         }
1227         SkUniqueCFRef<CFNumberRef> valueNumber(
1228             CFNumberCreate(kCFAllocatorDefault, kCFNumberDoubleType, &value));
1229         CFDictionaryAddValue(newCtVariation.get(), tagNumber, valueNumber.get());
1230     }
1231     return { SkUniqueCFRef<CFDictionaryRef>(std::move(newCtVariation)),
1232              SkUniqueCFRef<CFDictionaryRef>(std::move(wrongOpszVariation)),
1233              opsz };
1234 }
1235 
onMakeClone(const SkFontArguments & args) const1236 sk_sp<SkTypeface> SkTypeface_Mac::onMakeClone(const SkFontArguments& args) const {
1237     CTFontVariation ctVariation = ctvariation_from_SkFontArguments(fFontRef.get(),
1238                                                                    this->getVariationAxes(),
1239                                                                    args);
1240 
1241     SkUniqueCFRef<CTFontRef> ctVariant;
1242     if (ctVariation.variation) {
1243         SkUniqueCFRef<CFMutableDictionaryRef> attributes(
1244                 CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
1245                                           &kCFTypeDictionaryKeyCallBacks,
1246                                           &kCFTypeDictionaryValueCallBacks));
1247 
1248         CTFontRef ctFont = fFontRef.get();
1249         SkUniqueCFRef<CTFontRef> wrongOpszFont;
1250         if (ctVariation.wrongOpszVariation) {
1251             // On macOS 11 cloning a system font with an opsz axis and not changing the
1252             // value of the opsz axis (either by setting it to the same value or not
1253             // specifying it at all) when setting a variation causes the variation to
1254             // be set but the cloned font will still compare CFEqual to the original
1255             // font. Work around this by setting the opsz to something which isn't the
1256             // desired value before setting the entire desired variation.
1257             //
1258             // A similar issue occurs with fonts from data on macOS 10.15 and the same
1259             // work around seems to apply. This is less noticeable though since CFEqual
1260             // isn't used on these fonts.
1261             CFDictionarySetValue(attributes.get(),
1262                                  kCTFontVariationAttribute, ctVariation.wrongOpszVariation.get());
1263             SkUniqueCFRef<CTFontDescriptorRef> varDesc(
1264                 CTFontDescriptorCreateWithAttributes(attributes.get()));
1265             wrongOpszFont.reset(CTFontCreateCopyWithAttributes(ctFont, 0, nullptr, varDesc.get()));
1266             ctFont = wrongOpszFont.get();
1267         }
1268 
1269         CFDictionarySetValue(attributes.get(),
1270                              kCTFontVariationAttribute, ctVariation.variation.get());
1271         SkUniqueCFRef<CTFontDescriptorRef> varDesc(
1272                 CTFontDescriptorCreateWithAttributes(attributes.get()));
1273         ctVariant.reset(CTFontCreateCopyWithAttributes(ctFont, 0, nullptr, varDesc.get()));
1274     } else {
1275         ctVariant.reset((CTFontRef)CFRetain(fFontRef.get()));
1276     }
1277     if (!ctVariant) {
1278         return nullptr;
1279     }
1280 
1281     return SkTypeface_Mac::Make(std::move(ctVariant), ctVariation.opsz,
1282                                 fStream ? fStream->duplicate() : nullptr);
1283 }
1284 
skdata_from_skstreamasset(std::unique_ptr<SkStreamAsset> stream)1285 static sk_sp<SkData> skdata_from_skstreamasset(std::unique_ptr<SkStreamAsset> stream) {
1286     size_t size = stream->getLength();
1287     if (const void* base = stream->getMemoryBase()) {
1288         return SkData::MakeWithProc(base, size,
1289                                     [](const void*, void* ctx) -> void {
1290                                         delete (SkStreamAsset*)ctx;
1291                                     }, stream.release());
1292     }
1293     return SkData::MakeFromStream(stream.get(), size);
1294 }
1295 
cfdata_from_skdata(sk_sp<SkData> data)1296 static SkUniqueCFRef<CFDataRef> cfdata_from_skdata(sk_sp<SkData> data) {
1297     void const * const addr = data->data();
1298     size_t const size = data->size();
1299 
1300     CFAllocatorContext ctx = {
1301         0, // CFIndex version
1302         data.release(), // void* info
1303         nullptr, // const void *(*retain)(const void *info);
1304         nullptr, // void (*release)(const void *info);
1305         nullptr, // CFStringRef (*copyDescription)(const void *info);
1306         nullptr, // void * (*allocate)(CFIndex size, CFOptionFlags hint, void *info);
1307         nullptr, // void*(*reallocate)(void* ptr,CFIndex newsize,CFOptionFlags hint,void* info);
1308         [](void*,void* info) -> void { // void (*deallocate)(void *ptr, void *info);
1309             SkASSERT(info);
1310             ((SkData*)info)->unref();
1311         },
1312         nullptr, // CFIndex (*preferredSize)(CFIndex size, CFOptionFlags hint, void *info);
1313     };
1314     SkUniqueCFRef<CFAllocatorRef> alloc(CFAllocatorCreate(kCFAllocatorDefault, &ctx));
1315     return SkUniqueCFRef<CFDataRef>(CFDataCreateWithBytesNoCopy(
1316             kCFAllocatorDefault, (const UInt8 *)addr, size, alloc.get()));
1317 }
1318 
ctfont_from_skdata(sk_sp<SkData> data,int ttcIndex)1319 static SkUniqueCFRef<CTFontRef> ctfont_from_skdata(sk_sp<SkData> data, int ttcIndex) {
1320     // TODO: Use CTFontManagerCreateFontDescriptorsFromData when available.
1321     if (ttcIndex != 0) {
1322         return nullptr;
1323     }
1324 
1325     SkUniqueCFRef<CFDataRef> cfData(cfdata_from_skdata(std::move(data)));
1326 
1327     SkUniqueCFRef<CTFontDescriptorRef> desc(
1328             CTFontManagerCreateFontDescriptorFromData(cfData.get()));
1329     if (!desc) {
1330         return nullptr;
1331     }
1332     return SkUniqueCFRef<CTFontRef>(CTFontCreateWithFontDescriptor(desc.get(), 0, nullptr));
1333 }
1334 
MakeFromStream(std::unique_ptr<SkStreamAsset> stream,const SkFontArguments & args)1335 sk_sp<SkTypeface> SkTypeface_Mac::MakeFromStream(std::unique_ptr<SkStreamAsset> stream,
1336                                                  const SkFontArguments& args)
1337 {
1338     // TODO: Use CTFontManagerCreateFontDescriptorsFromData when available.
1339     int ttcIndex = args.getCollectionIndex();
1340     if (ttcIndex != 0) {
1341         return nullptr;
1342     }
1343 
1344     sk_sp<SkData> data = skdata_from_skstreamasset(stream->duplicate());
1345     if (!data) {
1346         return nullptr;
1347     }
1348     SkUniqueCFRef<CTFontRef> ct = ctfont_from_skdata(std::move(data), ttcIndex);
1349     if (!ct) {
1350         return nullptr;
1351     }
1352 
1353     SkUniqueCFRef<CTFontRef> ctVariant;
1354     CTFontVariation ctVariation;
1355     if (args.getVariationDesignPosition().coordinateCount == 0) {
1356         ctVariant.reset(ct.release());
1357     } else {
1358         SkUniqueCFRef<CFArrayRef> axes(CTFontCopyVariationAxes(ct.get()));
1359         ctVariation = ctvariation_from_SkFontArguments(ct.get(), axes.get(), args);
1360 
1361         if (ctVariation.variation) {
1362             SkUniqueCFRef<CFMutableDictionaryRef> attributes(
1363                      CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
1364                                                &kCFTypeDictionaryKeyCallBacks,
1365                                                &kCFTypeDictionaryValueCallBacks));
1366             CFDictionaryAddValue(attributes.get(),
1367                                  kCTFontVariationAttribute, ctVariation.variation.get());
1368             SkUniqueCFRef<CTFontDescriptorRef> varDesc(
1369                     CTFontDescriptorCreateWithAttributes(attributes.get()));
1370             ctVariant.reset(CTFontCreateCopyWithAttributes(ct.get(), 0, nullptr, varDesc.get()));
1371         } else {
1372             ctVariant.reset(ct.release());
1373         }
1374     }
1375     if (!ctVariant) {
1376         return nullptr;
1377     }
1378 
1379     return SkTypeface_Mac::Make(std::move(ctVariant), ctVariation.opsz, std::move(stream));
1380 }
1381 
onGetVariationDesignParameters(SkFontParameters::Variation::Axis parameters[],int parameterCount) const1382 int SkTypeface_Mac::onGetVariationDesignParameters(SkFontParameters::Variation::Axis parameters[],
1383                                                    int parameterCount) const
1384 {
1385     CFArrayRef ctAxes = this->getVariationAxes();
1386     if (!ctAxes) {
1387         return -1;
1388     }
1389     CFIndex axisCount = CFArrayGetCount(ctAxes);
1390 
1391     if (!parameters || parameterCount < axisCount) {
1392         return axisCount;
1393     }
1394 
1395     // Added in 10.13
1396     static CFStringRef* kCTFontVariationAxisHiddenKeyPtr =
1397             static_cast<CFStringRef*>(dlsym(RTLD_DEFAULT, "kCTFontVariationAxisHiddenKey"));
1398 
1399     for (int i = 0; i < axisCount; ++i) {
1400         CFDictionaryRef axisInfoDict;
1401         if (!SkCFDynamicCast(CFArrayGetValueAtIndex(ctAxes, i), &axisInfoDict, "Axis")) {
1402             return -1;
1403         }
1404 
1405         int64_t tagLong;
1406         CFTypeRef tag = CFDictionaryGetValue(axisInfoDict, kCTFontVariationAxisIdentifierKey);
1407         if (!SkCFNumberDynamicCast(tag, &tagLong, nullptr, "Axis tag")) {
1408             return -1;
1409         }
1410 
1411         double minDouble;
1412         double maxDouble;
1413         double defDouble;
1414         CFTypeRef min = CFDictionaryGetValue(axisInfoDict, kCTFontVariationAxisMinimumValueKey);
1415         CFTypeRef max = CFDictionaryGetValue(axisInfoDict, kCTFontVariationAxisMaximumValueKey);
1416         CFTypeRef def = CFDictionaryGetValue(axisInfoDict, kCTFontVariationAxisDefaultValueKey);
1417         if (!SkCFNumberDynamicCast(min, &minDouble, nullptr, "Axis min") ||
1418             !SkCFNumberDynamicCast(max, &maxDouble, nullptr, "Axis max") ||
1419             !SkCFNumberDynamicCast(def, &defDouble, nullptr, "Axis def"))
1420         {
1421             return -1;
1422         }
1423 
1424         SkFontParameters::Variation::Axis& skAxis = parameters[i];
1425         skAxis.tag = tagLong;
1426         skAxis.min = minDouble;
1427         skAxis.max = maxDouble;
1428         skAxis.def = defDouble;
1429         skAxis.setHidden(false);
1430         if (kCTFontVariationAxisHiddenKeyPtr) {
1431             CFTypeRef hidden = CFDictionaryGetValue(axisInfoDict,*kCTFontVariationAxisHiddenKeyPtr);
1432             if (hidden) {
1433                 // At least macOS 11 Big Sur Beta 4 uses CFNumberRef instead of CFBooleanRef.
1434                 // https://crbug.com/1113444
1435                 CFBooleanRef hiddenBoolean;
1436                 int hiddenInt;
1437                 if (SkCFDynamicCast(hidden, &hiddenBoolean, nullptr)) {
1438                     skAxis.setHidden(CFBooleanGetValue(hiddenBoolean));
1439                 } else if (SkCFNumberDynamicCast(hidden, &hiddenInt, nullptr, "Axis hidden")) {
1440                     skAxis.setHidden(hiddenInt);
1441                 } else {
1442                     return -1;
1443                 }
1444             }
1445         }
1446     }
1447     return axisCount;
1448 }
1449 
1450 #endif
1451