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