diff options
Diffstat (limited to 'src/gui/text/coretext')
-rw-r--r-- | src/gui/text/coretext/qcoretextfontdatabase.mm | 432 | ||||
-rw-r--r-- | src/gui/text/coretext/qcoretextfontdatabase_p.h | 62 | ||||
-rw-r--r-- | src/gui/text/coretext/qfontengine_coretext.mm | 137 | ||||
-rw-r--r-- | src/gui/text/coretext/qfontengine_coretext_p.h | 45 |
4 files changed, 410 insertions, 266 deletions
diff --git a/src/gui/text/coretext/qcoretextfontdatabase.mm b/src/gui/text/coretext/qcoretextfontdatabase.mm index ee145a03d5..2197c83f29 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,104 @@ 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 + 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 +1012,10 @@ QList<int> QCoreTextFontDatabase::standardSizes() const return ret; } +bool QCoreTextFontDatabase::supportsVariableApplicationFonts() const +{ + return true; +} + QT_END_NAMESPACE diff --git a/src/gui/text/coretext/qcoretextfontdatabase_p.h b/src/gui/text/coretext/qcoretextfontdatabase_p.h index 971ac3cfde..eeea9ad640 100644 --- a/src/gui/text/coretext/qcoretextfontdatabase_p.h +++ b/src/gui/text/coretext/qcoretextfontdatabase_p.h @@ -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 #ifndef QCORETEXTFONTDATABASE_H #define QCORETEXTFONTDATABASE_H @@ -60,8 +24,8 @@ Q_FORWARD_DECLARE_CF_TYPE(CTFontDescriptor); Q_FORWARD_DECLARE_CF_TYPE(CTFont); -Q_DECLARE_METATYPE(QCFType<CGFontRef>); -Q_DECLARE_METATYPE(QCFType<CFURLRef>); +QT_DECL_METATYPE_EXTERN_TAGGED(QCFType<CGFontRef>, QCFType_CGFontRef, Q_GUI_EXPORT) +QT_DECL_METATYPE_EXTERN_TAGGED(QCFType<CFURLRef>, QCFType_CFURLRef, Q_GUI_EXPORT) QT_BEGIN_NAMESPACE @@ -82,22 +46,26 @@ public: QFont defaultFont() const override; bool fontsAlwaysScalable() const override; QList<int> standardSizes() const override; + bool supportsVariableApplicationFonts() const override; - // For iOS and OS X platform themes + // For iOS and macOS platform themes QFont *themeFont(QPlatformTheme::Font) const; - const QHash<QPlatformTheme::Font, QFont *> &themeFonts() const; - -protected: - mutable QSet<CTFontDescriptorRef> m_systemFontDescriptors; private: + void populateThemeFonts(); void populateFromDescriptor(CTFontDescriptorRef font, const QString &familyName = QString(), QFontDatabasePrivate::ApplicationFont *applicationFont = nullptr); static CFArrayRef fallbacksForFamily(const QString &family); - mutable QString defaultFontName; + QHash<QPlatformTheme::Font, QFont *> m_themeFonts; + QHash<QString, QList<QCFType<CTFontDescriptorRef>>> m_systemFontDescriptors; + QHash<QChar::Script, QString> m_hardcodedFallbackFonts; + mutable QSet<QString> m_privateFamilies; - mutable QHash<QPlatformTheme::Font, QFont *> m_themeFonts; bool m_hasPopulatedAliases; + +#if defined(Q_OS_MACOS) + QMacNotificationObserver m_fontSetObserver; +#endif }; // Split out into separate template class so that the compiler doesn't have diff --git a/src/gui/text/coretext/qfontengine_coretext.mm b/src/gui/text/coretext/qfontengine_coretext.mm index 33a3f22e67..87ed95f3ac 100644 --- a/src/gui/text/coretext/qfontengine_coretext.mm +++ b/src/gui/text/coretext/qfontengine_coretext.mm @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtGui module 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 "qfontengine_coretext_p.h" @@ -48,6 +12,9 @@ #include <QtGui/qpainterpath.h> #include <private/qcoregraphics_p.h> #include <private/qimage_p.h> +#include <private/qguiapplication_p.h> +#include <private/qstringiterator_p.h> +#include <qpa/qplatformtheme.h> #include <cmath> @@ -161,9 +128,13 @@ public: QByteArray m_fontData; }; -QCoreTextFontEngine *QCoreTextFontEngine::create(const QByteArray &fontData, qreal pixelSize, QFont::HintingPreference hintingPreference) +QCoreTextFontEngine *QCoreTextFontEngine::create(const QByteArray &fontData, + qreal pixelSize, + QFont::HintingPreference hintingPreference, + const QMap<QFont::Tag, float> &variableAxisValues) { Q_UNUSED(hintingPreference); + Q_UNUSED(variableAxisValues); QCFType<CFDataRef> fontDataReference = fontData.toRawCFData(); QCFType<CGDataProviderRef> dataProvider = CGDataProviderCreateWithCFData(fontDataReference); @@ -222,6 +193,7 @@ void QCoreTextFontEngine::init() face_id.index = 0; QCFString name = CTFontCopyName(ctfont, kCTFontUniqueNameKey); face_id.filename = QString::fromCFString(name).toUtf8(); + face_id.variableAxes = fontDef.variableAxisValues; QCFString family = CTFontCopyFamilyName(ctfont); fontDef.families = QStringList(family); @@ -253,18 +225,20 @@ void QCoreTextFontEngine::init() }; QCFType<CFDictionaryRef> allTraits = CTFontCopyTraits(ctfont); - fontDef.weight = QCoreTextFontEngine::qtWeightFromCFWeight(getTraitValue(allTraits, kCTFontWeightTrait)); int slant = static_cast<int>(getTraitValue(allTraits, kCTFontSlantTrait) * 500 + 500); if (slant > 500 && !(traits & kCTFontItalicTrait)) fontDef.style = QFont::StyleOblique; if (fontDef.weight >= QFont::Bold && !(traits & kCTFontBoldTrait) && !qEnvironmentVariableIsSet("QT_NO_SYNTHESIZED_BOLD")) synthesisFlags |= SynthesizedBold; + else + fontDef.weight = QCoreTextFontEngine::qtWeightFromCFWeight(getTraitValue(allTraits, kCTFontWeightTrait)); + if (fontDef.style != QFont::StyleNormal && !(traits & kCTFontItalicTrait) && !qEnvironmentVariableIsSet("QT_NO_SYNTHESIZED_ITALIC")) synthesisFlags |= SynthesizedItalic; avgCharWidth = 0; - QByteArray os2Table = getSfntTable(MAKE_TAG('O', 'S', '/', '2')); + QByteArray os2Table = getSfntTable(QFont::Tag("OS/2").value()); unsigned emSize = CTFontGetUnitsPerEm(ctfont); if (os2Table.size() >= 10) { fsType = qFromBigEndian<quint16>(os2Table.constData() + 8); @@ -301,29 +275,30 @@ glyph_t QCoreTextFontEngine::glyphIndex(uint ucs4) const return glyphIndices[0]; } -bool QCoreTextFontEngine::stringToCMap(const QChar *str, int len, QGlyphLayout *glyphs, - int *nglyphs, QFontEngine::ShaperFlags flags) const +int QCoreTextFontEngine::stringToCMap(const QChar *str, int len, QGlyphLayout *glyphs, + int *nglyphs, QFontEngine::ShaperFlags flags) const { Q_ASSERT(glyphs->numGlyphs >= *nglyphs); if (*nglyphs < len) { *nglyphs = len; - return false; + return -1; } QVarLengthArray<CGGlyph> cgGlyphs(len); CTFontGetGlyphsForCharacters(ctfont, (const UniChar*)str, cgGlyphs.data(), len); int glyph_pos = 0; - for (int i = 0; i < len; ++i) { - glyphs->glyphs[glyph_pos] = cgGlyphs[i]; - if (glyph_pos < i) - cgGlyphs[glyph_pos] = cgGlyphs[i]; - glyph_pos++; - - // If it's a non-BMP char, skip the lower part of surrogate pair and go - // directly to the next char without increasing glyph_pos - if (str[i].isHighSurrogate() && i < len-1 && str[i+1].isLowSurrogate()) - ++i; + int mappedGlyphs = 0; + QStringIterator it(str, str + len); + while (it.hasNext()) { + qsizetype idx = it.index(); + char32_t ucs4 = it.next(); + glyphs->glyphs[glyph_pos] = cgGlyphs[idx]; + if (glyph_pos < idx) + cgGlyphs[glyph_pos] = cgGlyphs[idx]; + if (glyphs->glyphs[glyph_pos] != 0 || isIgnorableChar(ucs4)) + mappedGlyphs++; + glyph_pos++; } *nglyphs = glyph_pos; @@ -332,15 +307,7 @@ bool QCoreTextFontEngine::stringToCMap(const QChar *str, int len, QGlyphLayout * if (!(flags & GlyphIndicesOnly)) loadAdvancesForGlyphs(cgGlyphs, glyphs); - return true; -} - -glyph_metrics_t QCoreTextFontEngine::boundingBox(const QGlyphLayout &glyphs) -{ - QFixed w; - for (int i = 0; i < glyphs.numGlyphs; ++i) - w += glyphs.effectiveAdvance(i); - return glyph_metrics_t(0, -(ascent()), w - lastRightBearing(glyphs), ascent()+descent(), w, 0); + return mappedGlyphs; } glyph_metrics_t QCoreTextFontEngine::boundingBox(glyph_t glyph) @@ -369,7 +336,10 @@ void QCoreTextFontEngine::initializeHeightMetrics() const m_descent = QFixed::fromReal(CTFontGetDescent(ctfont)); m_leading = QFixed::fromReal(CTFontGetLeading(ctfont)); - QFontEngine::initializeHeightMetrics(); + if (preferTypoLineMetrics()) + QFontEngine::initializeHeightMetrics(); + else + m_heightMetricsQueried = true; } QFixed QCoreTextFontEngine::capHeight() const @@ -404,6 +374,7 @@ bool QCoreTextFontEngine::hasColorGlyphs() const return glyphFormat == QFontEngine::Format_ARGB; } +Q_GUI_EXPORT extern bool qt_scaleForTransform(const QTransform &transform, qreal *scale); // qtransform.cpp void QCoreTextFontEngine::draw(CGContextRef ctx, qreal x, qreal y, const QTextItemInt &ti, int paintDeviceHeight) { QVarLengthArray<QFixedPoint> positions; @@ -420,7 +391,8 @@ void QCoreTextFontEngine::draw(CGContextRef ctx, qreal x, qreal y, const QTextIt CGAffineTransform cgMatrix = CGAffineTransformMake(1, 0, 0, -1, 0, -paintDeviceHeight); - CGAffineTransformConcat(cgMatrix, oldTextMatrix); + // FIXME: Should we include the old matrix here? If so we need to assign it. + Q_UNUSED(CGAffineTransformConcat(cgMatrix, oldTextMatrix)); if (synthesisFlags & QFontEngine::SynthesizedItalic) cgMatrix = CGAffineTransformConcat(cgMatrix, CGAffineTransformMake(1, 0, -SYNTHETIC_ITALIC_SKEW, 1, 0, 0)); @@ -447,7 +419,15 @@ void QCoreTextFontEngine::draw(CGContextRef ctx, qreal x, qreal y, const QTextIt CTFontDrawGlyphs(ctfont, cgGlyphs.data(), cgPositions.data(), glyphs.size(), ctx); if (synthesisFlags & QFontEngine::SynthesizedBold) { - CGContextSetTextPosition(ctx, positions[0].x.toReal() + 0.5 * lineThickness().toReal(), + QTransform matrix(cgMatrix.a, cgMatrix.b, cgMatrix.c, cgMatrix.d, cgMatrix.tx, cgMatrix.ty); + + qreal boldOffset = 0.5 * lineThickness().toReal(); + qreal scale; + qt_scaleForTransform(matrix, &scale); + boldOffset *= scale; + + CGContextSetTextPosition(ctx, + positions[0].x.toReal() + boldOffset, positions[0].y.toReal()); CTFontDrawGlyphs(ctfont, cgGlyphs.data(), cgPositions.data(), glyphs.size(), ctx); } @@ -537,7 +517,11 @@ glyph_metrics_t QCoreTextFontEngine::alphaMapBoundingBox(glyph_t glyph, const QF return QFontEngine::alphaMapBoundingBox(glyph, subPixelPosition, matrix, format); glyph_metrics_t br = boundingBox(glyph); - qcoretextfontengine_scaleMetrics(br, matrix); + + QTransform xform = matrix; + if (fontDef.stretch != 100 && fontDef.stretch != QFont::AnyStretch) + xform.scale(fontDef.stretch / 100.0, 1.0); + qcoretextfontengine_scaleMetrics(br, xform); // Normalize width and height if (br.width < 0) @@ -744,10 +728,12 @@ QImage QCoreTextFontEngine::imageForGlyph(glyph_t glyph, const QFixedPoint &subP // draw with white or black fill, and then invert the glyph image in the latter case, // producing an alpha map. This covers the most common use-cases, but longer term we // should propagate the fill color all the way from the paint engine, and include it - //in the key for the glyph cache. + // in the key for the glyph cache. - if (!qt_mac_applicationIsInDarkMode()) - return kCGColorBlack; + if (auto *platformTheme = QGuiApplicationPrivate::platformTheme()) { + if (platformTheme->colorScheme() != Qt::ColorScheme::Dark) + return kCGColorBlack; + } } return kCGColorWhite; }(); @@ -790,7 +776,13 @@ QImage QCoreTextFontEngine::imageForGlyph(glyph_t glyph, const QFixedPoint &subP CTFontDrawGlyphs(ctfont, &cgGlyph, &CGPointZero, 1, ctx); if (synthesisFlags & QFontEngine::SynthesizedBold) { - CGContextSetTextPosition(ctx, pos_x + 0.5 * lineThickness().toReal(), pos_y); + qreal boldOffset = 0.5 * lineThickness().toReal(); + + qreal scale; + qt_scaleForTransform(matrix, &scale); + boldOffset *= scale; + + CGContextSetTextPosition(ctx, pos_x + boldOffset, pos_y); CTFontDrawGlyphs(ctfont, &cgGlyph, &CGPointZero, 1, ctx); } } else { @@ -882,8 +874,9 @@ void QCoreTextFontEngine::loadAdvancesForGlyphs(QVarLengthArray<CGGlyph> &cgGlyp QVarLengthArray<CGSize> advances(numGlyphs); CTFontGetAdvancesForGlyphs(ctfont, kCTFontOrientationHorizontal, cgGlyphs.data(), advances.data(), numGlyphs); + qreal stretch = fontDef.stretch != QFont::AnyStretch ? fontDef.stretch / 100.0 : 1.0; for (int i = 0; i < numGlyphs; ++i) - glyphs->advances[i] = QFixed::fromReal(advances[i].width); + glyphs->advances[i] = QFixed::fromReal(advances[i].width * stretch); } QFontEngine::FaceId QCoreTextFontEngine::faceId() const diff --git a/src/gui/text/coretext/qfontengine_coretext_p.h b/src/gui/text/coretext/qfontengine_coretext_p.h index 361153b37a..2f388c32bc 100644 --- a/src/gui/text/coretext/qfontengine_coretext_p.h +++ b/src/gui/text/coretext/qfontengine_coretext_p.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtGui module 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 #ifndef QFONTENGINE_CORETEXT_P_H #define QFONTENGINE_CORETEXT_P_H @@ -74,10 +38,9 @@ public: ~QCoreTextFontEngine(); glyph_t glyphIndex(uint ucs4) const override; - bool stringToCMap(const QChar *str, int len, QGlyphLayout *glyphs, int *nglyphs, ShaperFlags flags) const override; + int stringToCMap(const QChar *str, int len, QGlyphLayout *glyphs, int *nglyphs, ShaperFlags flags) const override; void recalcAdvances(QGlyphLayout *, ShaperFlags) const override; - glyph_metrics_t boundingBox(const QGlyphLayout &glyphs) override; glyph_metrics_t boundingBox(glyph_t glyph) override; QFixed capHeight() const override; @@ -128,7 +91,7 @@ public: static bool ct_getSfntTable(void *user_data, uint tag, uchar *buffer, uint *length); static QFont::Weight qtWeightFromCFWeight(float value); - static QCoreTextFontEngine *create(const QByteArray &fontData, qreal pixelSize, QFont::HintingPreference hintingPreference); + static QCoreTextFontEngine *create(const QByteArray &fontData, qreal pixelSize, QFont::HintingPreference hintingPreference, const QMap<QFont::Tag, float> &variableAxisValue); protected: QCoreTextFontEngine(const QFontDef &def); |