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