Index: src/ports/SkFontHost_mac.cpp |
diff --git a/src/ports/SkFontHost_mac.cpp b/src/ports/SkFontHost_mac.cpp |
index 1d68c433bf2243259cd1f7dfe804741f0b77763b..c4e80b67c0aa8be20d874f35cd73008c13d6347c 100644 |
--- a/src/ports/SkFontHost_mac.cpp |
+++ b/src/ports/SkFontHost_mac.cpp |
@@ -2371,6 +2371,145 @@ protected: |
return create_from_dataProvider(pr); |
} |
+ static CFNumberRef get_tag_for_name(CFStringRef name, CFArrayRef ctAxes) { |
+ CFIndex ctAxisCount = CFArrayGetCount(ctAxes); |
+ for (int i = 0; i < ctAxisCount; ++i) { |
+ CFTypeRef ctAxisInfo = CFArrayGetValueAtIndex(ctAxes, i); |
+ if (CFDictionaryGetTypeID() != CFGetTypeID(ctAxisInfo)) { |
+ return nullptr; |
+ } |
+ CFDictionaryRef ctAxisInfoDict = static_cast<CFDictionaryRef>(ctAxisInfo); |
+ |
+ CFTypeRef ctAxisName = CFDictionaryGetValue(ctAxisInfoDict, |
+ kCTFontVariationAxisNameKey); |
+ if (!ctAxisName || CFGetTypeID(ctAxisName) != CFStringGetTypeID()) { |
+ return nullptr; |
+ } |
+ |
+ if (CFEqual(name, ctAxisName)) { |
+ CFTypeRef tag = CFDictionaryGetValue(ctAxisInfoDict, |
+ kCTFontVariationAxisIdentifierKey); |
+ if (!tag || CFGetTypeID(tag) != CFNumberGetTypeID()) { |
+ return nullptr; |
+ } |
+ return static_cast<CFNumberRef>(tag); |
+ } |
+ } |
+ return nullptr; |
+ } |
+ static CFDictionaryRef get_axes(CGFontRef cg, const FontParameters& params) { |
+ AutoCFRelease<CFArrayRef> cgAxes(CGFontCopyVariationAxes(cg)); |
+ if (!cgAxes) { |
+ return nullptr; |
+ } |
+ CFIndex axisCount = CFArrayGetCount(cgAxes); |
+ |
+ // The CGFont variation data is keyed by name, and lacks the tag. |
+ // The CTFont variation data is keyed by tag, and also has the name. |
+ // We would like to work with CTFont variaitons, but creating a CTFont font with |
+ // CTFont variation dictionary runs into bugs. So use the CTFont variation data |
+ // to match names to tags to create the appropriate CGFont. |
+ AutoCFRelease<CTFontRef> ct(CTFontCreateWithGraphicsFont(cg, 0, nullptr, nullptr)); |
+ AutoCFRelease<CFArrayRef> ctAxes(CTFontCopyVariationAxes(ct)); |
+ if (!ctAxes || CFArrayGetCount(ctAxes) != axisCount) { |
+ return nullptr; |
+ } |
+ |
+ int paramAxisCount; |
+ const FontParameters::Axis* paramAxes = params.getAxes(¶mAxisCount); |
+ |
+ CFMutableDictionaryRef dict = CFDictionaryCreateMutable(kCFAllocatorDefault, axisCount, |
+ &kCFTypeDictionaryKeyCallBacks, |
+ &kCFTypeDictionaryValueCallBacks); |
+ for (int i = 0; i < axisCount; ++i) { |
+ CFTypeRef axisInfo = CFArrayGetValueAtIndex(cgAxes, i); |
+ if (CFDictionaryGetTypeID() != CFGetTypeID(axisInfo)) { |
+ return nullptr; |
+ } |
+ CFDictionaryRef axisInfoDict = static_cast<CFDictionaryRef>(axisInfo); |
+ |
+ CFTypeRef axisName = CFDictionaryGetValue(axisInfoDict, kCGFontVariationAxisName); |
+ if (!axisName || CFGetTypeID(axisName) != CFStringGetTypeID()) { |
+ return nullptr; |
+ } |
+ |
+ CFNumberRef tagNumber = get_tag_for_name(static_cast<CFStringRef>(axisName), ctAxes); |
+ if (!tagNumber) { |
+ // Could not find a tag to go with the name of this index. |
+ // This would be a bug in CG/CT. |
+ continue; |
+ } |
+ int64_t tagLong; |
+ if (!CFNumberGetValue(tagNumber, kCFNumberSInt64Type, &tagLong)) { |
+ return nullptr; |
+ } |
+ |
+ // The variation axes can be set to any value, but cg will effectively pin them. |
+ // Pin them here to normalize. |
+ CFTypeRef min = CFDictionaryGetValue(axisInfoDict, kCGFontVariationAxisMinValue); |
+ CFTypeRef max = CFDictionaryGetValue(axisInfoDict, kCGFontVariationAxisMaxValue); |
+ CFTypeRef def = CFDictionaryGetValue(axisInfoDict, kCGFontVariationAxisDefaultValue); |
+ if (!min || CFGetTypeID(min) != CFNumberGetTypeID() || |
+ !max || CFGetTypeID(max) != CFNumberGetTypeID() || |
+ !def || CFGetTypeID(def) != CFNumberGetTypeID()) |
+ { |
+ return nullptr; |
+ } |
+ CFNumberRef minNumber = static_cast<CFNumberRef>(min); |
+ CFNumberRef maxNumber = static_cast<CFNumberRef>(max); |
+ CFNumberRef defNumber = static_cast<CFNumberRef>(def); |
+ double minDouble; |
+ double maxDouble; |
+ double defDouble; |
+ if (!CFNumberGetValue(minNumber, kCFNumberDoubleType, &minDouble) || |
+ !CFNumberGetValue(maxNumber, kCFNumberDoubleType, &maxDouble) || |
+ !CFNumberGetValue(defNumber, kCFNumberDoubleType, &defDouble)) |
+ { |
+ return nullptr; |
+ } |
+ |
+ double value = defDouble; |
+ for (int j = 0; j < paramAxisCount; ++j) { |
+ if (paramAxes[j].fTag == tagLong) { |
+ value = SkTPin(SkScalarToDouble(paramAxes[j].fStyleValue),minDouble,maxDouble); |
+ break; |
+ } |
+ } |
+ CFNumberRef valueNumber = CFNumberCreate(kCFAllocatorDefault, kCFNumberDoubleType, |
+ &value); |
+ CFDictionaryAddValue(dict, axisName, valueNumber); |
+ CFRelease(valueNumber); |
+ } |
+ return dict; |
+ } |
+ SkTypeface* onCreateFromStream(SkStreamAsset* s, const FontParameters& params) const override { |
+ AutoCFRelease<CGDataProviderRef> provider(SkCreateDataProviderFromStream(s)); |
+ if (nullptr == provider) { |
+ return nullptr; |
+ } |
+ AutoCFRelease<CGFontRef> cg(CGFontCreateWithDataProvider(provider)); |
+ if (nullptr == cg) { |
+ return nullptr; |
+ } |
+ |
+ AutoCFRelease<CFDictionaryRef> cgVariations(get_axes(cg, params)); |
+ // The CGFontRef returned by CGFontCreateCopyWithVariations when the passed CGFontRef was |
+ // created from a data provider does not appear to have any ownership of the underlying |
+ // data. The original CGFontRef must be kept alive until the copy will no longer be used. |
+ AutoCFRelease<CGFontRef> cgVariant; |
+ if (cgVariations) { |
+ cgVariant.reset(CGFontCreateCopyWithVariations(cg, cgVariations)); |
+ } else { |
+ cgVariant.reset(cg.detach()); |
+ } |
+ |
+ CTFontRef ct = CTFontCreateWithGraphicsFont(cgVariant, 0, nullptr, nullptr); |
+ if (!ct) { |
+ return nullptr; |
+ } |
+ return NewFromFontRef(ct, cg.detach(), nullptr, true); |
+ } |
+ |
static CFDictionaryRef get_axes(CGFontRef cg, SkFontData* fontData) { |
AutoCFRelease<CFArrayRef> cgAxes(CGFontCopyVariationAxes(cg)); |
if (!cgAxes) { |