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