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