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