| /* |
| * Copyright 2006 The Android Open Source Project |
| * |
| * Use of this source code is governed by a BSD-style license that can be |
| * found in the LICENSE file. |
| */ |
| |
| #include "include/core/SkTypes.h" |
| #if defined(SK_BUILD_FOR_MAC) || defined(SK_BUILD_FOR_IOS) |
| |
| #ifdef SK_BUILD_FOR_MAC |
| #import <ApplicationServices/ApplicationServices.h> |
| #endif |
| |
| #ifdef SK_BUILD_FOR_IOS |
| #include <CoreText/CoreText.h> |
| #include <CoreText/CTFontManager.h> |
| #include <CoreGraphics/CoreGraphics.h> |
| #include <CoreFoundation/CoreFoundation.h> |
| #include <dlfcn.h> |
| #endif |
| |
| #include "include/core/SkData.h" |
| #include "include/core/SkFontArguments.h" |
| #include "include/core/SkFontMgr.h" |
| #include "include/core/SkFontStyle.h" |
| #include "include/core/SkStream.h" |
| #include "include/core/SkString.h" |
| #include "include/core/SkTypeface.h" |
| #include "include/ports/SkFontMgr_mac_ct.h" |
| #include "include/private/SkFixed.h" |
| #include "include/private/SkOnce.h" |
| #include "include/private/SkTPin.h" |
| #include "include/private/SkTemplates.h" |
| #include "include/private/SkTo.h" |
| #include "src/core/SkFontDescriptor.h" |
| #include "src/ports/SkTypeface_mac_ct.h" |
| #include "src/utils/SkUTF.h" |
| |
| #include <string.h> |
| #include <memory> |
| |
| #if (defined(SK_BUILD_FOR_IOS) && defined(__IPHONE_14_0) && \ |
| __IPHONE_OS_VERSION_MIN_REQUIRED >= __IPHONE_14_0) || \ |
| (defined(SK_BUILD_FOR_MAC) && defined(__MAC_11_0) && \ |
| __MAC_OS_VERSION_MIN_REQUIRED >= __MAC_11_0) |
| |
| static uint32_t SkGetCoreTextVersion() { |
| // If compiling for iOS 14.0+ or macOS 11.0+, the CoreText version number |
| // must be derived from the OS version number. |
| static const uint32_t kCoreTextVersionNEWER = 0x000D0000; |
| return kCoreTextVersionNEWER; |
| } |
| |
| #else |
| |
| static uint32_t SkGetCoreTextVersion() { |
| // Check for CoreText availability before calling CTGetCoreTextVersion(). |
| static const bool kCoreTextIsAvailable = (&CTGetCoreTextVersion != nullptr); |
| if (kCoreTextIsAvailable) { |
| return CTGetCoreTextVersion(); |
| } |
| |
| // Default to a value that's smaller than any known CoreText version. |
| static const uint32_t kCoreTextVersionUNKNOWN = 0; |
| return kCoreTextVersionUNKNOWN; |
| } |
| |
| #endif |
| |
| static SkUniqueCFRef<CFStringRef> make_CFString(const char s[]) { |
| return SkUniqueCFRef<CFStringRef>(CFStringCreateWithCString(nullptr, s, kCFStringEncodingUTF8)); |
| } |
| |
| /** Creates a typeface from a descriptor, searching the cache. */ |
| static sk_sp<SkTypeface> create_from_desc(CTFontDescriptorRef desc) { |
| SkUniqueCFRef<CTFontRef> ctFont(CTFontCreateWithFontDescriptor(desc, 0, nullptr)); |
| if (!ctFont) { |
| return nullptr; |
| } |
| |
| return SkTypeface_Mac::Make(std::move(ctFont), OpszVariation(), nullptr); |
| } |
| |
| static SkUniqueCFRef<CTFontDescriptorRef> create_descriptor(const char familyName[], |
| const SkFontStyle& style) { |
| SkUniqueCFRef<CFMutableDictionaryRef> cfAttributes( |
| CFDictionaryCreateMutable(kCFAllocatorDefault, 0, |
| &kCFTypeDictionaryKeyCallBacks, |
| &kCFTypeDictionaryValueCallBacks)); |
| |
| SkUniqueCFRef<CFMutableDictionaryRef> cfTraits( |
| CFDictionaryCreateMutable(kCFAllocatorDefault, 0, |
| &kCFTypeDictionaryKeyCallBacks, |
| &kCFTypeDictionaryValueCallBacks)); |
| |
| if (!cfAttributes || !cfTraits) { |
| return nullptr; |
| } |
| |
| // TODO(crbug.com/1018581) Some CoreText versions have errant behavior when |
| // certain traits set. Temporary workaround to omit specifying trait for those |
| // versions. |
| // Long term solution will involve serializing typefaces instead of relying upon |
| // this to match between processes. |
| // |
| // Compare CoreText.h in an up to date SDK for where these values come from. |
| static const uint32_t kSkiaLocalCTVersionNumber10_14 = 0x000B0000; |
| static const uint32_t kSkiaLocalCTVersionNumber10_15 = 0x000C0000; |
| |
| // CTFontTraits (symbolic) |
| // macOS 14 and iOS 12 seem to behave badly when kCTFontSymbolicTrait is set. |
| // macOS 15 yields LastResort font instead of a good default font when |
| // kCTFontSymbolicTrait is set. |
| if (SkGetCoreTextVersion() < kSkiaLocalCTVersionNumber10_14) { |
| CTFontSymbolicTraits ctFontTraits = 0; |
| if (style.weight() >= SkFontStyle::kBold_Weight) { |
| ctFontTraits |= kCTFontBoldTrait; |
| } |
| if (style.slant() != SkFontStyle::kUpright_Slant) { |
| ctFontTraits |= kCTFontItalicTrait; |
| } |
| SkUniqueCFRef<CFNumberRef> cfFontTraits( |
| CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &ctFontTraits)); |
| if (cfFontTraits) { |
| CFDictionaryAddValue(cfTraits.get(), kCTFontSymbolicTrait, cfFontTraits.get()); |
| } |
| } |
| |
| // CTFontTraits (weight) |
| CGFloat ctWeight = SkCTFontCTWeightForCSSWeight(style.weight()); |
| SkUniqueCFRef<CFNumberRef> cfFontWeight( |
| CFNumberCreate(kCFAllocatorDefault, kCFNumberCGFloatType, &ctWeight)); |
| if (cfFontWeight) { |
| CFDictionaryAddValue(cfTraits.get(), kCTFontWeightTrait, cfFontWeight.get()); |
| } |
| // CTFontTraits (width) |
| CGFloat ctWidth = SkCTFontCTWidthForCSSWidth(style.width()); |
| SkUniqueCFRef<CFNumberRef> cfFontWidth( |
| CFNumberCreate(kCFAllocatorDefault, kCFNumberCGFloatType, &ctWidth)); |
| if (cfFontWidth) { |
| CFDictionaryAddValue(cfTraits.get(), kCTFontWidthTrait, cfFontWidth.get()); |
| } |
| // CTFontTraits (slant) |
| // macOS 15 behaves badly when kCTFontSlantTrait is set. |
| if (SkGetCoreTextVersion() != kSkiaLocalCTVersionNumber10_15) { |
| CGFloat ctSlant = style.slant() == SkFontStyle::kUpright_Slant ? 0 : 1; |
| SkUniqueCFRef<CFNumberRef> cfFontSlant( |
| CFNumberCreate(kCFAllocatorDefault, kCFNumberCGFloatType, &ctSlant)); |
| if (cfFontSlant) { |
| CFDictionaryAddValue(cfTraits.get(), kCTFontSlantTrait, cfFontSlant.get()); |
| } |
| } |
| // CTFontTraits |
| CFDictionaryAddValue(cfAttributes.get(), kCTFontTraitsAttribute, cfTraits.get()); |
| |
| // CTFontFamilyName |
| if (familyName) { |
| SkUniqueCFRef<CFStringRef> cfFontName = make_CFString(familyName); |
| if (cfFontName) { |
| CFDictionaryAddValue(cfAttributes.get(), kCTFontFamilyNameAttribute, cfFontName.get()); |
| } |
| } |
| |
| return SkUniqueCFRef<CTFontDescriptorRef>( |
| CTFontDescriptorCreateWithAttributes(cfAttributes.get())); |
| } |
| |
| // Same as the above function except style is included so we can |
| // compare whether the created font conforms to the style. If not, we need |
| // to recreate the font with symbolic traits. This is needed due to MacOS 10.11 |
| // font creation problem https://bugs.chromium.org/p/skia/issues/detail?id=8447. |
| static sk_sp<SkTypeface> create_from_desc_and_style(CTFontDescriptorRef desc, |
| const SkFontStyle& style) { |
| SkUniqueCFRef<CTFontRef> ctFont(CTFontCreateWithFontDescriptor(desc, 0, nullptr)); |
| if (!ctFont) { |
| return nullptr; |
| } |
| |
| const CTFontSymbolicTraits traits = CTFontGetSymbolicTraits(ctFont.get()); |
| CTFontSymbolicTraits expected_traits = traits; |
| if (style.slant() != SkFontStyle::kUpright_Slant) { |
| expected_traits |= kCTFontItalicTrait; |
| } |
| if (style.weight() >= SkFontStyle::kBold_Weight) { |
| expected_traits |= kCTFontBoldTrait; |
| } |
| |
| if (expected_traits != traits) { |
| SkUniqueCFRef<CTFontRef> ctNewFont(CTFontCreateCopyWithSymbolicTraits( |
| ctFont.get(), 0, nullptr, expected_traits, expected_traits)); |
| if (ctNewFont) { |
| ctFont = std::move(ctNewFont); |
| } |
| } |
| |
| return SkTypeface_Mac::Make(std::move(ctFont), OpszVariation(), nullptr); |
| } |
| |
| /** Creates a typeface from a name, searching the cache. */ |
| static sk_sp<SkTypeface> create_from_name(const char familyName[], const SkFontStyle& style) { |
| SkUniqueCFRef<CTFontDescriptorRef> desc = create_descriptor(familyName, style); |
| if (!desc) { |
| return nullptr; |
| } |
| return create_from_desc_and_style(desc.get(), style); |
| } |
| |
| static const char* map_css_names(const char* name) { |
| static const struct { |
| const char* fFrom; // name the caller specified |
| const char* fTo; // "canonical" name we map to |
| } gPairs[] = { |
| { "sans-serif", "Helvetica" }, |
| { "serif", "Times" }, |
| { "monospace", "Courier" } |
| }; |
| |
| for (size_t i = 0; i < std::size(gPairs); i++) { |
| if (strcmp(name, gPairs[i].fFrom) == 0) { |
| return gPairs[i].fTo; |
| } |
| } |
| return name; // no change |
| } |
| |
| namespace { |
| |
| static sk_sp<SkData> skdata_from_skstreamasset(std::unique_ptr<SkStreamAsset> stream) { |
| size_t size = stream->getLength(); |
| if (const void* base = stream->getMemoryBase()) { |
| return SkData::MakeWithProc(base, size, |
| [](const void*, void* ctx) -> void { |
| delete (SkStreamAsset*)ctx; |
| }, stream.release()); |
| } |
| return SkData::MakeFromStream(stream.get(), size); |
| } |
| |
| static SkUniqueCFRef<CFDataRef> cfdata_from_skdata(sk_sp<SkData> data) { |
| void const * const addr = data->data(); |
| size_t const size = data->size(); |
| |
| CFAllocatorContext ctx = { |
| 0, // CFIndex version |
| data.release(), // void* info |
| nullptr, // const void *(*retain)(const void *info); |
| nullptr, // void (*release)(const void *info); |
| nullptr, // CFStringRef (*copyDescription)(const void *info); |
| nullptr, // void * (*allocate)(CFIndex size, CFOptionFlags hint, void *info); |
| nullptr, // void*(*reallocate)(void* ptr,CFIndex newsize,CFOptionFlags hint,void* info); |
| [](void*,void* info) -> void { // void (*deallocate)(void *ptr, void *info); |
| SkASSERT(info); |
| ((SkData*)info)->unref(); |
| }, |
| nullptr, // CFIndex (*preferredSize)(CFIndex size, CFOptionFlags hint, void *info); |
| }; |
| SkUniqueCFRef<CFAllocatorRef> alloc(CFAllocatorCreate(kCFAllocatorDefault, &ctx)); |
| return SkUniqueCFRef<CFDataRef>(CFDataCreateWithBytesNoCopy( |
| kCFAllocatorDefault, (const UInt8 *)addr, size, alloc.get())); |
| } |
| |
| static SkUniqueCFRef<CTFontRef> ctfont_from_skdata(sk_sp<SkData> data, int ttcIndex) { |
| // TODO: Use CTFontManagerCreateFontDescriptorsFromData when available. |
| if (ttcIndex != 0) { |
| return nullptr; |
| } |
| |
| SkUniqueCFRef<CFDataRef> cfData(cfdata_from_skdata(std::move(data))); |
| |
| SkUniqueCFRef<CTFontDescriptorRef> desc( |
| CTFontManagerCreateFontDescriptorFromData(cfData.get())); |
| if (!desc) { |
| return nullptr; |
| } |
| return SkUniqueCFRef<CTFontRef>(CTFontCreateWithFontDescriptor(desc.get(), 0, nullptr)); |
| } |
| |
| static bool find_desc_str(CTFontDescriptorRef desc, CFStringRef name, SkString* value) { |
| SkUniqueCFRef<CFStringRef> ref((CFStringRef)CTFontDescriptorCopyAttribute(desc, name)); |
| if (!ref) { |
| return false; |
| } |
| SkStringFromCFString(ref.get(), value); |
| return true; |
| } |
| |
| static inline int sqr(int value) { |
| SkASSERT(SkAbs32(value) < 0x7FFF); // check for overflow |
| return value * value; |
| } |
| |
| // We normalize each axis (weight, width, italic) to be base-900 |
| static int compute_metric(const SkFontStyle& a, const SkFontStyle& b) { |
| return sqr(a.weight() - b.weight()) + |
| sqr((a.width() - b.width()) * 100) + |
| sqr((a.slant() != b.slant()) * 900); |
| } |
| |
| class SkFontStyleSet_Mac : public SkFontStyleSet { |
| public: |
| SkFontStyleSet_Mac(CTFontDescriptorRef desc) |
| : fArray(CTFontDescriptorCreateMatchingFontDescriptors(desc, nullptr)) |
| , fCount(0) |
| { |
| if (!fArray) { |
| fArray.reset(CFArrayCreate(nullptr, nullptr, 0, nullptr)); |
| } |
| fCount = SkToInt(CFArrayGetCount(fArray.get())); |
| } |
| |
| int count() override { |
| return fCount; |
| } |
| |
| void getStyle(int index, SkFontStyle* style, SkString* name) override { |
| SkASSERT((unsigned)index < (unsigned)fCount); |
| CTFontDescriptorRef desc = (CTFontDescriptorRef)CFArrayGetValueAtIndex(fArray.get(), index); |
| if (style) { |
| *style = SkCTFontDescriptorGetSkFontStyle(desc, false); |
| } |
| if (name) { |
| if (!find_desc_str(desc, kCTFontStyleNameAttribute, name)) { |
| name->reset(); |
| } |
| } |
| } |
| |
| SkTypeface* createTypeface(int index) override { |
| SkASSERT((unsigned)index < (unsigned)CFArrayGetCount(fArray.get())); |
| CTFontDescriptorRef desc = (CTFontDescriptorRef)CFArrayGetValueAtIndex(fArray.get(), index); |
| |
| return create_from_desc(desc).release(); |
| } |
| |
| SkTypeface* matchStyle(const SkFontStyle& pattern) override { |
| if (0 == fCount) { |
| return nullptr; |
| } |
| return create_from_desc(findMatchingDesc(pattern)).release(); |
| } |
| |
| private: |
| SkUniqueCFRef<CFArrayRef> fArray; |
| int fCount; |
| |
| CTFontDescriptorRef findMatchingDesc(const SkFontStyle& pattern) const { |
| int bestMetric = SK_MaxS32; |
| CTFontDescriptorRef bestDesc = nullptr; |
| |
| for (int i = 0; i < fCount; ++i) { |
| CTFontDescriptorRef desc = (CTFontDescriptorRef)CFArrayGetValueAtIndex(fArray.get(), i); |
| int metric = compute_metric(pattern, SkCTFontDescriptorGetSkFontStyle(desc, false)); |
| if (0 == metric) { |
| return desc; |
| } |
| if (metric < bestMetric) { |
| bestMetric = metric; |
| bestDesc = desc; |
| } |
| } |
| SkASSERT(bestDesc); |
| return bestDesc; |
| } |
| }; |
| |
| SkUniqueCFRef<CFArrayRef> SkCopyAvailableFontFamilyNames(CTFontCollectionRef collection) { |
| // Create a CFArray of all available font descriptors. |
| SkUniqueCFRef<CFArrayRef> descriptors( |
| CTFontCollectionCreateMatchingFontDescriptors(collection)); |
| |
| // Copy the font family names of the font descriptors into a CFSet. |
| auto addDescriptorFamilyNameToSet = [](const void* value, void* context) -> void { |
| CTFontDescriptorRef descriptor = static_cast<CTFontDescriptorRef>(value); |
| CFMutableSetRef familyNameSet = static_cast<CFMutableSetRef>(context); |
| SkUniqueCFRef<CFTypeRef> familyName( |
| CTFontDescriptorCopyAttribute(descriptor, kCTFontFamilyNameAttribute)); |
| if (familyName) { |
| CFSetAddValue(familyNameSet, familyName.get()); |
| } |
| }; |
| SkUniqueCFRef<CFMutableSetRef> familyNameSet( |
| CFSetCreateMutable(kCFAllocatorDefault, 0, &kCFTypeSetCallBacks)); |
| CFArrayApplyFunction(descriptors.get(), CFRangeMake(0, CFArrayGetCount(descriptors.get())), |
| addDescriptorFamilyNameToSet, familyNameSet.get()); |
| |
| // Get the set of family names into an array; this does not retain. |
| CFIndex count = CFSetGetCount(familyNameSet.get()); |
| std::unique_ptr<const void*[]> familyNames(new const void*[count]); |
| CFSetGetValues(familyNameSet.get(), familyNames.get()); |
| |
| // Sort the array of family names (to match CTFontManagerCopyAvailableFontFamilyNames). |
| std::sort(familyNames.get(), familyNames.get() + count, [](const void* a, const void* b){ |
| return CFStringCompare((CFStringRef)a, (CFStringRef)b, 0) == kCFCompareLessThan; |
| }); |
| |
| // Copy family names into a CFArray; this does retain. |
| return SkUniqueCFRef<CFArrayRef>( |
| CFArrayCreate(kCFAllocatorDefault, familyNames.get(), count, &kCFTypeArrayCallBacks)); |
| } |
| |
| /** Use CTFontManagerCopyAvailableFontFamilyNames if available, simulate if not. */ |
| SkUniqueCFRef<CFArrayRef> SkCTFontManagerCopyAvailableFontFamilyNames() { |
| #ifdef SK_BUILD_FOR_IOS |
| using CTFontManagerCopyAvailableFontFamilyNamesProc = CFArrayRef (*)(void); |
| CTFontManagerCopyAvailableFontFamilyNamesProc ctFontManagerCopyAvailableFontFamilyNames; |
| *(void**)(&ctFontManagerCopyAvailableFontFamilyNames) = |
| dlsym(RTLD_DEFAULT, "CTFontManagerCopyAvailableFontFamilyNames"); |
| if (ctFontManagerCopyAvailableFontFamilyNames) { |
| return SkUniqueCFRef<CFArrayRef>(ctFontManagerCopyAvailableFontFamilyNames()); |
| } |
| SkUniqueCFRef<CTFontCollectionRef> collection( |
| CTFontCollectionCreateFromAvailableFonts(nullptr)); |
| return SkUniqueCFRef<CFArrayRef>(SkCopyAvailableFontFamilyNames(collection.get())); |
| #else |
| return SkUniqueCFRef<CFArrayRef>(CTFontManagerCopyAvailableFontFamilyNames()); |
| #endif |
| } |
| |
| } // namespace |
| |
| class SkFontMgr_Mac : public SkFontMgr { |
| SkUniqueCFRef<CFArrayRef> fNames; |
| int fCount; |
| |
| CFStringRef getFamilyNameAt(int index) const { |
| SkASSERT((unsigned)index < (unsigned)fCount); |
| return (CFStringRef)CFArrayGetValueAtIndex(fNames.get(), index); |
| } |
| |
| static SkFontStyleSet* CreateSet(CFStringRef cfFamilyName) { |
| SkUniqueCFRef<CFMutableDictionaryRef> cfAttr( |
| CFDictionaryCreateMutable(kCFAllocatorDefault, 0, |
| &kCFTypeDictionaryKeyCallBacks, |
| &kCFTypeDictionaryValueCallBacks)); |
| |
| CFDictionaryAddValue(cfAttr.get(), kCTFontFamilyNameAttribute, cfFamilyName); |
| |
| SkUniqueCFRef<CTFontDescriptorRef> desc( |
| CTFontDescriptorCreateWithAttributes(cfAttr.get())); |
| return new SkFontStyleSet_Mac(desc.get()); |
| } |
| |
| public: |
| SkUniqueCFRef<CTFontCollectionRef> fFontCollection; |
| SkFontMgr_Mac(CTFontCollectionRef fontCollection) |
| : fNames(fontCollection ? SkCopyAvailableFontFamilyNames(fontCollection) |
| : SkCTFontManagerCopyAvailableFontFamilyNames()) |
| , fCount(fNames ? SkToInt(CFArrayGetCount(fNames.get())) : 0) |
| , fFontCollection(fontCollection ? (CTFontCollectionRef)CFRetain(fontCollection) |
| : CTFontCollectionCreateFromAvailableFonts(nullptr)) |
| {} |
| |
| protected: |
| int onCountFamilies() const override { |
| return fCount; |
| } |
| |
| void onGetFamilyName(int index, SkString* familyName) const override { |
| if ((unsigned)index < (unsigned)fCount) { |
| SkStringFromCFString(this->getFamilyNameAt(index), familyName); |
| } else { |
| familyName->reset(); |
| } |
| } |
| |
| SkFontStyleSet* onCreateStyleSet(int index) const override { |
| if ((unsigned)index >= (unsigned)fCount) { |
| return nullptr; |
| } |
| return CreateSet(this->getFamilyNameAt(index)); |
| } |
| |
| SkFontStyleSet* onMatchFamily(const char familyName[]) const override { |
| if (!familyName) { |
| return nullptr; |
| } |
| SkUniqueCFRef<CFStringRef> cfName = make_CFString(familyName); |
| return CreateSet(cfName.get()); |
| } |
| |
| SkTypeface* onMatchFamilyStyle(const char familyName[], |
| const SkFontStyle& style) const override { |
| SkUniqueCFRef<CTFontDescriptorRef> desc = create_descriptor(familyName, style); |
| return create_from_desc(desc.get()).release(); |
| } |
| |
| SkTypeface* onMatchFamilyStyleCharacter(const char familyName[], |
| const SkFontStyle& style, |
| const char* bcp47[], int bcp47Count, |
| SkUnichar character) const override { |
| SkUniqueCFRef<CTFontDescriptorRef> desc = create_descriptor(familyName, style); |
| SkUniqueCFRef<CTFontRef> familyFont(CTFontCreateWithFontDescriptor(desc.get(), 0, nullptr)); |
| |
| // kCFStringEncodingUTF32 is BE unless there is a BOM. |
| // Since there is no machine endian option, explicitly state machine endian. |
| #ifdef SK_CPU_LENDIAN |
| constexpr CFStringEncoding encoding = kCFStringEncodingUTF32LE; |
| #else |
| constexpr CFStringEncoding encoding = kCFStringEncodingUTF32BE; |
| #endif |
| SkUniqueCFRef<CFStringRef> string(CFStringCreateWithBytes( |
| kCFAllocatorDefault, reinterpret_cast<const UInt8 *>(&character), sizeof(character), |
| encoding, false)); |
| // If 0xD800 <= codepoint <= 0xDFFF || 0x10FFFF < codepoint 'string' may be nullptr. |
| // No font should be covering such codepoints (even the magic fallback font). |
| if (!string) { |
| return nullptr; |
| } |
| CFRange range = CFRangeMake(0, CFStringGetLength(string.get())); // in UniChar units. |
| SkUniqueCFRef<CTFontRef> fallbackFont( |
| CTFontCreateForString(familyFont.get(), string.get(), range)); |
| return SkTypeface_Mac::Make(std::move(fallbackFont), OpszVariation(), nullptr).release(); |
| } |
| |
| sk_sp<SkTypeface> onMakeFromData(sk_sp<SkData> data, int ttcIndex) const override { |
| if (ttcIndex != 0) { |
| return nullptr; |
| } |
| |
| SkUniqueCFRef<CTFontRef> ct = ctfont_from_skdata(data, ttcIndex); |
| if (!ct) { |
| return nullptr; |
| } |
| |
| return SkTypeface_Mac::Make(std::move(ct), OpszVariation(), |
| SkMemoryStream::Make(std::move(data))); |
| } |
| |
| sk_sp<SkTypeface> onMakeFromStreamIndex(std::unique_ptr<SkStreamAsset> stream, |
| int ttcIndex) const override { |
| if (ttcIndex != 0) { |
| return nullptr; |
| } |
| |
| sk_sp<SkData> data = skdata_from_skstreamasset(stream->duplicate()); |
| if (!data) { |
| return nullptr; |
| } |
| SkUniqueCFRef<CTFontRef> ct = ctfont_from_skdata(std::move(data), ttcIndex); |
| if (!ct) { |
| return nullptr; |
| } |
| |
| return SkTypeface_Mac::Make(std::move(ct), OpszVariation(), std::move(stream)); |
| } |
| |
| sk_sp<SkTypeface> onMakeFromStreamArgs(std::unique_ptr<SkStreamAsset> stream, |
| const SkFontArguments& args) const override |
| { |
| // TODO: Use CTFontManagerCreateFontDescriptorsFromData when available. |
| int ttcIndex = args.getCollectionIndex(); |
| if (ttcIndex != 0) { |
| return nullptr; |
| } |
| |
| sk_sp<SkData> data = skdata_from_skstreamasset(stream->duplicate()); |
| if (!data) { |
| return nullptr; |
| } |
| SkUniqueCFRef<CTFontRef> ct = ctfont_from_skdata(std::move(data), ttcIndex); |
| if (!ct) { |
| return nullptr; |
| } |
| |
| SkUniqueCFRef<CFArrayRef> axes(CTFontCopyVariationAxes(ct.get())); |
| CTFontVariation ctVariation = SkCTVariationFromSkFontArguments(ct.get(), axes.get(), args); |
| |
| SkUniqueCFRef<CTFontRef> ctVariant; |
| if (ctVariation.variation) { |
| SkUniqueCFRef<CFMutableDictionaryRef> attributes( |
| CFDictionaryCreateMutable(kCFAllocatorDefault, 0, |
| &kCFTypeDictionaryKeyCallBacks, |
| &kCFTypeDictionaryValueCallBacks)); |
| CFDictionaryAddValue(attributes.get(), |
| kCTFontVariationAttribute, ctVariation.variation.get()); |
| SkUniqueCFRef<CTFontDescriptorRef> varDesc( |
| CTFontDescriptorCreateWithAttributes(attributes.get())); |
| ctVariant.reset(CTFontCreateCopyWithAttributes(ct.get(), 0, nullptr, varDesc.get())); |
| } else { |
| ctVariant.reset(ct.release()); |
| } |
| if (!ctVariant) { |
| return nullptr; |
| } |
| |
| return SkTypeface_Mac::Make(std::move(ctVariant), ctVariation.opsz, std::move(stream)); |
| } |
| |
| sk_sp<SkTypeface> onMakeFromFile(const char path[], int ttcIndex) const override { |
| if (ttcIndex != 0) { |
| return nullptr; |
| } |
| |
| sk_sp<SkData> data = SkData::MakeFromFileName(path); |
| if (!data) { |
| return nullptr; |
| } |
| |
| return this->onMakeFromData(std::move(data), ttcIndex); |
| } |
| |
| sk_sp<SkTypeface> onLegacyMakeTypeface(const char familyName[], SkFontStyle style) const override { |
| if (familyName) { |
| familyName = map_css_names(familyName); |
| } |
| |
| sk_sp<SkTypeface> face = create_from_name(familyName, style); |
| if (face) { |
| return face; |
| } |
| |
| static SkTypeface* gDefaultFace; |
| static SkOnce lookupDefault; |
| static const char FONT_DEFAULT_NAME[] = "Lucida Sans"; |
| lookupDefault([]{ |
| gDefaultFace = create_from_name(FONT_DEFAULT_NAME, SkFontStyle()).release(); |
| }); |
| return sk_ref_sp(gDefaultFace); |
| } |
| }; |
| |
| sk_sp<SkFontMgr> SkFontMgr_New_CoreText(CTFontCollectionRef fontCollection) { |
| return sk_make_sp<SkFontMgr_Mac>(fontCollection); |
| } |
| |
| #endif//defined(SK_BUILD_FOR_MAC) || defined(SK_BUILD_FOR_IOS) |