/* * This file is part of the internal font implementation. * * Copyright (C) 2006, 2007, 2008, 2009, 2010 Apple Inc. All rights reserved. * Copyright (c) 2010 Google Inc. All rights reserved. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. * */ #import "config.h" #import "FontPlatformData.h" #import "CoreTextSPI.h" #import "SharedBuffer.h" #import "WebCoreSystemInterface.h" #import #if !PLATFORM(IOS) #import #else #import "CoreGraphicsSPI.h" #import #endif namespace WebCore { // These CoreText Text Spacing feature selectors are not defined in CoreText. enum TextSpacingCTFeatureSelector { TextSpacingProportional, TextSpacingFullWidth, TextSpacingHalfWidth, TextSpacingThirdWidth, TextSpacingQuarterWidth }; FontPlatformData::FontPlatformData(CTFontRef font, float size, bool isPrinterFont, bool syntheticBold, bool syntheticOblique, FontOrientation orientation, FontWidthVariant widthVariant) : m_syntheticBold(syntheticBold) , m_syntheticOblique(syntheticOblique) , m_orientation(orientation) , m_size(size) , m_widthVariant(widthVariant) , m_font(font) , m_cgFont(adoptCF(CTFontCopyGraphicsFont(font, NULL))) , m_isColorBitmapFont(CTFontGetSymbolicTraits(font) & kCTFontTraitColorGlyphs) , m_isCompositeFontReference(CTFontGetSymbolicTraits(font) & kCTFontCompositeTrait) , m_isPrinterFont(isPrinterFont) { ASSERT_ARG(font, font); CFRetain(m_font); } #if USE(APPKIT) // FIXME: Remove this when all NSFont usage is removed. FontPlatformData::FontPlatformData(NSFont *font, float size, bool isPrinterFont, bool syntheticBold, bool syntheticOblique, FontOrientation orientation, FontWidthVariant widthVariant) : FontPlatformData((CTFontRef)font, size, isPrinterFont, syntheticBold, syntheticOblique, orientation, widthVariant) { } #endif FontPlatformData::~FontPlatformData() { if (m_font && m_font != reinterpret_cast(-1)) CFRelease(m_font); } void FontPlatformData::platformDataInit(const FontPlatformData& f) { m_font = f.m_font && f.m_font != reinterpret_cast(-1) ? static_cast(const_cast(CFRetain(f.m_font))) : f.m_font; #if PLATFORM(IOS) m_isEmoji = f.m_isEmoji; #endif m_cgFont = f.m_cgFont; m_ctFont = f.m_ctFont; m_isPrinterFont = f.m_isPrinterFont; } const FontPlatformData& FontPlatformData::platformDataAssign(const FontPlatformData& f) { m_cgFont = f.m_cgFont; #if PLATFORM(IOS) m_isEmoji = f.m_isEmoji; #endif if (m_font && m_font != reinterpret_cast(-1) && f.m_font && f.m_font != reinterpret_cast(-1) && CFEqual(m_font, f.m_font)) return *this; if (f.m_font && f.m_font != reinterpret_cast(-1)) CFRetain(f.m_font); if (m_font && m_font != reinterpret_cast(-1)) CFRelease(m_font); m_font = f.m_font; m_ctFont = f.m_ctFont; m_isPrinterFont = f.m_isPrinterFont; return *this; } bool FontPlatformData::platformIsEqual(const FontPlatformData& other) const { bool result = false; if (m_font || other.m_font) { #if PLATFORM(IOS) result = m_font && m_font != reinterpret_cast(-1) && other.m_font && other.m_font != reinterpret_cast(-1) && CFEqual(m_font, other.m_font); #if !ASSERT_DISABLED if (result) ASSERT(m_isEmoji == other.m_isEmoji); #endif #else result = m_font == other.m_font; #endif // PLATFORM(IOS) return result; } #if PLATFORM(IOS) #if !ASSERT_DISABLED if (m_cgFont == other.m_cgFont) ASSERT(m_isEmoji == other.m_isEmoji); #endif #endif // PLATFORM(IOS) return m_cgFont == other.m_cgFont; } void FontPlatformData::setFont(CTFontRef font) { ASSERT_ARG(font, font); ASSERT(m_font != reinterpret_cast(-1)); if (m_font == font) return; CFRetain(font); if (m_font) CFRelease(m_font); m_font = font; m_size = CTFontGetSize(font); m_cgFont = adoptCF(CTFontCopyGraphicsFont(font, nullptr)); CTFontSymbolicTraits traits = CTFontGetSymbolicTraits(m_font); m_isColorBitmapFont = traits & kCTFontTraitColorGlyphs; m_isCompositeFontReference = traits & kCTFontCompositeTrait; m_ctFont = nullptr; } bool FontPlatformData::roundsGlyphAdvances() const { #if USE(APPKIT) return [(NSFont *)m_font renderingMode] == NSFontAntialiasedIntegerAdvancementsRenderingMode; #else return false; #endif } bool FontPlatformData::allowsLigatures() const { #if USE(APPKIT) return ![[(NSFont *)m_font coveredCharacterSet] characterIsMember:'a']; #else if (!m_font) return false; RetainPtr characterSet = adoptCF(CTFontCopyCharacterSet(ctFont())); return !(characterSet.get() && CFCharacterSetIsCharacterMember(characterSet.get(), 'a')); #endif } inline int mapFontWidthVariantToCTFeatureSelector(FontWidthVariant variant) { switch(variant) { case RegularWidth: return TextSpacingProportional; case HalfWidth: return TextSpacingHalfWidth; case ThirdWidth: return TextSpacingThirdWidth; case QuarterWidth: return TextSpacingQuarterWidth; } ASSERT_NOT_REACHED(); return TextSpacingProportional; } static CFDictionaryRef createFeatureSettingDictionary(int featureTypeIdentifier, int featureSelectorIdentifier) { RetainPtr featureTypeIdentifierNumber = adoptCF(CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &featureTypeIdentifier)); RetainPtr featureSelectorIdentifierNumber = adoptCF(CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &featureSelectorIdentifier)); const void* settingKeys[] = { kCTFontFeatureTypeIdentifierKey, kCTFontFeatureSelectorIdentifierKey }; const void* settingValues[] = { featureTypeIdentifierNumber.get(), featureSelectorIdentifierNumber.get() }; return CFDictionaryCreate(kCFAllocatorDefault, settingKeys, settingValues, WTF_ARRAY_LENGTH(settingKeys), &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); } static CTFontDescriptorRef cascadeToLastResortFontDescriptor() { static CTFontDescriptorRef descriptor; if (descriptor) return descriptor; RetainPtr lastResort = adoptCF(CTFontDescriptorCreateWithNameAndSize(CFSTR("LastResort"), 0)); const void* descriptors[] = { lastResort.get() }; RetainPtr array = adoptCF(CFArrayCreate(kCFAllocatorDefault, descriptors, WTF_ARRAY_LENGTH(descriptors), &kCFTypeArrayCallBacks)); const void* keys[] = { kCTFontCascadeListAttribute }; const void* values[] = { array.get() }; RetainPtr attributes = adoptCF(CFDictionaryCreate(kCFAllocatorDefault, keys, values, WTF_ARRAY_LENGTH(keys), &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks)); descriptor = CTFontDescriptorCreateWithAttributes(attributes.get()); return descriptor; } static CTFontDescriptorRef cascadeToLastResortAndDisableSwashesFontDescriptor() { static CTFontDescriptorRef descriptor; if (descriptor) return descriptor; RetainPtr lineInitialSwashesOffSetting = adoptCF(createFeatureSettingDictionary(kSmartSwashType, kLineInitialSwashesOffSelector)); RetainPtr lineFinalSwashesOffSetting = adoptCF(createFeatureSettingDictionary(kSmartSwashType, kLineFinalSwashesOffSelector)); const void* settingDictionaries[] = { lineInitialSwashesOffSetting.get(), lineFinalSwashesOffSetting.get() }; RetainPtr featureSettings = adoptCF(CFArrayCreate(kCFAllocatorDefault, settingDictionaries, WTF_ARRAY_LENGTH(settingDictionaries), &kCFTypeArrayCallBacks)); const void* keys[] = { kCTFontFeatureSettingsAttribute }; const void* values[] = { featureSettings.get() }; RetainPtr attributes = adoptCF(CFDictionaryCreate(kCFAllocatorDefault, keys, values, WTF_ARRAY_LENGTH(keys), &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks)); descriptor = CTFontDescriptorCreateCopyWithAttributes(cascadeToLastResortFontDescriptor(), attributes.get()); return descriptor; } CTFontRef FontPlatformData::ctFont() const { if (m_ctFont) return m_ctFont.get(); ASSERT(m_cgFont.get()); #if !PLATFORM(IOS) m_ctFont = m_font; if (m_ctFont) { CTFontDescriptorRef fontDescriptor; RetainPtr postScriptName = adoptCF(CTFontCopyPostScriptName(m_ctFont.get())); // Hoefler Text Italic has line-initial and -final swashes enabled by default, so disable them. if (CFEqual(postScriptName.get(), CFSTR("HoeflerText-Italic")) || CFEqual(postScriptName.get(), CFSTR("HoeflerText-BlackItalic"))) fontDescriptor = cascadeToLastResortAndDisableSwashesFontDescriptor(); else fontDescriptor = cascadeToLastResortFontDescriptor(); m_ctFont = adoptCF(CTFontCreateCopyWithAttributes(m_ctFont.get(), m_size, 0, fontDescriptor)); } else m_ctFont = adoptCF(CTFontCreateWithGraphicsFont(m_cgFont.get(), m_size, 0, cascadeToLastResortFontDescriptor())); #else // Apple Color Emoji size is adjusted (and then re-adjusted by Core Text) and capped. CGFloat size = !m_isEmoji ? m_size : m_size <= 15 ? 4 * (m_size + 2) / static_cast(5) : 16; CTFontDescriptorRef fontDescriptor; const char* postScriptName = CGFontGetPostScriptName(m_cgFont.get()); if (postScriptName && (!strcmp(postScriptName, "HoeflerText-Italic") || !strcmp(postScriptName, "HoeflerText-BlackItalic"))) fontDescriptor = cascadeToLastResortAndDisableSwashesFontDescriptor(); else fontDescriptor = cascadeToLastResortFontDescriptor(); m_ctFont = adoptCF(CTFontCreateWithGraphicsFont(m_cgFont.get(), size, 0, fontDescriptor)); #endif // !PLATFORM(IOS) if (m_widthVariant != RegularWidth) { int featureTypeValue = kTextSpacingType; int featureSelectorValue = mapFontWidthVariantToCTFeatureSelector(m_widthVariant); RetainPtr sourceDescriptor = adoptCF(CTFontCopyFontDescriptor(m_ctFont.get())); RetainPtr featureType = adoptCF(CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &featureTypeValue)); RetainPtr featureSelector = adoptCF(CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &featureSelectorValue)); RetainPtr newDescriptor = adoptCF(CTFontDescriptorCreateCopyWithFeature(sourceDescriptor.get(), featureType.get(), featureSelector.get())); RetainPtr newFont = adoptCF(CTFontCreateWithFontDescriptor(newDescriptor.get(), m_size, 0)); if (newFont) m_ctFont = newFont; } return m_ctFont.get(); } RetainPtr FontPlatformData::objectForEqualityCheck(CTFontRef ctFont) { #if (PLATFORM(IOS) && __IPHONE_OS_VERSION_MIN_REQUIRED <= 80000) || (PLATFORM(MAC) && __MAC_OS_X_VERSION_MIN_REQUIRED <= 101000) auto fontDescriptor = adoptCF(CTFontCopyFontDescriptor(ctFont)); // FIXME: https://bugs.webkit.org/show_bug.cgi?id=138683 This is a shallow pointer compare for web fonts // because the URL contains the address of the font. This means we might erroneously get false negatives. RetainPtr url = adoptCF(static_cast(CTFontDescriptorCopyAttribute(fontDescriptor.get(), kCTFontReferenceURLAttribute))); ASSERT(CFGetTypeID(url.get()) == CFURLGetTypeID()); return url; #else return adoptCF(CTFontCopyGraphicsFont(ctFont, 0)); #endif } RetainPtr FontPlatformData::objectForEqualityCheck() const { return objectForEqualityCheck(ctFont()); } PassRefPtr FontPlatformData::openTypeTable(uint32_t table) const { if (RetainPtr data = adoptCF(CGFontCopyTableForTag(cgFont(), table))) return SharedBuffer::wrapCFData(data.get()); return nullptr; } #ifndef NDEBUG String FontPlatformData::description() const { RetainPtr cgFontDescription = adoptCF(CFCopyDescription(cgFont())); return String(cgFontDescription.get()) + " " + String::number(m_size) + (m_syntheticBold ? " synthetic bold" : "") + (m_syntheticOblique ? " synthetic oblique" : "") + (m_orientation ? " vertical orientation" : ""); } #endif } // namespace WebCore