diff options
Diffstat (limited to 'src/ports/SkFontHost_mac.cpp')
-rwxr-xr-x | src/ports/SkFontHost_mac.cpp | 205 |
1 files changed, 202 insertions, 3 deletions
diff --git a/src/ports/SkFontHost_mac.cpp b/src/ports/SkFontHost_mac.cpp index 3c7be46364..ff23b99905 100755 --- a/src/ports/SkFontHost_mac.cpp +++ b/src/ports/SkFontHost_mac.cpp @@ -428,10 +428,12 @@ static const uint32_t SkCTFontColorGlyphsTrait = (1 << 13); class SkTypeface_Mac : public SkTypeface { public: SkTypeface_Mac(const SkFontStyle& fs, bool isFixedPitch, - CTFontRef fontRef, const char requestedName[], bool isLocalStream) + CTFontRef fontRef, const char requestedName[], bool isLocalStream, + CGFontRef originatingCGFontRef = NULL) : SkTypeface(fs, SkTypefaceCache::NewFontID(), isFixedPitch) , fRequestedName(requestedName) , fFontRef(fontRef) // caller has already called CFRetain for us + , fOriginatingCGFontRef(originatingCGFontRef) , fHasColorGlyphs(SkToBool(CTFontGetSymbolicTraits(fFontRef) & SkCTFontColorGlyphsTrait)) , fIsLocalStream(isLocalStream) { @@ -440,11 +442,13 @@ public: SkString fRequestedName; AutoCFRelease<CTFontRef> fFontRef; + AutoCFRelease<CGFontRef> fOriginatingCGFontRef; const bool fHasColorGlyphs; protected: int onGetUPEM() const override; SkStreamAsset* onOpenStream(int* ttcIndex) const override; + SkFontData* onCreateFontData() const override; void onGetFamilyName(SkString* familyName) const override; SkTypeface::LocalizedStrings* onCreateFamilyNameIterator() const override; int onGetTableTags(SkFontTableTag tags[]) const override; @@ -467,12 +471,15 @@ private: }; /** Creates a typeface without searching the cache. Takes ownership of the CTFontRef. */ -static SkTypeface* NewFromFontRef(CTFontRef fontRef, const char name[], bool isLocalStream) { +static SkTypeface* NewFromFontRef(CTFontRef fontRef, const char name[], bool isLocalStream, + CGFontRef originatingCGFontRef = NULL) +{ SkASSERT(fontRef); bool isFixedPitch; SkFontStyle style = SkFontStyle(computeStyleBits(fontRef, &isFixedPitch)); - return new SkTypeface_Mac(style, isFixedPitch, fontRef, name, isLocalStream); + return new SkTypeface_Mac(style, isFixedPitch, fontRef, name, isLocalStream, + originatingCGFontRef); } static bool find_by_CTFontRef(SkTypeface* cached, const SkFontStyle&, void* context) { @@ -1752,6 +1759,115 @@ SkStreamAsset* SkTypeface_Mac::onOpenStream(int* ttcIndex) const { return stream; } +struct NonDefaultAxesContext { + SkFixed* axisValue; + CFArrayRef cgAxes; +}; +static void set_non_default_axes(CFTypeRef key, CFTypeRef value, void* context) { + NonDefaultAxesContext* self = static_cast<NonDefaultAxesContext*>(context); + + if (CFGetTypeID(key) != CFStringGetTypeID() || CFGetTypeID(value) != CFNumberGetTypeID()) { + return; + } + + // The key is a CFString which is a string from the 'name' table. + // Search the cgAxes for an axis with this name, and use its index to store the value. + CFIndex keyIndex = -1; + CFStringRef keyString = static_cast<CFStringRef>(key); + for (CFIndex i = 0; i < CFArrayGetCount(self->cgAxes); ++i) { + CFTypeRef cgAxis = CFArrayGetValueAtIndex(self->cgAxes, i); + if (CFGetTypeID(cgAxis) != CFDictionaryGetTypeID()) { + continue; + } + + CFDictionaryRef cgAxisDict = static_cast<CFDictionaryRef>(cgAxis); + CFTypeRef cgAxisName = CFDictionaryGetValue(cgAxisDict, kCGFontVariationAxisName); + if (!cgAxisName || CFGetTypeID(cgAxisName) != CFStringGetTypeID()) { + continue; + } + CFStringRef cgAxisNameString = static_cast<CFStringRef>(cgAxisName); + if (CFStringCompare(keyString, cgAxisNameString, 0) == kCFCompareEqualTo) { + keyIndex = i; + break; + } + } + if (keyIndex == -1) { + return; + } + + CFNumberRef valueNumber = static_cast<CFNumberRef>(value); + double valueDouble; + if (!CFNumberGetValue(valueNumber, kCFNumberDoubleType, &valueDouble) || + valueDouble < SkFixedToDouble(SK_FixedMin) || SkFixedToDouble(SK_FixedMax) < valueDouble) + { + return; + } + self->axisValue[keyIndex] = SkDoubleToFixed(valueDouble); +} +static bool get_variations(CTFontRef fFontRef, CFIndex* cgAxisCount, + SkAutoSTMalloc<4, SkFixed>* axisValues) +{ + // CTFontCopyVariationAxes and CTFontCopyVariation do not work when applied to fonts which + // started life with CGFontCreateWithDataProvider (they simply always return NULL). + // As a result, we are limited to CGFontCopyVariationAxes and CGFontCopyVariations. + AutoCFRelease<CGFontRef> cgFont(CTFontCopyGraphicsFont(fFontRef, NULL)); + + AutoCFRelease<CFDictionaryRef> cgVariations(CGFontCopyVariations(cgFont)); + // If a font has no variations CGFontCopyVariations returns NULL (instead of an empty dict). + if (!cgVariations.get()) { + return false; + } + + AutoCFRelease<CFArrayRef> cgAxes(CGFontCopyVariationAxes(cgFont)); + *cgAxisCount = CFArrayGetCount(cgAxes); + axisValues->reset(*cgAxisCount); + + // Set all of the axes to their default values. + // Fail if any default value cannot be determined. + for (CFIndex i = 0; i < *cgAxisCount; ++i) { + CFTypeRef cgAxis = CFArrayGetValueAtIndex(cgAxes, i); + if (CFGetTypeID(cgAxis) != CFDictionaryGetTypeID()) { + return false; + } + + CFDictionaryRef cgAxisDict = static_cast<CFDictionaryRef>(cgAxis); + CFTypeRef axisDefaultValue = CFDictionaryGetValue(cgAxisDict, + kCGFontVariationAxisDefaultValue); + if (!axisDefaultValue || CFGetTypeID(axisDefaultValue) != CFNumberGetTypeID()) { + return false; + } + CFNumberRef axisDefaultValueNumber = static_cast<CFNumberRef>(axisDefaultValue); + double axisDefaultValueDouble; + if (!CFNumberGetValue(axisDefaultValueNumber, kCFNumberDoubleType, &axisDefaultValueDouble)) + { + return false; + } + if (axisDefaultValueDouble < SkFixedToDouble(SK_FixedMin) || + SkFixedToDouble(SK_FixedMax) < axisDefaultValueDouble) + { + return false; + } + (*axisValues)[(int)i] = SkDoubleToFixed(axisDefaultValueDouble); + } + + // Override the default values with the given font's stated axis values. + NonDefaultAxesContext c = { axisValues->get(), cgAxes.get() }; + CFDictionaryApplyFunction(cgVariations, set_non_default_axes, &c); + + return true; +} +SkFontData* SkTypeface_Mac::onCreateFontData() const { + int index; + SkAutoTDelete<SkStreamAsset> stream(this->onOpenStream(&index)); + + CFIndex cgAxisCount; + SkAutoSTMalloc<4, SkFixed> axisValues; + if (get_variations(fFontRef, &cgAxisCount, &axisValues)) { + return new SkFontData(stream.detach(), index, axisValues.get(), cgAxisCount); + } + return new SkFontData(stream.detach(), index, NULL, 0); +} + /////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// @@ -2247,6 +2363,89 @@ protected: return create_from_dataProvider(pr); } + static CFDictionaryRef get_axes(CGFontRef cg, SkFontData* fontData) { + AutoCFRelease<CFArrayRef> cgAxes(CGFontCopyVariationAxes(cg)); + if (!cgAxes) { + return NULL; + } + + CFIndex axisCount = CFArrayGetCount(cgAxes); + if (0 == axisCount || axisCount != fontData->getAxisCount()) { + return NULL; + } + + CFMutableDictionaryRef dict = CFDictionaryCreateMutable(kCFAllocatorDefault, axisCount, + &kCFTypeDictionaryKeyCallBacks, + &kCFTypeDictionaryValueCallBacks); + for (int i = 0; i < fontData->getAxisCount(); ++i) { + CFTypeRef axisInfo = CFArrayGetValueAtIndex(cgAxes, i); + if (CFDictionaryGetTypeID() != CFGetTypeID(axisInfo)) { + return NULL; + } + CFDictionaryRef axisInfoDict = static_cast<CFDictionaryRef>(axisInfo); + + CFTypeRef axisName = CFDictionaryGetValue(axisInfoDict, kCGFontVariationAxisName); + if (!axisName || CFGetTypeID(axisName) != CFStringGetTypeID()) { + return NULL; + } + + // 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); + if (!min || CFGetTypeID(min) != CFNumberGetTypeID() || + !max || CFGetTypeID(max) != CFNumberGetTypeID()) + { + return NULL; + } + CFNumberRef minNumber = static_cast<CFNumberRef>(min); + CFNumberRef maxNumber = static_cast<CFNumberRef>(max); + double minDouble; + double maxDouble; + if (!CFNumberGetValue(minNumber, kCFNumberDoubleType, &minDouble) || + !CFNumberGetValue(maxNumber, kCFNumberDoubleType, &maxDouble)) + { + return NULL; + } + double value = SkTPin(SkFixedToDouble(fontData->getAxis()[i]), minDouble, maxDouble); + CFNumberRef valueNumber = CFNumberCreate(kCFAllocatorDefault, kCFNumberDoubleType, + &value); + + CFDictionaryAddValue(dict, axisName, valueNumber); + CFRelease(valueNumber); + } + return dict; + } + SkTypeface* onCreateFromFontData(SkFontData* data) const override { + SkAutoTDelete<SkFontData> fontData(data); + SkStreamAsset* stream = fontData->detachStream(); + AutoCFRelease<CGDataProviderRef> provider(SkCreateDataProviderFromStream(stream)); + if (NULL == provider) { + return NULL; + } + AutoCFRelease<CGFontRef> cg(CGFontCreateWithDataProvider(provider)); + if (NULL == cg) { + return NULL; + } + + AutoCFRelease<CFDictionaryRef> cgVariations(get_axes(cg, fontData)); + // 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, NULL, NULL); + if (!ct) { + return NULL; + } + return NewFromFontRef(ct, NULL, true, cg.detach()); + } + SkTypeface* onCreateFromFile(const char path[], int ttcIndex) const override { AutoCFRelease<CGDataProviderRef> pr(CGDataProviderCreateWithFilename(path)); if (NULL == pr) { |