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