summaryrefslogtreecommitdiffstats
path: root/src/gui/text/coretext/qcoretextfontdatabase.mm
diff options
context:
space:
mode:
Diffstat (limited to 'src/gui/text/coretext/qcoretextfontdatabase.mm')
-rw-r--r--src/gui/text/coretext/qcoretextfontdatabase.mm434
1 files changed, 328 insertions, 106 deletions
diff --git a/src/gui/text/coretext/qcoretextfontdatabase.mm b/src/gui/text/coretext/qcoretextfontdatabase.mm
index ee145a03d5..19f3a2b335 100644
--- a/src/gui/text/coretext/qcoretextfontdatabase.mm
+++ b/src/gui/text/coretext/qcoretextfontdatabase.mm
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** 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$
-**
-****************************************************************************/
+// 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"
@@ -49,6 +13,7 @@
#endif
#include <QtCore/qelapsedtimer.h>
+#include <QtCore/private/qcore_mac_p.h>
#include "qcoretextfontdatabase_p.h"
#include "qfontengine_coretext_p.h"
@@ -60,12 +25,19 @@
#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[] = {
- 0, // Any
+static const char languageForWritingSystem[][8] = {
+ "", // Any
"en", // Latin
"el", // Greek
"ru", // Cyrillic
@@ -95,24 +67,35 @@ static const char *languageForWritingSystem[] = {
"ja", // Japanese
"ko", // Korean
"vi", // Vietnamese
- 0, // Symbol
+ "", // Symbol
"sga", // Ogham
"non", // Runic
"man" // N'Ko
};
-enum { LanguageCount = sizeof(languageForWritingSystem) / sizeof(const char *) };
+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()
{
- for (CTFontDescriptorRef ref : qAsConst(m_systemFontDescriptors))
- CFRelease(ref);
+ qDeleteAll(m_themeFonts);
}
+CTFontDescriptorRef descriptorForFamily(const QString &familyName)
+{
+ return CTFontDescriptorCreateWithAttributes(CFDictionaryRef(@{
+ (id)kCTFontFamilyNameAttribute: familyName.toNSString()
+ }));
+}
void QCoreTextFontDatabase::populateFontDatabase()
{
qCDebug(lcQpaFonts) << "Populating font database...";
@@ -124,16 +107,121 @@ void QCoreTextFontDatabase::populateFontDatabase()
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";
- // Force creating the theme fonts to get the descriptors in m_systemFontDescriptors
- if (m_themeFonts.isEmpty())
- (void)themeFonts();
+ populateThemeFonts();
- qCDebug(lcQpaFonts) << "Resolving theme fonts took" << elapsed.restart() << "ms";
+ for (auto familyName : m_systemFontDescriptors.keys()) {
+ for (auto fontDescriptor : m_systemFontDescriptors.value(familyName))
+ populateFromDescriptor(fontDescriptor, familyName);
+ }
- for (CTFontDescriptorRef fontDesc : m_systemFontDescriptors)
- populateFromDescriptor(fontDesc);
+ // The font database now has a reference to the original descriptors
+ m_systemFontDescriptors.clear();
qCDebug(lcQpaFonts) << "Populating system descriptors took" << elapsed.restart() << "ms";
@@ -143,6 +231,12 @@ void QCoreTextFontDatabase::populateFontDatabase()
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;
@@ -190,14 +284,20 @@ bool QCoreTextFontDatabase::populateFamilyAliases(const QString &missingFamily)
#endif
}
+CTFontDescriptorRef descriptorForFamily(const char *familyName)
+{
+ return descriptorForFamily(QString::fromLatin1(familyName));
+}
+
void QCoreTextFontDatabase::populateFamily(const QString &familyName)
{
- QCFType<CFMutableDictionaryRef> attributes = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
- CFDictionaryAddValue(attributes, kCTFontFamilyNameAttribute, QCFString(familyName));
- QCFType<CTFontDescriptorRef> nameOnlyDescriptor = CTFontDescriptorCreateWithAttributes(attributes);
+ qCDebug(lcQpaFonts) << "Populating family" << familyName;
- // A single family might match several different fonts with different styles eg.
- QCFType<CFArrayRef> matchingFonts = (CFArrayRef) CTFontDescriptorCreateMatchingFontDescriptors(nameOnlyDescriptor, 0);
+ // 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;
@@ -210,7 +310,12 @@ void QCoreTextFontDatabase::populateFamily(const QString &familyName)
void QCoreTextFontDatabase::invalidate()
{
+ qCDebug(lcQpaFonts) << "Invalidating font database";
m_hasPopulatedAliases = false;
+
+ qDeleteAll(m_themeFonts);
+ m_themeFonts.clear();
+ QWindowSystemInterface::handleThemeChange<QWindowSystemInterface::SynchronousDelivery>();
}
struct FontDescription {
@@ -256,7 +361,7 @@ static void getFontDescription(CTFontDescriptorRef font, FontDescription *fd)
fd->fixedPitch = false;
if (QCFType<CTFontRef> tempFont = CTFontCreateWithFontDescriptor(font, 0.0, 0)) {
- uint tag = MAKE_TAG('O', 'S', '/', '2');
+ uint tag = QFont::Tag("OS/2").value();
CTFontRef tempFontRef = tempFont;
void *userData = reinterpret_cast<void *>(&tempFontRef);
uint length = 128;
@@ -313,7 +418,7 @@ static void getFontDescription(CTFontDescriptorRef font, FontDescription *fd)
if (QCFType<CFArrayRef> languages = (CFArrayRef) CTFontDescriptorCopyAttribute(font, kCTFontLanguagesAttribute)) {
CFIndex length = CFArrayGetCount(languages);
for (int i = 1; i < LanguageCount; ++i) {
- if (!languageForWritingSystem[i])
+ if (!*languageForWritingSystem[i])
continue;
QCFString lang = CFStringCreateWithCString(NULL, languageForWritingSystem[i], kCFStringEncodingASCII);
if (CFArrayContainsValue(languages, CFRangeMake(0, length), lang))
@@ -384,6 +489,31 @@ QFontEngine *QCoreTextFontDatabaseEngineFactory<QCoreTextFontEngine>::fontEngine
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);
@@ -399,11 +529,19 @@ QFontEngine *QCoreTextFontDatabaseEngineFactory<QFontEngineFT>::fontEngine(const
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));
+ static_cast<QFont::HintingPreference>(fontDef.hintingPreference), fontDef.variableAxisValues);
} else if (NSURL *url = descriptorAttribute<NSURL>(descriptor, kCTFontURLAttribute)) {
- Q_ASSERT(url.fileURL);
QFontEngine::FaceId faceId;
- faceId.filename = QString::fromNSString(url.path).toUtf8();
+
+ 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.
@@ -416,7 +554,7 @@ QFontEngine *QCoreTextFontDatabaseEngineFactory<QFontEngineFT>::fontEngine(const
template <class T>
QFontEngine *QCoreTextFontDatabaseEngineFactory<T>::fontEngine(const QByteArray &fontData, qreal pixelSize, QFont::HintingPreference hintingPreference)
{
- return T::create(fontData, pixelSize, hintingPreference);
+ return T::create(fontData, pixelSize, hintingPreference, {});
}
// Explicitly instantiate so that we don't need the plugin to involve FreeType
@@ -425,18 +563,6 @@ template class QCoreTextFontDatabaseEngineFactory<QCoreTextFontEngine>;
template class QCoreTextFontDatabaseEngineFactory<QFontEngineFT>;
#endif
-CTFontDescriptorRef descriptorForFamily(const QString &familyName)
-{
- return CTFontDescriptorCreateWithAttributes(CFDictionaryRef(@{
- (id)kCTFontFamilyNameAttribute: familyName.toNSString()
- }));
-}
-
-CTFontDescriptorRef descriptorForFamily(const char *familyName)
-{
- return descriptorForFamily(QString::fromLatin1(familyName));
-}
-
CFArrayRef fallbacksForDescriptor(CTFontDescriptorRef descriptor)
{
QCFType<CTFontRef> font = CTFontCreateWithFontDescriptor(descriptor, 0.0, nullptr);
@@ -446,7 +572,7 @@ CFArrayRef fallbacksForDescriptor(CTFontDescriptorRef descriptor)
}
CFArrayRef cascadeList = CFArrayRef(CTFontCopyDefaultCascadeListForLanguages(font,
- (CFArrayRef)[NSUserDefaults.standardUserDefaults stringArrayForKey:@"AppleLanguages"]));
+ (CFArrayRef)NSLocale.preferredLanguages));
if (!cascadeList) {
qCWarning(lcQpaFonts) << "Failed to create fallback cascade list for" << descriptor;
@@ -508,7 +634,7 @@ QStringList QCoreTextFontDatabase::fallbacksForFamily(const QString &family, QFo
Q_UNUSED(style);
qCDebug(lcQpaFonts).nospace() << "Resolving fallbacks families for"
- << (!family.isEmpty() ? qPrintable(QLatin1String(" family '%1' with").arg(family)) : "")
+ << (!family.isEmpty() ? qPrintable(" family '%1' with"_L1.arg(family)) : "")
<< " style hint " << styleHint;
QMacAutoReleasePool pool;
@@ -557,7 +683,7 @@ QStringList QCoreTextFontDatabase::fallbacksForFamily(const QString &family, QFo
// 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(QLatin1String(family));
+ int index = fallbackList.indexOf(QLatin1StringView(family));
if (index >= 0)
fallbackList.move(index, fallbackList.size() - 1);
}
@@ -574,6 +700,31 @@ QStringList QCoreTextFontDatabase::fallbacksForFamily(const QString &family, QFo
// 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 &);
@@ -590,13 +741,20 @@ QStringList QCoreTextFontDatabase::addApplicationFont(const QByteArray &fontData
if (!fontData.isEmpty()) {
QCFType<CFDataRef> fontDataReference = fontData.toRawCFData();
- if (QCFType<CTFontDescriptorRef> 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);
+ if (QCFType<CFArrayRef> descriptors = CTFontManagerCreateFontDescriptorsFromData(fontDataReference)) {
CFMutableArrayRef array = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks);
- CFArrayAppendValue(array, descriptor);
+ 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 {
@@ -624,7 +782,7 @@ QStringList QCoreTextFontDatabase::addApplicationFont(const QByteArray &fontData
bool QCoreTextFontDatabase::isPrivateFontFamily(const QString &family) const
{
- if (family.startsWith(QLatin1Char('.')) || family == QLatin1String("LastResort"))
+ if (family.startsWith(u'.') || family == "LastResort"_L1 || m_privateFamilies.contains(family))
return true;
return QPlatformFontDatabase::isPrivateFontFamily(family);
@@ -738,47 +896,106 @@ static CTFontDescriptorRef fontDescriptorFromTheme(QPlatformTheme::Font f)
UIFontDescriptor *desc = [UIFontDescriptor preferredFontDescriptorWithTextStyle:textStyle];
return static_cast<CTFontDescriptorRef>(CFBridgingRetain(desc));
}
-#endif // Q_OS_IOS, Q_OS_TVOS, Q_OS_WATCHOS
+#endif // QT_PLATFORM_UIKIT
// macOS default case and iOS fallback case
return descriptorForFontType(fontTypeFromTheme(f));
}
-const QHash<QPlatformTheme::Font, QFont *> &QCoreTextFontDatabase::themeFonts() const
+void QCoreTextFontDatabase::populateThemeFonts()
{
- if (m_themeFonts.isEmpty()) {
- for (long f = QPlatformTheme::SystemFont; f < QPlatformTheme::NFonts; f++) {
- QPlatformTheme::Font ft = static_cast<QPlatformTheme::Font>(f);
- m_themeFonts.insert(ft, themeFont(ft));
+ 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);
}
- return m_themeFonts;
+ qCDebug(lcQpaFonts) << "Populating theme fonts took" << elapsed.restart() << "ms";
}
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);
+ // 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();
- QFont *font = new QFont(fd.familyName, fd.pointSize, fd.weight, fd.style == QFont::StyleItalic);
- return font;
+ return m_themeFonts.value(f, nullptr);
}
QFont QCoreTextFontDatabase::defaultFont() const
{
- if (defaultFontName.isEmpty()) {
- QCFType<CTFontDescriptorRef> systemFont = descriptorForFontType(kCTFontUIFontSystem);
- defaultFontName = QCFString(CTFontDescriptorCopyAttribute(systemFont, kCTFontFamilyNameAttribute));
- }
-
- return QFont(defaultFontName);
+ return QFont(*themeFont(QPlatformTheme::SystemFont));
}
bool QCoreTextFontDatabase::fontsAlwaysScalable() const
@@ -797,5 +1014,10 @@ QList<int> QCoreTextFontDatabase::standardSizes() const
return ret;
}
+bool QCoreTextFontDatabase::supportsVariableApplicationFonts() const
+{
+ return true;
+}
+
QT_END_NAMESPACE