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