diff options
Diffstat (limited to 'src/gui/text/coretext/qcoretextfontdatabase.mm')
-rw-r--r-- | src/gui/text/coretext/qcoretextfontdatabase.mm | 1023 |
1 files changed, 1023 insertions, 0 deletions
diff --git a/src/gui/text/coretext/qcoretextfontdatabase.mm b/src/gui/text/coretext/qcoretextfontdatabase.mm new file mode 100644 index 0000000000..19f3a2b335 --- /dev/null +++ b/src/gui/text/coretext/qcoretextfontdatabase.mm @@ -0,0 +1,1023 @@ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +#include "qglobal.h" + +#include <sys/param.h> + +#if defined(Q_OS_MACOS) +#import <AppKit/AppKit.h> +#import <IOKit/graphics/IOGraphicsLib.h> +#elif defined(QT_PLATFORM_UIKIT) +#import <UIKit/UIFont.h> +#endif + +#include <QtCore/qelapsedtimer.h> +#include <QtCore/private/qcore_mac_p.h> + +#include "qcoretextfontdatabase_p.h" +#include "qfontengine_coretext_p.h" +#if QT_CONFIG(settings) +#include <QtCore/QSettings> +#endif +#include <QtCore/QtEndian> +#ifndef QT_NO_FREETYPE +#include <QtGui/private/qfontengine_ft_p.h> +#endif + +#include <QtGui/qpa/qwindowsysteminterface.h> + +QT_BEGIN_NAMESPACE + +using namespace Qt::StringLiterals; + +QT_IMPL_METATYPE_EXTERN_TAGGED(QCFType<CGFontRef>, QCFType_CGFontRef) +QT_IMPL_METATYPE_EXTERN_TAGGED(QCFType<CFURLRef>, QCFType_CFURLRef) + +// 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[][8] = { + "", // 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 + "", // Symbol + "sga", // Ogham + "non", // Runic + "man" // N'Ko +}; +enum { LanguageCount = sizeof languageForWritingSystem / sizeof *languageForWritingSystem }; + +QCoreTextFontDatabase::QCoreTextFontDatabase() + : m_hasPopulatedAliases(false) +{ +#if defined(Q_OS_MACOS) + m_fontSetObserver = QMacNotificationObserver(nil, NSFontSetChangedNotification, [] { + qCDebug(lcQpaFonts) << "Fonts have changed"; + QPlatformFontDatabase::repopulateFontDatabase(); + }); +#endif +} + +QCoreTextFontDatabase::~QCoreTextFontDatabase() +{ + qDeleteAll(m_themeFonts); +} + +CTFontDescriptorRef descriptorForFamily(const QString &familyName) +{ + return CTFontDescriptorCreateWithAttributes(CFDictionaryRef(@{ + (id)kCTFontFamilyNameAttribute: familyName.toNSString() + })); +} +void QCoreTextFontDatabase::populateFontDatabase() +{ + qCDebug(lcQpaFonts) << "Populating font database..."; + QElapsedTimer elapsed; + if (lcQpaFonts().isDebugEnabled()) + elapsed.start(); + + QCFType<CFArrayRef> familyNames = CTFontManagerCopyAvailableFontFamilyNames(); + for (NSString *familyName in familyNames.as<const NSArray *>()) + QPlatformFontDatabase::registerFontFamily(QString::fromNSString(familyName)); + + // Some fonts has special handling since macOS Catalina: It is available + // on the platform, so that it may be used by applications directly, but does not + // get enumerated. Since there are no alternatives, we hardcode it. + if (QOperatingSystemVersion::current() >= QOperatingSystemVersion::MacOSCatalina + && !qEnvironmentVariableIsSet("QT_NO_HARDCODED_FALLBACK_FONTS")) { + m_hardcodedFallbackFonts[QChar::Script_Adlam] = QStringLiteral("Noto Sans Adlam"); + m_hardcodedFallbackFonts[QChar::Script_Ahom] = QStringLiteral("Noto Serif Ahom"); + m_hardcodedFallbackFonts[QChar::Script_Avestan] = QStringLiteral("Noto Sans Avestan"); + m_hardcodedFallbackFonts[QChar::Script_Balinese] = QStringLiteral("Noto Serif Balinese"); + m_hardcodedFallbackFonts[QChar::Script_Bamum] = QStringLiteral("Noto Sans Bamum"); + m_hardcodedFallbackFonts[QChar::Script_BassaVah] = QStringLiteral("Noto Sans Bassa Vah"); + m_hardcodedFallbackFonts[QChar::Script_Batak] = QStringLiteral("Noto Sans Batak"); + m_hardcodedFallbackFonts[QChar::Script_Bhaiksuki] = QStringLiteral("Noto Sans Bhaiksuki"); + m_hardcodedFallbackFonts[QChar::Script_Brahmi] = QStringLiteral("Noto Sans Brahmi"); + m_hardcodedFallbackFonts[QChar::Script_Buginese] = QStringLiteral("Noto Sans Buginese"); + m_hardcodedFallbackFonts[QChar::Script_Buhid] = QStringLiteral("Noto Sans Buhid"); + m_hardcodedFallbackFonts[QChar::Script_Carian] = QStringLiteral("Noto Sans Carian"); + m_hardcodedFallbackFonts[QChar::Script_CaucasianAlbanian] = QStringLiteral("Noto Sans Caucasian Albanian"); + m_hardcodedFallbackFonts[QChar::Script_Chakma] = QStringLiteral("Noto Sans Chakma"); + m_hardcodedFallbackFonts[QChar::Script_Cham] = QStringLiteral("Noto Sans Cham"); + m_hardcodedFallbackFonts[QChar::Script_Coptic] = QStringLiteral("Noto Sans Coptic"); + m_hardcodedFallbackFonts[QChar::Script_Cuneiform] = QStringLiteral("Noto Sans Cuneiform"); + m_hardcodedFallbackFonts[QChar::Script_Cypriot] = QStringLiteral("Noto Sans Cypriot"); + m_hardcodedFallbackFonts[QChar::Script_Duployan] = QStringLiteral("Noto Sans Duployan"); + m_hardcodedFallbackFonts[QChar::Script_EgyptianHieroglyphs] = QStringLiteral("Noto Sans Egyptian Hieroglyphs"); + m_hardcodedFallbackFonts[QChar::Script_Elbasan] = QStringLiteral("Noto Sans Elbasan"); + m_hardcodedFallbackFonts[QChar::Script_Glagolitic] = QStringLiteral("Noto Sans Glagolitic"); + m_hardcodedFallbackFonts[QChar::Script_Gothic] = QStringLiteral("Noto Sans Gothic"); + m_hardcodedFallbackFonts[QChar::Script_HanifiRohingya] = QStringLiteral("Noto Sans Hanifi Rohingya"); + m_hardcodedFallbackFonts[QChar::Script_Hanunoo] = QStringLiteral("Noto Sans Hanunoo"); + m_hardcodedFallbackFonts[QChar::Script_Hatran] = QStringLiteral("Noto Sans Hatran"); + m_hardcodedFallbackFonts[QChar::Script_ImperialAramaic] = QStringLiteral("Noto Sans Imperial Aramaic"); + m_hardcodedFallbackFonts[QChar::Script_InscriptionalPahlavi] = QStringLiteral("Noto Sans Inscriptional Pahlavi"); + m_hardcodedFallbackFonts[QChar::Script_InscriptionalParthian] = QStringLiteral("Noto Sans Inscriptional Parthian"); + m_hardcodedFallbackFonts[QChar::Script_Javanese] = QStringLiteral("Noto Sans Javanese"); + m_hardcodedFallbackFonts[QChar::Script_Kaithi] = QStringLiteral("Noto Sans Kaithi"); + m_hardcodedFallbackFonts[QChar::Script_KayahLi] = QStringLiteral("Noto Sans Kayah Li"); + m_hardcodedFallbackFonts[QChar::Script_Kharoshthi] = QStringLiteral("Noto Sans Kharoshthi"); + m_hardcodedFallbackFonts[QChar::Script_Khojki] = QStringLiteral("Noto Sans Khojki"); + m_hardcodedFallbackFonts[QChar::Script_Khudawadi] = QStringLiteral("Noto Sans Khudawadi"); + m_hardcodedFallbackFonts[QChar::Script_Lepcha] = QStringLiteral("Noto Sans Lepcha"); + m_hardcodedFallbackFonts[QChar::Script_Limbu] = QStringLiteral("Noto Sans Limbu"); + m_hardcodedFallbackFonts[QChar::Script_LinearA] = QStringLiteral("Noto Sans Linear A"); + m_hardcodedFallbackFonts[QChar::Script_LinearB] = QStringLiteral("Noto Sans Linear B"); + m_hardcodedFallbackFonts[QChar::Script_Lisu] = QStringLiteral("Noto Sans Lisu"); + m_hardcodedFallbackFonts[QChar::Script_Lycian] = QStringLiteral("Noto Sans Lycian"); + m_hardcodedFallbackFonts[QChar::Script_Lydian] = QStringLiteral("Noto Sans Lydian"); + m_hardcodedFallbackFonts[QChar::Script_Mahajani] = QStringLiteral("Noto Sans Mahajani"); + m_hardcodedFallbackFonts[QChar::Script_Mandaic] = QStringLiteral("Noto Sans Mandaic"); + m_hardcodedFallbackFonts[QChar::Script_Manichaean] = QStringLiteral("Noto Sans Manichaean"); + m_hardcodedFallbackFonts[QChar::Script_Marchen] = QStringLiteral("Noto Sans Marchen"); + m_hardcodedFallbackFonts[QChar::Script_MendeKikakui] = QStringLiteral("Noto Sans Mende Kikakui"); + m_hardcodedFallbackFonts[QChar::Script_MeroiticCursive] = QStringLiteral("Noto Sans Meroitic"); + m_hardcodedFallbackFonts[QChar::Script_MeroiticHieroglyphs] = QStringLiteral("Noto Sans Meroitic"); + m_hardcodedFallbackFonts[QChar::Script_Miao] = QStringLiteral("Noto Sans Miao"); + m_hardcodedFallbackFonts[QChar::Script_Modi] = QStringLiteral("Noto Sans Modi"); + m_hardcodedFallbackFonts[QChar::Script_Mongolian] = QStringLiteral("Noto Sans Mongolian"); + m_hardcodedFallbackFonts[QChar::Script_Mro] = QStringLiteral("Noto Sans Mro"); + m_hardcodedFallbackFonts[QChar::Script_MeeteiMayek] = QStringLiteral("Noto Sans Meetei Mayek"); + m_hardcodedFallbackFonts[QChar::Script_Multani] = QStringLiteral("Noto Sans Multani"); + m_hardcodedFallbackFonts[QChar::Script_Nabataean] = QStringLiteral("Noto Sans Nabataean"); + m_hardcodedFallbackFonts[QChar::Script_Newa] = QStringLiteral("Noto Sans Newa"); + m_hardcodedFallbackFonts[QChar::Script_NewTaiLue] = QStringLiteral("Noto Sans New Tai Lue"); + m_hardcodedFallbackFonts[QChar::Script_Nko] = QStringLiteral("Noto Sans Nko"); + m_hardcodedFallbackFonts[QChar::Script_OlChiki] = QStringLiteral("Noto Sans Ol Chiki"); + m_hardcodedFallbackFonts[QChar::Script_OldHungarian] = QStringLiteral("Noto Sans Old Hungarian"); + m_hardcodedFallbackFonts[QChar::Script_OldItalic] = QStringLiteral("Noto Sans Old Italic"); + m_hardcodedFallbackFonts[QChar::Script_OldNorthArabian] = QStringLiteral("Noto Sans Old North Arabian"); + m_hardcodedFallbackFonts[QChar::Script_OldPermic] = QStringLiteral("Noto Sans Old Permic"); + m_hardcodedFallbackFonts[QChar::Script_OldPersian] = QStringLiteral("Noto Sans Old Persian"); + m_hardcodedFallbackFonts[QChar::Script_OldSouthArabian] = QStringLiteral("Noto Sans Old South Arabian"); + m_hardcodedFallbackFonts[QChar::Script_OldTurkic] = QStringLiteral("Noto Sans Old Turkic"); + m_hardcodedFallbackFonts[QChar::Script_Osage] = QStringLiteral("Noto Sans Osage"); + m_hardcodedFallbackFonts[QChar::Script_Osmanya] = QStringLiteral("Noto Sans Osmanya"); + m_hardcodedFallbackFonts[QChar::Script_PahawhHmong] = QStringLiteral("Noto Sans Pahawh Hmong"); + m_hardcodedFallbackFonts[QChar::Script_Palmyrene] = QStringLiteral("Noto Sans Palmyrene"); + m_hardcodedFallbackFonts[QChar::Script_PauCinHau] = QStringLiteral("Noto Sans Pau Cin Hau"); + m_hardcodedFallbackFonts[QChar::Script_PhagsPa] = QStringLiteral("Noto Sans PhagsPa"); + m_hardcodedFallbackFonts[QChar::Script_Phoenician] = QStringLiteral("Noto Sans Phoenician"); + m_hardcodedFallbackFonts[QChar::Script_PsalterPahlavi] = QStringLiteral("Noto Sans Psalter Pahlavi"); + m_hardcodedFallbackFonts[QChar::Script_Rejang] = QStringLiteral("Noto Sans Rejang"); + m_hardcodedFallbackFonts[QChar::Script_Samaritan] = QStringLiteral("Noto Sans Samaritan"); + m_hardcodedFallbackFonts[QChar::Script_Saurashtra] = QStringLiteral("Noto Sans Saurashtra"); + m_hardcodedFallbackFonts[QChar::Script_Sharada] = QStringLiteral("Noto Sans Sharada"); + m_hardcodedFallbackFonts[QChar::Script_Siddham] = QStringLiteral("Noto Sans Siddham"); + m_hardcodedFallbackFonts[QChar::Script_SoraSompeng] = QStringLiteral("Noto Sans Sora Sompeng"); + m_hardcodedFallbackFonts[QChar::Script_Sundanese] = QStringLiteral("Noto Sans Sundanese"); + m_hardcodedFallbackFonts[QChar::Script_SylotiNagri] = QStringLiteral("Noto Sans Syloti Nagri"); + m_hardcodedFallbackFonts[QChar::Script_Tagalog] = QStringLiteral("Noto Sans Tagalog"); + m_hardcodedFallbackFonts[QChar::Script_Tagbanwa] = QStringLiteral("Noto Sans Tagbanwa"); + m_hardcodedFallbackFonts[QChar::Script_Takri] = QStringLiteral("Noto Sans Takri"); + m_hardcodedFallbackFonts[QChar::Script_TaiLe] = QStringLiteral("Noto Sans Tai Le"); + m_hardcodedFallbackFonts[QChar::Script_TaiTham] = QStringLiteral("Noto Sans Tai Tham"); + m_hardcodedFallbackFonts[QChar::Script_TaiViet] = QStringLiteral("Noto Sans Tai Viet"); + m_hardcodedFallbackFonts[QChar::Script_Thaana] = QStringLiteral("Noto Sans Thaana"); + m_hardcodedFallbackFonts[QChar::Script_Tifinagh] = QStringLiteral("Noto Sans Tifinagh"); + m_hardcodedFallbackFonts[QChar::Script_Tirhuta] = QStringLiteral("Noto Sans Tirhuta"); + m_hardcodedFallbackFonts[QChar::Script_Ugaritic] = QStringLiteral("Noto Sans Ugaritic"); + m_hardcodedFallbackFonts[QChar::Script_Vai] = QStringLiteral("Noto Sans Vai"); + m_hardcodedFallbackFonts[QChar::Script_WarangCiti] = QStringLiteral("Noto Sans Warang Citi"); + m_hardcodedFallbackFonts[QChar::Script_Wancho] = QStringLiteral("Noto Sans Wancho"); + m_hardcodedFallbackFonts[QChar::Script_Yi] = QStringLiteral("Noto Sans Yi"); + } + + qCDebug(lcQpaFonts) << "Populating available families took" << elapsed.restart() << "ms"; + + populateThemeFonts(); + + for (auto familyName : m_systemFontDescriptors.keys()) { + for (auto fontDescriptor : m_systemFontDescriptors.value(familyName)) + populateFromDescriptor(fontDescriptor, familyName); + } + + // The font database now has a reference to the original descriptors + m_systemFontDescriptors.clear(); + + qCDebug(lcQpaFonts) << "Populating system descriptors took" << elapsed.restart() << "ms"; + + Q_ASSERT(!m_hasPopulatedAliases); +} + +bool QCoreTextFontDatabase::populateFamilyAliases(const QString &missingFamily) +{ +#if defined(Q_OS_MACOS) + if (isFamilyPopulated(missingFamily)) { + // We got here because one of the other properties of the font mismatched, + // for example the style, so there's no point in populating font aliases. + return false; + } + + if (m_hasPopulatedAliases) + return false; + + // There's no API to go from a localized family name to its non-localized + // name, so we have to resort to enumerating all the available fonts and + // doing a reverse lookup. + + qCDebug(lcQpaFonts) << "Populating family aliases..."; + QElapsedTimer elapsed; + elapsed.start(); + + QString nonLocalizedMatch; + QCFType<CFArrayRef> familyNames = CTFontManagerCopyAvailableFontFamilyNames(); + NSFontManager *fontManager = NSFontManager.sharedFontManager; + for (NSString *familyName in familyNames.as<const NSArray *>()) { + NSString *localizedFamilyName = [fontManager localizedNameForFamily:familyName face:nil]; + if (![localizedFamilyName isEqual:familyName]) { + QString nonLocalizedFamily = QString::fromNSString(familyName); + QString localizedFamily = QString::fromNSString(localizedFamilyName); + QPlatformFontDatabase::registerAliasToFontFamily(nonLocalizedFamily, localizedFamily); + if (localizedFamily == missingFamily) + nonLocalizedMatch = nonLocalizedFamily; + } + } + m_hasPopulatedAliases = true; + + if (lcQpaFonts().isWarningEnabled()) { + QString warningMessage; + QDebug msg(&warningMessage); + + msg << "Populating font family aliases took" << elapsed.restart() << "ms."; + if (!nonLocalizedMatch.isNull()) + msg << "Replace uses of" << missingFamily << "with its non-localized name" << nonLocalizedMatch; + else + msg << "Replace uses of missing font family" << missingFamily << "with one that exists"; + msg << "to avoid this cost."; + + qCWarning(lcQpaFonts) << qPrintable(warningMessage); + } + + return true; +#else + Q_UNUSED(missingFamily); + return false; +#endif +} + +CTFontDescriptorRef descriptorForFamily(const char *familyName) +{ + return descriptorForFamily(QString::fromLatin1(familyName)); +} + +void QCoreTextFontDatabase::populateFamily(const QString &familyName) +{ + qCDebug(lcQpaFonts) << "Populating family" << familyName; + + // A single family might match several different fonts with different styles. + // We need to add them all so that the font database has the full picture, + // as once a family has been populated we will not populate it again. + QCFType<CTFontDescriptorRef> familyDescriptor = descriptorForFamily(familyName); + QCFType<CFArrayRef> matchingFonts = CTFontDescriptorCreateMatchingFontDescriptors(familyDescriptor, nullptr); + if (!matchingFonts) { + qCWarning(lcQpaFonts) << "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() +{ + qCDebug(lcQpaFonts) << "Invalidating font database"; + m_hasPopulatedAliases = false; + + qDeleteAll(m_themeFonts); + m_themeFonts.clear(); + QWindowSystemInterface::handleThemeChange<QWindowSystemInterface::SynchronousDelivery>(); +} + +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<CFDictionaryRef> 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<CTFontRef> tempFont = CTFontCreateWithFontDescriptor(font, 0.0, 0)) { + uint tag = QFont::Tag("OS/2").value(); + CTFontRef tempFontRef = tempFont; + void *userData = reinterpret_cast<void *>(&tempFontRef); + uint length = 128; + QVarLengthArray<uchar, 128> 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); + } + fd->writingSystems = QPlatformFontDatabase::writingSystemsFromOS2Table(reinterpret_cast<const char *>(os2Table.data()), length); + } + } + + 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<CFNumberRef> 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<CFArrayRef> 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, QFontDatabasePrivate::ApplicationFont *applicationFont) +{ + 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<QString>(fd.familyName); + + if (applicationFont != nullptr) { + QFontDatabasePrivate::ApplicationFont::Properties properties; + properties.familyName = family; + properties.styleName = fd.styleName; + properties.weight = fd.weight; + properties.stretch = fd.stretch; + properties.style = fd.style; + + applicationFont->properties.append(properties); + } + + 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 <typename T> +T *descriptorAttribute(CTFontDescriptorRef descriptor, CFStringRef name) +{ + return [static_cast<T *>(CTFontDescriptorCopyAttribute(descriptor, name)) autorelease]; +} + +void QCoreTextFontDatabase::releaseHandle(void *handle) +{ + CTFontDescriptorRef descriptor = static_cast<CTFontDescriptorRef>(handle); + if (NSValue *fontDataValue = descriptorAttribute<NSValue>(descriptor, (CFStringRef)kQtFontDataAttribute)) { + QByteArray *fontData = static_cast<QByteArray *>(fontDataValue.pointerValue); + delete fontData; + } + CFRelease(descriptor); +} + +extern CGAffineTransform qt_transform_from_fontdef(const QFontDef &fontDef); + +template <> +QFontEngine *QCoreTextFontDatabaseEngineFactory<QCoreTextFontEngine>::fontEngine(const QFontDef &fontDef, void *usrPtr) +{ + QCFType<CTFontDescriptorRef> descriptor = QCFType<CTFontDescriptorRef>::constructFromGet( + static_cast<CTFontDescriptorRef>(usrPtr)); + + // 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 (!fontDef.variableAxisValues.isEmpty()) { + QCFType<CFMutableDictionaryRef> variations = CFDictionaryCreateMutable(nullptr, + fontDef.variableAxisValues.size(), + &kCFTypeDictionaryKeyCallBacks, + &kCFTypeDictionaryValueCallBacks); + for (auto it = fontDef.variableAxisValues.constBegin(); + it != fontDef.variableAxisValues.constEnd(); + ++it) { + const quint32 tag = it.key().value(); + const float value = it.value(); + QCFType<CFNumberRef> tagRef = CFNumberCreate(nullptr, kCFNumberIntType, &tag); + QCFType<CFNumberRef> valueRef = CFNumberCreate(nullptr, kCFNumberFloatType, &value); + + CFDictionarySetValue(variations, tagRef, valueRef); + } + QCFType<CFDictionaryRef> attributes = CFDictionaryCreate(nullptr, + (const void **) &kCTFontVariationAttribute, + (const void **) &variations, + 1, + &kCFTypeDictionaryKeyCallBacks, + &kCFTypeDictionaryValueCallBacks); + descriptor = CTFontDescriptorCreateCopyWithAttributes(descriptor, attributes); + } + + if (QCFType<CTFontRef> font = CTFontCreateWithFontDescriptor(descriptor, scaledPointSize, &matrix)) + return new QCoreTextFontEngine(font, fontDef); + + return nullptr; +} + +#ifndef QT_NO_FREETYPE +template <> +QFontEngine *QCoreTextFontDatabaseEngineFactory<QFontEngineFT>::fontEngine(const QFontDef &fontDef, void *usrPtr) +{ + CTFontDescriptorRef descriptor = static_cast<CTFontDescriptorRef>(usrPtr); + + if (NSValue *fontDataValue = descriptorAttribute<NSValue>(descriptor, (CFStringRef)kQtFontDataAttribute)) { + QByteArray *fontData = static_cast<QByteArray *>(fontDataValue.pointerValue); + return QFontEngineFT::create(*fontData, fontDef.pixelSize, + static_cast<QFont::HintingPreference>(fontDef.hintingPreference), fontDef.variableAxisValues); + } else if (NSURL *url = descriptorAttribute<NSURL>(descriptor, kCTFontURLAttribute)) { + QFontEngine::FaceId faceId; + + Q_ASSERT(url.fileURL); + QString faceFileName{QString::fromNSString(url.path)}; + faceId.filename = faceFileName.toUtf8(); + + QString styleName = QCFString(CTFontDescriptorCopyAttribute(descriptor, kCTFontStyleNameAttribute)); + faceId.index = QFreetypeFace::getFaceIndexByStyleName(faceFileName, styleName); + + faceId.variableAxes = fontDef.variableAxisValues; + + return QFontEngineFT::create(fontDef, faceId); + } + // We end up here with a descriptor does not contain Qt font data or kCTFontURLAttribute. + // Since the FT engine can't deal with a descriptor with just a NSFontNameAttribute, + // we should return nullptr. + return nullptr; +} +#endif + +template <class T> +QFontEngine *QCoreTextFontDatabaseEngineFactory<T>::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<QCoreTextFontEngine>; +#ifndef QT_NO_FREETYPE +template class QCoreTextFontDatabaseEngineFactory<QFontEngineFT>; +#endif + +CFArrayRef fallbacksForDescriptor(CTFontDescriptorRef descriptor) +{ + QCFType<CTFontRef> font = CTFontCreateWithFontDescriptor(descriptor, 0.0, nullptr); + if (!font) { + qCWarning(lcQpaFonts) << "Failed to create fallback font for" << descriptor; + return nullptr; + } + + CFArrayRef cascadeList = CFArrayRef(CTFontCopyDefaultCascadeListForLanguages(font, + (CFArrayRef)NSLocale.preferredLanguages)); + + if (!cascadeList) { + qCWarning(lcQpaFonts) << "Failed to create fallback cascade list for" << descriptor; + return nullptr; + } + + return cascadeList; +} + +CFArrayRef QCoreTextFontDatabase::fallbacksForFamily(const QString &family) +{ + if (family.isEmpty()) + return nullptr; + + QCFType<CTFontDescriptorRef> fontDescriptor = descriptorForFamily(family); + if (!fontDescriptor) { + qCWarning(lcQpaFonts) << "Failed to create fallback font descriptor for" << family; + return nullptr; + } + + // If the font is not available we want to fall back to the style hint. + // By creating a matching font descriptor we can verify whether the font + // is available or not, and avoid CTFontCreateWithFontDescriptor picking + // a default font for us based on incomplete information. + fontDescriptor = CTFontDescriptorCreateMatchingFontDescriptor(fontDescriptor, 0); + if (!fontDescriptor) + return nullptr; + + return fallbacksForDescriptor(fontDescriptor); +} + +CTFontDescriptorRef descriptorForFontType(CTFontUIFontType uiType) +{ + static const CGFloat kDefaultSizeForRequestedUIType = 0.0; + QCFType<CTFontRef> ctFont = CTFontCreateUIFontForLanguage( + uiType, kDefaultSizeForRequestedUIType, nullptr); + return CTFontCopyFontDescriptor(ctFont); +} + +CTFontDescriptorRef descriptorForStyle(QFont::StyleHint styleHint) +{ + switch (styleHint) { + case QFont::SansSerif: return descriptorForFamily("Helvetica"); + case QFont::Serif: return descriptorForFamily("Times New Roman"); + case QFont::Monospace: return descriptorForFamily("Menlo"); +#ifdef Q_OS_MACOS + case QFont::Cursive: return descriptorForFamily("Apple Chancery"); +#endif + case QFont::Fantasy: return descriptorForFamily("Zapfino"); + case QFont::TypeWriter: return descriptorForFamily("American Typewriter"); + case QFont::AnyStyle: Q_FALLTHROUGH(); + case QFont::System: return descriptorForFontType(kCTFontUIFontSystem); + default: return nullptr; // No matching font on this platform + } +} + +QStringList QCoreTextFontDatabase::fallbacksForFamily(const QString &family, QFont::Style style, QFont::StyleHint styleHint, QChar::Script script) const +{ + Q_UNUSED(style); + + qCDebug(lcQpaFonts).nospace() << "Resolving fallbacks families for" + << (!family.isEmpty() ? qPrintable(" family '%1' with"_L1.arg(family)) : "") + << " style hint " << styleHint; + + QMacAutoReleasePool pool; + + QStringList fallbackList; + + QCFType<CFArrayRef> fallbackFonts = fallbacksForFamily(family); + if (!fallbackFonts || !CFArrayGetCount(fallbackFonts)) { + // We were not able to find a fallback for the specific family, + // or the family was empty, so we fall back to the style hint. + if (!family.isEmpty()) + qCDebug(lcQpaFonts) << "No fallbacks found. Using style hint instead"; + + if (QCFType<CTFontDescriptorRef> styleDescriptor = descriptorForStyle(styleHint)) { + CFMutableArrayRef tmp = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks); + CFArrayAppendValue(tmp, styleDescriptor); + QCFType<CFArrayRef> styleFallbacks = fallbacksForDescriptor(styleDescriptor); + CFArrayAppendArray(tmp, styleFallbacks, CFRangeMake(0, CFArrayGetCount(styleFallbacks))); + fallbackFonts = tmp; + } + } + + if (!fallbackFonts) + return fallbackList; + + const int numberOfFallbacks = CFArrayGetCount(fallbackFonts); + for (int i = 0; i < numberOfFallbacks; ++i) { + auto fallbackDescriptor = CTFontDescriptorRef(CFArrayGetValueAtIndex(fallbackFonts, i)); + auto fallbackFamilyName = QCFString(CTFontDescriptorCopyAttribute(fallbackDescriptor, kCTFontFamilyNameAttribute)); + + if (!isFamilyPopulated(fallbackFamilyName)) { + // We need to populate, or at least register the fallback fonts, + // otherwise the Qt font database may not know they exist. + if (isPrivateFontFamily(fallbackFamilyName)) + const_cast<QCoreTextFontDatabase *>(this)->populateFromDescriptor(fallbackDescriptor); + else + registerFontFamily(fallbackFamilyName); + } + + fallbackList.append(fallbackFamilyName); + } + + // Some fallback fonts will have have an order in the list returned + // by Core Text that would indicate they should be preferred for e.g. + // Arabic, or Emoji, while in reality only supporting a tiny subset + // of the required glyphs, or representing them by question marks. + // Move these to the end, so that the proper fonts are preferred. + for (const char *family : { ".Apple Symbols Fallback", ".Noto Sans Universal" }) { + int index = fallbackList.indexOf(QLatin1StringView(family)); + if (index >= 0) + fallbackList.move(index, fallbackList.size() - 1); + } + +#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")); + // Some Noto* fonts are not automatically enumerated by system, despite being the main + // fonts for their writing system. + QString hardcodedFont = m_hardcodedFallbackFonts.value(script); + if (!hardcodedFont.isEmpty() && !fallbackList.contains(hardcodedFont)) { + if (!isFamilyPopulated(hardcodedFont)) { + if (!m_privateFamilies.contains(hardcodedFont)) { + QCFType<CTFontDescriptorRef> familyDescriptor = descriptorForFamily(hardcodedFont); + QCFType<CFArrayRef> matchingFonts = CTFontDescriptorCreateMatchingFontDescriptors(familyDescriptor, nullptr); + if (matchingFonts) { + const int numFonts = CFArrayGetCount(matchingFonts); + for (int i = 0; i < numFonts; ++i) + const_cast<QCoreTextFontDatabase *>(this)->populateFromDescriptor(CTFontDescriptorRef(CFArrayGetValueAtIndex(matchingFonts, i)), + hardcodedFont); + + fallbackList.append(hardcodedFont); + } + + // Register as private family even if the font is not found, in order to avoid + // redoing the check later. In later calls, the font will then just be ignored. + m_privateFamilies.insert(hardcodedFont); + } + } else { + fallbackList.append(hardcodedFont); + } + } +#endif + + extern QStringList qt_sort_families_by_writing_system(QChar::Script, const QStringList &); + fallbackList = qt_sort_families_by_writing_system(script, fallbackList); + + qCDebug(lcQpaFonts).nospace() << "Fallback families ordered by script " << script << ": " << fallbackList; + + return fallbackList; +} + +QStringList QCoreTextFontDatabase::addApplicationFont(const QByteArray &fontData, const QString &fileName, QFontDatabasePrivate::ApplicationFont *applicationFont) +{ + QCFType<CFArrayRef> fonts; + + if (!fontData.isEmpty()) { + QCFType<CFDataRef> fontDataReference = fontData.toRawCFData(); + if (QCFType<CFArrayRef> descriptors = CTFontManagerCreateFontDescriptorsFromData(fontDataReference)) { + CFMutableArrayRef array = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks); + const int count = CFArrayGetCount(descriptors); + + for (int i = 0; i < count; ++i) { + CTFontDescriptorRef descriptor = CTFontDescriptorRef(CFArrayGetValueAtIndex(descriptors, i)); + + // 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); + CFArrayAppendValue(array, descriptor); + } + + fonts = array; + } + } else { + QCFType<CFURLRef> 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, QString(), applicationFont); + QCFType<CFStringRef> 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(u'.') || family == "LastResort"_L1 || m_privateFamilies.contains(family)) + 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<CTFontDescriptorRef>(CFBridgingRetain(desc)); + } +#endif // QT_PLATFORM_UIKIT + + // macOS default case and iOS fallback case + return descriptorForFontType(fontTypeFromTheme(f)); +} + +void QCoreTextFontDatabase::populateThemeFonts() +{ + QMacAutoReleasePool pool; + + if (!m_themeFonts.isEmpty()) + return; + + QElapsedTimer elapsed; + if (lcQpaFonts().isDebugEnabled()) + elapsed.start(); + + qCDebug(lcQpaFonts) << "Populating theme fonts..."; + + for (long f = QPlatformTheme::SystemFont; f < QPlatformTheme::NFonts; f++) { + QPlatformTheme::Font themeFont = static_cast<QPlatformTheme::Font>(f); + QCFType<CTFontDescriptorRef> fontDescriptor = fontDescriptorFromTheme(themeFont); + FontDescription fd; + getFontDescription(fontDescriptor, &fd); + + // We might get here from QFontDatabase::systemFont() or QPlatformTheme::font(), + // before the font database has initialized itself and populated all available + // families. As a result, we can't populate the descriptor at this time, as that + // would result in the font database having > 0 families, which would result in + // skipping the initialization and population of all other font families. Instead + // we store the descriptors for later and populate them during populateFontDatabase(). + + bool haveRegisteredFamily = m_systemFontDescriptors.contains(fd.familyName); + qCDebug(lcQpaFonts) << "Got" << (haveRegisteredFamily ? "already registered" : "unseen") + << "family" << fd.familyName << "for" << themeFont; + + if (!haveRegisteredFamily) { + // We need to register all weights and variants of the theme font, + // as the user might tweak the returned QFont before use. + QList<QCFType<CTFontDescriptorRef>> themeFontVariants; + + auto addFontVariants = [&](CTFontDescriptorRef descriptor) { + QCFType<CFArrayRef> matchingDescriptors = CTFontDescriptorCreateMatchingFontDescriptors(descriptor, nullptr); + const int matchingDescriptorsCount = matchingDescriptors ? CFArrayGetCount(matchingDescriptors) : 0; + qCDebug(lcQpaFonts) << "Enumerating font variants based on" << id(descriptor) + << "resulted in" << matchingDescriptorsCount << "matching descriptors" + << matchingDescriptors.as<NSArray*>(); + + for (int i = 0; i < matchingDescriptorsCount; ++i) { + auto matchingDescriptor = CTFontDescriptorRef(CFArrayGetValueAtIndex(matchingDescriptors, i)); + themeFontVariants.append(QCFType<CTFontDescriptorRef>::constructFromGet(matchingDescriptor)); + } + }; + + // Try populating the font variants based on its UI design trait, if available + if (@available(macos 10.15, ios 13.0, *)) { + auto fontTraits = QCFType<CFDictionaryRef>(CTFontDescriptorCopyAttribute(fontDescriptor, kCTFontTraitsAttribute)); + static const NSString *kUIFontDesignTrait = @"NSCTFontUIFontDesignTrait"; + if (id uiFontDesignTrait = fontTraits.as<NSDictionary*>()[kUIFontDesignTrait]) { + QCFType<CTFontDescriptorRef> designTraitDescriptor = CTFontDescriptorCreateWithAttributes( + CFDictionaryRef(@{ (id)kCTFontTraitsAttribute: @{ kUIFontDesignTrait: uiFontDesignTrait } + })); + addFontVariants(designTraitDescriptor); + } + } + + if (themeFontVariants.isEmpty()) { + // Fall back to populating variants based on the family name alone + QCFType<CTFontDescriptorRef> familyDescriptor = descriptorForFamily(fd.familyName); + addFontVariants(familyDescriptor); + } + + if (themeFontVariants.isEmpty()) { + qCDebug(lcQpaFonts) << "No theme font variants found, falling back to single variant descriptor"; + themeFontVariants.append(fontDescriptor); + } + + m_systemFontDescriptors.insert(fd.familyName, themeFontVariants); + } + + QFont *font = new QFont(fd.familyName, fd.pointSize, fd.weight, fd.style == QFont::StyleItalic); + m_themeFonts.insert(themeFont, font); + } + + qCDebug(lcQpaFonts) << "Populating theme fonts took" << elapsed.restart() << "ms"; +} + +QFont *QCoreTextFontDatabase::themeFont(QPlatformTheme::Font f) const +{ + // The code paths via QFontDatabase::systemFont() or QPlatformTheme::font() + // do not ensure that the font database has been populated, so we need to + // manually populate the theme fonts lazily here just in case. + const_cast<QCoreTextFontDatabase*>(this)->populateThemeFonts(); + + return m_themeFonts.value(f, nullptr); +} + +QFont QCoreTextFontDatabase::defaultFont() const +{ + return QFont(*themeFont(QPlatformTheme::SystemFont)); +} + +bool QCoreTextFontDatabase::fontsAlwaysScalable() const +{ + return true; +} + +QList<int> QCoreTextFontDatabase::standardSizes() const +{ + QList<int> 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; +} + +bool QCoreTextFontDatabase::supportsVariableApplicationFonts() const +{ + return true; +} + +QT_END_NAMESPACE + |