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
onOpenExistingStream(int * ttcIndex) const778 std::unique_ptr<SkStreamAsset> SkTypeface_Mac::onOpenExistingStream(int* ttcIndex) const {
779 *ttcIndex = 0;
780 return fStream ? fStream->duplicate() : nullptr;
781 }
782
has_table(CTFontRef ctFont,SkFontTableTag tableTag)783 static bool has_table(CTFontRef ctFont, SkFontTableTag tableTag) {
784 SkUniqueCFRef<CFArrayRef> cfArray(
785 CTFontCopyAvailableTables(ctFont, kCTFontTableOptionNoOptions));
786 if (!cfArray) {
787 return 0;
788 }
789 CFIndex count = CFArrayGetCount(cfArray.get());
790 for (CFIndex i = 0; i < count; ++i) {
791 uintptr_t fontTag = reinterpret_cast<uintptr_t>(
792 CFArrayGetValueAtIndex(cfArray.get(), i));
793 if (tableTag == static_cast<SkFontTableTag>(fontTag)) {
794 return true;
795 }
796 }
797 return false;
798 }
onGlyphMaskNeedsCurrentColor() const799 bool SkTypeface_Mac::onGlyphMaskNeedsCurrentColor() const {
800 constexpr SkFontTableTag cpalTag = SkSetFourByteTag('C', 'P', 'A', 'L');
801 // CoreText only provides the size of a table with a copy, so do not use this->getTableSize().
802 return has_table(fFontRef.get(), cpalTag);
803 }
804
onGetVariationDesignPosition(SkFontArguments::VariationPosition::Coordinate coordinates[],int coordinateCount) const805 int SkTypeface_Mac::onGetVariationDesignPosition(
806 SkFontArguments::VariationPosition::Coordinate coordinates[], int coordinateCount) const
807 {
808 SkUniqueCFRef<CFArrayRef> ctAxes(CTFontCopyVariationAxes(fFontRef.get()));
809 if (!ctAxes) {
810 return -1;
811 }
812 CFIndex axisCount = CFArrayGetCount(ctAxes.get());
813 if (!coordinates || coordinateCount < axisCount) {
814 return axisCount;
815 }
816
817 // On 10.12 and later, this only returns non-default variations.
818 SkUniqueCFRef<CFDictionaryRef> ctVariation(CTFontCopyVariation(fFontRef.get()));
819 if (!ctVariation) {
820 return -1;
821 }
822
823 for (int i = 0; i < axisCount; ++i) {
824 CFDictionaryRef axisInfoDict;
825 if (!SkCFDynamicCast(CFArrayGetValueAtIndex(ctAxes.get(), i), &axisInfoDict, "Axis")) {
826 return -1;
827 }
828
829 int64_t tagLong;
830 CFNumberRef tagNumber;
831 CFTypeRef tag = CFDictionaryGetValue(axisInfoDict, kCTFontVariationAxisIdentifierKey);
832 if (!SkCFNumberDynamicCast(tag, &tagLong, &tagNumber, "Axis tag")) {
833 return -1;
834 }
835 coordinates[i].axis = tagLong;
836
837 CGFloat valueCGFloat;
838 CFTypeRef value = CFDictionaryGetValue(ctVariation.get(), tagNumber);
839 if (value) {
840 if (!SkCFNumberDynamicCast(value, &valueCGFloat, nullptr, "Variation value")) {
841 return -1;
842 }
843 } else {
844 CFTypeRef def = CFDictionaryGetValue(axisInfoDict, kCTFontVariationAxisDefaultValueKey);
845 if (!SkCFNumberDynamicCast(def, &valueCGFloat, nullptr, "Axis default value")) {
846 return -1;
847 }
848 }
849 coordinates[i].value = SkScalarFromCGFloat(valueCGFloat);
850 }
851 return axisCount;
852 }
853
onGetUPEM() const854 int SkTypeface_Mac::onGetUPEM() const {
855 SkUniqueCFRef<CGFontRef> cgFont(CTFontCopyGraphicsFont(fFontRef.get(), nullptr));
856 return CGFontGetUnitsPerEm(cgFont.get());
857 }
858
onCreateFamilyNameIterator() const859 SkTypeface::LocalizedStrings* SkTypeface_Mac::onCreateFamilyNameIterator() const {
860 sk_sp<SkTypeface::LocalizedStrings> nameIter =
861 SkOTUtils::LocalizedStrings_NameTable::MakeForFamilyNames(*this);
862 if (!nameIter) {
863 CFStringRef cfLanguageRaw;
864 SkUniqueCFRef<CFStringRef> cfFamilyName(
865 CTFontCopyLocalizedName(fFontRef.get(), kCTFontFamilyNameKey, &cfLanguageRaw));
866 SkUniqueCFRef<CFStringRef> cfLanguage(cfLanguageRaw);
867
868 SkString skLanguage;
869 SkString skFamilyName;
870 if (cfLanguage) {
871 SkStringFromCFString(cfLanguage.get(), &skLanguage);
872 } else {
873 skLanguage = "und"; //undetermined
874 }
875 if (cfFamilyName) {
876 SkStringFromCFString(cfFamilyName.get(), &skFamilyName);
877 }
878
879 nameIter = sk_make_sp<SkOTUtils::LocalizedStrings_SingleName>(skFamilyName, skLanguage);
880 }
881 return nameIter.release();
882 }
883
onGetTableTags(SkFontTableTag tags[]) const884 int SkTypeface_Mac::onGetTableTags(SkFontTableTag tags[]) const {
885 SkUniqueCFRef<CFArrayRef> cfArray(
886 CTFontCopyAvailableTables(fFontRef.get(), kCTFontTableOptionNoOptions));
887 if (!cfArray) {
888 return 0;
889 }
890 CFIndex count = CFArrayGetCount(cfArray.get());
891 if (tags) {
892 for (CFIndex i = 0; i < count; ++i) {
893 uintptr_t fontTag = reinterpret_cast<uintptr_t>(
894 CFArrayGetValueAtIndex(cfArray.get(), i));
895 tags[i] = static_cast<SkFontTableTag>(fontTag);
896 }
897 }
898 return count;
899 }
900
901 // If, as is the case with web fonts, the CTFont data isn't available,
902 // the CGFont data may work. While the CGFont may always provide the
903 // right result, leave the CTFont code path to minimize disruption.
copy_table_from_font(CTFontRef ctFont,SkFontTableTag tag)904 static SkUniqueCFRef<CFDataRef> copy_table_from_font(CTFontRef ctFont, SkFontTableTag tag) {
905 SkUniqueCFRef<CFDataRef> data(CTFontCopyTable(ctFont, (CTFontTableTag) tag,
906 kCTFontTableOptionNoOptions));
907 if (!data) {
908 SkUniqueCFRef<CGFontRef> cgFont(CTFontCopyGraphicsFont(ctFont, nullptr));
909 data.reset(CGFontCopyTableForTag(cgFont.get(), tag));
910 }
911 return data;
912 }
913
onGetTableData(SkFontTableTag tag,size_t offset,size_t length,void * dstData) const914 size_t SkTypeface_Mac::onGetTableData(SkFontTableTag tag, size_t offset,
915 size_t length, void* dstData) const {
916 SkUniqueCFRef<CFDataRef> srcData = copy_table_from_font(fFontRef.get(), tag);
917 if (!srcData) {
918 return 0;
919 }
920
921 size_t srcSize = CFDataGetLength(srcData.get());
922 if (offset >= srcSize) {
923 return 0;
924 }
925 if (length > srcSize - offset) {
926 length = srcSize - offset;
927 }
928 if (dstData) {
929 memcpy(dstData, CFDataGetBytePtr(srcData.get()) + offset, length);
930 }
931 return length;
932 }
933
onCopyTableData(SkFontTableTag tag) const934 sk_sp<SkData> SkTypeface_Mac::onCopyTableData(SkFontTableTag tag) const {
935 SkUniqueCFRef<CFDataRef> srcData = copy_table_from_font(fFontRef.get(), tag);
936 if (!srcData) {
937 return nullptr;
938 }
939 const UInt8* data = CFDataGetBytePtr(srcData.get());
940 CFIndex length = CFDataGetLength(srcData.get());
941 return SkData::MakeWithProc(data, length,
942 [](const void*, void* ctx) {
943 CFRelease((CFDataRef)ctx);
944 }, (void*)srcData.release());
945 }
946
onCreateScalerContext(const SkScalerContextEffects & effects,const SkDescriptor * desc) const947 std::unique_ptr<SkScalerContext> SkTypeface_Mac::onCreateScalerContext(
948 const SkScalerContextEffects& effects, const SkDescriptor* desc) const
949 {
950 return std::make_unique<SkScalerContext_Mac>(
951 sk_ref_sp(const_cast<SkTypeface_Mac*>(this)), effects, desc);
952 }
953
onFilterRec(SkScalerContextRec * rec) const954 void SkTypeface_Mac::onFilterRec(SkScalerContextRec* rec) const {
955 if (rec->fFlags & SkScalerContext::kLCD_BGROrder_Flag ||
956 rec->fFlags & SkScalerContext::kLCD_Vertical_Flag)
957 {
958 rec->fMaskFormat = SkMask::kA8_Format;
959 // Render the glyphs as close as possible to what was requested.
960 // The above turns off subpixel rendering, but the user requested it.
961 // Normal hinting will cause the A8 masks to be generated from CoreGraphics subpixel masks.
962 // See comments below for more details.
963 rec->setHinting(SkFontHinting::kNormal);
964 }
965
966 unsigned flagsWeDontSupport = SkScalerContext::kForceAutohinting_Flag |
967 SkScalerContext::kLCD_BGROrder_Flag |
968 SkScalerContext::kLCD_Vertical_Flag;
969
970 rec->fFlags &= ~flagsWeDontSupport;
971
972 const SkCTFontSmoothBehavior smoothBehavior = SkCTFontGetSmoothBehavior();
973
974 // Only two levels of hinting are supported.
975 // kNo_Hinting means avoid CoreGraphics outline dilation (smoothing).
976 // kNormal_Hinting means CoreGraphics outline dilation (smoothing) is allowed.
977 if (rec->getHinting() != SkFontHinting::kNone) {
978 rec->setHinting(SkFontHinting::kNormal);
979 }
980 // If smoothing has no effect, don't request it.
981 if (smoothBehavior == SkCTFontSmoothBehavior::none) {
982 rec->setHinting(SkFontHinting::kNone);
983 }
984
985 // FIXME: lcd smoothed un-hinted rasterization unsupported.
986 // Tracked by http://code.google.com/p/skia/issues/detail?id=915 .
987 // There is no current means to honor a request for unhinted lcd,
988 // so arbitrarilly ignore the hinting request and honor lcd.
989
990 // Hinting and smoothing should be orthogonal, but currently they are not.
991 // CoreGraphics has no API to influence hinting. However, its lcd smoothed
992 // output is drawn from auto-dilated outlines (the amount of which is
993 // determined by AppleFontSmoothing). Its regular anti-aliased output is
994 // drawn from un-dilated outlines.
995
996 // The behavior of Skia is as follows:
997 // [AA][no-hint]: generate AA using CoreGraphic's AA output.
998 // [AA][yes-hint]: use CoreGraphic's LCD output and reduce it to a single
999 // channel. This matches [LCD][yes-hint] in weight.
1000 // [LCD][no-hint]: currently unable to honor, and must pick which to respect.
1001 // Currently side with LCD, effectively ignoring the hinting setting.
1002 // [LCD][yes-hint]: generate LCD using CoreGraphic's LCD output.
1003 if (rec->fMaskFormat == SkMask::kLCD16_Format) {
1004 if (smoothBehavior == SkCTFontSmoothBehavior::subpixel) {
1005 //CoreGraphics creates 555 masks for smoothed text anyway.
1006 rec->fMaskFormat = SkMask::kLCD16_Format;
1007 rec->setHinting(SkFontHinting::kNormal);
1008 } else {
1009 rec->fMaskFormat = SkMask::kA8_Format;
1010 if (smoothBehavior != SkCTFontSmoothBehavior::none) {
1011 rec->setHinting(SkFontHinting::kNormal);
1012 }
1013 }
1014 }
1015
1016 // CoreText provides no information as to whether a glyph will be color or not.
1017 // Fonts may mix outlines and bitmaps, so information is needed on a glyph by glyph basis.
1018 // If a font contains an 'sbix' table, consider it to be a color font, and disable lcd.
1019 if (fHasColorGlyphs) {
1020 rec->fMaskFormat = SkMask::kARGB32_Format;
1021 }
1022
1023 // Unhinted A8 masks (those not derived from LCD masks) must respect SK_GAMMA_APPLY_TO_A8.
1024 // All other masks can use regular gamma.
1025 if (SkMask::kA8_Format == rec->fMaskFormat && SkFontHinting::kNone == rec->getHinting()) {
1026 #ifndef SK_GAMMA_APPLY_TO_A8
1027 // SRGBTODO: Is this correct? Do we want contrast boost?
1028 rec->ignorePreBlend();
1029 #endif
1030 } else {
1031 SkColor color = rec->getLuminanceColor();
1032 if (smoothBehavior == SkCTFontSmoothBehavior::some) {
1033 // CoreGraphics smoothed text without subpixel coverage blitting goes from a gamma of
1034 // 2.0 for black foreground to a gamma of 1.0 for white foreground. Emulate this
1035 // through the mask gamma by reducing the color values to 1/2.
1036 color = SkColorSetRGB(SkColorGetR(color) * 1/2,
1037 SkColorGetG(color) * 1/2,
1038 SkColorGetB(color) * 1/2);
1039 } else if (smoothBehavior == SkCTFontSmoothBehavior::subpixel) {
1040 // CoreGraphics smoothed text with subpixel coverage blitting goes from a gamma of
1041 // 2.0 for black foreground to a gamma of ~1.4? for white foreground. Emulate this
1042 // through the mask gamma by reducing the color values to 3/4.
1043 color = SkColorSetRGB(SkColorGetR(color) * 3/4,
1044 SkColorGetG(color) * 3/4,
1045 SkColorGetB(color) * 3/4);
1046 }
1047 rec->setLuminanceColor(color);
1048
1049 // CoreGraphics dialates smoothed text to provide contrast.
1050 rec->setContrast(0);
1051 }
1052 }
1053
1054 /** Takes ownership of the CFStringRef. */
get_str(CFStringRef ref,SkString * str)1055 static const char* get_str(CFStringRef ref, SkString* str) {
1056 if (nullptr == ref) {
1057 return nullptr;
1058 }
1059 SkStringFromCFString(ref, str);
1060 CFRelease(ref);
1061 return str->c_str();
1062 }
1063
onGetFamilyName(SkString * familyName) const1064 void SkTypeface_Mac::onGetFamilyName(SkString* familyName) const {
1065 get_str(CTFontCopyFamilyName(fFontRef.get()), familyName);
1066 }
1067
onGetPostScriptName(SkString * skPostScriptName) const1068 bool SkTypeface_Mac::onGetPostScriptName(SkString* skPostScriptName) const {
1069 SkUniqueCFRef<CFStringRef> ctPostScriptName(CTFontCopyPostScriptName(fFontRef.get()));
1070 if (!ctPostScriptName) {
1071 return false;
1072 }
1073 if (skPostScriptName) {
1074 SkStringFromCFString(ctPostScriptName.get(), skPostScriptName);
1075 }
1076 return true;
1077 }
1078
onGetFontDescriptor(SkFontDescriptor * desc,bool * isLocalStream) const1079 void SkTypeface_Mac::onGetFontDescriptor(SkFontDescriptor* desc,
1080 bool* isLocalStream) const {
1081 SkString tmpStr;
1082
1083 desc->setFamilyName(get_str(CTFontCopyFamilyName(fFontRef.get()), &tmpStr));
1084 desc->setFullName(get_str(CTFontCopyFullName(fFontRef.get()), &tmpStr));
1085 desc->setPostscriptName(get_str(CTFontCopyPostScriptName(fFontRef.get()), &tmpStr));
1086 desc->setStyle(this->fontStyle());
1087 *isLocalStream = fIsFromStream;
1088 }
1089
onCharsToGlyphs(const SkUnichar uni[],int count,SkGlyphID glyphs[]) const1090 void SkTypeface_Mac::onCharsToGlyphs(const SkUnichar uni[], int count, SkGlyphID glyphs[]) const {
1091 // Undocumented behavior of CTFontGetGlyphsForCharacters with non-bmp code points:
1092 // When a surrogate pair is detected, the glyph index used is the index of the high surrogate.
1093 // It is documented that if a mapping is unavailable, the glyph will be set to 0.
1094
1095 SkAutoSTMalloc<1024, UniChar> charStorage;
1096 const UniChar* src; // UniChar is a UTF-16 16-bit code unit.
1097 int srcCount;
1098 const SkUnichar* utf32 = reinterpret_cast<const SkUnichar*>(uni);
1099 UniChar* utf16 = charStorage.reset(2 * count);
1100 src = utf16;
1101 for (int i = 0; i < count; ++i) {
1102 utf16 += SkUTF::ToUTF16(utf32[i], utf16);
1103 }
1104 srcCount = SkToInt(utf16 - src);
1105
1106 // If there are any non-bmp code points, the provided 'glyphs' storage will be inadequate.
1107 SkAutoSTMalloc<1024, uint16_t> glyphStorage;
1108 uint16_t* macGlyphs = glyphs;
1109 if (srcCount > count) {
1110 macGlyphs = glyphStorage.reset(srcCount);
1111 }
1112
1113 CTFontGetGlyphsForCharacters(fFontRef.get(), src, macGlyphs, srcCount);
1114
1115 // If there were any non-bmp, then copy and compact.
1116 // If all are bmp, 'glyphs' already contains the compact glyphs.
1117 // If some are non-bmp, copy and compact into 'glyphs'.
1118 if (srcCount > count) {
1119 SkASSERT(glyphs != macGlyphs);
1120 int extra = 0;
1121 for (int i = 0; i < count; ++i) {
1122 glyphs[i] = macGlyphs[i + extra];
1123 if (SkUTF::IsLeadingSurrogateUTF16(src[i + extra])) {
1124 ++extra;
1125 }
1126 }
1127 } else {
1128 SkASSERT(glyphs == macGlyphs);
1129 }
1130 }
1131
onCountGlyphs() const1132 int SkTypeface_Mac::onCountGlyphs() const {
1133 return SkToInt(CTFontGetGlyphCount(fFontRef.get()));
1134 }
1135
1136 /** Creates a dictionary suitable for setting the axes on a CTFont. */
SkCTVariationFromSkFontArguments(CTFontRef ct,const SkFontArguments & args)1137 CTFontVariation SkCTVariationFromSkFontArguments(CTFontRef ct, const SkFontArguments& args) {
1138 OpszVariation opsz;
1139 constexpr const SkFourByteTag opszTag = SkSetFourByteTag('o','p','s','z');
1140
1141 SkUniqueCFRef<CFArrayRef> ctAxes(CTFontCopyVariationAxes(ct));
1142 if (!ctAxes) {
1143 return CTFontVariation();
1144 }
1145 CFIndex axisCount = CFArrayGetCount(ctAxes.get());
1146
1147 // On 10.12 and later, this only returns non-default variations.
1148 SkUniqueCFRef<CFDictionaryRef> oldCtVariation(CTFontCopyVariation(ct));
1149
1150 const SkFontArguments::VariationPosition position = args.getVariationDesignPosition();
1151
1152 SkUniqueCFRef<CFMutableDictionaryRef> newCtVariation(
1153 CFDictionaryCreateMutable(kCFAllocatorDefault, axisCount,
1154 &kCFTypeDictionaryKeyCallBacks,
1155 &kCFTypeDictionaryValueCallBacks));
1156 SkUniqueCFRef<CFMutableDictionaryRef> wrongOpszVariation;
1157
1158 for (int i = 0; i < axisCount; ++i) {
1159 CFDictionaryRef axisInfoDict;
1160 if (!SkCFDynamicCast(CFArrayGetValueAtIndex(ctAxes.get(), i), &axisInfoDict, "Axis")) {
1161 return CTFontVariation();
1162 }
1163
1164 int64_t tagLong;
1165 CFNumberRef tagNumber;
1166 CFTypeRef tag = CFDictionaryGetValue(axisInfoDict, kCTFontVariationAxisIdentifierKey);
1167 if (!SkCFNumberDynamicCast(tag, &tagLong, &tagNumber, "Axis tag")) {
1168 return CTFontVariation();
1169 }
1170
1171 // The variation axes can be set to any value, but cg will effectively pin them.
1172 // Pin them here to normalize.
1173 double minDouble;
1174 double maxDouble;
1175 double defDouble;
1176 CFTypeRef min = CFDictionaryGetValue(axisInfoDict, kCTFontVariationAxisMinimumValueKey);
1177 CFTypeRef max = CFDictionaryGetValue(axisInfoDict, kCTFontVariationAxisMaximumValueKey);
1178 CFTypeRef def = CFDictionaryGetValue(axisInfoDict, kCTFontVariationAxisDefaultValueKey);
1179 if (!SkCFNumberDynamicCast(min, &minDouble, nullptr, "Axis min") ||
1180 !SkCFNumberDynamicCast(max, &maxDouble, nullptr, "Axis max") ||
1181 !SkCFNumberDynamicCast(def, &defDouble, nullptr, "Axis def"))
1182 {
1183 return CTFontVariation();
1184 }
1185
1186 // Start with the default value.
1187 double value = defDouble;
1188
1189 // Then the current value.
1190 bool haveCurrentDouble = false;
1191 double currentDouble = 0;
1192 if (oldCtVariation) {
1193 CFTypeRef currentNumber = CFDictionaryGetValue(oldCtVariation.get(), tagNumber);
1194 if (currentNumber) {
1195 if (!SkCFNumberDynamicCast(currentNumber, &value, nullptr, "Variation value")) {
1196 return CTFontVariation();
1197 }
1198 currentDouble = value;
1199 haveCurrentDouble = true;
1200 }
1201 }
1202
1203 // Then the requested value.
1204 // The position may be over specified. If there are multiple values for a given axis,
1205 // use the last one since that's what css-fonts-4 requires.
1206 for (int j = position.coordinateCount; j --> 0;) {
1207 if (position.coordinates[j].axis == tagLong) {
1208 value = SkTPin<double>(position.coordinates[j].value, minDouble, maxDouble);
1209 if (tagLong == opszTag) {
1210 opsz.isSet = true;
1211 }
1212 break;
1213 }
1214 }
1215 if (tagLong == opszTag) {
1216 opsz.value = value;
1217 if (haveCurrentDouble && value == currentDouble) {
1218 // Calculate a value strictly in range but different from currentValue.
1219 double wrongOpszDouble = ((maxDouble - minDouble) / 2.0) + minDouble;
1220 if (wrongOpszDouble == currentDouble) {
1221 wrongOpszDouble = ((maxDouble - minDouble) / 4.0) + minDouble;
1222 }
1223 wrongOpszVariation.reset(
1224 CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
1225 &kCFTypeDictionaryKeyCallBacks,
1226 &kCFTypeDictionaryValueCallBacks));
1227 SkUniqueCFRef<CFNumberRef> wrongOpszNumber(
1228 CFNumberCreate(kCFAllocatorDefault, kCFNumberDoubleType, &wrongOpszDouble));
1229 CFDictionarySetValue(wrongOpszVariation.get(), tagNumber, wrongOpszNumber.get());
1230 }
1231 }
1232 SkUniqueCFRef<CFNumberRef> valueNumber(
1233 CFNumberCreate(kCFAllocatorDefault, kCFNumberDoubleType, &value));
1234 CFDictionaryAddValue(newCtVariation.get(), tagNumber, valueNumber.get());
1235 }
1236 return { SkUniqueCFRef<CFDictionaryRef>(std::move(newCtVariation)),
1237 SkUniqueCFRef<CFDictionaryRef>(std::move(wrongOpszVariation)),
1238 opsz };
1239 }
1240
onMakeClone(const SkFontArguments & args) const1241 sk_sp<SkTypeface> SkTypeface_Mac::onMakeClone(const SkFontArguments& args) const {
1242 CTFontVariation ctVariation = SkCTVariationFromSkFontArguments(fFontRef.get(), args);
1243
1244 SkUniqueCFRef<CTFontRef> ctVariant;
1245 if (ctVariation.variation) {
1246 SkUniqueCFRef<CFMutableDictionaryRef> attributes(
1247 CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
1248 &kCFTypeDictionaryKeyCallBacks,
1249 &kCFTypeDictionaryValueCallBacks));
1250
1251 CTFontRef ctFont = fFontRef.get();
1252 SkUniqueCFRef<CTFontRef> wrongOpszFont;
1253 if (ctVariation.wrongOpszVariation) {
1254 // On macOS 11 cloning a system font with an opsz axis and not changing the
1255 // value of the opsz axis (either by setting it to the same value or not
1256 // specifying it at all) when setting a variation causes the variation to
1257 // be set but the cloned font will still compare CFEqual to the original
1258 // font. Work around this by setting the opsz to something which isn't the
1259 // desired value before setting the entire desired variation.
1260 //
1261 // A similar issue occurs with fonts from data on macOS 10.15 and the same
1262 // work around seems to apply. This is less noticeable though since CFEqual
1263 // isn't used on these fonts.
1264 CFDictionarySetValue(attributes.get(),
1265 kCTFontVariationAttribute, ctVariation.wrongOpszVariation.get());
1266 SkUniqueCFRef<CTFontDescriptorRef> varDesc(
1267 CTFontDescriptorCreateWithAttributes(attributes.get()));
1268 wrongOpszFont.reset(CTFontCreateCopyWithAttributes(ctFont, 0, nullptr, varDesc.get()));
1269 ctFont = wrongOpszFont.get();
1270 }
1271
1272 CFDictionarySetValue(attributes.get(),
1273 kCTFontVariationAttribute, ctVariation.variation.get());
1274 SkUniqueCFRef<CTFontDescriptorRef> varDesc(
1275 CTFontDescriptorCreateWithAttributes(attributes.get()));
1276 ctVariant.reset(CTFontCreateCopyWithAttributes(ctFont, 0, nullptr, varDesc.get()));
1277 } else {
1278 ctVariant.reset((CTFontRef)CFRetain(fFontRef.get()));
1279 }
1280 if (!ctVariant) {
1281 return nullptr;
1282 }
1283
1284 return SkTypeface_Mac::Make(std::move(ctVariant), ctVariation.opsz,
1285 fStream ? fStream->duplicate() : nullptr);
1286 }
1287
onGetVariationDesignParameters(SkFontParameters::Variation::Axis parameters[],int parameterCount) const1288 int SkTypeface_Mac::onGetVariationDesignParameters(SkFontParameters::Variation::Axis parameters[],
1289 int parameterCount) const
1290 {
1291 SkUniqueCFRef<CFArrayRef> ctAxes(CTFontCopyVariationAxes(fFontRef.get()));
1292 if (!ctAxes) {
1293 return -1;
1294 }
1295 CFIndex axisCount = CFArrayGetCount(ctAxes.get());
1296
1297 if (!parameters || parameterCount < axisCount) {
1298 return axisCount;
1299 }
1300
1301 // Added in 10.13
1302 static CFStringRef* kCTFontVariationAxisHiddenKeyPtr =
1303 static_cast<CFStringRef*>(dlsym(RTLD_DEFAULT, "kCTFontVariationAxisHiddenKey"));
1304
1305 for (int i = 0; i < axisCount; ++i) {
1306 CFDictionaryRef axisInfoDict;
1307 if (!SkCFDynamicCast(CFArrayGetValueAtIndex(ctAxes.get(), i), &axisInfoDict, "Axis")) {
1308 return -1;
1309 }
1310
1311 int64_t tagLong;
1312 CFTypeRef tag = CFDictionaryGetValue(axisInfoDict, kCTFontVariationAxisIdentifierKey);
1313 if (!SkCFNumberDynamicCast(tag, &tagLong, nullptr, "Axis tag")) {
1314 return -1;
1315 }
1316
1317 double minDouble;
1318 double maxDouble;
1319 double defDouble;
1320 CFTypeRef min = CFDictionaryGetValue(axisInfoDict, kCTFontVariationAxisMinimumValueKey);
1321 CFTypeRef max = CFDictionaryGetValue(axisInfoDict, kCTFontVariationAxisMaximumValueKey);
1322 CFTypeRef def = CFDictionaryGetValue(axisInfoDict, kCTFontVariationAxisDefaultValueKey);
1323 if (!SkCFNumberDynamicCast(min, &minDouble, nullptr, "Axis min") ||
1324 !SkCFNumberDynamicCast(max, &maxDouble, nullptr, "Axis max") ||
1325 !SkCFNumberDynamicCast(def, &defDouble, nullptr, "Axis def"))
1326 {
1327 return -1;
1328 }
1329
1330 SkFontParameters::Variation::Axis& skAxis = parameters[i];
1331 skAxis.tag = tagLong;
1332 skAxis.min = minDouble;
1333 skAxis.max = maxDouble;
1334 skAxis.def = defDouble;
1335 skAxis.setHidden(false);
1336 if (kCTFontVariationAxisHiddenKeyPtr) {
1337 CFTypeRef hidden = CFDictionaryGetValue(axisInfoDict,*kCTFontVariationAxisHiddenKeyPtr);
1338 if (hidden) {
1339 // At least macOS 11 Big Sur Beta 4 uses CFNumberRef instead of CFBooleanRef.
1340 // https://crbug.com/1113444
1341 CFBooleanRef hiddenBoolean;
1342 int hiddenInt;
1343 if (SkCFDynamicCast(hidden, &hiddenBoolean, nullptr)) {
1344 skAxis.setHidden(CFBooleanGetValue(hiddenBoolean));
1345 } else if (SkCFNumberDynamicCast(hidden, &hiddenInt, nullptr, "Axis hidden")) {
1346 skAxis.setHidden(hiddenInt);
1347 } else {
1348 return -1;
1349 }
1350 }
1351 }
1352 }
1353 return axisCount;
1354 }
1355
1356 #endif
1357