From cb524b7cc183fcd78d26b5b001c79584e1f401c9 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Tue, 4 Jun 2019 16:03:08 +0530 Subject: [PATCH] Port the macOS headless plugin to work with Qt 5.12 --- .../headless/coretext_fontdatabase-new.mm | 763 ++++++++++++++ .../headless/coretext_fontdatabase-old.mm | 949 +++++++++++++++++ src/calibre/headless/coretext_fontdatabase.mm | 955 +----------------- src/calibre/headless/headless_integration.cpp | 4 + 4 files changed, 1726 insertions(+), 945 deletions(-) create mode 100644 src/calibre/headless/coretext_fontdatabase-new.mm create mode 100644 src/calibre/headless/coretext_fontdatabase-old.mm diff --git a/src/calibre/headless/coretext_fontdatabase-new.mm b/src/calibre/headless/coretext_fontdatabase-new.mm new file mode 100644 index 0000000000..acaae860be --- /dev/null +++ b/src/calibre/headless/coretext_fontdatabase-new.mm @@ -0,0 +1,763 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include + +#include + +#if defined(Q_OS_OSX) +#import +#import +#elif defined(QT_PLATFORM_UIKIT) +#import +#endif + +#include +#include +#if QT_CONFIG(settings) +#include +#endif +#include +#ifndef QT_NO_FREETYPE +#include +#endif + +QT_BEGIN_NAMESPACE + +// this could become a list of all languages used for each writing +// system, instead of using the single most common language. +static const char *languageForWritingSystem[] = { + 0, // Any + "en", // Latin + "el", // Greek + "ru", // Cyrillic + "hy", // Armenian + "he", // Hebrew + "ar", // Arabic + "syr", // Syriac + "div", // Thaana + "hi", // Devanagari + "bn", // Bengali + "pa", // Gurmukhi + "gu", // Gujarati + "or", // Oriya + "ta", // Tamil + "te", // Telugu + "kn", // Kannada + "ml", // Malayalam + "si", // Sinhala + "th", // Thai + "lo", // Lao + "bo", // Tibetan + "my", // Myanmar + "ka", // Georgian + "km", // Khmer + "zh-Hans", // SimplifiedChinese + "zh-Hant", // TraditionalChinese + "ja", // Japanese + "ko", // Korean + "vi", // Vietnamese + 0, // Symbol + "sga", // Ogham + "non", // Runic + "man" // N'Ko +}; +enum { LanguageCount = sizeof(languageForWritingSystem) / sizeof(const char *) }; + +#ifdef Q_OS_OSX +static NSInteger languageMapSort(id obj1, id obj2, void *context) +{ + NSArray *map1 = reinterpret_cast *>(obj1); + NSArray *map2 = reinterpret_cast *>(obj2); + NSArray *languages = reinterpret_cast *>(context); + + NSString *lang1 = [map1 objectAtIndex:0]; + NSString *lang2 = [map2 objectAtIndex:0]; + + return [languages indexOfObject:lang1] - [languages indexOfObject:lang2]; +} +#endif + +QCoreTextFontDatabase::QCoreTextFontDatabase() + : m_hasPopulatedAliases(false) +{ +} + +QCoreTextFontDatabase::~QCoreTextFontDatabase() +{ + for (CTFontDescriptorRef ref : qAsConst(m_systemFontDescriptors)) + CFRelease(ref); +} + +void QCoreTextFontDatabase::populateFontDatabase() +{ + QCFType familyNames = CTFontManagerCopyAvailableFontFamilyNames(); + for (NSString *familyName in familyNames.as()) + QPlatformFontDatabase::registerFontFamily(QString::fromNSString(familyName)); + + // Force creating the theme fonts to get the descriptors in m_systemFontDescriptors + if (m_themeFonts.isEmpty()) + (void)themeFonts(); + + Q_FOREACH (CTFontDescriptorRef fontDesc, m_systemFontDescriptors) + populateFromDescriptor(fontDesc); + + Q_ASSERT(!m_hasPopulatedAliases); +} + +bool QCoreTextFontDatabase::populateFamilyAliases() +{ +#if defined(Q_OS_MACOS) + if (m_hasPopulatedAliases) + return false; + + QCFType familyNames = CTFontManagerCopyAvailableFontFamilyNames(); + for (NSString *familyName in familyNames.as()) { + NSFontManager *fontManager = [NSFontManager sharedFontManager]; + NSString *localizedFamilyName = [fontManager localizedNameForFamily:familyName face:nil]; + if (![localizedFamilyName isEqual:familyName]) { + QPlatformFontDatabase::registerAliasToFontFamily( + QString::fromNSString(familyName), + QString::fromNSString(localizedFamilyName)); + } + } + m_hasPopulatedAliases = true; + return true; +#else + return false; +#endif +} + +void QCoreTextFontDatabase::populateFamily(const QString &familyName) +{ + QCFType attributes = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); + CFDictionaryAddValue(attributes, kCTFontFamilyNameAttribute, QCFString(familyName)); + QCFType nameOnlyDescriptor = CTFontDescriptorCreateWithAttributes(attributes); + + // A single family might match several different fonts with different styles eg. + QCFType matchingFonts = (CFArrayRef) CTFontDescriptorCreateMatchingFontDescriptors(nameOnlyDescriptor, 0); + if (!matchingFonts) { + qWarning() << "QCoreTextFontDatabase: Found no matching fonts for family" << familyName; + return; + } + + const int numFonts = CFArrayGetCount(matchingFonts); + for (int i = 0; i < numFonts; ++i) + populateFromDescriptor(CTFontDescriptorRef(CFArrayGetValueAtIndex(matchingFonts, i)), familyName); +} + +void QCoreTextFontDatabase::invalidate() +{ + m_hasPopulatedAliases = false; +} + +struct FontDescription { + QCFString familyName; + QCFString styleName; + QString foundryName; + QFont::Weight weight; + QFont::Style style; + QFont::Stretch stretch; + qreal pointSize; + bool fixedPitch; + QSupportedWritingSystems writingSystems; +}; + +#ifndef QT_NO_DEBUG_STREAM +Q_DECL_UNUSED static inline QDebug operator<<(QDebug debug, const FontDescription &fd) +{ + QDebugStateSaver saver(debug); + return debug.nospace() << "FontDescription(" + << "familyName=" << QString(fd.familyName) + << ", styleName=" << QString(fd.styleName) + << ", foundry=" << fd.foundryName + << ", weight=" << fd.weight + << ", style=" << fd.style + << ", stretch=" << fd.stretch + << ", pointSize=" << fd.pointSize + << ", fixedPitch=" << fd.fixedPitch + << ", writingSystems=" << fd.writingSystems + << ")"; +} +#endif + +static void getFontDescription(CTFontDescriptorRef font, FontDescription *fd) +{ + QCFType styles = (CFDictionaryRef) CTFontDescriptorCopyAttribute(font, kCTFontTraitsAttribute); + + fd->foundryName = QStringLiteral("CoreText"); + fd->familyName = (CFStringRef) CTFontDescriptorCopyAttribute(font, kCTFontFamilyNameAttribute); + fd->styleName = (CFStringRef)CTFontDescriptorCopyAttribute(font, kCTFontStyleNameAttribute); + fd->weight = QFont::Normal; + fd->style = QFont::StyleNormal; + fd->stretch = QFont::Unstretched; + fd->fixedPitch = false; + + if (QCFType tempFont = CTFontCreateWithFontDescriptor(font, 0.0, 0)) { + uint tag = MAKE_TAG('O', 'S', '/', '2'); + CTFontRef tempFontRef = tempFont; + void *userData = reinterpret_cast(&tempFontRef); + uint length = 128; + QVarLengthArray os2Table(length); + if (QCoreTextFontEngine::ct_getSfntTable(userData, tag, os2Table.data(), &length) && length >= 86) { + if (length > uint(os2Table.length())) { + os2Table.resize(length); + if (!QCoreTextFontEngine::ct_getSfntTable(userData, tag, os2Table.data(), &length)) + Q_UNREACHABLE(); + Q_ASSERT(length >= 86); + } + quint32 unicodeRange[4] = { + qFromBigEndian(os2Table.data() + 42), + qFromBigEndian(os2Table.data() + 46), + qFromBigEndian(os2Table.data() + 50), + qFromBigEndian(os2Table.data() + 54) + }; + quint32 codePageRange[2] = { + qFromBigEndian(os2Table.data() + 78), + qFromBigEndian(os2Table.data() + 82) + }; + fd->writingSystems = QPlatformFontDatabase::writingSystemsFromTrueTypeBits(unicodeRange, codePageRange); + } + } + + if (styles) { + if (CFNumberRef weightValue = (CFNumberRef) CFDictionaryGetValue(styles, kCTFontWeightTrait)) { + double normalizedWeight; + if (CFNumberGetValue(weightValue, kCFNumberFloat64Type, &normalizedWeight)) + fd->weight = QCoreTextFontEngine::qtWeightFromCFWeight(float(normalizedWeight)); + } + if (CFNumberRef italic = (CFNumberRef) CFDictionaryGetValue(styles, kCTFontSlantTrait)) { + double d; + if (CFNumberGetValue(italic, kCFNumberDoubleType, &d)) { + if (d > 0.0) + fd->style = QFont::StyleItalic; + } + } + if (CFNumberRef symbolic = (CFNumberRef) CFDictionaryGetValue(styles, kCTFontSymbolicTrait)) { + int d; + if (CFNumberGetValue(symbolic, kCFNumberSInt32Type, &d)) { + if (d & kCTFontMonoSpaceTrait) + fd->fixedPitch = true; + if (d & kCTFontExpandedTrait) + fd->stretch = QFont::Expanded; + else if (d & kCTFontCondensedTrait) + fd->stretch = QFont::Condensed; + } + } + } + + if (QCFType size = (CFNumberRef) CTFontDescriptorCopyAttribute(font, kCTFontSizeAttribute)) { + if (CFNumberIsFloatType(size)) { + double d; + CFNumberGetValue(size, kCFNumberDoubleType, &d); + fd->pointSize = d; + } else { + int i; + CFNumberGetValue(size, kCFNumberIntType, &i); + fd->pointSize = i; + } + } + + if (QCFType languages = (CFArrayRef) CTFontDescriptorCopyAttribute(font, kCTFontLanguagesAttribute)) { + CFIndex length = CFArrayGetCount(languages); + for (int i = 1; i < LanguageCount; ++i) { + if (!languageForWritingSystem[i]) + continue; + QCFString lang = CFStringCreateWithCString(NULL, languageForWritingSystem[i], kCFStringEncodingASCII); + if (CFArrayContainsValue(languages, CFRangeMake(0, length), lang)) + fd->writingSystems.setSupported(QFontDatabase::WritingSystem(i)); + } + } +} + +void QCoreTextFontDatabase::populateFromDescriptor(CTFontDescriptorRef font, const QString &familyName) +{ + FontDescription fd; + getFontDescription(font, &fd); + + // Note: The familyName we are registering, and the family name of the font descriptor, may not + // match, as CTFontDescriptorCreateMatchingFontDescriptors will return descriptors for replacement + // fonts if a font family does not have any fonts available on the system. + QString family = !familyName.isNull() ? familyName : static_cast(fd.familyName); + + CFRetain(font); + QPlatformFontDatabase::registerFont(family, fd.styleName, fd.foundryName, fd.weight, fd.style, fd.stretch, + true /* antialiased */, true /* scalable */, 0 /* pixelSize, ignored as font is scalable */, + fd.fixedPitch, fd.writingSystems, (void *)font); +} + +static NSString * const kQtFontDataAttribute = @"QtFontDataAttribute"; + +template +T *descriptorAttribute(CTFontDescriptorRef descriptor, CFStringRef name) +{ + return [static_cast(CTFontDescriptorCopyAttribute(descriptor, name)) autorelease]; +} + +void QCoreTextFontDatabase::releaseHandle(void *handle) +{ + CTFontDescriptorRef descriptor = static_cast(handle); + if (NSValue *fontDataValue = descriptorAttribute(descriptor, (CFStringRef)kQtFontDataAttribute)) { + QByteArray *fontData = static_cast(fontDataValue.pointerValue); + delete fontData; + } + CFRelease(descriptor); +} + +extern CGAffineTransform qt_transform_from_fontdef(const QFontDef &fontDef); + +template <> +QFontEngine *QCoreTextFontDatabaseEngineFactory::fontEngine(const QFontDef &fontDef, void *usrPtr) +{ + QCFType descriptor = QCFType::constructFromGet( + static_cast(usrPtr)); + + // CoreText will sometimes invalidate information in font descriptors that refer + // to system fonts in certain function calls or application states. While the descriptor + // looks the same from the outside, some internal plumbing is different, causing the results + // of creating CTFonts from those descriptors unreliable. The work-around for this + // is to copy the attributes of those descriptors each time we make a new CTFont + // from them instead of referring to the original, as that may trigger the CoreText bug. + if (m_systemFontDescriptors.contains(descriptor)) { + QCFType attributes = CTFontDescriptorCopyAttributes(descriptor); + descriptor = CTFontDescriptorCreateWithAttributes(attributes); + } + + // Since we do not pass in the destination DPI to CoreText when making + // the font, we need to pass in a point size which is scaled to include + // the DPI. The default DPI for the screen is 72, thus the scale factor + // is destinationDpi / 72, but since pixelSize = pointSize / 72 * dpi, + // the pixelSize is actually the scaled point size for the destination + // DPI, and we can use that directly. + qreal scaledPointSize = fontDef.pixelSize; + + CGAffineTransform matrix = qt_transform_from_fontdef(fontDef); + if (QCFType font = CTFontCreateWithFontDescriptor(descriptor, scaledPointSize, &matrix)) + return new QCoreTextFontEngine(font, fontDef); + + return nullptr; +} + +#ifndef QT_NO_FREETYPE +template <> +QFontEngine *QCoreTextFontDatabaseEngineFactory::fontEngine(const QFontDef &fontDef, void *usrPtr) +{ + CTFontDescriptorRef descriptor = static_cast(usrPtr); + + if (NSValue *fontDataValue = descriptorAttribute(descriptor, (CFStringRef)kQtFontDataAttribute)) { + QByteArray *fontData = static_cast(fontDataValue.pointerValue); + return QFontEngineFT::create(*fontData, fontDef.pixelSize, + static_cast(fontDef.hintingPreference)); + } else if (NSURL *url = descriptorAttribute(descriptor, kCTFontURLAttribute)) { + Q_ASSERT(url.fileURL); + QFontEngine::FaceId faceId; + faceId.filename = QString::fromNSString(url.path).toUtf8(); + return QFontEngineFT::create(fontDef, faceId); + } + Q_UNREACHABLE(); +} +#endif + +template +QFontEngine *QCoreTextFontDatabaseEngineFactory::fontEngine(const QByteArray &fontData, qreal pixelSize, QFont::HintingPreference hintingPreference) +{ + return T::create(fontData, pixelSize, hintingPreference); +} + +// Explicitly instantiate so that we don't need the plugin to involve FreeType +template class QCoreTextFontDatabaseEngineFactory; +#ifndef QT_NO_FREETYPE +template class QCoreTextFontDatabaseEngineFactory; +#endif + +QFont::StyleHint styleHintFromNSString(NSString *style) +{ + if ([style isEqual: @"sans-serif"]) + return QFont::SansSerif; + else if ([style isEqual: @"monospace"]) + return QFont::Monospace; + else if ([style isEqual: @"cursive"]) + return QFont::Cursive; + else if ([style isEqual: @"serif"]) + return QFont::Serif; + else if ([style isEqual: @"fantasy"]) + return QFont::Fantasy; + else // if ([style isEqual: @"default"]) + return QFont::AnyStyle; +} + +#ifdef Q_OS_OSX +static QString familyNameFromPostScriptName(NSString *psName) +{ + QCFType fontDescriptor = (CTFontDescriptorRef) CTFontDescriptorCreateWithNameAndSize((CFStringRef)psName, 12.0); + QCFString familyName = (CFStringRef) CTFontDescriptorCopyAttribute(fontDescriptor, kCTFontFamilyNameAttribute); + QString name = QString::fromCFString(familyName); + if (name.isEmpty()) + qWarning() << "QCoreTextFontDatabase: Failed to resolve family name for PostScript name " << QString::fromCFString((CFStringRef)psName); + + return name; +} +#endif + +static void addExtraFallbacks(QStringList *fallbackList) +{ +#if defined(Q_OS_MACOS) + // Since we are only returning a list of default fonts for the current language, we do not + // cover all unicode completely. This was especially an issue for some of the common script + // symbols such as mathematical symbols, currency or geometric shapes. To minimize the risk + // of missing glyphs, we add Arial Unicode MS as a final fail safe, since this covers most + // of Unicode 2.1. + if (!fallbackList->contains(QStringLiteral("Arial Unicode MS"))) + fallbackList->append(QStringLiteral("Arial Unicode MS")); + // Since some symbols (specifically Braille) are not in Arial Unicode MS, we + // add Apple Symbols to cover those too. + if (!fallbackList->contains(QStringLiteral("Apple Symbols"))) + fallbackList->append(QStringLiteral("Apple Symbols")); +#else + Q_UNUSED(fallbackList) +#endif +} + +QStringList QCoreTextFontDatabase::fallbacksForFamily(const QString &family, QFont::Style style, QFont::StyleHint styleHint, QChar::Script script) const +{ + Q_UNUSED(style); + Q_UNUSED(script); + + QMacAutoReleasePool pool; + + static QHash fallbackLists; + + if (!family.isEmpty()) { + QCFType attributes = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); + CFDictionaryAddValue(attributes, kCTFontFamilyNameAttribute, QCFString(family)); + if (QCFType fontDescriptor = CTFontDescriptorCreateWithAttributes(attributes)) { + if (QCFType font = CTFontCreateWithFontDescriptor(fontDescriptor, 12.0, 0)) { + NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; + NSArray *languages = [defaults stringArrayForKey: @"AppleLanguages"]; + + QCFType cascadeList = (CFArrayRef) CTFontCopyDefaultCascadeListForLanguages(font, (CFArrayRef) languages); + if (cascadeList) { + QStringList fallbackList; + const int numCascades = CFArrayGetCount(cascadeList); + for (int i = 0; i < numCascades; ++i) { + CTFontDescriptorRef fontFallback = (CTFontDescriptorRef) CFArrayGetValueAtIndex(cascadeList, i); + QCFString fallbackFamilyName = (CFStringRef) CTFontDescriptorCopyAttribute(fontFallback, kCTFontFamilyNameAttribute); + fallbackList.append(QString::fromCFString(fallbackFamilyName)); + } + + addExtraFallbacks(&fallbackList); + extern QStringList qt_sort_families_by_writing_system(QChar::Script, const QStringList &); + fallbackList = qt_sort_families_by_writing_system(script, fallbackList); + + return fallbackList; + } + } + } + } + + // We were not able to find a fallback for the specific family, + // so we fall back to the stylehint. + + static const QString styleLookupKey = QString::fromLatin1(".QFontStyleHint_%1"); + + static bool didPopulateStyleFallbacks = false; + if (!didPopulateStyleFallbacks) { +#if defined(Q_OS_MACX) + NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; + NSArray *languages = [defaults stringArrayForKey:@"AppleLanguages"]; + + NSDictionary *fallbackDict = [NSDictionary dictionaryWithContentsOfFile:@"/System/Library/Frameworks/ApplicationServices.framework/Frameworks/CoreText.framework/Resources/DefaultFontFallbacks.plist"]; + + for (NSString *style in [fallbackDict allKeys]) { + NSArray *list = [fallbackDict valueForKey:style]; + QFont::StyleHint fallbackStyleHint = styleHintFromNSString(style); + QStringList fallbackList; + for (id item in list) { + // sort the array based on system language preferences + if ([item isKindOfClass:[NSArray class]]) { + NSArray *langs = [reinterpret_cast(item) + sortedArrayUsingFunction:languageMapSort context:languages]; + for (NSArray *map in langs) + fallbackList.append(familyNameFromPostScriptName([map objectAtIndex:1])); + } + else if ([item isKindOfClass: [NSString class]]) + fallbackList.append(familyNameFromPostScriptName(item)); + } + + fallbackList.append(QLatin1String("Apple Color Emoji")); + + addExtraFallbacks(&fallbackList); + fallbackLists[styleLookupKey.arg(fallbackStyleHint)] = fallbackList; + } +#else + QStringList staticFallbackList; + staticFallbackList << QString::fromLatin1("Helvetica,Apple Color Emoji,Geeza Pro,Arial Hebrew,Thonburi,Kailasa" + "Hiragino Kaku Gothic ProN,.Heiti J,Apple SD Gothic Neo,.Heiti K,Heiti SC,Heiti TC" + "Bangla Sangam MN,Devanagari Sangam MN,Gujarati Sangam MN,Gurmukhi MN,Kannada Sangam MN" + "Malayalam Sangam MN,Oriya Sangam MN,Sinhala Sangam MN,Tamil Sangam MN,Telugu Sangam MN" + "Euphemia UCAS,.PhoneFallback").split(QLatin1String(",")); + + for (int i = QFont::Helvetica; i <= QFont::Fantasy; ++i) + fallbackLists[styleLookupKey.arg(i)] = staticFallbackList; +#endif + + didPopulateStyleFallbacks = true; + } + + Q_ASSERT(!fallbackLists.isEmpty()); + return fallbackLists[styleLookupKey.arg(styleHint)]; +} + +QStringList QCoreTextFontDatabase::addApplicationFont(const QByteArray &fontData, const QString &fileName) +{ + QCFType fonts; + + if (!fontData.isEmpty()) { + QCFType fontDataReference = fontData.toRawCFData(); + if (QCFType descriptor = CTFontManagerCreateFontDescriptorFromData(fontDataReference)) { + // There's no way to get the data back out of a font descriptor created with + // CTFontManagerCreateFontDescriptorFromData, so we attach the data manually. + NSDictionary *attributes = @{ kQtFontDataAttribute : [NSValue valueWithPointer:new QByteArray(fontData)] }; + descriptor = CTFontDescriptorCreateCopyWithAttributes(descriptor, (CFDictionaryRef)attributes); + CFMutableArrayRef array = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks); + CFArrayAppendValue(array, descriptor); + fonts = array; + } + } else { + QCFType fontURL = QUrl::fromLocalFile(fileName).toCFURL(); + fonts = CTFontManagerCreateFontDescriptorsFromURL(fontURL); + } + + if (!fonts) + return QStringList(); + + QStringList families; + const int numFonts = CFArrayGetCount(fonts); + for (int i = 0; i < numFonts; ++i) { + CTFontDescriptorRef fontDescriptor = CTFontDescriptorRef(CFArrayGetValueAtIndex(fonts, i)); + populateFromDescriptor(fontDescriptor); + QCFType familyName = CFStringRef(CTFontDescriptorCopyAttribute(fontDescriptor, kCTFontFamilyNameAttribute)); + families.append(QString::fromCFString(familyName)); + } + + // Note: We don't do font matching via CoreText for application fonts, so we don't + // need to enable font matching for them via CTFontManagerEnableFontDescriptors. + + return families; +} + +bool QCoreTextFontDatabase::isPrivateFontFamily(const QString &family) const +{ + if (family.startsWith(QLatin1Char('.')) || family == QLatin1String("LastResort")) + return true; + + return QPlatformFontDatabase::isPrivateFontFamily(family); +} + +static CTFontUIFontType fontTypeFromTheme(QPlatformTheme::Font f) +{ + switch (f) { + case QPlatformTheme::SystemFont: + return kCTFontUIFontSystem; + + case QPlatformTheme::MenuFont: + case QPlatformTheme::MenuBarFont: + case QPlatformTheme::MenuItemFont: + return kCTFontUIFontMenuItem; + + case QPlatformTheme::MessageBoxFont: + return kCTFontUIFontEmphasizedSystem; + + case QPlatformTheme::LabelFont: + return kCTFontUIFontSystem; + + case QPlatformTheme::TipLabelFont: + return kCTFontUIFontToolTip; + + case QPlatformTheme::StatusBarFont: + return kCTFontUIFontSystem; + + case QPlatformTheme::TitleBarFont: + return kCTFontUIFontWindowTitle; + + case QPlatformTheme::MdiSubWindowTitleFont: + return kCTFontUIFontSystem; + + case QPlatformTheme::DockWidgetTitleFont: + return kCTFontUIFontSmallSystem; + + case QPlatformTheme::PushButtonFont: + return kCTFontUIFontPushButton; + + case QPlatformTheme::CheckBoxFont: + case QPlatformTheme::RadioButtonFont: + return kCTFontUIFontSystem; + + case QPlatformTheme::ToolButtonFont: + return kCTFontUIFontSmallToolbar; + + case QPlatformTheme::ItemViewFont: + return kCTFontUIFontSystem; + + case QPlatformTheme::ListViewFont: + return kCTFontUIFontViews; + + case QPlatformTheme::HeaderViewFont: + return kCTFontUIFontSmallSystem; + + case QPlatformTheme::ListBoxFont: + return kCTFontUIFontViews; + + case QPlatformTheme::ComboMenuItemFont: + return kCTFontUIFontSystem; + + case QPlatformTheme::ComboLineEditFont: + return kCTFontUIFontViews; + + case QPlatformTheme::SmallFont: + return kCTFontUIFontSmallSystem; + + case QPlatformTheme::MiniFont: + return kCTFontUIFontMiniSystem; + + case QPlatformTheme::FixedFont: + return kCTFontUIFontUserFixedPitch; + + default: + return kCTFontUIFontSystem; + } +} + +static CTFontDescriptorRef fontDescriptorFromTheme(QPlatformTheme::Font f) +{ +#if defined(QT_PLATFORM_UIKIT) + // Use Dynamic Type to resolve theme fonts if possible, to get + // correct font sizes and style based on user configuration. + NSString *textStyle = 0; + switch (f) { + case QPlatformTheme::TitleBarFont: + case QPlatformTheme::HeaderViewFont: + textStyle = UIFontTextStyleHeadline; + break; + case QPlatformTheme::MdiSubWindowTitleFont: + textStyle = UIFontTextStyleSubheadline; + break; + case QPlatformTheme::TipLabelFont: + case QPlatformTheme::SmallFont: + textStyle = UIFontTextStyleFootnote; + break; + case QPlatformTheme::MiniFont: + textStyle = UIFontTextStyleCaption2; + break; + case QPlatformTheme::FixedFont: + // Fall back to regular code path, as iOS doesn't provide + // an appropriate text style for this theme font. + break; + default: + textStyle = UIFontTextStyleBody; + break; + } + + if (textStyle) { + UIFontDescriptor *desc = [UIFontDescriptor preferredFontDescriptorWithTextStyle:textStyle]; + return static_cast(CFBridgingRetain(desc)); + } +#endif // Q_OS_IOS, Q_OS_TVOS, Q_OS_WATCHOS + + // OSX default case and iOS fallback case + CTFontUIFontType fontType = fontTypeFromTheme(f); + QCFType ctFont = CTFontCreateUIFontForLanguage(fontType, 0.0, NULL); + return CTFontCopyFontDescriptor(ctFont); +} + +const QHash &QCoreTextFontDatabase::themeFonts() const +{ + if (m_themeFonts.isEmpty()) { + for (long f = QPlatformTheme::SystemFont; f < QPlatformTheme::NFonts; f++) { + QPlatformTheme::Font ft = static_cast(f); + m_themeFonts.insert(ft, themeFont(ft)); + } + } + + return m_themeFonts; +} + +QFont *QCoreTextFontDatabase::themeFont(QPlatformTheme::Font f) const +{ + CTFontDescriptorRef fontDesc = fontDescriptorFromTheme(f); + FontDescription fd; + getFontDescription(fontDesc, &fd); + + if (!m_systemFontDescriptors.contains(fontDesc)) + m_systemFontDescriptors.insert(fontDesc); + else + CFRelease(fontDesc); + + QFont *font = new QFont(fd.familyName, fd.pointSize, fd.weight, fd.style == QFont::StyleItalic); + return font; +} + +QFont QCoreTextFontDatabase::defaultFont() const +{ + if (defaultFontName.isEmpty()) { + QCFType font = CTFontCreateUIFontForLanguage(kCTFontUIFontSystem, 12.0, NULL); + defaultFontName = (QString) QCFString(CTFontCopyFullName(font)); + } + + return QFont(defaultFontName); +} + +bool QCoreTextFontDatabase::fontsAlwaysScalable() const +{ + return true; +} + +QList QCoreTextFontDatabase::standardSizes() const +{ + QList ret; + static const unsigned short standard[] = + { 9, 10, 11, 12, 13, 14, 18, 24, 36, 48, 64, 72, 96, 144, 288, 0 }; + ret.reserve(int(sizeof(standard) / sizeof(standard[0]))); + const unsigned short *sizes = standard; + while (*sizes) ret << *sizes++; + return ret; +} + +QT_END_NAMESPACE + diff --git a/src/calibre/headless/coretext_fontdatabase-old.mm b/src/calibre/headless/coretext_fontdatabase-old.mm new file mode 100644 index 0000000000..50fb04c365 --- /dev/null +++ b/src/calibre/headless/coretext_fontdatabase-old.mm @@ -0,0 +1,949 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL21$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qglobal.h" + +#include + +#if defined(Q_OS_MACX) +#import +#import +#elif defined(Q_OS_IOS) +#import +#endif + +#include +#include +#include +#include +#ifndef QT_NO_FREETYPE +#include +#endif + +QT_BEGIN_NAMESPACE + +// this could become a list of all languages used for each writing +// system, instead of using the single most common language. +static const char *languageForWritingSystem[] = { + 0, // Any + "en", // Latin + "el", // Greek + "ru", // Cyrillic + "hy", // Armenian + "he", // Hebrew + "ar", // Arabic + "syr", // Syriac + "div", // Thaana + "hi", // Devanagari + "bn", // Bengali + "pa", // Gurmukhi + "gu", // Gujarati + "or", // Oriya + "ta", // Tamil + "te", // Telugu + "kn", // Kannada + "ml", // Malayalam + "si", // Sinhala + "th", // Thai + "lo", // Lao + "bo", // Tibetan + "my", // Myanmar + "ka", // Georgian + "km", // Khmer + "zh-Hans", // SimplifiedChinese + "zh-Hant", // TraditionalChinese + "ja", // Japanese + "ko", // Korean + "vi", // Vietnamese + 0, // Symbol + "sga", // Ogham + "non", // Runic + "man" // N'Ko +}; +enum { LanguageCount = sizeof(languageForWritingSystem) / sizeof(const char *) }; + +#ifdef Q_OS_OSX +static NSInteger languageMapSort(id obj1, id obj2, void *context) +{ + NSArray *map1 = (NSArray *) obj1; + NSArray *map2 = (NSArray *) obj2; + NSArray *languages = (NSArray *) context; + + NSString *lang1 = [map1 objectAtIndex: 0]; + NSString *lang2 = [map2 objectAtIndex: 0]; + + return [languages indexOfObject: lang1] - [languages indexOfObject: lang2]; +} +#endif + +QCoreTextFontDatabase::QCoreTextFontDatabase(bool useFreeType) +#ifndef QT_NO_FREETYPE + : m_useFreeType(useFreeType) +#endif +{ + Q_UNUSED(useFreeType) + QCoreTextFontEngine::defaultGlyphFormat = QFontEngine::Format_A8; +} + +QCoreTextFontDatabase::~QCoreTextFontDatabase() +{ + foreach (CTFontDescriptorRef ref, m_systemFontDescriptors) + CFRelease(ref); +} + +static CFArrayRef availableFamilyNames() +{ +#if defined(Q_OS_OSX) + return CTFontManagerCopyAvailableFontFamilyNames(); +#elif defined(Q_OS_IOS) + return (CFArrayRef) [[UIFont familyNames] retain]; +#endif +} + +void QCoreTextFontDatabase::populateFontDatabase() +{ + // The caller (QFontDB) expects the db to be populate only with system fonts, so we need + // to make sure that any previously registered app fonts become invisible. + removeApplicationFonts(); + + QCFType familyNames = availableFamilyNames(); + const int numberOfFamilies = CFArrayGetCount(familyNames); + for (int i = 0; i < numberOfFamilies; ++i) { + CFStringRef familyNameRef = (CFStringRef) CFArrayGetValueAtIndex(familyNames, i); + QString familyName = QCFString::toQString(familyNameRef); + + // Don't populate internal fonts + if (familyName.startsWith(QLatin1Char('.')) || familyName == QLatin1String("LastResort")) + continue; + +#if defined(Q_OS_IOS) || defined(Q_OS_TVOS) + // Skip font families with no corresponding fonts + if (![UIFont fontNamesForFamilyName:(NSString*)familyNameRef].count) + continue; +#endif + + QPlatformFontDatabase::registerFontFamily(familyName); + +#if defined(Q_OS_OSX) + QString localizedFamilyName = QString::fromNSString([[NSFontManager sharedFontManager] localizedNameForFamily:(NSString*)familyNameRef face:nil]); + if (familyName != localizedFamilyName) + QPlatformFontDatabase::registerAliasToFontFamily(familyName, localizedFamilyName); +#endif + } + + // Force creating the theme fonts to get the descriptors in m_systemFontDescriptors + if (m_themeFonts.isEmpty()) + (void)themeFonts(); + + Q_FOREACH (CTFontDescriptorRef fontDesc, m_systemFontDescriptors) + populateFromDescriptor(fontDesc); +} + +void QCoreTextFontDatabase::populateFamily(const QString &familyName) +{ + QCFType attributes = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); + CFDictionaryAddValue(attributes, kCTFontFamilyNameAttribute, QCFString(familyName)); + QCFType nameOnlyDescriptor = CTFontDescriptorCreateWithAttributes(attributes); + + // A single family might match several different fonts with different styles eg. + QCFType matchingFonts = (CFArrayRef) CTFontDescriptorCreateMatchingFontDescriptors(nameOnlyDescriptor, 0); + if (!matchingFonts) { + qWarning() << "QCoreTextFontDatabase: Found no matching fonts for family" << familyName; + return; + } + + const int numFonts = CFArrayGetCount(matchingFonts); + for (int i = 0; i < numFonts; ++i) + populateFromDescriptor(CTFontDescriptorRef(CFArrayGetValueAtIndex(matchingFonts, i))); +} + +struct FontDescription { + QCFString familyName; + QCFString styleName; + QString foundryName; + QFont::Weight weight; + QFont::Style style; + QFont::Stretch stretch; + int pixelSize; + bool fixedPitch; + QSupportedWritingSystems writingSystems; +}; + +static void getFontDescription(CTFontDescriptorRef font, FontDescription *fd) +{ + QCFType styles = (CFDictionaryRef) CTFontDescriptorCopyAttribute(font, kCTFontTraitsAttribute); + + fd->foundryName = QStringLiteral("CoreText"); + fd->familyName = (CFStringRef) CTFontDescriptorCopyAttribute(font, kCTFontFamilyNameAttribute); + fd->styleName = (CFStringRef)CTFontDescriptorCopyAttribute(font, kCTFontStyleNameAttribute); + fd->weight = QFont::Normal; + fd->style = QFont::StyleNormal; + fd->stretch = QFont::Unstretched; + fd->fixedPitch = false; + + if (QCFType tempFont = CTFontCreateWithFontDescriptor(font, 0.0, 0)) { + uint tag = MAKE_TAG('O', 'S', '/', '2'); + CTFontRef tempFontRef = tempFont; + void *userData = reinterpret_cast(&tempFontRef); + uint length = 128; + QVarLengthArray os2Table(length); + if (QCoreTextFontEngine::ct_getSfntTable(userData, tag, os2Table.data(), &length) && length >= 86) { + if (length > uint(os2Table.length())) { + os2Table.resize(length); + if (!QCoreTextFontEngine::ct_getSfntTable(userData, tag, os2Table.data(), &length)) + Q_UNREACHABLE(); + Q_ASSERT(length >= 86); + } + quint32 unicodeRange[4] = { + qFromBigEndian(os2Table.data() + 42), + qFromBigEndian(os2Table.data() + 46), + qFromBigEndian(os2Table.data() + 50), + qFromBigEndian(os2Table.data() + 54) + }; + quint32 codePageRange[2] = { + qFromBigEndian(os2Table.data() + 78), + qFromBigEndian(os2Table.data() + 82) + }; + fd->writingSystems = QPlatformFontDatabase::writingSystemsFromTrueTypeBits(unicodeRange, codePageRange); + } + } + + if (styles) { + if (CFNumberRef weightValue = (CFNumberRef) CFDictionaryGetValue(styles, kCTFontWeightTrait)) { + double normalizedWeight; + if (CFNumberGetValue(weightValue, kCFNumberFloat64Type, &normalizedWeight)) + fd->weight = QCoreTextFontEngine::qtWeightFromCFWeight(float(normalizedWeight)); + } + if (CFNumberRef italic = (CFNumberRef) CFDictionaryGetValue(styles, kCTFontSlantTrait)) { + double d; + if (CFNumberGetValue(italic, kCFNumberDoubleType, &d)) { + if (d > 0.0) + fd->style = QFont::StyleItalic; + } + } + if (CFNumberRef symbolic = (CFNumberRef) CFDictionaryGetValue(styles, kCTFontSymbolicTrait)) { + int d; + if (CFNumberGetValue(symbolic, kCFNumberSInt32Type, &d)) { + if (d & kCTFontMonoSpaceTrait) + fd->fixedPitch = true; + if (d & kCTFontExpandedTrait) + fd->stretch = QFont::Expanded; + else if (d & kCTFontCondensedTrait) + fd->stretch = QFont::Condensed; + } + } + } + + if (QCFType size = (CFNumberRef) CTFontDescriptorCopyAttribute(font, kCTFontSizeAttribute)) { + if (CFNumberIsFloatType(size)) { + double d; + CFNumberGetValue(size, kCFNumberDoubleType, &d); + fd->pixelSize = d; + } else { + CFNumberGetValue(size, kCFNumberIntType, &fd->pixelSize); + } + } + + if (QCFType languages = (CFArrayRef) CTFontDescriptorCopyAttribute(font, kCTFontLanguagesAttribute)) { + CFIndex length = CFArrayGetCount(languages); + for (int i = 1; i < LanguageCount; ++i) { + if (!languageForWritingSystem[i]) + continue; + QCFString lang = CFStringCreateWithCString(NULL, languageForWritingSystem[i], kCFStringEncodingASCII); + if (CFArrayContainsValue(languages, CFRangeMake(0, length), lang)) + fd->writingSystems.setSupported(QFontDatabase::WritingSystem(i)); + } + } +} + +void QCoreTextFontDatabase::populateFromDescriptor(CTFontDescriptorRef font) +{ + FontDescription fd; + getFontDescription(font, &fd); + + CFRetain(font); + QPlatformFontDatabase::registerFont(fd.familyName, fd.styleName, fd.foundryName, fd.weight, fd.style, fd.stretch, + true /* antialiased */, true /* scalable */, + fd.pixelSize, fd.fixedPitch, fd.writingSystems, (void *) font); +} + +void QCoreTextFontDatabase::releaseHandle(void *handle) +{ + CFRelease(CTFontDescriptorRef(handle)); +} + +#ifndef QT_NO_FREETYPE +static QByteArray filenameForCFUrl(CFURLRef url) +{ + // The on-stack buffer prevents that a QByteArray allocated for the worst case (MAXPATHLEN) + // stays around for the lifetime of the font. Additionally, it helps to move the char + // signedness cast to an acceptable place. + uchar buffer[MAXPATHLEN]; + QByteArray filename; + + if (!CFURLGetFileSystemRepresentation(url, true, buffer, sizeof(buffer))) { + qWarning("QCoreTextFontDatabase::filenameForCFUrl: could not resolve file for URL %s", + url ? qPrintable(QString::fromCFString(CFURLGetString(url))) : "(null)"); + } else { + QCFType scheme = CFURLCopyScheme(url); + if (QString::fromCFString(scheme) == QLatin1String("qrc")) + filename = ":"; + + filename += reinterpret_cast(buffer); + } + + return filename; +} +#endif + +extern CGAffineTransform qt_transform_from_fontdef(const QFontDef &fontDef); + +QFontEngine *QCoreTextFontDatabase::fontEngine(const QFontDef &f, void *usrPtr) +{ + CTFontDescriptorRef descriptor = static_cast(usrPtr); + +#ifndef QT_NO_FREETYPE + if (m_useFreeType) { + QCFType url(static_cast(CTFontDescriptorCopyAttribute(descriptor, kCTFontURLAttribute))); + + QByteArray filename; + if (url) + filename = filenameForCFUrl(url); + + return freeTypeFontEngine(f, filename); + } +#endif + + // Since we do not pass in the destination DPI to CoreText when making + // the font, we need to pass in a point size which is scaled to include + // the DPI. The default DPI for the screen is 72, thus the scale factor + // is destinationDpi / 72, but since pixelSize = pointSize / 72 * dpi, + // the pixelSize is actually the scaled point size for the destination + // DPI, and we can use that directly. + qreal scaledPointSize = f.pixelSize; + + CGAffineTransform matrix = qt_transform_from_fontdef(f); + CTFontRef font = CTFontCreateWithFontDescriptor(descriptor, scaledPointSize, &matrix); + if (font) { + QFontEngine *engine = new QCoreTextFontEngine(font, f); + engine->fontDef = f; + CFRelease(font); + return engine; + } + + return NULL; +} + +static void releaseFontData(void* info, const void* data, size_t size) +{ + Q_UNUSED(data); + Q_UNUSED(size); + delete (QByteArray*)info; +} + +QFontEngine *QCoreTextFontDatabase::fontEngine(const QByteArray &fontData, qreal pixelSize, QFont::HintingPreference hintingPreference) +{ +#ifndef QT_NO_FREETYPE + if (m_useFreeType) { + QByteArray *fontDataCopy = new QByteArray(fontData); + QCFType dataProvider = CGDataProviderCreateWithData(fontDataCopy, + fontDataCopy->constData(), fontDataCopy->size(), releaseFontData); + QCFType cgFont(CGFontCreateWithDataProvider(dataProvider)); + + if (!cgFont) { + qWarning("QCoreTextFontDatabase::fontEngine: CGFontCreateWithDataProvider failed"); + return Q_NULLPTR; + } + + QFontDef fontDef; + fontDef.pixelSize = pixelSize; + fontDef.pointSize = pixelSize * 72.0 / qt_defaultDpi(); + fontDef.hintingPreference = hintingPreference; + CGAffineTransform transform = qt_transform_from_fontdef(fontDef); + QCFType ctFont(CTFontCreateWithGraphicsFont(cgFont, fontDef.pixelSize, &transform, Q_NULLPTR)); + QCFType url(static_cast(CTFontCopyAttribute(ctFont, kCTFontURLAttribute))); + return freeTypeFontEngine(fontDef, filenameForCFUrl(url), fontData); + } +#endif + + Q_UNUSED(hintingPreference); + + QByteArray* fontDataCopy = new QByteArray(fontData); + QCFType dataProvider = CGDataProviderCreateWithData(fontDataCopy, + fontDataCopy->constData(), fontDataCopy->size(), releaseFontData); + + CGFontRef cgFont = CGFontCreateWithDataProvider(dataProvider); + + QFontEngine *fontEngine = NULL; + if (cgFont == NULL) { + qWarning("QCoreTextFontDatabase::fontEngine: CGFontCreateWithDataProvider failed"); + } else { + QFontDef def; + def.pixelSize = pixelSize; + def.pointSize = pixelSize * 72.0 / qt_defaultDpi(); + fontEngine = new QCoreTextFontEngine(cgFont, def); + CFRelease(cgFont); + } + + return fontEngine; +} + +QFont::StyleHint styleHintFromNSString(NSString *style) +{ + if ([style isEqual: @"sans-serif"]) + return QFont::SansSerif; + else if ([style isEqual: @"monospace"]) + return QFont::Monospace; + else if ([style isEqual: @"cursive"]) + return QFont::Cursive; + else if ([style isEqual: @"serif"]) + return QFont::Serif; + else if ([style isEqual: @"fantasy"]) + return QFont::Fantasy; + else // if ([style isEqual: @"default"]) + return QFont::AnyStyle; +} + +#ifdef Q_OS_OSX +static QString familyNameFromPostScriptName(NSString *psName) +{ + QCFType fontDescriptor = (CTFontDescriptorRef) CTFontDescriptorCreateWithNameAndSize((CFStringRef)psName, 12.0); + QCFString familyName = (CFStringRef) CTFontDescriptorCopyAttribute(fontDescriptor, kCTFontFamilyNameAttribute); + QString name = QCFString::toQString(familyName); + if (name.isEmpty()) + qWarning() << "QCoreTextFontDatabase: Failed to resolve family name for PostScript name " << QCFString::toQString((CFStringRef)psName); + + return name; +} +#endif + +QStringList QCoreTextFontDatabase::fallbacksForFamily(const QString &family, QFont::Style style, QFont::StyleHint styleHint, QChar::Script script) const +{ + Q_UNUSED(style); + Q_UNUSED(script); + + QMacAutoReleasePool pool; + + static QHash fallbackLists; + + if (!family.isEmpty()) { +#if QT_MAC_PLATFORM_SDK_EQUAL_OR_ABOVE(__MAC_10_8, __IPHONE_6_0) + // CTFontCopyDefaultCascadeListForLanguages is available in the SDK + #if QT_MAC_DEPLOYMENT_TARGET_BELOW(__MAC_10_8, __IPHONE_6_0) + // But we have to feature check at runtime + if (&CTFontCopyDefaultCascadeListForLanguages) + #endif + { + QCFType attributes = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); + CFDictionaryAddValue(attributes, kCTFontFamilyNameAttribute, QCFString(family)); + if (QCFType fontDescriptor = CTFontDescriptorCreateWithAttributes(attributes)) { + if (QCFType font = CTFontCreateWithFontDescriptor(fontDescriptor, 12.0, 0)) { + NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; + NSArray *languages = [defaults stringArrayForKey: @"AppleLanguages"]; + + QCFType cascadeList = (CFArrayRef) CTFontCopyDefaultCascadeListForLanguages(font, (CFArrayRef) languages); + if (cascadeList) { + QStringList fallbackList; + const int numCascades = CFArrayGetCount(cascadeList); + for (int i = 0; i < numCascades; ++i) { + CTFontDescriptorRef fontFallback = (CTFontDescriptorRef) CFArrayGetValueAtIndex(cascadeList, i); + QCFString fallbackFamilyName = (CFStringRef) CTFontDescriptorCopyAttribute(fontFallback, kCTFontFamilyNameAttribute); + fallbackList.append(QCFString::toQString(fallbackFamilyName)); + } + +#if defined(Q_OS_OSX) + // Since we are only returning a list of default fonts for the current language, we do not + // cover all unicode completely. This was especially an issue for some of the common script + // symbols such as mathematical symbols, currency or geometric shapes. To minimize the risk + // of missing glyphs, we add Arial Unicode MS as a final fail safe, since this covers most + // of Unicode 2.1. + if (!fallbackList.contains(QStringLiteral("Arial Unicode MS"))) + fallbackList.append(QStringLiteral("Arial Unicode MS")); +#endif + + return fallbackList; + } + } + } + } +#endif + } + + // We were not able to find a fallback for the specific family, + // so we fall back to the stylehint. + + static const QString styleLookupKey = QString::fromLatin1(".QFontStyleHint_%1"); + + static bool didPopulateStyleFallbacks = false; + if (!didPopulateStyleFallbacks) { +#if defined(Q_OS_MACX) + NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; + NSArray *languages = [defaults stringArrayForKey: @"AppleLanguages"]; + + NSDictionary *fallbackDict = [NSDictionary dictionaryWithContentsOfFile: @"/System/Library/Frameworks/ApplicationServices.framework/Frameworks/CoreText.framework/Resources/DefaultFontFallbacks.plist"]; + + for (NSString *style in [fallbackDict allKeys]) { + NSArray *list = [fallbackDict valueForKey: style]; + QFont::StyleHint fallbackStyleHint = styleHintFromNSString(style); + QStringList fallbackList; + for (id item in list) { + // sort the array based on system language preferences + if ([item isKindOfClass: [NSArray class]]) { + NSArray *langs = [(NSArray *) item sortedArrayUsingFunction: languageMapSort + context: languages]; + for (NSArray *map in langs) + fallbackList.append(familyNameFromPostScriptName([map objectAtIndex: 1])); + } + else if ([item isKindOfClass: [NSString class]]) + fallbackList.append(familyNameFromPostScriptName(item)); + } + + fallbackList.append(QLatin1String("Apple Color Emoji")); + + // Since we are only returning a list of default fonts for the current language, we do not + // cover all unicode completely. This was especially an issue for some of the common script + // symbols such as mathematical symbols, currency or geometric shapes. To minimize the risk + // of missing glyphs, we add Arial Unicode MS as a final fail safe, since this covers most + // of Unicode 2.1. + if (!fallbackList.contains(QStringLiteral("Arial Unicode MS"))) + fallbackList.append(QStringLiteral("Arial Unicode MS")); + + fallbackLists[styleLookupKey.arg(fallbackStyleHint)] = fallbackList; + } +#else + QStringList staticFallbackList; + staticFallbackList << QString::fromLatin1("Helvetica,Apple Color Emoji,Geeza Pro,Arial Hebrew,Thonburi,Kailasa" + "Hiragino Kaku Gothic ProN,.Heiti J,Apple SD Gothic Neo,.Heiti K,Heiti SC,Heiti TC" + "Bangla Sangam MN,Devanagari Sangam MN,Gujarati Sangam MN,Gurmukhi MN,Kannada Sangam MN" + "Malayalam Sangam MN,Oriya Sangam MN,Sinhala Sangam MN,Tamil Sangam MN,Telugu Sangam MN" + "Euphemia UCAS,.PhoneFallback").split(QLatin1String(",")); + + for (int i = QFont::Helvetica; i <= QFont::Fantasy; ++i) + fallbackLists[styleLookupKey.arg(i)] = staticFallbackList; +#endif + + didPopulateStyleFallbacks = true; + } + + Q_ASSERT(!fallbackLists.isEmpty()); + return fallbackLists[styleLookupKey.arg(styleHint)]; +} + +#if HAVE_CORETEXT +static CFArrayRef createDescriptorArrayForFont(CTFontRef font, const QString &fileName = QString()) +{ + CFMutableArrayRef array = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks); + QCFType descriptor = CTFontCopyFontDescriptor(font); + + Q_UNUSED(fileName) +#ifndef QT_NO_FREETYPE + // The physical font source URL (usually a local file or Qt resource) is only required for + // FreeType, when using non-system fonts, and needs some hackery to attach in a format + // agreeable to OSX. + if (!fileName.isEmpty()) { + QCFType fontURL; + + if (fileName.startsWith(QLatin1String(":/"))) { + // QUrl::fromLocalFile() doesn't accept qrc pseudo-paths like ":/fonts/myfont.ttf". + // Therefore construct from QString with the qrc:// scheme -> "qrc:///fonts/myfont.ttf". + fontURL = QUrl(QStringLiteral("qrc://") + fileName.mid(1)).toCFURL(); + } else if (!fileName.isEmpty()) { + // At this point we hope that filename is in a format that QUrl can handle. + fontURL = QUrl::fromLocalFile(fileName).toCFURL(); + } + + QCFType attributes = CFDictionaryCreateMutable(kCFAllocatorDefault, 1, + &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); + CFDictionaryAddValue(attributes, kCTFontURLAttribute, fontURL); + descriptor = CTFontDescriptorCreateCopyWithAttributes(descriptor, attributes); + } +#endif + + CFArrayAppendValue(array, descriptor); + return array; +} +#endif + +QStringList QCoreTextFontDatabase::addApplicationFont(const QByteArray &fontData, const QString &fileName) +{ + QCFType fonts; + QStringList families; + +#if HAVE_CORETEXT + if (&CTFontManagerRegisterGraphicsFont) { + CFErrorRef error = 0; + if (!fontData.isEmpty()) { + QByteArray* fontDataCopy = new QByteArray(fontData); + QCFType dataProvider = CGDataProviderCreateWithData(fontDataCopy, + fontDataCopy->constData(), fontDataCopy->size(), releaseFontData); + QCFType cgFont = CGFontCreateWithDataProvider(dataProvider); + if (cgFont) { + if (CTFontManagerRegisterGraphicsFont(cgFont, &error)) { + QCFType font = CTFontCreateWithGraphicsFont(cgFont, 0.0, NULL, NULL); + fonts = createDescriptorArrayForFont(font +#ifndef QT_NO_FREETYPE + , m_useFreeType ? fileName : QString() +#endif + ); + m_applicationFonts.append(QVariant::fromValue(QCFType::constructFromGet(cgFont))); + } + } + } else { + QCFType fontURL = CFURLCreateWithFileSystemPath(NULL, QCFString(fileName), kCFURLPOSIXPathStyle, false); + if (CTFontManagerRegisterFontsForURL(fontURL, kCTFontManagerScopeProcess, &error)) { +#if QT_MAC_PLATFORM_SDK_EQUAL_OR_ABOVE(__MAC_10_6, __IPHONE_7_0) + if (&CTFontManagerCreateFontDescriptorsFromURL) + fonts = CTFontManagerCreateFontDescriptorsFromURL(fontURL); + else +#endif + { + // We're limited to a single font per file, unless we dive into the font tables + QCFType attributes = CFDictionaryCreateMutable(kCFAllocatorDefault, 1, + &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); + CFDictionaryAddValue(attributes, kCTFontURLAttribute, fontURL); + QCFType descriptor = CTFontDescriptorCreateWithAttributes(attributes); + QCFType font = CTFontCreateWithFontDescriptor(descriptor, 0.0, NULL); + fonts = createDescriptorArrayForFont(font); + } + + m_applicationFonts.append(QVariant::fromValue(QCFType::constructFromGet(fontURL))); + } + } + + if (error) { + NSLog(@"Unable to register font: %@", error); + CFRelease(error); + } + } +#endif +#if HAVE_CORETEXT && HAVE_ATS + else +#endif +#if HAVE_ATS + { + ATSFontContainerRef fontContainer; + OSStatus e; + + if (!fontData.isEmpty()) { + e = ATSFontActivateFromMemory((void *) fontData.constData(), fontData.size(), + kATSFontContextLocal, kATSFontFormatUnspecified, NULL, + kATSOptionFlagsDefault, &fontContainer); + } else { + FSRef ref; + if (FSPathMakeRef(reinterpret_cast(fileName.toUtf8().constData()), + &ref, 0) != noErr) + return QStringList(); + e = ATSFontActivateFromFileReference(&ref, kATSFontContextLocal, kATSFontFormatUnspecified, 0, + kATSOptionFlagsDefault, &fontContainer); + } + + if (e == noErr) { + ItemCount fontCount = 0; + e = ATSFontFindFromContainer(fontContainer, kATSOptionFlagsDefault, 0, 0, &fontCount); + if (e != noErr) + return QStringList(); + + QVarLengthArray containedFonts(fontCount); + e = ATSFontFindFromContainer(fontContainer, kATSOptionFlagsDefault, fontCount, containedFonts.data(), &fontCount); + if (e != noErr) + return QStringList(); + + CFMutableArrayRef fontsArray = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); + for (int i = 0; i < containedFonts.size(); ++i) { + QCFType font = CTFontCreateWithPlatformFont(containedFonts[i], 12.0, NULL, NULL); + CFArrayAppendValue(fontsArray, QCFType(CTFontCopyFontDescriptor(font))); + } + + fonts = fontsArray; + + m_applicationFonts.append(QVariant::fromValue(fontContainer)); + } + } +#endif + + if (fonts) { + const int numFonts = CFArrayGetCount(fonts); + for (int i = 0; i < numFonts; ++i) { + CTFontDescriptorRef fontDescriptor = CTFontDescriptorRef(CFArrayGetValueAtIndex(fonts, i)); + populateFromDescriptor(fontDescriptor); + QCFType familyName = CFStringRef(CTFontDescriptorCopyAttribute(fontDescriptor, kCTFontFamilyNameAttribute)); + families.append(QCFString(familyName)); + } + } + + return families; +} + +bool QCoreTextFontDatabase::isPrivateFontFamily(const QString &family) const +{ + if (family.startsWith(QLatin1Char('.'))) + return true; + + return QPlatformFontDatabase::isPrivateFontFamily(family); +} + +static CTFontUIFontType fontTypeFromTheme(QPlatformTheme::Font f) +{ + switch (f) { + case QPlatformTheme::SystemFont: + return kCTFontUIFontSystem; + + case QPlatformTheme::MenuFont: + case QPlatformTheme::MenuBarFont: + case QPlatformTheme::MenuItemFont: + return kCTFontUIFontMenuItem; + + case QPlatformTheme::MessageBoxFont: + return kCTFontUIFontEmphasizedSystem; + + case QPlatformTheme::LabelFont: + return kCTFontUIFontSystem; + + case QPlatformTheme::TipLabelFont: + return kCTFontToolTipFontType; + + case QPlatformTheme::StatusBarFont: + return kCTFontUIFontSystem; + + case QPlatformTheme::TitleBarFont: + return kCTFontUIFontWindowTitle; + + case QPlatformTheme::MdiSubWindowTitleFont: + case QPlatformTheme::DockWidgetTitleFont: + return kCTFontUIFontSystem; + + case QPlatformTheme::PushButtonFont: + return kCTFontUIFontPushButton; + + case QPlatformTheme::CheckBoxFont: + case QPlatformTheme::RadioButtonFont: + return kCTFontUIFontSystem; + + case QPlatformTheme::ToolButtonFont: + return kCTFontUIFontSmallToolbar; + + case QPlatformTheme::ItemViewFont: + return kCTFontUIFontSystem; + + case QPlatformTheme::ListViewFont: + return kCTFontUIFontViews; + + case QPlatformTheme::HeaderViewFont: + return kCTFontUIFontSmallSystem; + + case QPlatformTheme::ListBoxFont: + return kCTFontUIFontViews; + + case QPlatformTheme::ComboMenuItemFont: + return kCTFontUIFontSystem; + + case QPlatformTheme::ComboLineEditFont: + return kCTFontUIFontViews; + + case QPlatformTheme::SmallFont: + return kCTFontUIFontSmallSystem; + + case QPlatformTheme::MiniFont: + return kCTFontUIFontMiniSystem; + + case QPlatformTheme::FixedFont: + return kCTFontUIFontUserFixedPitch; + + default: + return kCTFontUIFontSystem; + } +} + +static CTFontDescriptorRef fontDescriptorFromTheme(QPlatformTheme::Font f) +{ +#ifdef Q_OS_IOS + if (QSysInfo::MacintoshVersion >= QSysInfo::MV_IOS_7_0) { + // Use Dynamic Type to resolve theme fonts if possible, to get + // correct font sizes and style based on user configuration. + NSString *textStyle = 0; + switch (f) { + case QPlatformTheme::TitleBarFont: + case QPlatformTheme::HeaderViewFont: + textStyle = UIFontTextStyleHeadline; + break; + case QPlatformTheme::MdiSubWindowTitleFont: + textStyle = UIFontTextStyleSubheadline; + break; + case QPlatformTheme::TipLabelFont: + case QPlatformTheme::SmallFont: + textStyle = UIFontTextStyleFootnote; + break; + case QPlatformTheme::MiniFont: + textStyle = UIFontTextStyleCaption2; + break; + case QPlatformTheme::FixedFont: + // Fall back to regular code path, as iOS doesn't provide + // an appropriate text style for this theme font. + break; + default: + textStyle = UIFontTextStyleBody; + break; + } + + if (textStyle) { + UIFontDescriptor *desc = [UIFontDescriptor preferredFontDescriptorWithTextStyle:textStyle]; + return static_cast(CFBridgingRetain(desc)); + } + } +#endif // Q_OS_IOS + + // OSX default case and iOS fallback case + CTFontUIFontType fontType = fontTypeFromTheme(f); + QCFType ctFont = CTFontCreateUIFontForLanguage(fontType, 0.0, NULL); + return CTFontCopyFontDescriptor(ctFont); +} + +const QHash &QCoreTextFontDatabase::themeFonts() const +{ + if (m_themeFonts.isEmpty()) { + for (long f = QPlatformTheme::SystemFont; f < QPlatformTheme::NFonts; f++) { + QPlatformTheme::Font ft = static_cast(f); + m_themeFonts.insert(ft, themeFont(ft)); + } + } + + return m_themeFonts; +} + +QFont *QCoreTextFontDatabase::themeFont(QPlatformTheme::Font f) const +{ + CTFontDescriptorRef fontDesc = fontDescriptorFromTheme(f); + FontDescription fd; + getFontDescription(fontDesc, &fd); + + if (!m_systemFontDescriptors.contains(fontDesc)) + m_systemFontDescriptors.insert(fontDesc); + else + CFRelease(fontDesc); + + QFont *font = new QFont(fd.familyName, fd.pixelSize, fd.weight, fd.style == QFont::StyleItalic); + return font; +} + +QFont QCoreTextFontDatabase::defaultFont() const +{ + if (defaultFontName.isEmpty()) { + QCFType font = CTFontCreateUIFontForLanguage(kCTFontUIFontSystem, 12.0, NULL); + defaultFontName = (QString) QCFString(CTFontCopyFullName(font)); + } + + return QFont(defaultFontName); +} + +bool QCoreTextFontDatabase::fontsAlwaysScalable() const +{ + return true; +} + +QList QCoreTextFontDatabase::standardSizes() const +{ + QList ret; + static const unsigned short standard[] = + { 9, 10, 11, 12, 13, 14, 18, 24, 36, 48, 64, 72, 96, 144, 288, 0 }; + ret.reserve(int(sizeof(standard) / sizeof(standard[0]))); + const unsigned short *sizes = standard; + while (*sizes) ret << *sizes++; + return ret; +} + +void QCoreTextFontDatabase::removeApplicationFonts() +{ + if (m_applicationFonts.isEmpty()) + return; + + foreach (const QVariant &font, m_applicationFonts) { +#if HAVE_CORETEXT + if (&CTFontManagerUnregisterGraphicsFont && &CTFontManagerUnregisterFontsForURL) { + CFErrorRef error; + if (font.canConvert(qMetaTypeId >())) { + CTFontManagerUnregisterGraphicsFont(font.value >(), &error); + } else if (font.canConvert(qMetaTypeId >())) { + CTFontManagerUnregisterFontsForURL(font.value >(), kCTFontManagerScopeProcess, &error); + } + } +#endif +#if HAVE_CORETEXT && HAVE_ATS + else +#endif +#if HAVE_ATS + if (font.canConvert(qMetaTypeId())) { + ATSFontDeactivate(font.value(), 0, kATSOptionFlagsDoNotNotify); + } +#endif + } + + m_applicationFonts.clear(); + +#if HAVE_ATS + ATSFontNotify(kATSFontNotifyActionFontsChanged, 0); +#endif +} + +#ifndef QT_NO_FREETYPE +QFontEngine *QCoreTextFontDatabase::freeTypeFontEngine(const QFontDef &fontDef, const QByteArray &filename, + const QByteArray &fontData) +{ + QFontEngine::FaceId faceId; + faceId.filename = filename; + const bool antialias = !(fontDef.styleStrategy & QFont::NoAntialias); + + QScopedPointer engine(new QFontEngineFT(fontDef)); + QFontEngineFT::GlyphFormat format = QFontEngineFT::Format_Mono; + if (antialias) { + QFontEngine::SubpixelAntialiasingType subpixelType = subpixelAntialiasingTypeHint(); + if (subpixelType == QFontEngine::Subpixel_None || (fontDef.styleStrategy & QFont::NoSubpixelAntialias)) { + format = QFontEngineFT::Format_A8; + engine->subpixelType = QFontEngine::Subpixel_None; + } else { + format = QFontEngineFT::Format_A32; + engine->subpixelType = subpixelType; + } + } + + if (!engine->init(faceId, antialias, format, fontData) || engine->invalid()) { + qWarning() << "QCoreTextFontDatabase::freeTypefontEngine Failed to create engine"; + return Q_NULLPTR; + } + engine->setQtDefaultHintStyle(static_cast(fontDef.hintingPreference)); + + return engine.take(); +} +#endif + +QT_END_NAMESPACE + diff --git a/src/calibre/headless/coretext_fontdatabase.mm b/src/calibre/headless/coretext_fontdatabase.mm index 50fb04c365..db76ded409 100644 --- a/src/calibre/headless/coretext_fontdatabase.mm +++ b/src/calibre/headless/coretext_fontdatabase.mm @@ -1,949 +1,14 @@ -/**************************************************************************** -** -** Copyright (C) 2015 The Qt Company Ltd. -** Contact: http://www.qt.io/licensing/ -** -** This file is part of the plugins of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL21$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see http://www.qt.io/terms-conditions. For further -** information use the contact form at http://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 or version 3 as published by the Free -** Software Foundation and appearing in the file LICENSE.LGPLv21 and -** LICENSE.LGPLv3 included in the packaging of this file. Please review the -** following information to ensure the GNU Lesser General Public License -** requirements will be met: https://www.gnu.org/licenses/lgpl.html and -** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** As a special exception, The Qt Company gives you certain additional -** rights. These rights are described in The Qt Company LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +/* + * coretext_fontdatabase.mm + * Copyright (C) 2019 Kovid Goyal + * + * Distributed under terms of the GPL3 license. + */ -#include "qglobal.h" -#include - -#if defined(Q_OS_MACX) -#import -#import -#elif defined(Q_OS_IOS) -#import -#endif - -#include -#include -#include -#include -#ifndef QT_NO_FREETYPE -#include -#endif - -QT_BEGIN_NAMESPACE - -// this could become a list of all languages used for each writing -// system, instead of using the single most common language. -static const char *languageForWritingSystem[] = { - 0, // Any - "en", // Latin - "el", // Greek - "ru", // Cyrillic - "hy", // Armenian - "he", // Hebrew - "ar", // Arabic - "syr", // Syriac - "div", // Thaana - "hi", // Devanagari - "bn", // Bengali - "pa", // Gurmukhi - "gu", // Gujarati - "or", // Oriya - "ta", // Tamil - "te", // Telugu - "kn", // Kannada - "ml", // Malayalam - "si", // Sinhala - "th", // Thai - "lo", // Lao - "bo", // Tibetan - "my", // Myanmar - "ka", // Georgian - "km", // Khmer - "zh-Hans", // SimplifiedChinese - "zh-Hant", // TraditionalChinese - "ja", // Japanese - "ko", // Korean - "vi", // Vietnamese - 0, // Symbol - "sga", // Ogham - "non", // Runic - "man" // N'Ko -}; -enum { LanguageCount = sizeof(languageForWritingSystem) / sizeof(const char *) }; - -#ifdef Q_OS_OSX -static NSInteger languageMapSort(id obj1, id obj2, void *context) -{ - NSArray *map1 = (NSArray *) obj1; - NSArray *map2 = (NSArray *) obj2; - NSArray *languages = (NSArray *) context; - - NSString *lang1 = [map1 objectAtIndex: 0]; - NSString *lang2 = [map2 objectAtIndex: 0]; - - return [languages indexOfObject: lang1] - [languages indexOfObject: lang2]; -} -#endif - -QCoreTextFontDatabase::QCoreTextFontDatabase(bool useFreeType) -#ifndef QT_NO_FREETYPE - : m_useFreeType(useFreeType) -#endif -{ - Q_UNUSED(useFreeType) - QCoreTextFontEngine::defaultGlyphFormat = QFontEngine::Format_A8; -} - -QCoreTextFontDatabase::~QCoreTextFontDatabase() -{ - foreach (CTFontDescriptorRef ref, m_systemFontDescriptors) - CFRelease(ref); -} - -static CFArrayRef availableFamilyNames() -{ -#if defined(Q_OS_OSX) - return CTFontManagerCopyAvailableFontFamilyNames(); -#elif defined(Q_OS_IOS) - return (CFArrayRef) [[UIFont familyNames] retain]; -#endif -} - -void QCoreTextFontDatabase::populateFontDatabase() -{ - // The caller (QFontDB) expects the db to be populate only with system fonts, so we need - // to make sure that any previously registered app fonts become invisible. - removeApplicationFonts(); - - QCFType familyNames = availableFamilyNames(); - const int numberOfFamilies = CFArrayGetCount(familyNames); - for (int i = 0; i < numberOfFamilies; ++i) { - CFStringRef familyNameRef = (CFStringRef) CFArrayGetValueAtIndex(familyNames, i); - QString familyName = QCFString::toQString(familyNameRef); - - // Don't populate internal fonts - if (familyName.startsWith(QLatin1Char('.')) || familyName == QLatin1String("LastResort")) - continue; - -#if defined(Q_OS_IOS) || defined(Q_OS_TVOS) - // Skip font families with no corresponding fonts - if (![UIFont fontNamesForFamilyName:(NSString*)familyNameRef].count) - continue; -#endif - - QPlatformFontDatabase::registerFontFamily(familyName); - -#if defined(Q_OS_OSX) - QString localizedFamilyName = QString::fromNSString([[NSFontManager sharedFontManager] localizedNameForFamily:(NSString*)familyNameRef face:nil]); - if (familyName != localizedFamilyName) - QPlatformFontDatabase::registerAliasToFontFamily(familyName, localizedFamilyName); -#endif - } - - // Force creating the theme fonts to get the descriptors in m_systemFontDescriptors - if (m_themeFonts.isEmpty()) - (void)themeFonts(); - - Q_FOREACH (CTFontDescriptorRef fontDesc, m_systemFontDescriptors) - populateFromDescriptor(fontDesc); -} - -void QCoreTextFontDatabase::populateFamily(const QString &familyName) -{ - QCFType attributes = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); - CFDictionaryAddValue(attributes, kCTFontFamilyNameAttribute, QCFString(familyName)); - QCFType nameOnlyDescriptor = CTFontDescriptorCreateWithAttributes(attributes); - - // A single family might match several different fonts with different styles eg. - QCFType matchingFonts = (CFArrayRef) CTFontDescriptorCreateMatchingFontDescriptors(nameOnlyDescriptor, 0); - if (!matchingFonts) { - qWarning() << "QCoreTextFontDatabase: Found no matching fonts for family" << familyName; - return; - } - - const int numFonts = CFArrayGetCount(matchingFonts); - for (int i = 0; i < numFonts; ++i) - populateFromDescriptor(CTFontDescriptorRef(CFArrayGetValueAtIndex(matchingFonts, i))); -} - -struct FontDescription { - QCFString familyName; - QCFString styleName; - QString foundryName; - QFont::Weight weight; - QFont::Style style; - QFont::Stretch stretch; - int pixelSize; - bool fixedPitch; - QSupportedWritingSystems writingSystems; -}; - -static void getFontDescription(CTFontDescriptorRef font, FontDescription *fd) -{ - QCFType styles = (CFDictionaryRef) CTFontDescriptorCopyAttribute(font, kCTFontTraitsAttribute); - - fd->foundryName = QStringLiteral("CoreText"); - fd->familyName = (CFStringRef) CTFontDescriptorCopyAttribute(font, kCTFontFamilyNameAttribute); - fd->styleName = (CFStringRef)CTFontDescriptorCopyAttribute(font, kCTFontStyleNameAttribute); - fd->weight = QFont::Normal; - fd->style = QFont::StyleNormal; - fd->stretch = QFont::Unstretched; - fd->fixedPitch = false; - - if (QCFType tempFont = CTFontCreateWithFontDescriptor(font, 0.0, 0)) { - uint tag = MAKE_TAG('O', 'S', '/', '2'); - CTFontRef tempFontRef = tempFont; - void *userData = reinterpret_cast(&tempFontRef); - uint length = 128; - QVarLengthArray os2Table(length); - if (QCoreTextFontEngine::ct_getSfntTable(userData, tag, os2Table.data(), &length) && length >= 86) { - if (length > uint(os2Table.length())) { - os2Table.resize(length); - if (!QCoreTextFontEngine::ct_getSfntTable(userData, tag, os2Table.data(), &length)) - Q_UNREACHABLE(); - Q_ASSERT(length >= 86); - } - quint32 unicodeRange[4] = { - qFromBigEndian(os2Table.data() + 42), - qFromBigEndian(os2Table.data() + 46), - qFromBigEndian(os2Table.data() + 50), - qFromBigEndian(os2Table.data() + 54) - }; - quint32 codePageRange[2] = { - qFromBigEndian(os2Table.data() + 78), - qFromBigEndian(os2Table.data() + 82) - }; - fd->writingSystems = QPlatformFontDatabase::writingSystemsFromTrueTypeBits(unicodeRange, codePageRange); - } - } - - if (styles) { - if (CFNumberRef weightValue = (CFNumberRef) CFDictionaryGetValue(styles, kCTFontWeightTrait)) { - double normalizedWeight; - if (CFNumberGetValue(weightValue, kCFNumberFloat64Type, &normalizedWeight)) - fd->weight = QCoreTextFontEngine::qtWeightFromCFWeight(float(normalizedWeight)); - } - if (CFNumberRef italic = (CFNumberRef) CFDictionaryGetValue(styles, kCTFontSlantTrait)) { - double d; - if (CFNumberGetValue(italic, kCFNumberDoubleType, &d)) { - if (d > 0.0) - fd->style = QFont::StyleItalic; - } - } - if (CFNumberRef symbolic = (CFNumberRef) CFDictionaryGetValue(styles, kCTFontSymbolicTrait)) { - int d; - if (CFNumberGetValue(symbolic, kCFNumberSInt32Type, &d)) { - if (d & kCTFontMonoSpaceTrait) - fd->fixedPitch = true; - if (d & kCTFontExpandedTrait) - fd->stretch = QFont::Expanded; - else if (d & kCTFontCondensedTrait) - fd->stretch = QFont::Condensed; - } - } - } - - if (QCFType size = (CFNumberRef) CTFontDescriptorCopyAttribute(font, kCTFontSizeAttribute)) { - if (CFNumberIsFloatType(size)) { - double d; - CFNumberGetValue(size, kCFNumberDoubleType, &d); - fd->pixelSize = d; - } else { - CFNumberGetValue(size, kCFNumberIntType, &fd->pixelSize); - } - } - - if (QCFType languages = (CFArrayRef) CTFontDescriptorCopyAttribute(font, kCTFontLanguagesAttribute)) { - CFIndex length = CFArrayGetCount(languages); - for (int i = 1; i < LanguageCount; ++i) { - if (!languageForWritingSystem[i]) - continue; - QCFString lang = CFStringCreateWithCString(NULL, languageForWritingSystem[i], kCFStringEncodingASCII); - if (CFArrayContainsValue(languages, CFRangeMake(0, length), lang)) - fd->writingSystems.setSupported(QFontDatabase::WritingSystem(i)); - } - } -} - -void QCoreTextFontDatabase::populateFromDescriptor(CTFontDescriptorRef font) -{ - FontDescription fd; - getFontDescription(font, &fd); - - CFRetain(font); - QPlatformFontDatabase::registerFont(fd.familyName, fd.styleName, fd.foundryName, fd.weight, fd.style, fd.stretch, - true /* antialiased */, true /* scalable */, - fd.pixelSize, fd.fixedPitch, fd.writingSystems, (void *) font); -} - -void QCoreTextFontDatabase::releaseHandle(void *handle) -{ - CFRelease(CTFontDescriptorRef(handle)); -} - -#ifndef QT_NO_FREETYPE -static QByteArray filenameForCFUrl(CFURLRef url) -{ - // The on-stack buffer prevents that a QByteArray allocated for the worst case (MAXPATHLEN) - // stays around for the lifetime of the font. Additionally, it helps to move the char - // signedness cast to an acceptable place. - uchar buffer[MAXPATHLEN]; - QByteArray filename; - - if (!CFURLGetFileSystemRepresentation(url, true, buffer, sizeof(buffer))) { - qWarning("QCoreTextFontDatabase::filenameForCFUrl: could not resolve file for URL %s", - url ? qPrintable(QString::fromCFString(CFURLGetString(url))) : "(null)"); - } else { - QCFType scheme = CFURLCopyScheme(url); - if (QString::fromCFString(scheme) == QLatin1String("qrc")) - filename = ":"; - - filename += reinterpret_cast(buffer); - } - - return filename; -} -#endif - -extern CGAffineTransform qt_transform_from_fontdef(const QFontDef &fontDef); - -QFontEngine *QCoreTextFontDatabase::fontEngine(const QFontDef &f, void *usrPtr) -{ - CTFontDescriptorRef descriptor = static_cast(usrPtr); - -#ifndef QT_NO_FREETYPE - if (m_useFreeType) { - QCFType url(static_cast(CTFontDescriptorCopyAttribute(descriptor, kCTFontURLAttribute))); - - QByteArray filename; - if (url) - filename = filenameForCFUrl(url); - - return freeTypeFontEngine(f, filename); - } -#endif - - // Since we do not pass in the destination DPI to CoreText when making - // the font, we need to pass in a point size which is scaled to include - // the DPI. The default DPI for the screen is 72, thus the scale factor - // is destinationDpi / 72, but since pixelSize = pointSize / 72 * dpi, - // the pixelSize is actually the scaled point size for the destination - // DPI, and we can use that directly. - qreal scaledPointSize = f.pixelSize; - - CGAffineTransform matrix = qt_transform_from_fontdef(f); - CTFontRef font = CTFontCreateWithFontDescriptor(descriptor, scaledPointSize, &matrix); - if (font) { - QFontEngine *engine = new QCoreTextFontEngine(font, f); - engine->fontDef = f; - CFRelease(font); - return engine; - } - - return NULL; -} - -static void releaseFontData(void* info, const void* data, size_t size) -{ - Q_UNUSED(data); - Q_UNUSED(size); - delete (QByteArray*)info; -} - -QFontEngine *QCoreTextFontDatabase::fontEngine(const QByteArray &fontData, qreal pixelSize, QFont::HintingPreference hintingPreference) -{ -#ifndef QT_NO_FREETYPE - if (m_useFreeType) { - QByteArray *fontDataCopy = new QByteArray(fontData); - QCFType dataProvider = CGDataProviderCreateWithData(fontDataCopy, - fontDataCopy->constData(), fontDataCopy->size(), releaseFontData); - QCFType cgFont(CGFontCreateWithDataProvider(dataProvider)); - - if (!cgFont) { - qWarning("QCoreTextFontDatabase::fontEngine: CGFontCreateWithDataProvider failed"); - return Q_NULLPTR; - } - - QFontDef fontDef; - fontDef.pixelSize = pixelSize; - fontDef.pointSize = pixelSize * 72.0 / qt_defaultDpi(); - fontDef.hintingPreference = hintingPreference; - CGAffineTransform transform = qt_transform_from_fontdef(fontDef); - QCFType ctFont(CTFontCreateWithGraphicsFont(cgFont, fontDef.pixelSize, &transform, Q_NULLPTR)); - QCFType url(static_cast(CTFontCopyAttribute(ctFont, kCTFontURLAttribute))); - return freeTypeFontEngine(fontDef, filenameForCFUrl(url), fontData); - } -#endif - - Q_UNUSED(hintingPreference); - - QByteArray* fontDataCopy = new QByteArray(fontData); - QCFType dataProvider = CGDataProviderCreateWithData(fontDataCopy, - fontDataCopy->constData(), fontDataCopy->size(), releaseFontData); - - CGFontRef cgFont = CGFontCreateWithDataProvider(dataProvider); - - QFontEngine *fontEngine = NULL; - if (cgFont == NULL) { - qWarning("QCoreTextFontDatabase::fontEngine: CGFontCreateWithDataProvider failed"); - } else { - QFontDef def; - def.pixelSize = pixelSize; - def.pointSize = pixelSize * 72.0 / qt_defaultDpi(); - fontEngine = new QCoreTextFontEngine(cgFont, def); - CFRelease(cgFont); - } - - return fontEngine; -} - -QFont::StyleHint styleHintFromNSString(NSString *style) -{ - if ([style isEqual: @"sans-serif"]) - return QFont::SansSerif; - else if ([style isEqual: @"monospace"]) - return QFont::Monospace; - else if ([style isEqual: @"cursive"]) - return QFont::Cursive; - else if ([style isEqual: @"serif"]) - return QFont::Serif; - else if ([style isEqual: @"fantasy"]) - return QFont::Fantasy; - else // if ([style isEqual: @"default"]) - return QFont::AnyStyle; -} - -#ifdef Q_OS_OSX -static QString familyNameFromPostScriptName(NSString *psName) -{ - QCFType fontDescriptor = (CTFontDescriptorRef) CTFontDescriptorCreateWithNameAndSize((CFStringRef)psName, 12.0); - QCFString familyName = (CFStringRef) CTFontDescriptorCopyAttribute(fontDescriptor, kCTFontFamilyNameAttribute); - QString name = QCFString::toQString(familyName); - if (name.isEmpty()) - qWarning() << "QCoreTextFontDatabase: Failed to resolve family name for PostScript name " << QCFString::toQString((CFStringRef)psName); - - return name; -} -#endif - -QStringList QCoreTextFontDatabase::fallbacksForFamily(const QString &family, QFont::Style style, QFont::StyleHint styleHint, QChar::Script script) const -{ - Q_UNUSED(style); - Q_UNUSED(script); - - QMacAutoReleasePool pool; - - static QHash fallbackLists; - - if (!family.isEmpty()) { -#if QT_MAC_PLATFORM_SDK_EQUAL_OR_ABOVE(__MAC_10_8, __IPHONE_6_0) - // CTFontCopyDefaultCascadeListForLanguages is available in the SDK - #if QT_MAC_DEPLOYMENT_TARGET_BELOW(__MAC_10_8, __IPHONE_6_0) - // But we have to feature check at runtime - if (&CTFontCopyDefaultCascadeListForLanguages) - #endif - { - QCFType attributes = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); - CFDictionaryAddValue(attributes, kCTFontFamilyNameAttribute, QCFString(family)); - if (QCFType fontDescriptor = CTFontDescriptorCreateWithAttributes(attributes)) { - if (QCFType font = CTFontCreateWithFontDescriptor(fontDescriptor, 12.0, 0)) { - NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; - NSArray *languages = [defaults stringArrayForKey: @"AppleLanguages"]; - - QCFType cascadeList = (CFArrayRef) CTFontCopyDefaultCascadeListForLanguages(font, (CFArrayRef) languages); - if (cascadeList) { - QStringList fallbackList; - const int numCascades = CFArrayGetCount(cascadeList); - for (int i = 0; i < numCascades; ++i) { - CTFontDescriptorRef fontFallback = (CTFontDescriptorRef) CFArrayGetValueAtIndex(cascadeList, i); - QCFString fallbackFamilyName = (CFStringRef) CTFontDescriptorCopyAttribute(fontFallback, kCTFontFamilyNameAttribute); - fallbackList.append(QCFString::toQString(fallbackFamilyName)); - } - -#if defined(Q_OS_OSX) - // Since we are only returning a list of default fonts for the current language, we do not - // cover all unicode completely. This was especially an issue for some of the common script - // symbols such as mathematical symbols, currency or geometric shapes. To minimize the risk - // of missing glyphs, we add Arial Unicode MS as a final fail safe, since this covers most - // of Unicode 2.1. - if (!fallbackList.contains(QStringLiteral("Arial Unicode MS"))) - fallbackList.append(QStringLiteral("Arial Unicode MS")); -#endif - - return fallbackList; - } - } - } - } -#endif - } - - // We were not able to find a fallback for the specific family, - // so we fall back to the stylehint. - - static const QString styleLookupKey = QString::fromLatin1(".QFontStyleHint_%1"); - - static bool didPopulateStyleFallbacks = false; - if (!didPopulateStyleFallbacks) { -#if defined(Q_OS_MACX) - NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; - NSArray *languages = [defaults stringArrayForKey: @"AppleLanguages"]; - - NSDictionary *fallbackDict = [NSDictionary dictionaryWithContentsOfFile: @"/System/Library/Frameworks/ApplicationServices.framework/Frameworks/CoreText.framework/Resources/DefaultFontFallbacks.plist"]; - - for (NSString *style in [fallbackDict allKeys]) { - NSArray *list = [fallbackDict valueForKey: style]; - QFont::StyleHint fallbackStyleHint = styleHintFromNSString(style); - QStringList fallbackList; - for (id item in list) { - // sort the array based on system language preferences - if ([item isKindOfClass: [NSArray class]]) { - NSArray *langs = [(NSArray *) item sortedArrayUsingFunction: languageMapSort - context: languages]; - for (NSArray *map in langs) - fallbackList.append(familyNameFromPostScriptName([map objectAtIndex: 1])); - } - else if ([item isKindOfClass: [NSString class]]) - fallbackList.append(familyNameFromPostScriptName(item)); - } - - fallbackList.append(QLatin1String("Apple Color Emoji")); - - // Since we are only returning a list of default fonts for the current language, we do not - // cover all unicode completely. This was especially an issue for some of the common script - // symbols such as mathematical symbols, currency or geometric shapes. To minimize the risk - // of missing glyphs, we add Arial Unicode MS as a final fail safe, since this covers most - // of Unicode 2.1. - if (!fallbackList.contains(QStringLiteral("Arial Unicode MS"))) - fallbackList.append(QStringLiteral("Arial Unicode MS")); - - fallbackLists[styleLookupKey.arg(fallbackStyleHint)] = fallbackList; - } +#include +#if (QT_VERSION >= QT_VERSION_CHECK(5, 12, 0)) +#include "coretext_fontdatabase-new.mm" #else - QStringList staticFallbackList; - staticFallbackList << QString::fromLatin1("Helvetica,Apple Color Emoji,Geeza Pro,Arial Hebrew,Thonburi,Kailasa" - "Hiragino Kaku Gothic ProN,.Heiti J,Apple SD Gothic Neo,.Heiti K,Heiti SC,Heiti TC" - "Bangla Sangam MN,Devanagari Sangam MN,Gujarati Sangam MN,Gurmukhi MN,Kannada Sangam MN" - "Malayalam Sangam MN,Oriya Sangam MN,Sinhala Sangam MN,Tamil Sangam MN,Telugu Sangam MN" - "Euphemia UCAS,.PhoneFallback").split(QLatin1String(",")); - - for (int i = QFont::Helvetica; i <= QFont::Fantasy; ++i) - fallbackLists[styleLookupKey.arg(i)] = staticFallbackList; +#include "coretext_fontdatabase-old.mm" #endif - - didPopulateStyleFallbacks = true; - } - - Q_ASSERT(!fallbackLists.isEmpty()); - return fallbackLists[styleLookupKey.arg(styleHint)]; -} - -#if HAVE_CORETEXT -static CFArrayRef createDescriptorArrayForFont(CTFontRef font, const QString &fileName = QString()) -{ - CFMutableArrayRef array = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks); - QCFType descriptor = CTFontCopyFontDescriptor(font); - - Q_UNUSED(fileName) -#ifndef QT_NO_FREETYPE - // The physical font source URL (usually a local file or Qt resource) is only required for - // FreeType, when using non-system fonts, and needs some hackery to attach in a format - // agreeable to OSX. - if (!fileName.isEmpty()) { - QCFType fontURL; - - if (fileName.startsWith(QLatin1String(":/"))) { - // QUrl::fromLocalFile() doesn't accept qrc pseudo-paths like ":/fonts/myfont.ttf". - // Therefore construct from QString with the qrc:// scheme -> "qrc:///fonts/myfont.ttf". - fontURL = QUrl(QStringLiteral("qrc://") + fileName.mid(1)).toCFURL(); - } else if (!fileName.isEmpty()) { - // At this point we hope that filename is in a format that QUrl can handle. - fontURL = QUrl::fromLocalFile(fileName).toCFURL(); - } - - QCFType attributes = CFDictionaryCreateMutable(kCFAllocatorDefault, 1, - &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); - CFDictionaryAddValue(attributes, kCTFontURLAttribute, fontURL); - descriptor = CTFontDescriptorCreateCopyWithAttributes(descriptor, attributes); - } -#endif - - CFArrayAppendValue(array, descriptor); - return array; -} -#endif - -QStringList QCoreTextFontDatabase::addApplicationFont(const QByteArray &fontData, const QString &fileName) -{ - QCFType fonts; - QStringList families; - -#if HAVE_CORETEXT - if (&CTFontManagerRegisterGraphicsFont) { - CFErrorRef error = 0; - if (!fontData.isEmpty()) { - QByteArray* fontDataCopy = new QByteArray(fontData); - QCFType dataProvider = CGDataProviderCreateWithData(fontDataCopy, - fontDataCopy->constData(), fontDataCopy->size(), releaseFontData); - QCFType cgFont = CGFontCreateWithDataProvider(dataProvider); - if (cgFont) { - if (CTFontManagerRegisterGraphicsFont(cgFont, &error)) { - QCFType font = CTFontCreateWithGraphicsFont(cgFont, 0.0, NULL, NULL); - fonts = createDescriptorArrayForFont(font -#ifndef QT_NO_FREETYPE - , m_useFreeType ? fileName : QString() -#endif - ); - m_applicationFonts.append(QVariant::fromValue(QCFType::constructFromGet(cgFont))); - } - } - } else { - QCFType fontURL = CFURLCreateWithFileSystemPath(NULL, QCFString(fileName), kCFURLPOSIXPathStyle, false); - if (CTFontManagerRegisterFontsForURL(fontURL, kCTFontManagerScopeProcess, &error)) { -#if QT_MAC_PLATFORM_SDK_EQUAL_OR_ABOVE(__MAC_10_6, __IPHONE_7_0) - if (&CTFontManagerCreateFontDescriptorsFromURL) - fonts = CTFontManagerCreateFontDescriptorsFromURL(fontURL); - else -#endif - { - // We're limited to a single font per file, unless we dive into the font tables - QCFType attributes = CFDictionaryCreateMutable(kCFAllocatorDefault, 1, - &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); - CFDictionaryAddValue(attributes, kCTFontURLAttribute, fontURL); - QCFType descriptor = CTFontDescriptorCreateWithAttributes(attributes); - QCFType font = CTFontCreateWithFontDescriptor(descriptor, 0.0, NULL); - fonts = createDescriptorArrayForFont(font); - } - - m_applicationFonts.append(QVariant::fromValue(QCFType::constructFromGet(fontURL))); - } - } - - if (error) { - NSLog(@"Unable to register font: %@", error); - CFRelease(error); - } - } -#endif -#if HAVE_CORETEXT && HAVE_ATS - else -#endif -#if HAVE_ATS - { - ATSFontContainerRef fontContainer; - OSStatus e; - - if (!fontData.isEmpty()) { - e = ATSFontActivateFromMemory((void *) fontData.constData(), fontData.size(), - kATSFontContextLocal, kATSFontFormatUnspecified, NULL, - kATSOptionFlagsDefault, &fontContainer); - } else { - FSRef ref; - if (FSPathMakeRef(reinterpret_cast(fileName.toUtf8().constData()), - &ref, 0) != noErr) - return QStringList(); - e = ATSFontActivateFromFileReference(&ref, kATSFontContextLocal, kATSFontFormatUnspecified, 0, - kATSOptionFlagsDefault, &fontContainer); - } - - if (e == noErr) { - ItemCount fontCount = 0; - e = ATSFontFindFromContainer(fontContainer, kATSOptionFlagsDefault, 0, 0, &fontCount); - if (e != noErr) - return QStringList(); - - QVarLengthArray containedFonts(fontCount); - e = ATSFontFindFromContainer(fontContainer, kATSOptionFlagsDefault, fontCount, containedFonts.data(), &fontCount); - if (e != noErr) - return QStringList(); - - CFMutableArrayRef fontsArray = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); - for (int i = 0; i < containedFonts.size(); ++i) { - QCFType font = CTFontCreateWithPlatformFont(containedFonts[i], 12.0, NULL, NULL); - CFArrayAppendValue(fontsArray, QCFType(CTFontCopyFontDescriptor(font))); - } - - fonts = fontsArray; - - m_applicationFonts.append(QVariant::fromValue(fontContainer)); - } - } -#endif - - if (fonts) { - const int numFonts = CFArrayGetCount(fonts); - for (int i = 0; i < numFonts; ++i) { - CTFontDescriptorRef fontDescriptor = CTFontDescriptorRef(CFArrayGetValueAtIndex(fonts, i)); - populateFromDescriptor(fontDescriptor); - QCFType familyName = CFStringRef(CTFontDescriptorCopyAttribute(fontDescriptor, kCTFontFamilyNameAttribute)); - families.append(QCFString(familyName)); - } - } - - return families; -} - -bool QCoreTextFontDatabase::isPrivateFontFamily(const QString &family) const -{ - if (family.startsWith(QLatin1Char('.'))) - return true; - - return QPlatformFontDatabase::isPrivateFontFamily(family); -} - -static CTFontUIFontType fontTypeFromTheme(QPlatformTheme::Font f) -{ - switch (f) { - case QPlatformTheme::SystemFont: - return kCTFontUIFontSystem; - - case QPlatformTheme::MenuFont: - case QPlatformTheme::MenuBarFont: - case QPlatformTheme::MenuItemFont: - return kCTFontUIFontMenuItem; - - case QPlatformTheme::MessageBoxFont: - return kCTFontUIFontEmphasizedSystem; - - case QPlatformTheme::LabelFont: - return kCTFontUIFontSystem; - - case QPlatformTheme::TipLabelFont: - return kCTFontToolTipFontType; - - case QPlatformTheme::StatusBarFont: - return kCTFontUIFontSystem; - - case QPlatformTheme::TitleBarFont: - return kCTFontUIFontWindowTitle; - - case QPlatformTheme::MdiSubWindowTitleFont: - case QPlatformTheme::DockWidgetTitleFont: - return kCTFontUIFontSystem; - - case QPlatformTheme::PushButtonFont: - return kCTFontUIFontPushButton; - - case QPlatformTheme::CheckBoxFont: - case QPlatformTheme::RadioButtonFont: - return kCTFontUIFontSystem; - - case QPlatformTheme::ToolButtonFont: - return kCTFontUIFontSmallToolbar; - - case QPlatformTheme::ItemViewFont: - return kCTFontUIFontSystem; - - case QPlatformTheme::ListViewFont: - return kCTFontUIFontViews; - - case QPlatformTheme::HeaderViewFont: - return kCTFontUIFontSmallSystem; - - case QPlatformTheme::ListBoxFont: - return kCTFontUIFontViews; - - case QPlatformTheme::ComboMenuItemFont: - return kCTFontUIFontSystem; - - case QPlatformTheme::ComboLineEditFont: - return kCTFontUIFontViews; - - case QPlatformTheme::SmallFont: - return kCTFontUIFontSmallSystem; - - case QPlatformTheme::MiniFont: - return kCTFontUIFontMiniSystem; - - case QPlatformTheme::FixedFont: - return kCTFontUIFontUserFixedPitch; - - default: - return kCTFontUIFontSystem; - } -} - -static CTFontDescriptorRef fontDescriptorFromTheme(QPlatformTheme::Font f) -{ -#ifdef Q_OS_IOS - if (QSysInfo::MacintoshVersion >= QSysInfo::MV_IOS_7_0) { - // Use Dynamic Type to resolve theme fonts if possible, to get - // correct font sizes and style based on user configuration. - NSString *textStyle = 0; - switch (f) { - case QPlatformTheme::TitleBarFont: - case QPlatformTheme::HeaderViewFont: - textStyle = UIFontTextStyleHeadline; - break; - case QPlatformTheme::MdiSubWindowTitleFont: - textStyle = UIFontTextStyleSubheadline; - break; - case QPlatformTheme::TipLabelFont: - case QPlatformTheme::SmallFont: - textStyle = UIFontTextStyleFootnote; - break; - case QPlatformTheme::MiniFont: - textStyle = UIFontTextStyleCaption2; - break; - case QPlatformTheme::FixedFont: - // Fall back to regular code path, as iOS doesn't provide - // an appropriate text style for this theme font. - break; - default: - textStyle = UIFontTextStyleBody; - break; - } - - if (textStyle) { - UIFontDescriptor *desc = [UIFontDescriptor preferredFontDescriptorWithTextStyle:textStyle]; - return static_cast(CFBridgingRetain(desc)); - } - } -#endif // Q_OS_IOS - - // OSX default case and iOS fallback case - CTFontUIFontType fontType = fontTypeFromTheme(f); - QCFType ctFont = CTFontCreateUIFontForLanguage(fontType, 0.0, NULL); - return CTFontCopyFontDescriptor(ctFont); -} - -const QHash &QCoreTextFontDatabase::themeFonts() const -{ - if (m_themeFonts.isEmpty()) { - for (long f = QPlatformTheme::SystemFont; f < QPlatformTheme::NFonts; f++) { - QPlatformTheme::Font ft = static_cast(f); - m_themeFonts.insert(ft, themeFont(ft)); - } - } - - return m_themeFonts; -} - -QFont *QCoreTextFontDatabase::themeFont(QPlatformTheme::Font f) const -{ - CTFontDescriptorRef fontDesc = fontDescriptorFromTheme(f); - FontDescription fd; - getFontDescription(fontDesc, &fd); - - if (!m_systemFontDescriptors.contains(fontDesc)) - m_systemFontDescriptors.insert(fontDesc); - else - CFRelease(fontDesc); - - QFont *font = new QFont(fd.familyName, fd.pixelSize, fd.weight, fd.style == QFont::StyleItalic); - return font; -} - -QFont QCoreTextFontDatabase::defaultFont() const -{ - if (defaultFontName.isEmpty()) { - QCFType font = CTFontCreateUIFontForLanguage(kCTFontUIFontSystem, 12.0, NULL); - defaultFontName = (QString) QCFString(CTFontCopyFullName(font)); - } - - return QFont(defaultFontName); -} - -bool QCoreTextFontDatabase::fontsAlwaysScalable() const -{ - return true; -} - -QList QCoreTextFontDatabase::standardSizes() const -{ - QList ret; - static const unsigned short standard[] = - { 9, 10, 11, 12, 13, 14, 18, 24, 36, 48, 64, 72, 96, 144, 288, 0 }; - ret.reserve(int(sizeof(standard) / sizeof(standard[0]))); - const unsigned short *sizes = standard; - while (*sizes) ret << *sizes++; - return ret; -} - -void QCoreTextFontDatabase::removeApplicationFonts() -{ - if (m_applicationFonts.isEmpty()) - return; - - foreach (const QVariant &font, m_applicationFonts) { -#if HAVE_CORETEXT - if (&CTFontManagerUnregisterGraphicsFont && &CTFontManagerUnregisterFontsForURL) { - CFErrorRef error; - if (font.canConvert(qMetaTypeId >())) { - CTFontManagerUnregisterGraphicsFont(font.value >(), &error); - } else if (font.canConvert(qMetaTypeId >())) { - CTFontManagerUnregisterFontsForURL(font.value >(), kCTFontManagerScopeProcess, &error); - } - } -#endif -#if HAVE_CORETEXT && HAVE_ATS - else -#endif -#if HAVE_ATS - if (font.canConvert(qMetaTypeId())) { - ATSFontDeactivate(font.value(), 0, kATSOptionFlagsDoNotNotify); - } -#endif - } - - m_applicationFonts.clear(); - -#if HAVE_ATS - ATSFontNotify(kATSFontNotifyActionFontsChanged, 0); -#endif -} - -#ifndef QT_NO_FREETYPE -QFontEngine *QCoreTextFontDatabase::freeTypeFontEngine(const QFontDef &fontDef, const QByteArray &filename, - const QByteArray &fontData) -{ - QFontEngine::FaceId faceId; - faceId.filename = filename; - const bool antialias = !(fontDef.styleStrategy & QFont::NoAntialias); - - QScopedPointer engine(new QFontEngineFT(fontDef)); - QFontEngineFT::GlyphFormat format = QFontEngineFT::Format_Mono; - if (antialias) { - QFontEngine::SubpixelAntialiasingType subpixelType = subpixelAntialiasingTypeHint(); - if (subpixelType == QFontEngine::Subpixel_None || (fontDef.styleStrategy & QFont::NoSubpixelAntialias)) { - format = QFontEngineFT::Format_A8; - engine->subpixelType = QFontEngine::Subpixel_None; - } else { - format = QFontEngineFT::Format_A32; - engine->subpixelType = subpixelType; - } - } - - if (!engine->init(faceId, antialias, format, fontData) || engine->invalid()) { - qWarning() << "QCoreTextFontDatabase::freeTypefontEngine Failed to create engine"; - return Q_NULLPTR; - } - engine->setQtDefaultHintStyle(static_cast(fontDef.hintingPreference)); - - return engine.take(); -} -#endif - -QT_END_NAMESPACE - diff --git a/src/calibre/headless/headless_integration.cpp b/src/calibre/headless/headless_integration.cpp index 500d7f930d..5363d87d15 100644 --- a/src/calibre/headless/headless_integration.cpp +++ b/src/calibre/headless/headless_integration.cpp @@ -2,7 +2,11 @@ #include "headless_integration.h" #include "headless_backingstore.h" #ifdef __APPLE__ +#if (QT_VERSION >= QT_VERSION_CHECK(5, 8, 0)) +#include +#else #include +#endif #include #include #else