summaryrefslogtreecommitdiffstats
path: root/src/gui/text
diff options
context:
space:
mode:
Diffstat (limited to 'src/gui/text')
-rw-r--r--src/gui/text/coretext/coretext.pri19
-rw-r--r--src/gui/text/coretext/qcoretextfontdatabase.mm439
-rw-r--r--src/gui/text/coretext/qcoretextfontdatabase_p.h62
-rw-r--r--src/gui/text/coretext/qfontengine_coretext.mm153
-rw-r--r--src/gui/text/coretext/qfontengine_coretext_p.h60
-rw-r--r--src/gui/text/freetype/freetype.pri9
-rw-r--r--src/gui/text/freetype/qfontengine_ft.cpp446
-rw-r--r--src/gui/text/freetype/qfontengine_ft_p.h122
-rw-r--r--src/gui/text/freetype/qfreetypefontdatabase.cpp239
-rw-r--r--src/gui/text/freetype/qfreetypefontdatabase_p.h55
-rw-r--r--src/gui/text/qabstracttextdocumentlayout.cpp50
-rw-r--r--src/gui/text/qabstracttextdocumentlayout.h43
-rw-r--r--src/gui/text/qabstracttextdocumentlayout_p.h53
-rw-r--r--src/gui/text/qcssparser.cpp424
-rw-r--r--src/gui/text/qcssparser_p.h106
-rw-r--r--src/gui/text/qcssscanner.cpp42
-rw-r--r--src/gui/text/qdistancefield.cpp54
-rw-r--r--src/gui/text/qdistancefield_p.h40
-rw-r--r--src/gui/text/qfont.cpp943
-rw-r--r--src/gui/text/qfont.h135
-rw-r--r--src/gui/text/qfont_p.h75
-rw-r--r--src/gui/text/qfontdatabase.cpp877
-rw-r--r--src/gui/text/qfontdatabase.h68
-rw-r--r--src/gui/text/qfontdatabase_p.h96
-rw-r--r--src/gui/text/qfontengine.cpp430
-rw-r--r--src/gui/text/qfontengine_p.h127
-rw-r--r--src/gui/text/qfontengineglyphcache.cpp40
-rw-r--r--src/gui/text/qfontengineglyphcache_p.h42
-rw-r--r--src/gui/text/qfontinfo.h49
-rw-r--r--src/gui/text/qfontmetrics.cpp324
-rw-r--r--src/gui/text/qfontmetrics.h60
-rw-r--r--src/gui/text/qfontsubset.cpp160
-rw-r--r--src/gui/text/qfontsubset_agl.cpp40
-rw-r--r--src/gui/text/qfontsubset_p.h51
-rw-r--r--src/gui/text/qfragmentmap.cpp40
-rw-r--r--src/gui/text/qfragmentmap_p.h40
-rw-r--r--src/gui/text/qglyphrun.cpp119
-rw-r--r--src/gui/text/qglyphrun.h52
-rw-r--r--src/gui/text/qglyphrun_p.h45
-rw-r--r--src/gui/text/qharfbuzzng.cpp79
-rw-r--r--src/gui/text/qharfbuzzng_p.h42
-rw-r--r--src/gui/text/qinputcontrol.cpp47
-rw-r--r--src/gui/text/qinputcontrol_p.h41
-rw-r--r--src/gui/text/qplatformfontdatabase.cpp118
-rw-r--r--src/gui/text/qplatformfontdatabase.h55
-rw-r--r--src/gui/text/qrawfont.cpp96
-rw-r--r--src/gui/text/qrawfont.h47
-rw-r--r--src/gui/text/qrawfont_p.h40
-rw-r--r--src/gui/text/qstatictext.cpp44
-rw-r--r--src/gui/text/qstatictext.h46
-rw-r--r--src/gui/text/qstatictext_p.h42
-rw-r--r--src/gui/text/qsyntaxhighlighter.cpp66
-rw-r--r--src/gui/text/qsyntaxhighlighter.h40
-rw-r--r--src/gui/text/qt_attribution.json2
-rw-r--r--src/gui/text/qtextcursor.cpp92
-rw-r--r--src/gui/text/qtextcursor.h51
-rw-r--r--src/gui/text/qtextcursor_p.h40
-rw-r--r--src/gui/text/qtextdocument.cpp1012
-rw-r--r--src/gui/text/qtextdocument.h62
-rw-r--r--src/gui/text/qtextdocument_p.cpp87
-rw-r--r--src/gui/text/qtextdocument_p.h57
-rw-r--r--src/gui/text/qtextdocumentfragment.cpp185
-rw-r--r--src/gui/text/qtextdocumentfragment.h50
-rw-r--r--src/gui/text/qtextdocumentfragment_p.h44
-rw-r--r--src/gui/text/qtextdocumentlayout.cpp188
-rw-r--r--src/gui/text/qtextdocumentlayout_p.h40
-rw-r--r--src/gui/text/qtextdocumentwriter.cpp52
-rw-r--r--src/gui/text/qtextdocumentwriter.h40
-rw-r--r--src/gui/text/qtextengine.cpp636
-rw-r--r--src/gui/text/qtextengine_p.h78
-rw-r--r--src/gui/text/qtextformat.cpp294
-rw-r--r--src/gui/text/qtextformat.h114
-rw-r--r--src/gui/text/qtextformat_p.h43
-rw-r--r--src/gui/text/qtexthtmlparser.cpp626
-rw-r--r--src/gui/text/qtexthtmlparser_p.h70
-rw-r--r--src/gui/text/qtextimagehandler.cpp228
-rw-r--r--src/gui/text/qtextimagehandler_p.h40
-rw-r--r--src/gui/text/qtextlayout.cpp949
-rw-r--r--src/gui/text/qtextlayout.h80
-rw-r--r--src/gui/text/qtextlist.cpp87
-rw-r--r--src/gui/text/qtextlist.h40
-rw-r--r--src/gui/text/qtextmarkdownimporter.cpp248
-rw-r--r--src/gui/text/qtextmarkdownimporter_p.h52
-rw-r--r--src/gui/text/qtextmarkdownwriter.cpp412
-rw-r--r--src/gui/text/qtextmarkdownwriter_p.h43
-rw-r--r--src/gui/text/qtextobject.cpp77
-rw-r--r--src/gui/text/qtextobject.h87
-rw-r--r--src/gui/text/qtextobject_p.h40
-rw-r--r--src/gui/text/qtextodfwriter.cpp113
-rw-r--r--src/gui/text/qtextodfwriter_p.h40
-rw-r--r--src/gui/text/qtextoption.cpp50
-rw-r--r--src/gui/text/qtextoption.h48
-rw-r--r--src/gui/text/qtexttable.cpp67
-rw-r--r--src/gui/text/qtexttable.h40
-rw-r--r--src/gui/text/qtexttable_p.h47
-rw-r--r--src/gui/text/qzip.cpp1366
-rw-r--r--src/gui/text/qzipreader_p.h127
-rw-r--r--src/gui/text/qzipwriter_p.h117
-rw-r--r--src/gui/text/text.pri139
-rw-r--r--src/gui/text/unix/qfontconfigdatabase.cpp294
-rw-r--r--src/gui/text/unix/qfontconfigdatabase_p.h42
-rw-r--r--src/gui/text/unix/qfontenginemultifontconfig.cpp42
-rw-r--r--src/gui/text/unix/qfontenginemultifontconfig_p.h40
-rw-r--r--src/gui/text/unix/qgenericunixfontdatabase_p.h47
-rw-r--r--src/gui/text/unix/unix.pri13
-rw-r--r--src/gui/text/windows/qwindowsdirectwritefontdatabase.cpp751
-rw-r--r--src/gui/text/windows/qwindowsdirectwritefontdatabase_p.h64
-rw-r--r--src/gui/text/windows/qwindowsfontdatabase.cpp373
-rw-r--r--src/gui/text/windows/qwindowsfontdatabase_ft.cpp84
-rw-r--r--src/gui/text/windows/qwindowsfontdatabase_ft_p.h43
-rw-r--r--src/gui/text/windows/qwindowsfontdatabase_p.h57
-rw-r--r--src/gui/text/windows/qwindowsfontdatabasebase.cpp381
-rw-r--r--src/gui/text/windows/qwindowsfontdatabasebase_p.h58
-rw-r--r--src/gui/text/windows/qwindowsfontengine.cpp147
-rw-r--r--src/gui/text/windows/qwindowsfontengine_p.h60
-rw-r--r--src/gui/text/windows/qwindowsfontenginedirectwrite.cpp414
-rw-r--r--src/gui/text/windows/qwindowsfontenginedirectwrite_p.h87
-rw-r--r--src/gui/text/windows/qwindowsnativeimage.cpp40
-rw-r--r--src/gui/text/windows/qwindowsnativeimage_p.h41
-rw-r--r--src/gui/text/windows/windows.pri35
120 files changed, 8561 insertions, 9984 deletions
diff --git a/src/gui/text/coretext/coretext.pri b/src/gui/text/coretext/coretext.pri
deleted file mode 100644
index 31470ae583..0000000000
--- a/src/gui/text/coretext/coretext.pri
+++ /dev/null
@@ -1,19 +0,0 @@
-HEADERS += $$PWD/qcoretextfontdatabase_p.h $$PWD/qfontengine_coretext_p.h
-OBJECTIVE_SOURCES += $$PWD/qfontengine_coretext.mm $$PWD/qcoretextfontdatabase.mm
-
-LIBS_PRIVATE += \
- -framework CoreFoundation \
- -framework CoreGraphics \
- -framework CoreText \
- -framework Foundation
-
-macos: \
- LIBS_PRIVATE += -framework AppKit
-else: \
- LIBS_PRIVATE += -framework UIKit
-
-CONFIG += watchos_coretext
-
-qtConfig(freetype) {
- QMAKE_USE_PRIVATE += freetype
-}
diff --git a/src/gui/text/coretext/qcoretextfontdatabase.mm b/src/gui/text/coretext/qcoretextfontdatabase.mm
index ba3c2424d0..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,21 +529,32 @@ 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);
}
- Q_UNREACHABLE();
+ // 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);
+ return T::create(fontData, pixelSize, hintingPreference, {});
}
// Explicitly instantiate so that we don't need the plugin to involve FreeType
@@ -422,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);
@@ -443,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;
@@ -505,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;
@@ -554,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);
}
@@ -571,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 &);
@@ -587,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 {
@@ -621,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);
@@ -735,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
@@ -794,5 +1014,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 d066d6f5a1..1050c03d75 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,9 +193,10 @@ 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.family = family;
+ fontDef.families = QStringList(family);
QCFString styleName = (CFStringRef) CTFontCopyAttribute(ctfont, kCTFontStyleNameAttribute);
fontDef.styleName = styleName;
@@ -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,7 @@ void QCoreTextFontEngine::initializeHeightMetrics() const
m_descent = QFixed::fromReal(CTFontGetDescent(ctfont));
m_leading = QFixed::fromReal(CTFontGetLeading(ctfont));
- QFontEngine::initializeHeightMetrics();
+ m_heightMetricsQueried = true;
}
QFixed QCoreTextFontEngine::capHeight() const
@@ -404,6 +371,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 +388,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 +416,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);
}
@@ -531,13 +508,17 @@ static void qcoretextfontengine_scaleMetrics(glyph_metrics_t &br, const QTransfo
}
}
-glyph_metrics_t QCoreTextFontEngine::alphaMapBoundingBox(glyph_t glyph, QFixed subPixelPosition, const QTransform &matrix, GlyphFormat format)
+glyph_metrics_t QCoreTextFontEngine::alphaMapBoundingBox(glyph_t glyph, const QFixedPoint &subPixelPosition, const QTransform &matrix, GlyphFormat format)
{
if (matrix.type() > QTransform::TxScale)
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)
@@ -713,7 +694,7 @@ qreal QCoreTextFontEngine::fontSmoothingGamma()
return 2.0;
}
-QImage QCoreTextFontEngine::imageForGlyph(glyph_t glyph, QFixed subPixelPosition, const QTransform &matrix, const QColor &color)
+QImage QCoreTextFontEngine::imageForGlyph(glyph_t glyph, const QFixedPoint &subPixelPosition, const QTransform &matrix, const QColor &color)
{
glyph_metrics_t br = alphaMapBoundingBox(glyph, subPixelPosition, matrix, glyphFormat);
@@ -744,10 +725,12 @@ QImage QCoreTextFontEngine::imageForGlyph(glyph_t glyph, QFixed subPixelPosition
// 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;
}();
@@ -773,8 +756,9 @@ QImage QCoreTextFontEngine::imageForGlyph(glyph_t glyph, QFixed subPixelPosition
cgMatrix = CGAffineTransformConcat(cgMatrix, CGAffineTransformMakeScale(matrix.m11(), matrix.m22()));
CGGlyph cgGlyph = glyph;
- qreal pos_x = -br.x.truncate() + subPixelPosition.toReal();
- qreal pos_y = im.height() + br.y.toReal();
+
+ qreal pos_x = -br.x.truncate() + subPixelPosition.x.toReal();
+ qreal pos_y = im.height() + br.y.toReal() - subPixelPosition.y.toReal();
if (!hasColorGlyphs()) {
CGContextSetTextMatrix(ctx, cgMatrix);
@@ -789,7 +773,13 @@ QImage QCoreTextFontEngine::imageForGlyph(glyph_t glyph, QFixed subPixelPosition
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 {
@@ -817,12 +807,12 @@ QImage QCoreTextFontEngine::imageForGlyph(glyph_t glyph, QFixed subPixelPosition
return im;
}
-QImage QCoreTextFontEngine::alphaMapForGlyph(glyph_t glyph, QFixed subPixelPosition)
+QImage QCoreTextFontEngine::alphaMapForGlyph(glyph_t glyph, const QFixedPoint &subPixelPosition)
{
return alphaMapForGlyph(glyph, subPixelPosition, QTransform());
}
-QImage QCoreTextFontEngine::alphaMapForGlyph(glyph_t glyph, QFixed subPixelPosition, const QTransform &x)
+QImage QCoreTextFontEngine::alphaMapForGlyph(glyph_t glyph, const QFixedPoint &subPixelPosition, const QTransform &x)
{
if (x.type() > QTransform::TxScale)
return QFontEngine::alphaMapForGlyph(glyph, subPixelPosition, x);
@@ -844,7 +834,7 @@ QImage QCoreTextFontEngine::alphaMapForGlyph(glyph_t glyph, QFixed subPixelPosit
return alphaMap;
}
-QImage QCoreTextFontEngine::alphaRGBMapForGlyph(glyph_t glyph, QFixed subPixelPosition, const QTransform &x)
+QImage QCoreTextFontEngine::alphaRGBMapForGlyph(glyph_t glyph, const QFixedPoint &subPixelPosition, const QTransform &x)
{
if (x.type() > QTransform::TxScale)
return QFontEngine::alphaRGBMapForGlyph(glyph, subPixelPosition, x);
@@ -852,7 +842,7 @@ QImage QCoreTextFontEngine::alphaRGBMapForGlyph(glyph_t glyph, QFixed subPixelPo
return imageForGlyph(glyph, subPixelPosition, x);
}
-QImage QCoreTextFontEngine::bitmapForGlyph(glyph_t glyph, QFixed subPixelPosition, const QTransform &t, const QColor &color)
+QImage QCoreTextFontEngine::bitmapForGlyph(glyph_t glyph, const QFixedPoint &subPixelPosition, const QTransform &t, const QColor &color)
{
if (t.type() > QTransform::TxScale)
return QFontEngine::bitmapForGlyph(glyph, subPixelPosition, t, color);
@@ -881,8 +871,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 1239072677..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;
@@ -91,7 +54,8 @@ public:
bool canRender(const QChar *string, int len) const override;
int synthesized() const override { return synthesisFlags; }
- bool supportsSubPixelPositions() const override { return true; }
+ bool supportsHorizontalSubPixelPositions() const override { return true; }
+ bool supportsVerticalSubPixelPositions() const override { return false; }
QFixed lineThickness() const override;
QFixed underlinePosition() const override;
@@ -101,11 +65,11 @@ public:
FaceId faceId() const override;
bool getSfntTableData(uint /*tag*/, uchar * /*buffer*/, uint * /*length*/) const override;
void getUnscaledGlyph(glyph_t glyph, QPainterPath *path, glyph_metrics_t *metrics) override;
- QImage alphaMapForGlyph(glyph_t, QFixed subPixelPosition) override;
- QImage alphaMapForGlyph(glyph_t glyph, QFixed subPixelPosition, const QTransform &t) override;
- QImage alphaRGBMapForGlyph(glyph_t, QFixed subPixelPosition, const QTransform &t) override;
- glyph_metrics_t alphaMapBoundingBox(glyph_t glyph, QFixed, const QTransform &matrix, GlyphFormat) override;
- QImage bitmapForGlyph(glyph_t, QFixed subPixelPosition, const QTransform &t, const QColor &color) override;
+ QImage alphaMapForGlyph(glyph_t, const QFixedPoint &subPixelPosition) override;
+ QImage alphaMapForGlyph(glyph_t glyph, const QFixedPoint &subPixelPosition, const QTransform &t) override;
+ QImage alphaRGBMapForGlyph(glyph_t, const QFixedPoint &subPixelPosition, const QTransform &t) override;
+ glyph_metrics_t alphaMapBoundingBox(glyph_t glyph, const QFixedPoint &, const QTransform &matrix, GlyphFormat) override;
+ QImage bitmapForGlyph(glyph_t, const QFixedPoint &subPixelPosition, const QTransform &t, const QColor &color) override;
QFixed emSquareSize() const override;
void doKerning(QGlyphLayout *g, ShaperFlags flags) const override;
@@ -127,12 +91,12 @@ 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);
void init();
- QImage imageForGlyph(glyph_t glyph, QFixed subPixelPosition, const QTransform &m, const QColor &color = QColor());
+ QImage imageForGlyph(glyph_t glyph, const QFixedPoint &subPixelPosition, const QTransform &m, const QColor &color = QColor());
void loadAdvancesForGlyphs(QVarLengthArray<CGGlyph> &cgGlyphs, QGlyphLayout *glyphs) const;
bool hasColorGlyphs() const;
bool shouldAntialias() const;
diff --git a/src/gui/text/freetype/freetype.pri b/src/gui/text/freetype/freetype.pri
deleted file mode 100644
index 7bda687ef4..0000000000
--- a/src/gui/text/freetype/freetype.pri
+++ /dev/null
@@ -1,9 +0,0 @@
-HEADERS += \
- $$PWD/qfreetypefontdatabase_p.h \
- $$PWD/qfontengine_ft_p.h
-
-SOURCES += \
- $$PWD/qfreetypefontdatabase.cpp \
- $$PWD/qfontengine_ft.cpp
-
-QMAKE_USE_PRIVATE += freetype
diff --git a/src/gui/text/freetype/qfontengine_ft.cpp b/src/gui/text/freetype/qfontengine_ft.cpp
index 42cf147901..d3791f1f6e 100644
--- a/src/gui/text/freetype/qfontengine_ft.cpp
+++ b/src/gui/text/freetype/qfontengine_ft.cpp
@@ -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) 2021 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 "qdir.h"
#include "qmetatype.h"
@@ -48,6 +12,7 @@
#include <qscreen.h>
#include <qpa/qplatformscreen.h>
#include <QtCore/QUuid>
+#include <QtCore/QLoggingCategory>
#include <QtGui/QPainterPath>
#ifndef QT_NO_FREETYPE
@@ -59,6 +24,8 @@
#include <qmath.h>
#include <qendian.h>
+#include <memory>
+
#include <ft2build.h>
#include FT_FREETYPE_H
#include FT_OUTLINE_H
@@ -68,6 +35,7 @@
#include FT_GLYPH_H
#include FT_MODULE_H
#include FT_LCD_FILTER_H
+#include FT_MULTIPLE_MASTERS_H
#if defined(FT_CONFIG_OPTIONS_H)
#include FT_CONFIG_OPTIONS_H
@@ -87,6 +55,10 @@
QT_BEGIN_NAMESPACE
+Q_DECLARE_LOGGING_CATEGORY(lcFontMatch)
+
+using namespace Qt::StringLiterals;
+
#define FLOOR(x) ((x) & -64)
#define CEIL(x) (((x)+63) & -64)
#define TRUNC(x) ((x) >> 6)
@@ -126,19 +98,43 @@ public:
{ }
~QtFreetypeData();
+ struct FaceStyle {
+ QString faceFileName;
+ QString styleName;
+
+ FaceStyle(QString faceFileName, QString styleName)
+ : faceFileName(std::move(faceFileName)),
+ styleName(std::move(styleName))
+ {}
+ };
+
FT_Library library;
QHash<QFontEngine::FaceId, QFreetypeFace *> faces;
+ QHash<FaceStyle, int> faceIndices;
};
QtFreetypeData::~QtFreetypeData()
{
- for (QHash<QFontEngine::FaceId, QFreetypeFace *>::ConstIterator iter = faces.cbegin(); iter != faces.cend(); ++iter)
+ for (auto iter = faces.cbegin(); iter != faces.cend(); ++iter) {
iter.value()->cleanup();
+ if (!iter.value()->ref.deref())
+ delete iter.value();
+ }
faces.clear();
FT_Done_FreeType(library);
library = nullptr;
}
+inline bool operator==(const QtFreetypeData::FaceStyle &style1, const QtFreetypeData::FaceStyle &style2)
+{
+ return style1.faceFileName == style2.faceFileName && style1.styleName == style2.styleName;
+}
+
+inline size_t qHash(const QtFreetypeData::FaceStyle &style, size_t seed)
+{
+ return qHashMulti(seed, style.faceFileName, style.styleName);
+}
+
Q_GLOBAL_STATIC(QThreadStorage<QtFreetypeData *>, theFreetypeData)
QtFreetypeData *qt_getFreetypeData()
@@ -194,10 +190,15 @@ int QFreetypeFace::getPointInOutline(glyph_t glyph, int flags, quint32 point, QF
return Err_Ok;
}
+bool QFreetypeFace::isScalable() const
+{
+ return FT_IS_SCALABLE(face);
+}
+
bool QFreetypeFace::isScalableBitmap() const
{
#ifdef FT_HAS_COLOR
- return !FT_IS_SCALABLE(face) && FT_HAS_COLOR(face);
+ return !isScalable() && FT_HAS_COLOR(face);
#else
return false;
#endif
@@ -220,18 +221,37 @@ QFreetypeFace *QFreetypeFace::getFace(const QFontEngine::FaceId &face_id,
QtFreetypeData *freetypeData = qt_getFreetypeData();
- QFreetypeFace *freetype = freetypeData->faces.value(face_id, nullptr);
- if (freetype) {
- freetype->ref.ref();
- } else {
- QScopedPointer<QFreetypeFace> newFreetype(new QFreetypeFace);
+ QFreetypeFace *freetype = nullptr;
+ auto it = freetypeData->faces.find(face_id);
+ if (it != freetypeData->faces.end()) {
+ freetype = *it;
+
+ Q_ASSERT(freetype->ref.loadRelaxed() > 0);
+ if (freetype->ref.loadRelaxed() == 1) {
+ // If there is only one reference left to the face, it means it is only referenced by
+ // the cache itself, and thus it is in cleanup state (but the final outside reference
+ // was removed on a different thread so it could not be deleted right away). We then
+ // complete the cleanup and pretend we didn't find it, so that it can be re-created with
+ // the present state.
+ freetype->cleanup();
+ freetypeData->faces.erase(it);
+ delete freetype;
+ freetype = nullptr;
+ } else {
+ freetype->ref.ref();
+ }
+ }
+
+ if (!freetype) {
+ const auto deleter = [](QFreetypeFace *f) { delete f; };
+ std::unique_ptr<QFreetypeFace, decltype(deleter)> newFreetype(new QFreetypeFace, deleter);
FT_Face face;
if (!face_id.filename.isEmpty()) {
QString fileName = QFile::decodeName(face_id.filename);
- if (face_id.filename.startsWith(":qmemoryfonts/")) {
+ if (const char *prefix = ":qmemoryfonts/"; face_id.filename.startsWith(prefix)) {
// from qfontdatabase.cpp
QByteArray idx = face_id.filename;
- idx.remove(0, 14); // remove ':qmemoryfonts/'
+ idx.remove(0, strlen(prefix)); // remove ':qmemoryfonts/'
bool ok = false;
newFreetype->fontData = qt_fontdata_from_index(idx.toInt(&ok));
if (!ok)
@@ -253,7 +273,23 @@ QFreetypeFace *QFreetypeFace::getFace(const QFontEngine::FaceId &face_id,
} else if (FT_New_Face(freetypeData->library, face_id.filename, face_id.index, &face)) {
return nullptr;
}
+
+#if (FREETYPE_MAJOR*10000 + FREETYPE_MINOR*100 + FREETYPE_PATCH) >= 20900
+ if (face_id.instanceIndex >= 0) {
+ qCDebug(lcFontMatch)
+ << "Selecting named instance" << (face_id.instanceIndex)
+ << "in" << face_id.filename;
+ FT_Set_Named_Instance(face, face_id.instanceIndex + 1);
+ }
+#endif
newFreetype->face = face;
+ newFreetype->mm_var = nullptr;
+ if (FT_IS_NAMED_INSTANCE(newFreetype->face)) {
+ FT_Error ftresult;
+ ftresult = FT_Get_MM_Var(face, &newFreetype->mm_var);
+ if (ftresult != FT_Err_Ok)
+ newFreetype->mm_var = nullptr;
+ }
newFreetype->ref.storeRelaxed(1);
newFreetype->xsize = 0;
@@ -292,14 +328,34 @@ QFreetypeFace *QFreetypeFace::getFace(const QFontEngine::FaceId &face_id,
FT_Set_Char_Size(face, newFreetype->face->available_sizes[0].x_ppem, newFreetype->face->available_sizes[0].y_ppem, 0, 0);
FT_Set_Charmap(newFreetype->face, newFreetype->unicode_map);
+
+ if (!face_id.variableAxes.isEmpty()) {
+ FT_MM_Var *var = nullptr;
+ FT_Get_MM_Var(newFreetype->face, &var);
+ if (var != nullptr) {
+ QVarLengthArray<FT_Fixed, 16> coords(var->num_axis);
+ FT_Get_Var_Design_Coordinates(face, var->num_axis, coords.data());
+ for (FT_UInt i = 0; i < var->num_axis; ++i) {
+ if (const auto tag = QFont::Tag::fromValue(var->axis[i].tag)) {
+ const auto it = face_id.variableAxes.constFind(*tag);
+ if (it != face_id.variableAxes.constEnd())
+ coords[i] = FT_Fixed(*it * 65536);
+ }
+ }
+ FT_Set_Var_Design_Coordinates(face, var->num_axis, coords.data());
+ FT_Done_MM_Var(qt_getFreetype(), var);
+ }
+ }
+
QT_TRY {
- freetypeData->faces.insert(face_id, newFreetype.data());
+ freetypeData->faces.insert(face_id, newFreetype.get());
} QT_CATCH(...) {
- newFreetype.take()->release(face_id);
+ newFreetype.release()->release(face_id);
// we could return null in principle instead of throwing
QT_RETHROW;
}
- freetype = newFreetype.take();
+ freetype = newFreetype.release();
+ freetype->ref.ref();
}
return freetype;
}
@@ -307,32 +363,93 @@ QFreetypeFace *QFreetypeFace::getFace(const QFontEngine::FaceId &face_id,
void QFreetypeFace::cleanup()
{
hbFace.reset();
+ if (mm_var && face && face->glyph)
+ FT_Done_MM_Var(face->glyph->library, mm_var);
+ mm_var = nullptr;
FT_Done_Face(face);
face = nullptr;
}
void QFreetypeFace::release(const QFontEngine::FaceId &face_id)
{
- if (!ref.deref()) {
- if (face) {
- QtFreetypeData *freetypeData = qt_getFreetypeData();
+ Q_UNUSED(face_id);
+ bool deleteThis = !ref.deref();
+
+ // If the only reference left over is the cache's reference, we remove it from the cache,
+ // granted that we are on the correct thread. If not, we leave it there to be cleaned out
+ // later. While we are at it, we also purge all left over faces which are only referenced
+ // from the cache.
+ if (face && ref.loadRelaxed() == 1) {
+ QtFreetypeData *freetypeData = qt_getFreetypeData();
+ for (auto it = freetypeData->faces.constBegin(); it != freetypeData->faces.constEnd(); ) {
+ if (it.value()->ref.loadRelaxed() == 1) {
+ it.value()->cleanup();
+ if (it.value() == this)
+ deleteThis = true; // This face, delete at end of function for safety
+ else
+ delete it.value();
+ it = freetypeData->faces.erase(it);
+ } else {
+ ++it;
+ }
+ }
- cleanup();
+ if (freetypeData->faces.isEmpty()) {
+ FT_Done_FreeType(freetypeData->library);
+ freetypeData->library = nullptr;
+ }
+ }
- auto it = freetypeData->faces.constFind(face_id);
- if (it != freetypeData->faces.constEnd())
- freetypeData->faces.erase(it);
+ if (deleteThis)
+ delete this;
+}
- if (freetypeData->faces.isEmpty()) {
- FT_Done_FreeType(freetypeData->library);
- freetypeData->library = nullptr;
- }
+static int computeFaceIndex(const QString &faceFileName, const QString &styleName)
+{
+ FT_Library library = qt_getFreetype();
+
+ int faceIndex = 0;
+ int numFaces = 0;
+
+ do {
+ FT_Face face;
+
+ FT_Error error = FT_New_Face(library, faceFileName.toUtf8().constData(), faceIndex, &face);
+ if (error != FT_Err_Ok) {
+ qDebug() << "FT_New_Face failed for face index" << faceIndex << ':' << Qt::hex << error;
+ break;
}
- delete this;
- }
+ const bool found = QLatin1StringView(face->style_name) == styleName;
+ numFaces = face->num_faces;
+
+ FT_Done_Face(face);
+
+ if (found)
+ return faceIndex;
+ } while (++faceIndex < numFaces);
+
+ // Fall back to the first font face in the file
+ return 0;
}
+int QFreetypeFace::getFaceIndexByStyleName(const QString &faceFileName, const QString &styleName)
+{
+ QtFreetypeData *freetypeData = qt_getFreetypeData();
+
+ // Try to get from cache
+ QtFreetypeData::FaceStyle faceStyle(faceFileName, styleName);
+ int faceIndex = freetypeData->faceIndices.value(faceStyle, -1);
+
+ if (faceIndex >= 0)
+ return faceIndex;
+
+ faceIndex = computeFaceIndex(faceFileName, styleName);
+
+ freetypeData->faceIndices.insert(faceStyle, faceIndex);
+
+ return faceIndex;
+}
void QFreetypeFace::computeSize(const QFontDef &fontDef, int *xsize, int *ysize, bool *outline_drawing, QFixed *scalableBitmapScaleFactor)
{
@@ -594,7 +711,7 @@ static QFontEngine::SubpixelAntialiasingType subpixelAntialiasingTypeHint()
QFontEngineFT *QFontEngineFT::create(const QFontDef &fontDef, FaceId faceId, const QByteArray &fontData)
{
- QScopedPointer<QFontEngineFT> engine(new QFontEngineFT(fontDef));
+ auto engine = std::make_unique<QFontEngineFT>(fontDef);
QFontEngineFT::GlyphFormat format = QFontEngineFT::Format_Mono;
const bool antialias = !(fontDef.styleStrategy & QFont::NoAntialias);
@@ -616,7 +733,7 @@ QFontEngineFT *QFontEngineFT::create(const QFontDef &fontDef, FaceId faceId, con
}
engine->setQtDefaultHintStyle(static_cast<QFont::HintingPreference>(fontDef.hintingPreference));
- return engine.take();
+ return engine.release();
}
namespace {
@@ -629,7 +746,7 @@ namespace {
void updateFamilyNameAndStyle()
{
- fontDef.family = QString::fromLatin1(freetype->face->family_name);
+ fontDef.families = QStringList(QString::fromLatin1(freetype->face->family_name));
if (freetype->face->style_flags & FT_STYLE_FLAG_ITALIC)
fontDef.style = QFont::StyleItalic;
@@ -638,27 +755,32 @@ namespace {
fontDef.weight = QFont::Bold;
}
- bool initFromData(const QByteArray &fontData)
+ bool initFromData(const QByteArray &fontData, const QMap<QFont::Tag, float> &variableAxisValues)
{
FaceId faceId;
faceId.filename = "";
faceId.index = 0;
faceId.uuid = QUuid::createUuid().toByteArray();
+ faceId.variableAxes = variableAxisValues;
return init(faceId, true, Format_None, fontData);
}
};
}
-QFontEngineFT *QFontEngineFT::create(const QByteArray &fontData, qreal pixelSize, QFont::HintingPreference hintingPreference)
+QFontEngineFT *QFontEngineFT::create(const QByteArray &fontData,
+ qreal pixelSize,
+ QFont::HintingPreference hintingPreference,
+ const QMap<QFont::Tag, float> &variableAxisValues)
{
QFontDef fontDef;
fontDef.pixelSize = pixelSize;
fontDef.stretch = QFont::Unstretched;
fontDef.hintingPreference = hintingPreference;
+ fontDef.variableAxisValues = variableAxisValues;
QFontEngineFTRawData *fe = new QFontEngineFTRawData(fontDef);
- if (!fe->initFromData(fontData)) {
+ if (!fe->initFromData(fontData, variableAxisValues)) {
delete fe;
return nullptr;
}
@@ -711,6 +833,37 @@ bool QFontEngineFT::init(FaceId faceId, bool antialias, GlyphFormat format,
static void dont_delete(void*) {}
+static FT_UShort calculateActualWeight(QFreetypeFace *freetypeFace, FT_Face face, QFontEngine::FaceId faceId)
+{
+ FT_MM_Var *var = freetypeFace->mm_var;
+ if (var != nullptr && faceId.instanceIndex >= 0 && FT_UInt(faceId.instanceIndex) < var->num_namedstyles) {
+ for (FT_UInt axis = 0; axis < var->num_axis; ++axis) {
+ if (var->axis[axis].tag == QFont::Tag("wght").value()) {
+ return var->namedstyle[faceId.instanceIndex].coords[axis] >> 16;
+ }
+ }
+ }
+ if (const TT_OS2 *os2 = reinterpret_cast<const TT_OS2 *>(FT_Get_Sfnt_Table(face, ft_sfnt_os2))) {
+ return os2->usWeightClass;
+ }
+
+ return 700;
+}
+
+static bool calculateActualItalic(QFreetypeFace *freetypeFace, FT_Face face, QFontEngine::FaceId faceId)
+{
+ FT_MM_Var *var = freetypeFace->mm_var;
+ if (var != nullptr && faceId.instanceIndex >= 0 && FT_UInt(faceId.instanceIndex) < var->num_namedstyles) {
+ for (FT_UInt axis = 0; axis < var->num_axis; ++axis) {
+ if (var->axis[axis].tag == QFont::Tag("ital").value()) {
+ return (var->namedstyle[faceId.instanceIndex].coords[axis] >> 16) == 1;
+ }
+ }
+ }
+
+ return (face->style_flags & FT_STYLE_FLAG_ITALIC);
+}
+
bool QFontEngineFT::init(FaceId faceId, bool antialias, GlyphFormat format,
QFreetypeFace *freetypeFace)
{
@@ -734,7 +887,7 @@ bool QFontEngineFT::init(FaceId faceId, bool antialias, GlyphFormat format,
PS_FontInfoRec psrec;
// don't assume that type1 fonts are symbol fonts by default
if (FT_Get_PS_Font_Info(freetype->face, &psrec) == FT_Err_Ok) {
- symbol = bool(fontDef.family.contains(QLatin1String("symbol"), Qt::CaseInsensitive));
+ symbol = !fontDef.families.isEmpty() && bool(fontDef.families.constFirst().contains("symbol"_L1, Qt::CaseInsensitive));
}
freetype->computeSize(fontDef, &xsize, &ysize, &defaultGlyphSet.outline_drawing, &scalableBitmapScaleFactor);
@@ -742,34 +895,36 @@ bool QFontEngineFT::init(FaceId faceId, bool antialias, GlyphFormat format,
FT_Face face = lockFace();
if (FT_IS_SCALABLE(face)) {
- bool fake_oblique = (fontDef.style != QFont::StyleNormal) && !(face->style_flags & FT_STYLE_FLAG_ITALIC) && !qEnvironmentVariableIsSet("QT_NO_SYNTHESIZED_ITALIC");
+ bool isItalic = calculateActualItalic(freetype, face, faceId);
+ bool fake_oblique = (fontDef.style != QFont::StyleNormal) && !isItalic && !qEnvironmentVariableIsSet("QT_NO_SYNTHESIZED_ITALIC");
if (fake_oblique)
obliquen = true;
FT_Set_Transform(face, &matrix, nullptr);
freetype->matrix = matrix;
// fake bold
if ((fontDef.weight >= QFont::Bold) && !(face->style_flags & FT_STYLE_FLAG_BOLD) && !FT_IS_FIXED_WIDTH(face) && !qEnvironmentVariableIsSet("QT_NO_SYNTHESIZED_BOLD")) {
- if (const TT_OS2 *os2 = reinterpret_cast<const TT_OS2 *>(FT_Get_Sfnt_Table(face, ft_sfnt_os2))) {
- if (os2->usWeightClass < 700 && fontDef.pixelSize < 64)
- embolden = true;
+ FT_UShort actualWeight = calculateActualWeight(freetype, face, faceId);
+ if (actualWeight < 700 &&
+ (fontDef.pixelSize < 64 || qEnvironmentVariableIsSet("QT_NO_SYNTHESIZED_BOLD_LIMIT"))) {
+ embolden = true;
}
}
// underline metrics
line_thickness = QFixed::fromFixed(FT_MulFix(face->underline_thickness, face->size->metrics.y_scale));
- underline_position = QFixed::fromFixed(-FT_MulFix(face->underline_position, face->size->metrics.y_scale));
+ QFixed center_position = QFixed::fromFixed(-FT_MulFix(face->underline_position, face->size->metrics.y_scale));
+ underline_position = center_position - line_thickness / 2;
} else {
// ad hoc algorithm
int score = fontDef.weight * fontDef.pixelSize;
- line_thickness = score / 700;
+ line_thickness = score / 7000;
// looks better with thicker line for small pointsizes
if (line_thickness < 2 && score >= 1050)
line_thickness = 2;
underline_position = ((line_thickness * 2) + 3) / 6;
- if (isScalableBitmap()) {
+ cacheEnabled = false;
+ if (isScalableBitmap())
glyphFormat = defaultFormat = GlyphFormat::Format_ARGB;
- cacheEnabled = false;
- }
}
if (line_thickness < 1)
line_thickness = 1;
@@ -780,7 +935,7 @@ bool QFontEngineFT::init(FaceId faceId, bool antialias, GlyphFormat format,
TrueType fonts with embedded bitmaps may have a bitmap font specific
ascent/descent in the EBLC table. There is no direct public API
to extract those values. The only way we've found is to trick freetype
- into thinking that it's not a scalable font in FT_SelectSize so that
+ into thinking that it's not a scalable font in FT_Select_Size so that
the metrics are retrieved from the bitmap strikes.
*/
if (FT_IS_SCALABLE(face)) {
@@ -794,7 +949,7 @@ bool QFontEngineFT::init(FaceId faceId, bool antialias, GlyphFormat format,
metrics.ascender = face->size->metrics.ascender;
metrics.descender = face->size->metrics.descender;
if (metrics.descender > 0
- && QString::fromUtf8(face->family_name) == QLatin1String("Courier New")) {
+ && QString::fromUtf8(face->family_name) == "Courier New"_L1) {
metrics.descender *= -1;
}
metrics.height = metrics.ascender - metrics.descender + leading;
@@ -903,7 +1058,7 @@ int QFontEngineFT::loadFlags(QGlyphSet *set, GlyphFormat format, int flags,
static inline bool areMetricsTooLarge(const QFontEngineFT::GlyphInfo &info)
{
// false if exceeds QFontEngineFT::Glyph metrics
- return info.width > 0xFF || info.height > 0xFF;
+ return info.width > 0xFF || info.height > 0xFF || info.linearAdvance > 0x7FFF;
}
static inline void transformBoundingBox(int *left, int *top, int *right, int *bottom, FT_Matrix *matrix)
@@ -943,7 +1098,7 @@ static inline void transformBoundingBox(int *left, int *top, int *right, int *bo
}
QFontEngineFT::Glyph *QFontEngineFT::loadGlyph(QGlyphSet *set, uint glyph,
- QFixed subPixelPosition,
+ const QFixedPoint &subPixelPosition,
GlyphFormat format,
bool fetchMetricsOnly,
bool disableOutlineDrawing) const
@@ -967,8 +1122,8 @@ QFontEngineFT::Glyph *QFontEngineFT::loadGlyph(QGlyphSet *set, uint glyph,
FT_Matrix matrix = freetype->matrix;
FT_Vector v;
- v.x = format == Format_Mono ? 0 : FT_Pos(subPixelPosition.value());
- v.y = 0;
+ v.x = format == Format_Mono ? 0 : FT_Pos(subPixelPosition.x.value());
+ v.y = format == Format_Mono ? 0 : FT_Pos(-subPixelPosition.y.value());
FT_Set_Transform(face, &matrix, &v);
bool hsubpixel = false;
@@ -1051,6 +1206,7 @@ QFontEngineFT::Glyph *QFontEngineFT::loadGlyph(QGlyphSet *set, uint glyph,
info.height = TRUNC(top - bottom);
// If any of the metrics are too large to fit, don't cache them
+ // Also, avoid integer overflow when linearAdvance is to large to fit in a signed short
if (areMetricsTooLarge(info))
return nullptr;
@@ -1071,14 +1227,18 @@ QFontEngineFT::Glyph *QFontEngineFT::loadGlyph(QGlyphSet *set, uint glyph,
}
int glyph_buffer_size = 0;
- QScopedArrayPointer<uchar> glyph_buffer;
+ std::unique_ptr<uchar[]> glyph_buffer;
FT_Render_Mode renderMode = (default_hint_style == HintLight) ? FT_RENDER_MODE_LIGHT : FT_RENDER_MODE_NORMAL;
switch (format) {
case Format_Mono:
renderMode = FT_RENDER_MODE_MONO;
break;
case Format_A32:
- Q_ASSERT(hsubpixel || vfactor != 1);
+ if (!hsubpixel && vfactor == 1) {
+ qWarning("Format_A32 requested, but subpixel layout is unknown.");
+ return nullptr;
+ }
+
renderMode = hsubpixel ? FT_RENDER_MODE_LCD : FT_RENDER_MODE_LCD_V;
break;
case Format_A8:
@@ -1112,7 +1272,7 @@ QFontEngineFT::Glyph *QFontEngineFT::loadGlyph(QGlyphSet *set, uint glyph,
if (slot->bitmap.pixel_mode == FT_PIXEL_MODE_MONO) {
uchar *src = slot->bitmap.buffer;
- uchar *dst = glyph_buffer.data();
+ uchar *dst = glyph_buffer.get();
int h = slot->bitmap.rows;
// Some fonts return bitmaps even when we requested something else:
if (format == Format_Mono) {
@@ -1141,7 +1301,7 @@ QFontEngineFT::Glyph *QFontEngineFT::loadGlyph(QGlyphSet *set, uint glyph,
} else if (slot->bitmap.pixel_mode == 7 /*FT_PIXEL_MODE_BGRA*/) {
Q_ASSERT(format == Format_ARGB);
uchar *src = slot->bitmap.buffer;
- uchar *dst = glyph_buffer.data();
+ uchar *dst = glyph_buffer.get();
int h = slot->bitmap.rows;
while (h--) {
#if Q_BYTE_ORDER == Q_BIG_ENDIAN
@@ -1161,7 +1321,7 @@ QFontEngineFT::Glyph *QFontEngineFT::loadGlyph(QGlyphSet *set, uint glyph,
} else if (slot->bitmap.pixel_mode == FT_PIXEL_MODE_GRAY) {
Q_ASSERT(format == Format_A8);
uchar *src = slot->bitmap.buffer;
- uchar *dst = glyph_buffer.data();
+ uchar *dst = glyph_buffer.get();
int h = slot->bitmap.rows;
int bytes = info.width;
while (h--) {
@@ -1171,10 +1331,10 @@ QFontEngineFT::Glyph *QFontEngineFT::loadGlyph(QGlyphSet *set, uint glyph,
}
} else if (slot->bitmap.pixel_mode == FT_PIXEL_MODE_LCD) {
Q_ASSERT(format == Format_A32);
- convertRGBToARGB(slot->bitmap.buffer, (uint *)glyph_buffer.data(), info.width, info.height, slot->bitmap.pitch, subpixelType != Subpixel_RGB);
+ convertRGBToARGB(slot->bitmap.buffer, (uint *)glyph_buffer.get(), info.width, info.height, slot->bitmap.pitch, subpixelType != Subpixel_RGB);
} else if (slot->bitmap.pixel_mode == FT_PIXEL_MODE_LCD_V) {
Q_ASSERT(format == Format_A32);
- convertRGBToARGB_V(slot->bitmap.buffer, (uint *)glyph_buffer.data(), info.width, info.height, slot->bitmap.pitch, subpixelType != Subpixel_VRGB);
+ convertRGBToARGB_V(slot->bitmap.buffer, (uint *)glyph_buffer.get(), info.width, info.height, slot->bitmap.pitch, subpixelType != Subpixel_VRGB);
} else {
qWarning("QFontEngine: Glyph rendered in unknown pixel_mode=%d", slot->bitmap.pixel_mode);
return nullptr;
@@ -1193,7 +1353,7 @@ QFontEngineFT::Glyph *QFontEngineFT::loadGlyph(QGlyphSet *set, uint glyph,
g->advance = info.xOff;
g->format = format;
delete [] g->data;
- g->data = glyph_buffer.take();
+ g->data = glyph_buffer.release();
if (set)
set->setGlyph(glyph, subPixelPosition, g);
@@ -1210,7 +1370,7 @@ QFontEngine::Properties QFontEngineFT::properties() const
{
Properties p = freetype->properties();
if (p.postscriptName.isEmpty()) {
- p.postscriptName = QFontEngine::convertToPostscriptFontFamilyName(fontDef.family.toUtf8());
+ p.postscriptName = QFontEngine::convertToPostscriptFontFamilyName(fontDef.families.constFirst().toUtf8());
}
return freetype->properties();
@@ -1437,7 +1597,7 @@ void QFontEngineFT::getUnscaledGlyph(glyph_t glyph, QPainterPath *path, glyph_me
bool QFontEngineFT::supportsTransformation(const QTransform &transform) const
{
- return transform.type() <= QTransform::TxRotate;
+ return transform.type() <= QTransform::TxRotate && (freetype->isScalable() || freetype->isScalableBitmap());
}
void QFontEngineFT::addOutlineToPath(qreal x, qreal y, const QGlyphLayout &glyphs, QPainterPath *path, QTextItem::RenderFlags flags)
@@ -1518,15 +1678,16 @@ glyph_t QFontEngineFT::glyphIndex(uint ucs4) const
return glyph;
}
-bool QFontEngineFT::stringToCMap(const QChar *str, int len, QGlyphLayout *glyphs, int *nglyphs,
+int QFontEngineFT::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;
}
+ int mappedGlyphs = 0;
int glyph_pos = 0;
if (freetype->symbol_map) {
FT_Face face = freetype->face;
@@ -1559,6 +1720,8 @@ bool QFontEngineFT::stringToCMap(const QChar *str, int len, QGlyphLayout *glyphs
if (uc < QFreetypeFace::cmapCacheSize)
freetype->cmapCache[uc] = glyph;
}
+ if (glyphs->glyphs[glyph_pos] || isIgnorableChar(uc))
+ mappedGlyphs++;
++glyph_pos;
}
} else {
@@ -1580,6 +1743,8 @@ bool QFontEngineFT::stringToCMap(const QChar *str, int len, QGlyphLayout *glyphs
freetype->cmapCache[uc] = glyph;
}
}
+ if (glyphs->glyphs[glyph_pos] || isIgnorableChar(uc))
+ mappedGlyphs++;
++glyph_pos;
}
}
@@ -1590,7 +1755,7 @@ bool QFontEngineFT::stringToCMap(const QChar *str, int len, QGlyphLayout *glyphs
if (!(flags & GlyphIndicesOnly))
recalcAdvances(glyphs, flags);
- return true;
+ return mappedGlyphs;
}
bool QFontEngineFT::shouldUseDesignMetrics(QFontEngine::ShaperFlags flags) const
@@ -1644,7 +1809,11 @@ void QFontEngineFT::recalcAdvances(QGlyphLayout *glyphs, QFontEngine::ShaperFlag
} else {
if (!face)
face = lockFace();
- g = loadGlyph(cacheEnabled ? &defaultGlyphSet : nullptr, glyphs->glyphs[i], 0, Format_None, true);
+ g = loadGlyph(cacheEnabled ? &defaultGlyphSet : nullptr,
+ glyphs->glyphs[i],
+ QFixedPoint(),
+ Format_None,
+ true);
if (g)
glyphs->advances[i] = design ? QFixed::fromFixed(g->linearAdvance) : QFixed(g->advance);
else
@@ -1685,16 +1854,19 @@ glyph_metrics_t QFontEngineFT::boundingBox(const QGlyphLayout &glyphs)
if (!g) {
if (!face)
face = lockFace();
- g = loadGlyph(cacheEnabled ? &defaultGlyphSet : nullptr, glyphs.glyphs[i], 0, Format_None, true);
+ g = loadGlyph(cacheEnabled ? &defaultGlyphSet : nullptr,
+ glyphs.glyphs[i],
+ QFixedPoint(),
+ Format_None,
+ true);
}
if (g) {
QFixed x = overall.xoff + glyphs.offsets[i].x + g->x;
QFixed y = overall.yoff + glyphs.offsets[i].y - g->y;
overall.x = qMin(overall.x, x);
overall.y = qMin(overall.y, y);
- xmax = qMax(xmax, x + g->width);
- ymax = qMax(ymax, y + g->height);
- overall.xoff += g->advance;
+ xmax = qMax(xmax, x.ceil() + g->width);
+ ymax = qMax(ymax, y.ceil() + g->height);
if (!cacheEnabled && g != &emptyGlyph)
delete g;
} else {
@@ -1709,8 +1881,8 @@ glyph_metrics_t QFontEngineFT::boundingBox(const QGlyphLayout &glyphs)
overall.y = qMin(overall.y, y);
xmax = qMax(xmax, x + TRUNC(right - left));
ymax = qMax(ymax, y + TRUNC(top - bottom));
- overall.xoff += int(TRUNC(ROUND(face->glyph->advance.x)));
}
+ overall.xoff += glyphs.effectiveAdvance(i);
}
overall.height = qMax(overall.height, ymax - overall.y);
overall.width = xmax - overall.x;
@@ -1730,7 +1902,11 @@ glyph_metrics_t QFontEngineFT::boundingBox(glyph_t glyph)
Glyph *g = cacheEnabled ? defaultGlyphSet.getGlyph(glyph) : nullptr;
if (!g) {
face = lockFace();
- g = loadGlyph(cacheEnabled ? &defaultGlyphSet : nullptr, glyph, 0, Format_None, true);
+ g = loadGlyph(cacheEnabled ? &defaultGlyphSet : nullptr,
+ glyph,
+ QFixedPoint(),
+ Format_None,
+ true);
}
if (g) {
overall.x = g->x;
@@ -1762,12 +1938,23 @@ glyph_metrics_t QFontEngineFT::boundingBox(glyph_t glyph)
glyph_metrics_t QFontEngineFT::boundingBox(glyph_t glyph, const QTransform &matrix)
{
- return alphaMapBoundingBox(glyph, 0, matrix, QFontEngine::Format_None);
+ return alphaMapBoundingBox(glyph, QFixedPoint(), matrix, QFontEngine::Format_None);
}
-glyph_metrics_t QFontEngineFT::alphaMapBoundingBox(glyph_t glyph, QFixed subPixelPosition, const QTransform &matrix, QFontEngine::GlyphFormat format)
+glyph_metrics_t QFontEngineFT::alphaMapBoundingBox(glyph_t glyph,
+ const QFixedPoint &subPixelPosition,
+ const QTransform &matrix,
+ QFontEngine::GlyphFormat format)
{
- Glyph *g = loadGlyphFor(glyph, subPixelPosition, format, matrix, true);
+ // When rendering glyphs into a cache via the alphaMap* functions, we disable
+ // outline drawing. To ensure the bounding box matches the rendered glyph, we
+ // need to do the same here.
+
+ const bool needsImageTransform = !FT_IS_SCALABLE(freetype->face)
+ && matrix.type() > QTransform::TxTranslate;
+ if (needsImageTransform && format == QFontEngine::Format_Mono)
+ format = QFontEngine::Format_A8;
+ Glyph *g = loadGlyphFor(glyph, subPixelPosition, format, matrix, true, true);
glyph_metrics_t overall;
if (g) {
@@ -1793,7 +1980,7 @@ glyph_metrics_t QFontEngineFT::alphaMapBoundingBox(glyph_t glyph, QFixed subPixe
unlockFace();
}
- if (isScalableBitmap())
+ if (isScalableBitmap() || needsImageTransform)
overall = scaledBitmapMetrics(overall, matrix);
return overall;
}
@@ -1828,8 +2015,10 @@ static inline QImage alphaMapFromGlyphData(QFontEngineFT::Glyph *glyph, QFontEng
return img;
}
-QFontEngine::Glyph *QFontEngineFT::glyphData(glyph_t glyphIndex, QFixed subPixelPosition,
- QFontEngine::GlyphFormat neededFormat, const QTransform &t)
+QFontEngine::Glyph *QFontEngineFT::glyphData(glyph_t glyphIndex,
+ const QFixedPoint &subPixelPosition,
+ QFontEngine::GlyphFormat neededFormat,
+ const QTransform &t)
{
Q_ASSERT(cacheEnabled);
@@ -1854,7 +2043,7 @@ static inline bool is2dRotation(const QTransform &t)
}
QFontEngineFT::Glyph *QFontEngineFT::loadGlyphFor(glyph_t g,
- QFixed subPixelPosition,
+ const QFixedPoint &subPixelPosition,
GlyphFormat format,
const QTransform &t,
bool fetchBoundingBox,
@@ -1882,19 +2071,26 @@ QFontEngineFT::Glyph *QFontEngineFT::loadGlyphFor(glyph_t g,
return glyph;
}
-QImage QFontEngineFT::alphaMapForGlyph(glyph_t g, QFixed subPixelPosition)
+QImage QFontEngineFT::alphaMapForGlyph(glyph_t g, const QFixedPoint &subPixelPosition)
{
return alphaMapForGlyph(g, subPixelPosition, QTransform());
}
-QImage QFontEngineFT::alphaMapForGlyph(glyph_t g, QFixed subPixelPosition, const QTransform &t)
+QImage QFontEngineFT::alphaMapForGlyph(glyph_t g,
+ const QFixedPoint &subPixelPosition,
+ const QTransform &t)
{
- const GlyphFormat neededFormat = antialias ? Format_A8 : Format_Mono;
+ const bool needsImageTransform = !FT_IS_SCALABLE(freetype->face)
+ && t.type() > QTransform::TxTranslate;
+ const GlyphFormat neededFormat = antialias || needsImageTransform ? Format_A8 : Format_Mono;
Glyph *glyph = loadGlyphFor(g, subPixelPosition, neededFormat, t, false, true);
QImage img = alphaMapFromGlyphData(glyph, neededFormat);
- img = img.copy();
+ if (needsImageTransform)
+ img = img.transformed(t, Qt::SmoothTransformation);
+ else
+ img = img.copy();
if (!cacheEnabled && glyph != &emptyGlyph)
delete glyph;
@@ -1902,7 +2098,9 @@ QImage QFontEngineFT::alphaMapForGlyph(glyph_t g, QFixed subPixelPosition, const
return img;
}
-QImage QFontEngineFT::alphaRGBMapForGlyph(glyph_t g, QFixed subPixelPosition, const QTransform &t)
+QImage QFontEngineFT::alphaRGBMapForGlyph(glyph_t g,
+ const QFixedPoint &subPixelPosition,
+ const QTransform &t)
{
if (t.type() > QTransform::TxRotate)
return QFontEngine::alphaRGBMapForGlyph(g, subPixelPosition, t);
@@ -1923,7 +2121,10 @@ QImage QFontEngineFT::alphaRGBMapForGlyph(glyph_t g, QFixed subPixelPosition, co
return QFontEngine::alphaRGBMapForGlyph(g, subPixelPosition, t);
}
-QImage QFontEngineFT::bitmapForGlyph(glyph_t g, QFixed subPixelPosition, const QTransform &t, const QColor &color)
+QImage QFontEngineFT::bitmapForGlyph(glyph_t g,
+ const QFixedPoint &subPixelPosition,
+ const QTransform &t,
+ const QColor &color)
{
Q_UNUSED(color);
@@ -1952,7 +2153,7 @@ QImage QFontEngineFT::bitmapForGlyph(glyph_t g, QFixed subPixelPosition, const Q
void QFontEngineFT::removeGlyphFromCache(glyph_t glyph)
{
- defaultGlyphSet.removeGlyphFromCache(glyph, 0);
+ defaultGlyphSet.removeGlyphFromCache(glyph, QFixedPoint());
}
int QFontEngineFT::glyphCount() const
@@ -2033,7 +2234,8 @@ void QFontEngineFT::QGlyphSet::clear()
glyph_data.clear();
}
-void QFontEngineFT::QGlyphSet::removeGlyphFromCache(glyph_t index, QFixed subPixelPosition)
+void QFontEngineFT::QGlyphSet::removeGlyphFromCache(glyph_t index,
+ const QFixedPoint &subPixelPosition)
{
if (useFastGlyphData(index, subPixelPosition)) {
if (fast_glyph_data[index]) {
@@ -2047,7 +2249,9 @@ void QFontEngineFT::QGlyphSet::removeGlyphFromCache(glyph_t index, QFixed subPix
}
}
-void QFontEngineFT::QGlyphSet::setGlyph(glyph_t index, QFixed subPixelPosition, Glyph *glyph)
+void QFontEngineFT::QGlyphSet::setGlyph(glyph_t index,
+ const QFixedPoint &subPixelPosition,
+ Glyph *glyph)
{
if (useFastGlyphData(index, subPixelPosition)) {
if (!fast_glyph_data[index])
diff --git a/src/gui/text/freetype/qfontengine_ft_p.h b/src/gui/text/freetype/qfontengine_ft_p.h
index dcbe49ff99..bdd4549827 100644
--- a/src/gui/text/freetype/qfontengine_ft_p.h
+++ b/src/gui/text/freetype/qfontengine_ft_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) 2021 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_FT_P_H
#define QFONTENGINE_FT_P_H
//
@@ -55,6 +19,7 @@
#include <ft2build.h>
#include FT_FREETYPE_H
+#include FT_MULTIPLE_MASTERS_H
#ifndef Q_OS_WIN
@@ -85,6 +50,8 @@ public:
const QByteArray &fontData = QByteArray());
void release(const QFontEngine::FaceId &face_id);
+ static int getFaceIndexByStyleName(const QString &faceFileName, const QString &styleName);
+
// locks the struct for usage. Any read/write operations require locking.
void lock()
{
@@ -96,6 +63,7 @@ public:
}
FT_Face face;
+ FT_MM_Var *mm_var;
int xsize; // 26.6
int ysize; // 26.6
FT_Matrix matrix;
@@ -109,6 +77,7 @@ public:
int getPointInOutline(glyph_t glyph, int flags, quint32 point, QFixed *xpos, QFixed *ypos, quint32 *nPoints);
+ bool isScalable() const;
bool isScalableBitmap() const;
static void addGlyphToPath(FT_Face face, FT_GlyphSlot g, const QFixedPoint &point, QPainterPath *path, FT_Fixed x_scale, FT_Fixed y_scale);
@@ -117,7 +86,6 @@ public:
private:
friend class QFontEngineFT;
friend class QtFreetypeData;
- friend struct QScopedPointerDeleter<QFreetypeFace>;
QFreetypeFace() = default;
~QFreetypeFace() {}
void cleanup();
@@ -143,7 +111,7 @@ public:
struct GlyphAndSubPixelPosition
{
- GlyphAndSubPixelPosition(glyph_t g, QFixed spp) : glyph(g), subPixelPosition(spp) {}
+ GlyphAndSubPixelPosition(glyph_t g, const QFixedPoint spp) : glyph(g), subPixelPosition(spp) {}
bool operator==(const GlyphAndSubPixelPosition &other) const
{
@@ -151,7 +119,7 @@ public:
}
glyph_t glyph;
- QFixed subPixelPosition;
+ QFixedPoint subPixelPosition;
};
struct QGlyphSet
@@ -161,13 +129,14 @@ public:
FT_Matrix transformationMatrix;
bool outline_drawing;
- void removeGlyphFromCache(glyph_t index, QFixed subPixelPosition);
+ void removeGlyphFromCache(glyph_t index, const QFixedPoint &subPixelPosition);
void clear();
- inline bool useFastGlyphData(glyph_t index, QFixed subPixelPosition) const {
- return (index < 256 && subPixelPosition == 0);
+ inline bool useFastGlyphData(glyph_t index, const QFixedPoint &subPixelPosition) const {
+ return (index < 256 && subPixelPosition.x == 0 && subPixelPosition.y == 0);
}
- inline Glyph *getGlyph(glyph_t index, QFixed subPixelPosition = 0) const;
- void setGlyph(glyph_t index, QFixed spp, Glyph *glyph);
+ inline Glyph *getGlyph(glyph_t index,
+ const QFixedPoint &subPixelPositionX = QFixedPoint()) const;
+ void setGlyph(glyph_t index, const QFixedPoint &spp, Glyph *glyph);
inline bool isGlyphMissing(glyph_t index) const { return missing_glyphs.contains(index); }
inline void setGlyphMissing(glyph_t index) const { missing_glyphs.insert(index); }
@@ -182,12 +151,17 @@ private:
QFontEngine::FaceId faceId() const override;
QFontEngine::Properties properties() const override;
QFixed emSquareSize() const override;
- bool supportsSubPixelPositions() const override
+ bool supportsHorizontalSubPixelPositions() const override
{
return default_hint_style == HintLight ||
default_hint_style == HintNone;
}
+ bool supportsVerticalSubPixelPositions() const override
+ {
+ return supportsHorizontalSubPixelPositions();
+ }
+
bool getSfntTableData(uint tag, uchar *buffer, uint *length) const override;
int synthesized() const override;
@@ -212,24 +186,26 @@ private:
void addOutlineToPath(qreal x, qreal y, const QGlyphLayout &glyphs,
QPainterPath *path, QTextItem::RenderFlags flags) 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;
glyph_metrics_t boundingBox(const QGlyphLayout &glyphs) override;
glyph_metrics_t boundingBox(glyph_t glyph) override;
glyph_metrics_t boundingBox(glyph_t glyph, const QTransform &matrix) override;
void recalcAdvances(QGlyphLayout *glyphs, ShaperFlags flags) const override;
- QImage alphaMapForGlyph(glyph_t g) override { return alphaMapForGlyph(g, 0); }
- QImage alphaMapForGlyph(glyph_t, QFixed) override;
- QImage alphaMapForGlyph(glyph_t glyph, QFixed subPixelPosition, const QTransform &t) override;
- QImage alphaRGBMapForGlyph(glyph_t, QFixed subPixelPosition, const QTransform &t) override;
- QImage bitmapForGlyph(glyph_t, QFixed subPixelPosition, const QTransform &t, const QColor &color) override;
+ QImage alphaMapForGlyph(glyph_t g) override { return alphaMapForGlyph(g, QFixedPoint()); }
+ QImage alphaMapForGlyph(glyph_t, const QFixedPoint &) override;
+ QImage alphaMapForGlyph(glyph_t glyph, const QFixedPoint &subPixelPosition, const QTransform &t) override;
+ QImage alphaRGBMapForGlyph(glyph_t, const QFixedPoint &subPixelPosition, const QTransform &t) override;
+ QImage bitmapForGlyph(glyph_t, const QFixedPoint &subPixelPosition, const QTransform &t, const QColor &color) override;
glyph_metrics_t alphaMapBoundingBox(glyph_t glyph,
- QFixed subPixelPosition,
+ const QFixedPoint &subPixelPosition,
const QTransform &matrix,
QFontEngine::GlyphFormat format) override;
- Glyph *glyphData(glyph_t glyph, QFixed subPixelPosition,
- GlyphFormat neededFormat, const QTransform &t) override;
+ Glyph *glyphData(glyph_t glyph,
+ const QFixedPoint &subPixelPosition,
+ GlyphFormat neededFormat,
+ const QTransform &t) override;
bool hasInternalCaching() const override { return cacheEnabled; }
bool expectsGammaCorrectedBlending() const override;
@@ -252,10 +228,24 @@ private:
inline bool isBitmapFont() const { return defaultFormat == Format_Mono; }
inline bool isScalableBitmap() const { return freetype->isScalableBitmap(); }
- inline Glyph *loadGlyph(uint glyph, QFixed subPixelPosition, GlyphFormat format = Format_None, bool fetchMetricsOnly = false, bool disableOutlineDrawing = false) const
+ inline Glyph *loadGlyph(uint glyph,
+ const QFixedPoint &subPixelPosition,
+ GlyphFormat format = Format_None,
+ bool fetchMetricsOnly = false,
+ bool disableOutlineDrawing = false) const
{ return loadGlyph(cacheEnabled ? &defaultGlyphSet : nullptr, glyph, subPixelPosition, format, fetchMetricsOnly, disableOutlineDrawing); }
- Glyph *loadGlyph(QGlyphSet *set, uint glyph, QFixed subPixelPosition, GlyphFormat = Format_None, bool fetchMetricsOnly = false, bool disableOutlineDrawing = false) const;
- Glyph *loadGlyphFor(glyph_t g, QFixed subPixelPosition, GlyphFormat format, const QTransform &t, bool fetchBoundingBox = false, bool disableOutlineDrawing = false);
+ Glyph *loadGlyph(QGlyphSet *set,
+ uint glyph,
+ const QFixedPoint &subPixelPosition,
+ GlyphFormat = Format_None,
+ bool fetchMetricsOnly = false,
+ bool disableOutlineDrawing = false) const;
+ Glyph *loadGlyphFor(glyph_t g,
+ const QFixedPoint &subPixelPosition,
+ GlyphFormat format,
+ const QTransform &t,
+ bool fetchBoundingBox = false,
+ bool disableOutlineDrawing = false);
QGlyphSet *loadGlyphSet(const QTransform &matrix);
@@ -279,7 +269,7 @@ private:
HintStyle defaultHintStyle() const { return default_hint_style; }
static QFontEngineFT *create(const QFontDef &fontDef, FaceId faceId, const QByteArray &fontData = QByteArray());
- static QFontEngineFT *create(const QByteArray &fontData, qreal pixelSize, QFont::HintingPreference hintingPreference);
+ static QFontEngineFT *create(const QByteArray &fontData, qreal pixelSize, QFont::HintingPreference hintingPreference, const QMap<QFont::Tag, float> &variableAxisValue);
protected:
@@ -338,15 +328,17 @@ private:
QFixed scalableBitmapScaleFactor;
};
-Q_DECLARE_TYPEINFO(QFontEngineFT::QGlyphSet, Q_MOVABLE_TYPE);
-
-inline size_t qHash(const QFontEngineFT::GlyphAndSubPixelPosition &g)
+inline size_t qHash(const QFontEngineFT::GlyphAndSubPixelPosition &g, size_t seed = 0)
{
- return (g.glyph << 8) | (g.subPixelPosition * 10).round().toInt();
+ return qHashMulti(seed,
+ g.glyph,
+ g.subPixelPosition.x.value(),
+ g.subPixelPosition.y.value());
}
-inline QFontEngineFT::Glyph *QFontEngineFT::QGlyphSet::getGlyph(glyph_t index, QFixed subPixelPosition) const
+inline QFontEngineFT::Glyph *QFontEngineFT::QGlyphSet::getGlyph(glyph_t index,
+ const QFixedPoint &subPixelPosition) const
{
if (useFastGlyphData(index, subPixelPosition))
return fast_glyph_data[index];
diff --git a/src/gui/text/freetype/qfreetypefontdatabase.cpp b/src/gui/text/freetype/qfreetypefontdatabase.cpp
index ac7520c495..018e590ac2 100644
--- a/src/gui/text/freetype/qfreetypefontdatabase.cpp
+++ b/src/gui/text/freetype/qfreetypefontdatabase.cpp
@@ -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 "qfreetypefontdatabase_p.h"
@@ -46,6 +10,8 @@
#include <QtCore/QLibraryInfo>
#include <QtCore/QDir>
#include <QtCore/QtEndian>
+#include <QtCore/QLoggingCategory>
+#include <QtCore/QUuid>
#undef QT_NO_FREETYPE
#include "qfontengine_ft_p.h"
@@ -54,8 +20,16 @@
#include FT_TRUETYPE_TABLES_H
#include FT_ERRORS_H
+#include FT_MULTIPLE_MASTERS_H
+#include FT_SFNT_NAMES_H
+#include FT_TRUETYPE_IDS_H
+
QT_BEGIN_NAMESPACE
+Q_DECLARE_LOGGING_CATEGORY(lcFontDb)
+
+using namespace Qt::StringLiterals;
+
void QFreeTypeFontDatabase::populateFontDatabase()
{
QString fontpath = fontDir();
@@ -68,14 +42,14 @@ void QFreeTypeFontDatabase::populateFontDatabase()
return;
}
- QStringList nameFilters;
- nameFilters << QLatin1String("*.ttf")
- << QLatin1String("*.ttc")
- << QLatin1String("*.pfa")
- << QLatin1String("*.pfb")
- << QLatin1String("*.otf");
+ static const QString nameFilters[] = {
+ u"*.ttf"_s,
+ u"*.pfa"_s,
+ u"*.pfb"_s,
+ u"*.otf"_s,
+ };
- const auto fis = dir.entryInfoList(nameFilters, QDir::Files);
+ const auto fis = dir.entryInfoList(QStringList::fromReadOnlyData(nameFilters), QDir::Files);
for (const QFileInfo &fi : fis) {
const QByteArray file = QFile::encodeName(fi.absoluteFilePath());
QFreeTypeFontDatabase::addTTFile(QByteArray(), file);
@@ -88,14 +62,24 @@ QFontEngine *QFreeTypeFontDatabase::fontEngine(const QFontDef &fontDef, void *us
QFontEngine::FaceId faceId;
faceId.filename = QFile::encodeName(fontfile->fileName);
faceId.index = fontfile->indexValue;
+ faceId.instanceIndex = fontfile->instanceIndex;
+ faceId.variableAxes = fontDef.variableAxisValues;
+
+ // Make sure the FaceId compares uniquely in cases where a
+ // file name is not provided.
+ if (faceId.filename.isEmpty()) {
+ QUuid::Id128Bytes id{};
+ memcpy(&id, &usrPtr, sizeof(usrPtr));
+ faceId.uuid = QUuid(id).toByteArray();
+ }
- return QFontEngineFT::create(fontDef, faceId);
+ return QFontEngineFT::create(fontDef, faceId, fontfile->data);
}
QFontEngine *QFreeTypeFontDatabase::fontEngine(const QByteArray &fontData, qreal pixelSize,
QFont::HintingPreference hintingPreference)
{
- return QFontEngineFT::create(fontData, pixelSize, hintingPreference);
+ return QFontEngineFT::create(fontData, pixelSize, hintingPreference, {});
}
QStringList QFreeTypeFontDatabase::addApplicationFont(const QByteArray &fontData, const QString &fileName, QFontDatabasePrivate::ApplicationFont *applicationFont)
@@ -111,6 +95,114 @@ void QFreeTypeFontDatabase::releaseHandle(void *handle)
extern FT_Library qt_getFreetype();
+void QFreeTypeFontDatabase::addNamedInstancesForFace(void *face_,
+ int faceIndex,
+ const QString &family,
+ const QString &styleName,
+ QFont::Weight weight,
+ QFont::Stretch stretch,
+ QFont::Style style,
+ bool fixedPitch,
+ const QSupportedWritingSystems &writingSystems,
+ const QByteArray &fileName,
+ const QByteArray &fontData)
+{
+ FT_Face face = reinterpret_cast<FT_Face>(face_);
+
+ // Note: The following does not actually depend on API from 2.9, but the
+ // FT_Set_Named_Instance() was added in 2.9, so to avoid populating the database with
+ // named instances that cannot be selected, we disable the feature on older Freetype
+ // versions.
+#if (FREETYPE_MAJOR*10000 + FREETYPE_MINOR*100 + FREETYPE_PATCH) >= 20900
+ FT_MM_Var *var = nullptr;
+ FT_Get_MM_Var(face, &var);
+ if (var != nullptr) {
+ std::unique_ptr<FT_MM_Var, void(*)(FT_MM_Var*)> varGuard(var, [](FT_MM_Var *res) {
+ FT_Done_MM_Var(qt_getFreetype(), res);
+ });
+
+ for (FT_UInt i = 0; i < var->num_namedstyles; ++i) {
+ FT_UInt id = var->namedstyle[i].strid;
+
+ QFont::Weight instanceWeight = weight;
+ QFont::Stretch instanceStretch = stretch;
+ QFont::Style instanceStyle = style;
+ for (FT_UInt axis = 0; axis < var->num_axis; ++axis) {
+ if (var->axis[axis].tag == QFont::Tag("wght").value()) {
+ instanceWeight = QFont::Weight(var->namedstyle[i].coords[axis] >> 16);
+ } else if (var->axis[axis].tag == QFont::Tag("wdth").value()) {
+ instanceStretch = QFont::Stretch(var->namedstyle[i].coords[axis] >> 16);
+ } else if (var->axis[axis].tag == QFont::Tag("ital").value()) {
+ FT_UInt ital = var->namedstyle[i].coords[axis] >> 16;
+ if (ital == 1)
+ instanceStyle = QFont::StyleItalic;
+ else
+ instanceStyle = QFont::StyleNormal;
+ }
+ }
+
+ FT_UInt count = FT_Get_Sfnt_Name_Count(face);
+ for (FT_UInt j = 0; j < count; ++j) {
+ FT_SfntName name;
+ if (FT_Get_Sfnt_Name(face, j, &name))
+ continue;
+
+ if (name.name_id != id)
+ continue;
+
+ // Only support Unicode for now
+ if (name.encoding_id != TT_MS_ID_UNICODE_CS)
+ continue;
+
+ // Sfnt names stored as UTF-16BE
+ QString instanceName;
+ for (FT_UInt k = 0; k < name.string_len; k += 2)
+ instanceName += QChar((name.string[k] << 8) + name.string[k + 1]);
+ if (instanceName != styleName) {
+ FontFile *variantFontFile = new FontFile{
+ QFile::decodeName(fileName),
+ faceIndex,
+ int(i),
+ fontData
+ };
+
+ qCDebug(lcFontDb) << "Registering named instance" << i
+ << ":" << instanceName
+ << "for font family" << family
+ << "with weight" << instanceWeight
+ << ", style" << instanceStyle
+ << ", stretch" << instanceStretch;
+
+ registerFont(family,
+ instanceName,
+ QString(),
+ instanceWeight,
+ instanceStyle,
+ instanceStretch,
+ true,
+ true,
+ 0,
+ fixedPitch,
+ writingSystems,
+ variantFontFile);
+ }
+ }
+ }
+ }
+#else
+ Q_UNUSED(face);
+ Q_UNUSED(family);
+ Q_UNUSED(styleName);
+ Q_UNUSED(weight);
+ Q_UNUSED(stretch);
+ Q_UNUSED(style);
+ Q_UNUSED(fixedPitch);
+ Q_UNUSED(writingSystems);
+ Q_UNUSED(fontData);
+#endif
+
+}
+
QStringList QFreeTypeFontDatabase::addTTFile(const QByteArray &fontData, const QByteArray &file, QFontDatabasePrivate::ApplicationFont *applicationFont)
{
FT_Library library = qt_getFreetype();
@@ -153,6 +245,7 @@ QStringList QFreeTypeFontDatabase::addTTFile(const QByteArray &fontData, const Q
}
}
+ QFont::Stretch stretch = QFont::Unstretched;
TT_OS2 *os2 = (TT_OS2 *)FT_Get_Sfnt_Table(face, ft_sfnt_os2);
if (os2) {
quint32 unicodeRange[4] = {
@@ -191,14 +284,46 @@ QStringList QFreeTypeFontDatabase::addTTFile(const QByteArray &fontData, const Q
else if (w <= 10)
weight = QFont::Black;
}
+
+ switch (os2->usWidthClass) {
+ case 1:
+ stretch = QFont::UltraCondensed;
+ break;
+ case 2:
+ stretch = QFont::ExtraCondensed;
+ break;
+ case 3:
+ stretch = QFont::Condensed;
+ break;
+ case 4:
+ stretch = QFont::SemiCondensed;
+ break;
+ case 5:
+ stretch = QFont::Unstretched;
+ break;
+ case 6:
+ stretch = QFont::SemiExpanded;
+ break;
+ case 7:
+ stretch = QFont::Expanded;
+ break;
+ case 8:
+ stretch = QFont::ExtraExpanded;
+ break;
+ case 9:
+ stretch = QFont::UltraExpanded;
+ break;
+ }
}
QString family = QString::fromLatin1(face->family_name);
- FontFile *fontFile = new FontFile;
- fontFile->fileName = QFile::decodeName(file);
- fontFile->indexValue = index;
+ FontFile *fontFile = new FontFile{
+ QFile::decodeName(file),
+ index,
+ -1,
+ fontData
+ };
- QFont::Stretch stretch = QFont::Unstretched;
QString styleName = QString::fromLatin1(face->style_name);
if (applicationFont != nullptr) {
@@ -213,6 +338,9 @@ QStringList QFreeTypeFontDatabase::addTTFile(const QByteArray &fontData, const Q
}
registerFont(family, styleName, QString(), weight, style, stretch, true, true, 0, fixedPitch, writingSystems, fontFile);
+
+ addNamedInstancesForFace(face, index, family, styleName, weight, stretch, style, fixedPitch, writingSystems, file, fontData);
+
families.append(family);
FT_Done_Face(face);
@@ -221,4 +349,13 @@ QStringList QFreeTypeFontDatabase::addTTFile(const QByteArray &fontData, const Q
return families;
}
+bool QFreeTypeFontDatabase::supportsVariableApplicationFonts() const
+{
+#if (FREETYPE_MAJOR*10000 + FREETYPE_MINOR*100 + FREETYPE_PATCH) >= 20900
+ return true;
+#else
+ return false;
+#endif
+}
+
QT_END_NAMESPACE
diff --git a/src/gui/text/freetype/qfreetypefontdatabase_p.h b/src/gui/text/freetype/qfreetypefontdatabase_p.h
index 2cc725bd06..5fcec585d2 100644
--- a/src/gui/text/freetype/qfreetypefontdatabase_p.h
+++ b/src/gui/text/freetype/qfreetypefontdatabase_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) 2020 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 QFREETYPEFONTDATABASE_H
#define QFREETYPEFONTDATABASE_H
@@ -54,6 +18,7 @@
#include <qpa/qplatformfontdatabase.h>
#include <QtCore/QByteArray>
#include <QtCore/QString>
+#include <QtCore/private/qglobal_p.h>
QT_BEGIN_NAMESPACE
@@ -61,6 +26,12 @@ struct FontFile
{
QString fileName;
int indexValue;
+ int instanceIndex = -1;
+
+ // Note: The data may be implicitly shared throughout the
+ // font database and platform font database, so be careful
+ // to never detach when accessing this member!
+ const QByteArray data;
};
class Q_GUI_EXPORT QFreeTypeFontDatabase : public QPlatformFontDatabase
@@ -71,6 +42,14 @@ public:
QFontEngine *fontEngine(const QByteArray &fontData, qreal pixelSize, QFont::HintingPreference hintingPreference) override;
QStringList addApplicationFont(const QByteArray &fontData, const QString &fileName, QFontDatabasePrivate::ApplicationFont *applicationFont = nullptr) override;
void releaseHandle(void *handle) override;
+ bool supportsVariableApplicationFonts() const override;
+
+ static void addNamedInstancesForFace(void *face, int faceIndex,
+ const QString &family, const QString &styleName,
+ QFont::Weight weight, QFont::Stretch stretch,
+ QFont::Style style, bool fixedPitch,
+ const QSupportedWritingSystems &writingSystems,
+ const QByteArray &fileName, const QByteArray &fontData);
static QStringList addTTFile(const QByteArray &fontData, const QByteArray &file, QFontDatabasePrivate::ApplicationFont *applicationFont = nullptr);
};
diff --git a/src/gui/text/qabstracttextdocumentlayout.cpp b/src/gui/text/qabstracttextdocumentlayout.cpp
index 90726100e2..3e5144b157 100644
--- a/src/gui/text/qabstracttextdocumentlayout.cpp
+++ b/src/gui/text/qabstracttextdocumentlayout.cpp
@@ -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 <qabstracttextdocumentlayout.h>
#include <qtextformat.h>
@@ -137,7 +101,7 @@ QTextObjectInterface::~QTextObjectInterface()
\warning Copy and Paste operations ignore custom text objects.
- \sa {Text Object Example}, QTextCharFormat, QTextLayout
+ \sa QTextCharFormat, QTextLayout
*/
/*!
@@ -435,7 +399,8 @@ void QAbstractTextDocumentLayout::registerHandler(int objectType, QObject *compo
if (!iface)
return; // ### print error message on terminal?
- connect(component, SIGNAL(destroyed(QObject*)), this, SLOT(_q_handlerDestroyed(QObject*)));
+ QObjectPrivate::connect(component, &QObject::destroyed, d,
+ &QAbstractTextDocumentLayoutPrivate::_q_handlerDestroyed);
QTextObjectHandler h;
h.iface = iface;
@@ -456,7 +421,8 @@ void QAbstractTextDocumentLayout::unregisterHandler(int objectType, QObject *com
const auto it = d->handlers.constFind(objectType);
if (it != d->handlers.cend() && (!component || component == it->component)) {
if (component)
- disconnect(component, SIGNAL(destroyed(QObject*)), this, SLOT(_q_handlerDestroyed(QObject*)));
+ QObjectPrivate::disconnect(component, &QObject::destroyed, d,
+ &QAbstractTextDocumentLayoutPrivate::_q_handlerDestroyed);
d->handlers.erase(it);
}
}
@@ -636,7 +602,7 @@ QTextFormat QAbstractTextDocumentLayout::formatAt(const QPointF &pos) const
if (blockBr.contains(pos)) {
QTextLayout *layout = block.layout();
int relativeCursorPos = cursorPos - block.position();
- const int preeditLength = layout ? layout->preeditAreaText().length() : 0;
+ const int preeditLength = layout ? layout->preeditAreaText().size() : 0;
if (preeditLength > 0 && relativeCursorPos > layout->preeditAreaPosition())
cursorPos -= qMin(cursorPos - layout->preeditAreaPosition(), preeditLength);
break;
diff --git a/src/gui/text/qabstracttextdocumentlayout.h b/src/gui/text/qabstracttextdocumentlayout.h
index 4dd3e16160..dfafb1098f 100644
--- a/src/gui/text/qabstracttextdocumentlayout.h
+++ b/src/gui/text/qabstracttextdocumentlayout.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 QABSTRACTTEXTDOCUMENTLAYOUT_H
#define QABSTRACTTEXTDOCUMENTLAYOUT_H
@@ -129,7 +93,6 @@ private:
friend class QTextEngine;
friend class QTextLayout;
friend class QTextLine;
- Q_PRIVATE_SLOT(d_func(), void _q_handlerDestroyed(QObject *obj))
Q_PRIVATE_SLOT(d_func(), int _q_dynamicPageCountSlot())
Q_PRIVATE_SLOT(d_func(), QSizeF _q_dynamicDocumentSizeSlot())
};
@@ -144,7 +107,7 @@ public:
virtual void drawObject(QPainter *painter, const QRectF &rect, QTextDocument *doc, int posInDocument, const QTextFormat &format) = 0;
};
-#ifndef Q_CLANG_QDOC
+#ifndef Q_QDOC
Q_DECLARE_INTERFACE(QTextObjectInterface, "org.qt-project.Qt.QTextObjectInterface")
#endif
diff --git a/src/gui/text/qabstracttextdocumentlayout_p.h b/src/gui/text/qabstracttextdocumentlayout_p.h
index 88035eb66d..6bd42d78d8 100644
--- a/src/gui/text/qabstracttextdocumentlayout_p.h
+++ b/src/gui/text/qabstracttextdocumentlayout_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 QABSTRACTTEXTDOCUMENTLAYOUT_P_H
#define QABSTRACTTEXTDOCUMENTLAYOUT_P_H
@@ -54,7 +18,10 @@
#include <QtGui/private/qtguiglobal_p.h>
#include "private/qobject_p.h"
#include "qtextdocument_p.h"
+#include "qabstracttextdocumentlayout.h"
+
#include "QtCore/qhash.h"
+#include <QtCore/qpointer.h>
QT_BEGIN_NAMESPACE
@@ -82,6 +49,16 @@ public:
docPrivate = QTextDocumentPrivate::get(doc);
}
+ static QAbstractTextDocumentLayoutPrivate *get(QAbstractTextDocumentLayout *layout)
+ {
+ return layout->d_func();
+ }
+
+ bool hasHandlers() const
+ {
+ return !handlers.isEmpty();
+ }
+
inline int _q_dynamicPageCountSlot() const
{ return q_func()->pageCount(); }
inline QSizeF _q_dynamicDocumentSizeSlot() const
diff --git a/src/gui/text/qcssparser.cpp b/src/gui/text/qcssparser.cpp
index f67c0f485a..e5815ffce2 100644
--- a/src/gui/text/qcssparser.cpp
+++ b/src/gui/text/qcssparser.cpp
@@ -1,44 +1,9 @@
-/****************************************************************************
-**
-** 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 "qcssparser_p.h"
+#include <QtCore/qmap.h>
#include <qdebug.h>
#include <qicon.h>
#include <qcolor.h>
@@ -54,6 +19,12 @@
QT_BEGIN_NAMESPACE
+using namespace Qt::StringLiterals;
+
+QT_IMPL_METATYPE_EXTERN_TAGGED(QCss::BackgroundData, QCss__BackgroundData)
+QT_IMPL_METATYPE_EXTERN_TAGGED(QCss::LengthData, QCss__LengthData)
+QT_IMPL_METATYPE_EXTERN_TAGGED(QCss::BorderData, QCss__BorderData)
+
#include "qcssscanner.cpp"
using namespace QCss;
@@ -68,14 +39,18 @@ static const QCssKnownValue properties[NumProperties - 1] = {
{ "-qt-background-role", QtBackgroundRole },
{ "-qt-block-indent", QtBlockIndent },
{ "-qt-fg-texture-cachekey", QtForegroundTextureCacheKey },
+ { "-qt-foreground", QtForeground },
{ "-qt-line-height-type", QtLineHeightType },
{ "-qt-list-indent", QtListIndent },
{ "-qt-list-number-prefix", QtListNumberPrefix },
{ "-qt-list-number-suffix", QtListNumberSuffix },
{ "-qt-paragraph-type", QtParagraphType },
+ { "-qt-stroke-color", QtStrokeColor },
+ { "-qt-stroke-width", QtStrokeWidth },
{ "-qt-style-features", QtStyleFeatures },
{ "-qt-table-type", QtTableType },
{ "-qt-user-state", QtUserState },
+ { "accent-color", QtAccent },
{ "alternate-background-color", QtAlternateBackground },
{ "background", Background },
{ "background-attachment", BackgroundAttachment },
@@ -157,6 +132,7 @@ static const QCssKnownValue properties[NumProperties - 1] = {
{ "padding-top", PaddingTop },
{ "page-break-after", PageBreakAfter },
{ "page-break-before", PageBreakBefore },
+ { "placeholder-text-color", QtPlaceHolderTextColor },
{ "position", Position },
{ "right", Right },
{ "selection-background-color", QtSelectionBackground },
@@ -166,6 +142,7 @@ static const QCssKnownValue properties[NumProperties - 1] = {
{ "subcontrol-position", QtPosition },
{ "text-align", TextAlignment },
{ "text-decoration", TextDecoration },
+ { "text-decoration-color", TextDecorationColor },
{ "text-indent", TextIndent },
{ "text-transform", TextTransform },
{ "text-underline-style", TextUnderlineStyle },
@@ -261,7 +238,7 @@ static const short indexOfId[NumKnownValues] = { 0, 41, 48, 42, 49, 50, 55, 35,
QString Value::toString() const
{
if (type == KnownIdentifier) {
- return QLatin1String(values[indexOfId[variant.toInt()]].name);
+ return QLatin1StringView(values[indexOfId[variant.toInt()]].name);
} else {
return variant.toString();
}
@@ -354,12 +331,12 @@ static const QCssKnownValue styleFeatures[NumKnownStyleFeatures - 1] = {
static bool operator<(const QString &name, const QCssKnownValue &prop)
{
- return QString::compare(name, QLatin1String(prop.name), Qt::CaseInsensitive) < 0;
+ return QString::compare(name, QLatin1StringView(prop.name), Qt::CaseInsensitive) < 0;
}
static bool operator<(const QCssKnownValue &prop, const QString &name)
{
- return QString::compare(QLatin1String(prop.name), name, Qt::CaseInsensitive) < 0;
+ return QString::compare(QLatin1StringView(prop.name), name, Qt::CaseInsensitive) < 0;
}
static quint64 findKnownValue(const QString &name, const QCssKnownValue *start, int numValues)
@@ -436,7 +413,7 @@ int ValueExtractor::lengthValue(const Declaration &decl)
{
if (decl.d->parsed.isValid())
return lengthValueFromData(qvariant_cast<LengthData>(decl.d->parsed), f);
- if (decl.d->values.count() < 1)
+ if (decl.d->values.size() < 1)
return 0;
LengthData data = lengthValue(decl.d->values.at(0));
decl.d->parsed = QVariant::fromValue<LengthData>(data);
@@ -455,7 +432,7 @@ void ValueExtractor::lengthValues(const Declaration &decl, int *m)
LengthData datas[4];
int i;
- for (i = 0; i < qMin(decl.d->values.count(), 4); i++)
+ for (i = 0; i < qMin(decl.d->values.size(), 4); i++)
datas[i] = lengthValue(decl.d->values[i]);
if (i == 0) {
@@ -483,7 +460,7 @@ bool ValueExtractor::extractGeometry(int *w, int *h, int *minw, int *minh, int *
{
extractFont();
bool hit = false;
- for (int i = 0; i < declarations.count(); i++) {
+ for (int i = 0; i < declarations.size(); i++) {
const Declaration &decl = declarations.at(i);
switch (decl.d->propertyId) {
case Width: *w = lengthValue(decl); break;
@@ -505,7 +482,7 @@ bool ValueExtractor::extractPosition(int *left, int *top, int *right, int *botto
{
extractFont();
bool hit = false;
- for (int i = 0; i < declarations.count(); i++) {
+ for (int i = 0; i < declarations.size(); i++) {
const Declaration &decl = declarations.at(i);
switch (decl.d->propertyId) {
case Left: *left = lengthValue(decl); break;
@@ -528,7 +505,7 @@ bool ValueExtractor::extractBox(int *margins, int *paddings, int *spacing)
{
extractFont();
bool hit = false;
- for (int i = 0; i < declarations.count(); i++) {
+ for (int i = 0; i < declarations.size(); i++) {
const Declaration &decl = declarations.at(i);
switch (decl.d->propertyId) {
case PaddingLeft: paddings[LeftEdge] = lengthValue(decl); break;
@@ -555,7 +532,7 @@ bool ValueExtractor::extractBox(int *margins, int *paddings, int *spacing)
int ValueExtractor::extractStyleFeatures()
{
int features = StyleFeature_None;
- for (int i = 0; i < declarations.count(); i++) {
+ for (int i = 0; i < declarations.size(); i++) {
const Declaration &decl = declarations.at(i);
if (decl.d->propertyId == QtStyleFeatures)
features = decl.styleFeaturesValue();
@@ -572,9 +549,9 @@ QSize ValueExtractor::sizeValue(const Declaration &decl)
}
LengthData x[2] = { {0, LengthData::None }, {0, LengthData::None} };
- if (decl.d->values.count() > 0)
+ if (decl.d->values.size() > 0)
x[0] = lengthValue(decl.d->values.at(0));
- if (decl.d->values.count() > 1)
+ if (decl.d->values.size() > 1)
x[1] = lengthValue(decl.d->values.at(1));
else
x[1] = x[0];
@@ -596,7 +573,7 @@ bool ValueExtractor::extractBorder(int *borders, QBrush *colors, BorderStyle *st
{
extractFont();
bool hit = false;
- for (int i = 0; i < declarations.count(); i++) {
+ for (int i = 0; i < declarations.size(); i++) {
const Declaration &decl = declarations.at(i);
switch (decl.d->propertyId) {
case BorderLeftWidth: borders[LeftEdge] = lengthValue(decl); break;
@@ -655,7 +632,7 @@ bool ValueExtractor::extractOutline(int *borders, QBrush *colors, BorderStyle *s
{
extractFont();
bool hit = false;
- for (int i = 0; i < declarations.count(); i++) {
+ for (int i = 0; i < declarations.size(); i++) {
const Declaration &decl = declarations.at(i);
switch (decl.d->propertyId) {
case OutlineWidth: lengthValues(decl, borders); break;
@@ -724,11 +701,11 @@ static ColorData parseColorValue(QCss::Value v)
return ColorData();
QStringList lst = v.variant.toStringList();
- if (lst.count() != 2)
+ if (lst.size() != 2)
return ColorData();
const QString &identifier = lst.at(0);
- if ((identifier.compare(QLatin1String("palette"), Qt::CaseInsensitive)) == 0) {
+ if ((identifier.compare("palette"_L1, Qt::CaseInsensitive)) == 0) {
int role = findKnownValue(lst.at(1).trimmed(), values, NumKnownValues);
if (role >= Value_FirstColorRole && role <= Value_LastColorRole)
return (QPalette::ColorRole)(role-Value_FirstColorRole);
@@ -736,14 +713,14 @@ static ColorData parseColorValue(QCss::Value v)
return ColorData();
}
- const bool rgb = identifier.startsWith(QLatin1String("rgb"));
- const bool hsv = !rgb && identifier.startsWith(QLatin1String("hsv"));
- const bool hsl = !rgb && !hsv && identifier.startsWith(QLatin1String("hsl"));
+ const bool rgb = identifier.startsWith("rgb"_L1);
+ const bool hsv = !rgb && identifier.startsWith("hsv"_L1);
+ const bool hsl = !rgb && !hsv && identifier.startsWith("hsl"_L1);
if (!rgb && !hsv && !hsl)
return ColorData();
- const bool hasAlpha = identifier.size() == 4 && identifier.at(3) == QLatin1Char('a');
+ const bool hasAlpha = identifier.size() == 4 && identifier.at(3) == u'a';
if (identifier.size() > 3 && !hasAlpha)
return ColorData();
@@ -754,7 +731,7 @@ static ColorData parseColorValue(QCss::Value v)
QList<QCss::Value> colorDigits;
if (!p.parseExpr(&colorDigits))
return ColorData();
- const int tokenCount = colorDigits.count();
+ const int tokenCount = colorDigits.size();
for (int i = 0; i < qMin(tokenCount, 7); i += 2) {
if (colorDigits.at(i).type == Value::Percentage) {
@@ -770,11 +747,14 @@ static ColorData parseColorValue(QCss::Value v)
if (tokenCount < 5)
return ColorData();
- // ### Qt6: replace this with a check and return invalid color when token count does not match
- if (hasAlpha && tokenCount != 7)
- qWarning("QCssParser::parseColorValue: Specified color with alpha value but no alpha given: '%s'", qPrintable(lst.join(QLatin1Char(' '))));
- if (!hasAlpha && tokenCount != 5)
- qWarning("QCssParser::parseColorValue: Specified color without alpha value but alpha given: '%s'", qPrintable(lst.join(QLatin1Char(' '))));
+ if (hasAlpha && tokenCount != 7) {
+ qWarning("QCssParser::parseColorValue: Specified color with alpha value but no alpha given: '%s'", qPrintable(lst.join(u' ')));
+ return ColorData();
+ }
+ if (!hasAlpha && tokenCount != 5) {
+ qWarning("QCssParser::parseColorValue: Specified color without alpha value but alpha given: '%s'", qPrintable(lst.join(u' ')));
+ return ColorData();
+ }
int v1 = colorDigits.at(0).variant.toInt();
int v2 = colorDigits.at(2).variant.toInt();
@@ -818,11 +798,11 @@ static BrushData parseBrushValue(const QCss::Value &v, const QPalette &pal)
return BrushData();
QStringList lst = v.variant.toStringList();
- if (lst.count() != 2)
+ if (lst.size() != 2)
return BrushData();
QStringList gradFuncs;
- gradFuncs << QLatin1String("qlineargradient") << QLatin1String("qradialgradient") << QLatin1String("qconicalgradient") << QLatin1String("qgradient");
+ gradFuncs << "qlineargradient"_L1 << "qradialgradient"_L1 << "qconicalgradient"_L1 << "qgradient"_L1;
int gradType = -1;
if ((gradType = gradFuncs.indexOf(lst.at(0).toLower())) == -1)
@@ -833,7 +813,11 @@ static BrushData parseBrushValue(const QCss::Value &v, const QPalette &pal)
int spread = -1;
QStringList spreads;
- spreads << QLatin1String("pad") << QLatin1String("reflect") << QLatin1String("repeat");
+ spreads << "pad"_L1 << "reflect"_L1 << "repeat"_L1;
+
+ int coordinateMode = -1;
+ QStringList coordinateModes;
+ coordinateModes << "logical"_L1 << "stretchtodevice"_L1 << "objectbounding"_L1 << "object"_L1;
bool dependsOnThePalette = false;
Parser parser(lst.at(1));
@@ -846,7 +830,7 @@ static BrushData parseBrushValue(const QCss::Value &v, const QPalette &pal)
if (!parser.test(COLON))
return BrushData();
parser.skipSpace();
- if (attr.compare(QLatin1String("stop"), Qt::CaseInsensitive) == 0) {
+ if (attr.compare("stop"_L1, Qt::CaseInsensitive) == 0) {
QCss::Value stop, color;
parser.next();
if (!parser.parseTerm(&stop)) return BrushData();
@@ -854,27 +838,28 @@ static BrushData parseBrushValue(const QCss::Value &v, const QPalette &pal)
parser.next();
if (!parser.parseTerm(&color)) return BrushData();
ColorData cd = parseColorValue(color);
- if(cd.type == ColorData::Role)
+ if (cd.type == ColorData::Role)
dependsOnThePalette = true;
stops.append(QGradientStop(stop.variant.toReal(), colorFromData(cd, pal)));
} else {
parser.next();
QCss::Value value;
(void)parser.parseTerm(&value);
- if (attr.compare(QLatin1String("spread"), Qt::CaseInsensitive) == 0) {
+ if (attr.compare("spread"_L1, Qt::CaseInsensitive) == 0)
spread = spreads.indexOf(value.variant.toString());
- } else {
+ else if (attr.compare("coordinatemode"_L1, Qt::CaseInsensitive) == 0)
+ coordinateMode = coordinateModes.indexOf(value.variant.toString());
+ else
vars[attr] = value.variant.toReal();
- }
}
parser.skipSpace();
(void)parser.test(COMMA);
}
if (gradType == 0) {
- QLinearGradient lg(vars.value(QLatin1String("x1")), vars.value(QLatin1String("y1")),
- vars.value(QLatin1String("x2")), vars.value(QLatin1String("y2")));
- lg.setCoordinateMode(QGradient::ObjectBoundingMode);
+ QLinearGradient lg(vars.value("x1"_L1), vars.value("y1"_L1),
+ vars.value("x2"_L1), vars.value("y2"_L1));
+ lg.setCoordinateMode(coordinateMode < 0 ? QGradient::ObjectBoundingMode : QGradient::CoordinateMode(coordinateMode));
lg.setStops(stops);
if (spread != -1)
lg.setSpread(QGradient::Spread(spread));
@@ -885,10 +870,10 @@ static BrushData parseBrushValue(const QCss::Value &v, const QPalette &pal)
}
if (gradType == 1) {
- QRadialGradient rg(vars.value(QLatin1String("cx")), vars.value(QLatin1String("cy")),
- vars.value(QLatin1String("radius")), vars.value(QLatin1String("fx")),
- vars.value(QLatin1String("fy")));
- rg.setCoordinateMode(QGradient::ObjectBoundingMode);
+ QRadialGradient rg(vars.value("cx"_L1), vars.value("cy"_L1),
+ vars.value("radius"_L1), vars.value("fx"_L1),
+ vars.value("fy"_L1));
+ rg.setCoordinateMode(coordinateMode < 0 ? QGradient::ObjectBoundingMode : QGradient::CoordinateMode(coordinateMode));
rg.setStops(stops);
if (spread != -1)
rg.setSpread(QGradient::Spread(spread));
@@ -899,9 +884,8 @@ static BrushData parseBrushValue(const QCss::Value &v, const QPalette &pal)
}
if (gradType == 2) {
- QConicalGradient cg(vars.value(QLatin1String("cx")), vars.value(QLatin1String("cy")),
- vars.value(QLatin1String("angle")));
- cg.setCoordinateMode(QGradient::ObjectBoundingMode);
+ QConicalGradient cg(vars.value("cx"_L1), vars.value("cy"_L1), vars.value("angle"_L1));
+ cg.setCoordinateMode(coordinateMode < 0 ? QGradient::ObjectBoundingMode : QGradient::CoordinateMode(coordinateMode));
cg.setStops(stops);
if (spread != -1)
cg.setSpread(QGradient::Spread(spread));
@@ -985,7 +969,7 @@ void ValueExtractor::borderValue(const Declaration &decl, int *width, QCss::Bord
if (decl.d->values.at(i).type == Value::Length || decl.d->values.at(i).type == Value::Number) {
data.width = lengthValue(decl.d->values.at(i));
*width = lengthValueFromData(data.width, f);
- if (++i >= decl.d->values.count()) {
+ if (++i >= decl.d->values.size()) {
decl.d->parsed = QVariant::fromValue<BorderData>(data);
return;
}
@@ -994,7 +978,7 @@ void ValueExtractor::borderValue(const Declaration &decl, int *width, QCss::Bord
data.style = parseStyleValue(decl.d->values.at(i));
if (data.style != BorderStyle_Unknown) {
*style = data.style;
- if (++i >= decl.d->values.count()) {
+ if (++i >= decl.d->values.size()) {
decl.d->parsed = QVariant::fromValue<BorderData>(data);
return;
}
@@ -1003,9 +987,11 @@ void ValueExtractor::borderValue(const Declaration &decl, int *width, QCss::Bord
}
data.color = parseBrushValue(decl.d->values.at(i), pal);
- *color = brushFromData(data.color, pal);
- if (data.color.type != BrushData::DependsOnThePalette)
- decl.d->parsed = QVariant::fromValue<BorderData>(data);
+ if (data.color.type != BrushData::Invalid) {
+ *color = brushFromData(data.color, pal);
+ if (data.color.type != BrushData::DependsOnThePalette)
+ decl.d->parsed = QVariant::fromValue<BorderData>(data);
+ }
}
static void parseShorthandBackgroundProperty(const QList<QCss::Value> &values, BrushData *brush, QString *image, Repeat *repeat, Qt::Alignment *alignment, const QPalette &pal)
@@ -1015,7 +1001,7 @@ static void parseShorthandBackgroundProperty(const QList<QCss::Value> &values, B
*repeat = Repeat_XY;
*alignment = Qt::AlignTop | Qt::AlignLeft;
- for (int i = 0; i < values.count(); ++i) {
+ for (int i = 0; i < values.size(); ++i) {
const QCss::Value &v = values.at(i);
if (v.type == Value::Uri) {
*image = v.variant.toString();
@@ -1037,7 +1023,7 @@ static void parseShorthandBackgroundProperty(const QList<QCss::Value> &values, B
if (v.type == Value::KnownIdentifier) {
const int start = i;
int count = 1;
- if (i < values.count() - 1
+ if (i < values.size() - 1
&& values.at(i + 1).type == Value::KnownIdentifier) {
++i;
++count;
@@ -1059,7 +1045,7 @@ bool ValueExtractor::extractBackground(QBrush *brush, QString *image, Repeat *re
Origin *clip)
{
bool hit = false;
- for (int i = 0; i < declarations.count(); ++i) {
+ for (int i = 0; i < declarations.size(); ++i) {
const Declaration &decl = declarations.at(i);
if (decl.d->values.isEmpty())
continue;
@@ -1136,18 +1122,18 @@ static bool setFontSizeFromValue(QCss::Value value, QFont *font, int *fontSizeAd
bool valid = false;
QString s = value.variant.toString();
- if (s.endsWith(QLatin1String("pt"), Qt::CaseInsensitive)) {
+ if (s.endsWith("pt"_L1, Qt::CaseInsensitive)) {
s.chop(2);
value.variant = s;
if (value.variant.convert(QMetaType::fromType<qreal>())) {
- font->setPointSizeF(value.variant.toReal());
+ font->setPointSizeF(qBound(qreal(0), value.variant.toReal(), qreal(1 << 24) - 1));
valid = true;
}
- } else if (s.endsWith(QLatin1String("px"), Qt::CaseInsensitive)) {
+ } else if (s.endsWith("px"_L1, Qt::CaseInsensitive)) {
s.chop(2);
value.variant = s;
if (value.variant.convert(QMetaType::fromType<int>())) {
- font->setPixelSize(value.variant.toInt());
+ font->setPixelSize(qBound(0, value.variant.toInt(), (1 << 24) - 1));
valid = true;
}
}
@@ -1192,7 +1178,8 @@ static bool setFontWeightFromValue(const QCss::Value &value, QFont *font)
}
if (value.type != Value::Number)
return false;
- font->setWeight(QFont::Weight(value.variant.toInt()));
+ // .toInt() would call qRound64() and might overflow the long long there
+ font->setWeight(QFont::Weight(qRound(qBound(0.0, value.variant.toDouble(), 1001.0))));
return true;
}
@@ -1206,7 +1193,7 @@ static bool setFontFamilyFromValues(const QList<QCss::Value> &values, QFont *fon
QString family;
QStringList families;
bool shouldAddSpace = false;
- for (int i = start; i < values.count(); ++i) {
+ for (int i = start; i < values.size(); ++i) {
const QCss::Value &v = values.at(i);
if (v.type == Value::TermOperatorComma) {
families << family;
@@ -1218,7 +1205,7 @@ static bool setFontFamilyFromValues(const QList<QCss::Value> &values, QFont *fon
if (str.isEmpty())
break;
if (shouldAddSpace)
- family += QLatin1Char(' ');
+ family += u' ';
family += str;
shouldAddSpace = true;
}
@@ -1226,14 +1213,13 @@ static bool setFontFamilyFromValues(const QList<QCss::Value> &values, QFont *fon
families << family;
if (families.isEmpty())
return false;
- font->setFamily(families.at(0));
font->setFamilies(families);
return true;
}
static void setTextDecorationFromValues(const QList<QCss::Value> &values, QFont *font)
{
- for (int i = 0; i < values.count(); ++i) {
+ for (int i = 0; i < values.size(); ++i) {
if (values.at(i).type != Value::KnownIdentifier)
continue;
switch (values.at(i).variant.toInt()) {
@@ -1255,12 +1241,12 @@ static void setLetterSpacingFromValue(const QCss::Value &value, QFont *font)
QString s = value.variant.toString();
qreal val;
bool ok = false;
- if (s.endsWith(QLatin1String("em"), Qt::CaseInsensitive)) {
+ if (s.endsWith("em"_L1, Qt::CaseInsensitive)) {
s.chop(2);
val = s.toDouble(&ok);
if (ok)
font->setLetterSpacing(QFont::PercentageSpacing, (val + 1.0) * 100);
- } else if (s.endsWith(QLatin1String("px"), Qt::CaseInsensitive)) {
+ } else if (s.endsWith("px"_L1, Qt::CaseInsensitive)) {
s.chop(2);
val = s.toDouble(&ok);
if (ok)
@@ -1271,7 +1257,7 @@ static void setLetterSpacingFromValue(const QCss::Value &value, QFont *font)
static void setWordSpacingFromValue(const QCss::Value &value, QFont *font)
{
QString s = value.variant.toString();
- if (s.endsWith(QLatin1String("px"), Qt::CaseInsensitive)) {
+ if (s.endsWith("px"_L1, Qt::CaseInsensitive)) {
s.chop(2);
qreal val;
bool ok = false;
@@ -1288,7 +1274,7 @@ static void parseShorthandFontProperty(const QList<QCss::Value> &values, QFont *
*fontSizeAdjustment = -255;
int i = 0;
- while (i < values.count()) {
+ while (i < values.size()) {
if (setFontStyleFromValue(values.at(i), font)
|| setFontWeightFromValue(values.at(i), font))
++i;
@@ -1296,12 +1282,12 @@ static void parseShorthandFontProperty(const QList<QCss::Value> &values, QFont *
break;
}
- if (i < values.count()) {
+ if (i < values.size()) {
setFontSizeFromValue(values.at(i), font, fontSizeAdjustment);
++i;
}
- if (i < values.count()) {
+ if (i < values.size()) {
setFontFamilyFromValues(values, font, i);
}
}
@@ -1338,7 +1324,7 @@ bool ValueExtractor::extractFont(QFont *font, int *fontSizeAdjustment)
}
bool hit = false;
- for (int i = 0; i < declarations.count(); ++i) {
+ for (int i = 0; i < declarations.size(); ++i) {
const Declaration &decl = declarations.at(i);
if (decl.d->values.isEmpty())
continue;
@@ -1366,16 +1352,23 @@ bool ValueExtractor::extractFont(QFont *font, int *fontSizeAdjustment)
return hit;
}
-bool ValueExtractor::extractPalette(QBrush *fg, QBrush *sfg, QBrush *sbg, QBrush *abg)
+bool ValueExtractor::extractPalette(QBrush *foreground,
+ QBrush *selectedForeground,
+ QBrush *selectedBackground,
+ QBrush *alternateBackground,
+ QBrush *placeHolderTextForeground,
+ QBrush *accent)
{
bool hit = false;
- for (int i = 0; i < declarations.count(); ++i) {
+ for (int i = 0; i < declarations.size(); ++i) {
const Declaration &decl = declarations.at(i);
switch (decl.d->propertyId) {
- case Color: *fg = decl.brushValue(pal); break;
- case QtSelectionForeground: *sfg = decl.brushValue(pal); break;
- case QtSelectionBackground: *sbg = decl.brushValue(pal); break;
- case QtAlternateBackground: *abg = decl.brushValue(pal); break;
+ case Color: *foreground = decl.brushValue(pal); break;
+ case QtSelectionForeground: *selectedForeground = decl.brushValue(pal); break;
+ case QtSelectionBackground: *selectedBackground = decl.brushValue(pal); break;
+ case QtAlternateBackground: *alternateBackground = decl.brushValue(pal); break;
+ case QtPlaceHolderTextColor: *placeHolderTextForeground = decl.brushValue(pal); break;
+ case QtAccent: *accent = decl.brushValue(pal); break;
default: continue;
}
hit = true;
@@ -1394,12 +1387,12 @@ void ValueExtractor::extractFont()
bool ValueExtractor::extractImage(QIcon *icon, Qt::Alignment *a, QSize *size)
{
bool hit = false;
- for (int i = 0; i < declarations.count(); ++i) {
+ for (int i = 0; i < declarations.size(); ++i) {
const Declaration &decl = declarations.at(i);
switch (decl.d->propertyId) {
case QtImage:
*icon = decl.iconValue();
- if (decl.d->values.count() > 0 && decl.d->values.at(0).type == Value::Uri) {
+ if (decl.d->values.size() > 0 && decl.d->values.at(0).type == Value::Uri) {
// try to pull just the size from the image...
QImageReader imageReader(decl.d->values.at(0).variant.toString());
if ((*size = imageReader.size()).isNull()) {
@@ -1452,18 +1445,27 @@ bool ValueExtractor::extractIcon(QIcon *icon, QSize *size)
// Declaration
QColor Declaration::colorValue(const QPalette &pal) const
{
- if (d->values.count() != 1)
+ if (d->values.size() != 1)
return QColor();
if (d->parsed.isValid()) {
- if (d->parsed.userType() == QMetaType::QColor)
+ switch (d->parsed.typeId()) {
+ case qMetaTypeId<QColor>():
return qvariant_cast<QColor>(d->parsed);
- if (d->parsed.userType() == QMetaType::Int)
+ case qMetaTypeId<int>():
return pal.color((QPalette::ColorRole)(d->parsed.toInt()));
+ case qMetaTypeId<QList<QVariant>>():
+ if (d->parsed.toList().size() == 1) {
+ auto parsedList = d->parsed.toList();
+ const auto &value = parsedList.at(0);
+ return qvariant_cast<QColor>(value);
+ }
+ break;
+ }
}
ColorData color = parseColorValue(d->values.at(0));
- if(color.type == ColorData::Role) {
+ if (color.type == ColorData::Role) {
d->parsed = QVariant::fromValue<int>(color.role);
return pal.color((QPalette::ColorRole)(color.role));
} else {
@@ -1474,7 +1476,7 @@ QColor Declaration::colorValue(const QPalette &pal) const
QBrush Declaration::brushValue(const QPalette &pal) const
{
- if (d->values.count() != 1)
+ if (d->values.size() != 1)
return QBrush();
if (d->parsed.isValid()) {
@@ -1486,7 +1488,7 @@ QBrush Declaration::brushValue(const QPalette &pal) const
BrushData data = parseBrushValue(d->values.at(0), pal);
- if(data.type == BrushData::Role) {
+ if (data.type == BrushData::Role) {
d->parsed = QVariant::fromValue<int>(data.role);
return pal.color((QPalette::ColorRole)(data.role));
} else {
@@ -1503,8 +1505,9 @@ void Declaration::brushValues(QBrush *c, const QPalette &pal) const
int i = 0;
if (d->parsed.isValid()) {
needParse = 0;
+ Q_ASSERT(d->parsed.metaType() == QMetaType::fromType<QList<QVariant>>());
QList<QVariant> v = d->parsed.toList();
- for (i = 0; i < qMin(v.count(), 4); i++) {
+ for (i = 0; i < qMin(v.size(), 4); i++) {
if (v.at(i).userType() == QMetaType::QBrush) {
c[i] = qvariant_cast<QBrush>(v.at(i));
} else if (v.at(i).userType() == QMetaType::Int) {
@@ -1516,11 +1519,11 @@ void Declaration::brushValues(QBrush *c, const QPalette &pal) const
}
if (needParse != 0) {
QList<QVariant> v;
- for (i = 0; i < qMin(d->values.count(), 4); i++) {
+ for (i = 0; i < qMin(d->values.size(), 4); i++) {
if (!(needParse & (1<<i)))
continue;
BrushData data = parseBrushValue(d->values.at(i), pal);
- if(data.type == BrushData::Role) {
+ if (data.type == BrushData::Role) {
v += QVariant::fromValue<int>(data.role);
c[i] = pal.color((QPalette::ColorRole)(data.role));
} else {
@@ -1543,7 +1546,7 @@ void Declaration::brushValues(QBrush *c, const QPalette &pal) const
bool Declaration::realValue(qreal *real, const char *unit) const
{
- if (d->values.count() != 1)
+ if (d->values.size() != 1)
return false;
const Value &v = d->values.at(0);
if (unit && v.type != Value::Length)
@@ -1551,7 +1554,7 @@ bool Declaration::realValue(qreal *real, const char *unit) const
const QString str = v.variant.toString();
QStringView s(str);
if (unit) {
- const QLatin1String unitStr(unit);
+ const QLatin1StringView unitStr(unit);
if (!s.endsWith(unitStr, Qt::CaseInsensitive))
return false;
s.chop(unitStr.size());
@@ -1570,7 +1573,7 @@ static bool intValueHelper(const QCss::Value &v, int *i, const char *unit)
const QString str = v.variant.toString();
QStringView s(str);
if (unit) {
- const QLatin1String unitStr(unit);
+ const QLatin1StringView unitStr(unit);
if (!s.endsWith(unitStr, Qt::CaseInsensitive))
return false;
s.chop(unitStr.size());
@@ -1584,7 +1587,7 @@ static bool intValueHelper(const QCss::Value &v, int *i, const char *unit)
bool Declaration::intValue(int *i, const char *unit) const
{
- if (d->values.count() != 1)
+ if (d->values.size() != 1)
return false;
return intValueHelper(d->values.at(0), i, unit);
}
@@ -1595,11 +1598,25 @@ QSize Declaration::sizeValue() const
return qvariant_cast<QSize>(d->parsed);
int x[2] = { 0, 0 };
- if (d->values.count() > 0)
- intValueHelper(d->values.at(0), &x[0], "px");
- if (d->values.count() > 1)
- intValueHelper(d->values.at(1), &x[1], "px");
- else
+ const int count = d->values.size();
+ for (int i = 0; i < count; ++i) {
+ if (i > 1) {
+ qWarning("QCssParser::sizeValue: Too many values provided");
+ break;
+ }
+ const auto &value = d->values.at(i);
+ const QString valueString = value.variant.toString();
+ if (valueString.endsWith(u"pt", Qt::CaseInsensitive)) {
+ intValueHelper(value, &x[i], "pt");
+ // according to https://www.w3.org/TR/css3-values/#absolute-lengths
+ // 1pt = 1/72th of 1 inch, and 1px = 1/96th of 1 inch
+ x[i] = (x[i] * 72) / 96;
+ } else {
+ // by default we use 'px'
+ intValueHelper(value, &x[i], "px");
+ }
+ }
+ if (count == 1)
x[1] = x[0];
QSize size(x[0], x[1]);
d->parsed = QVariant::fromValue<QSize>(size);
@@ -1608,7 +1625,7 @@ QSize Declaration::sizeValue() const
QRect Declaration::rectValue() const
{
- if (d->values.count() != 1)
+ if (d->values.size() != 1)
return QRect();
if (d->parsed.isValid())
@@ -1618,10 +1635,10 @@ QRect Declaration::rectValue() const
if (v.type != Value::Function)
return QRect();
const QStringList func = v.variant.toStringList();
- if (func.count() != 2 || func.at(0).compare(QLatin1String("rect")) != 0)
+ if (func.size() != 2 || func.at(0).compare("rect"_L1) != 0)
return QRect();
- const auto args = QStringView{func[1]}.split(QLatin1Char(' '), Qt::SkipEmptyParts);
- if (args.count() != 4)
+ const auto args = QStringView{func[1]}.split(u' ', Qt::SkipEmptyParts);
+ if (args.size() != 4)
return QRect();
QRect rect(args[0].toInt(), args[1].toInt(), args[2].toInt(), args[3].toInt());
d->parsed = QVariant::fromValue<QRect>(rect);
@@ -1633,7 +1650,7 @@ void Declaration::colorValues(QColor *c, const QPalette &pal) const
int i;
if (d->parsed.isValid()) {
QList<QVariant> v = d->parsed.toList();
- for (i = 0; i < qMin(d->values.count(), 4); i++) {
+ for (i = 0; i < qMin(d->values.size(), 4); i++) {
if (v.at(i).userType() == QMetaType::QColor) {
c[i] = qvariant_cast<QColor>(v.at(i));
} else {
@@ -1642,9 +1659,9 @@ void Declaration::colorValues(QColor *c, const QPalette &pal) const
}
} else {
QList<QVariant> v;
- for (i = 0; i < qMin(d->values.count(), 4); i++) {
+ for (i = 0; i < qMin(d->values.size(), 4); i++) {
ColorData color = parseColorValue(d->values.at(i));
- if(color.type == ColorData::Role) {
+ if (color.type == ColorData::Role) {
v += QVariant::fromValue<int>(color.role);
c[i] = pal.color((QPalette::ColorRole)(color.role));
} else {
@@ -1663,7 +1680,7 @@ void Declaration::colorValues(QColor *c, const QPalette &pal) const
BorderStyle Declaration::styleValue() const
{
- if (d->values.count() != 1)
+ if (d->values.size() != 1)
return BorderStyle_None;
return parseStyleValue(d->values.at(0));
}
@@ -1671,7 +1688,7 @@ BorderStyle Declaration::styleValue() const
void Declaration::styleValues(BorderStyle *s) const
{
int i;
- for (i = 0; i < qMin(d->values.count(), 4); i++)
+ for (i = 0; i < qMin(d->values.size(), 4); i++)
s[i] = parseStyleValue(d->values.at(i));
if (i == 0) s[0] = s[1] = s[2] = s[3] = BorderStyle_None;
else if (i == 1) s[3] = s[2] = s[1] = s[0];
@@ -1683,7 +1700,7 @@ Repeat Declaration::repeatValue() const
{
if (d->parsed.isValid())
return static_cast<Repeat>(d->parsed.toInt());
- if (d->values.count() != 1)
+ if (d->values.size() != 1)
return Repeat_Unknown;
int v = findKnownValue(d->values.at(0).variant.toString(),
repeats, NumKnownRepeats);
@@ -1695,7 +1712,7 @@ Origin Declaration::originValue() const
{
if (d->parsed.isValid())
return static_cast<Origin>(d->parsed.toInt());
- if (d->values.count() != 1)
+ if (d->values.size() != 1)
return Origin_Unknown;
int v = findKnownValue(d->values.at(0).variant.toString(),
origins, NumKnownOrigins);
@@ -1707,7 +1724,7 @@ PositionMode Declaration::positionValue() const
{
if (d->parsed.isValid())
return static_cast<PositionMode>(d->parsed.toInt());
- if (d->values.count() != 1)
+ if (d->values.size() != 1)
return PositionMode_Unknown;
int v = findKnownValue(d->values.at(0).variant.toString(),
positions, NumKnownPositionModes);
@@ -1719,7 +1736,7 @@ Attachment Declaration::attachmentValue() const
{
if (d->parsed.isValid())
return static_cast<Attachment>(d->parsed.toInt());
- if (d->values.count() != 1)
+ if (d->values.size() != 1)
return Attachment_Unknown;
int v = findKnownValue(d->values.at(0).variant.toString(),
attachments, NumKnownAttachments);
@@ -1733,7 +1750,7 @@ int Declaration::styleFeaturesValue() const
if (d->parsed.isValid())
return d->parsed.toInt();
int features = StyleFeature_None;
- for (int i = 0; i < d->values.count(); i++) {
+ for (int i = 0; i < d->values.size(); i++) {
features |= static_cast<int>(findKnownValue(d->values.value(i).variant.toString(),
styleFeatures, NumKnownStyleFeatures));
}
@@ -1752,10 +1769,10 @@ Qt::Alignment Declaration::alignmentValue() const
{
if (d->parsed.isValid())
return Qt::Alignment(d->parsed.toInt());
- if (d->values.isEmpty() || d->values.count() > 2)
+ if (d->values.isEmpty() || d->values.size() > 2)
return Qt::AlignLeft | Qt::AlignTop;
- Qt::Alignment v = parseAlignment(d->values.constData(), d->values.count());
+ Qt::Alignment v = parseAlignment(d->values.constData(), d->values.size());
d->parsed = int(v);
return v;
}
@@ -1769,12 +1786,12 @@ void Declaration::borderImageValue(QString *image, int *cuts,
cuts[i] = -1;
*h = *v = TileMode_Stretch;
- if (d->values.count() < 2)
+ if (d->values.size() < 2)
return;
if (d->values.at(1).type == Value::Number) { // cuts!
int i;
- for (i = 0; i < qMin(d->values.count()-1, 4); i++) {
+ for (i = 0; i < qMin(d->values.size()-1, 4); i++) {
const Value& v = d->values.at(i+1);
if (v.type != Value::Number)
break;
@@ -1790,9 +1807,9 @@ void Declaration::borderImageValue(QString *image, int *cuts,
*v = static_cast<TileMode>(findKnownValue(d->values.last().variant.toString(),
tileModes, NumKnownTileModes));
}
- if (d->values[d->values.count() - 2].type == Value::Identifier) {
+ if (d->values[d->values.size() - 2].type == Value::Identifier) {
*h = static_cast<TileMode>
- (findKnownValue(d->values[d->values.count()-2].variant.toString(),
+ (findKnownValue(d->values[d->values.size()-2].variant.toString(),
tileModes, NumKnownTileModes));
} else
*h = *v;
@@ -1800,10 +1817,10 @@ void Declaration::borderImageValue(QString *image, int *cuts,
bool Declaration::borderCollapseValue() const
{
- if (d->values.count() != 1)
+ if (d->values.size() != 1)
return false;
else
- return d->values.at(0).toString() == QLatin1String("collapse");
+ return d->values.at(0).toString() == "collapse"_L1;
}
QIcon Declaration::iconValue() const
@@ -1812,7 +1829,7 @@ QIcon Declaration::iconValue() const
return qvariant_cast<QIcon>(d->parsed);
QIcon icon;
- for (int i = 0; i < d->values.count();) {
+ for (int i = 0; i < d->values.size();) {
const Value &value = d->values.at(i++);
if (value.type != Value::Uri)
break;
@@ -1820,7 +1837,7 @@ QIcon Declaration::iconValue() const
QIcon::Mode mode = QIcon::Normal;
QIcon::State state = QIcon::Off;
for (int j = 0; j < 2; j++) {
- if (i != d->values.count() && d->values.at(i).type == Value::KnownIdentifier) {
+ if (i != d->values.size() && d->values.at(i).type == Value::KnownIdentifier) {
switch (d->values.at(i).variant.toInt()) {
case Value_Disabled: mode = QIcon::Disabled; break;
case Value_Active: mode = QIcon::Active; break;
@@ -1842,7 +1859,7 @@ QIcon Declaration::iconValue() const
else
icon.addPixmap(uri, mode, state);
- if (i == d->values.count())
+ if (i == d->values.size())
break;
if (d->values.at(i).type == Value::TermOperatorComma)
@@ -1858,13 +1875,13 @@ QIcon Declaration::iconValue() const
int Selector::specificity() const
{
int val = 0;
- for (int i = 0; i < basicSelectors.count(); ++i) {
+ for (int i = 0; i < basicSelectors.size(); ++i) {
const BasicSelector &sel = basicSelectors.at(i);
if (!sel.elementName.isEmpty())
val += 1;
- val += (sel.pseudos.count() + sel.attributeSelectors.count()) * 0x10;
- val += sel.ids.count() * 0x100;
+ val += (sel.pseudos.size() + sel.attributeSelectors.size()) * 0x10;
+ val += sel.ids.size() * 0x100;
}
return val;
}
@@ -1883,7 +1900,7 @@ quint64 Selector::pseudoClass(quint64 *negated) const
if (bs.pseudos.isEmpty())
return PseudoClass_Unspecified;
quint64 pc = PseudoClass_Unknown;
- for (int i = !pseudoElement().isEmpty(); i < bs.pseudos.count(); i++) {
+ for (int i = !pseudoElement().isEmpty(); i < bs.pseudos.size(); i++) {
const Pseudo &pseudo = bs.pseudos.at(i);
if (pseudo.type == PseudoClass_Unknown)
return PseudoClass_Unknown;
@@ -1900,23 +1917,23 @@ quint64 Selector::pseudoClass(quint64 *negated) const
void StyleSheet::buildIndexes(Qt::CaseSensitivity nameCaseSensitivity)
{
QList<StyleRule> universals;
- for (int i = 0; i < styleRules.count(); ++i) {
+ for (int i = 0; i < styleRules.size(); ++i) {
const StyleRule &rule = styleRules.at(i);
QList<Selector> universalsSelectors;
- for (int j = 0; j < rule.selectors.count(); ++j) {
+ for (int j = 0; j < rule.selectors.size(); ++j) {
const Selector& selector = rule.selectors.at(j);
if (selector.basicSelectors.isEmpty())
continue;
if (selector.basicSelectors.at(0).relationToNext == BasicSelector::NoRelation) {
- if (selector.basicSelectors.count() != 1)
+ if (selector.basicSelectors.size() != 1)
continue;
- } else if (selector.basicSelectors.count() <= 1) {
+ } else if (selector.basicSelectors.size() <= 1) {
continue;
}
- const BasicSelector &sel = selector.basicSelectors.at(selector.basicSelectors.count() - 1);
+ const BasicSelector &sel = selector.basicSelectors.at(selector.basicSelectors.size() - 1);
if (!sel.ids.isEmpty()) {
StyleRule nr;
@@ -1961,7 +1978,7 @@ bool StyleSelector::nodeNameEquals(NodePtr node, const QString& nodeName) const
QStringList StyleSelector::nodeIds(NodePtr node) const
{
- return QStringList(attribute(node, QLatin1String("id")));
+ return QStringList(attributeValue(node, QCss::AttributeSelector{"id"_L1, {}, AttributeSelector::NoMatch}));
}
bool StyleSelector::selectorMatches(const Selector &selector, NodePtr node)
@@ -1970,14 +1987,14 @@ bool StyleSelector::selectorMatches(const Selector &selector, NodePtr node)
return false;
if (selector.basicSelectors.at(0).relationToNext == BasicSelector::NoRelation) {
- if (selector.basicSelectors.count() != 1)
+ if (selector.basicSelectors.size() != 1)
return false;
return basicSelectorMatches(selector.basicSelectors.at(0), node);
}
- if (selector.basicSelectors.count() <= 1)
+ if (selector.basicSelectors.size() <= 1)
return false;
- int i = selector.basicSelectors.count() - 1;
+ int i = selector.basicSelectors.size() - 1;
node = duplicateNode(node);
bool match = true;
@@ -1985,7 +2002,7 @@ bool StyleSelector::selectorMatches(const Selector &selector, NodePtr node)
do {
match = basicSelectorMatches(sel, node);
if (!match) {
- if (i == selector.basicSelectors.count() - 1) // first element must always match!
+ if (i == selector.basicSelectors.size() - 1) // first element must always match!
break;
if (sel.relationToNext != BasicSelector::MatchNextSelectorIfAncestor &&
sel.relationToNext != BasicSelector::MatchNextSelectorIfIndirectAdjecent)
@@ -2030,10 +2047,10 @@ bool StyleSelector::basicSelectorMatches(const BasicSelector &sel, NodePtr node)
if (!hasAttributes(node))
return false;
- for (int i = 0; i < sel.attributeSelectors.count(); ++i) {
+ for (int i = 0; i < sel.attributeSelectors.size(); ++i) {
const QCss::AttributeSelector &a = sel.attributeSelectors.at(i);
- const QString attrValue = attribute(node, a.name);
+ const QString attrValue = attributeValue(node, a);
if (attrValue.isNull())
return false;
@@ -2045,13 +2062,20 @@ bool StyleSelector::basicSelectorMatches(const BasicSelector &sel, NodePtr node)
return false;
break;
case QCss::AttributeSelector::MatchIncludes: {
- const auto lst = QStringView{attrValue}.split(u' ');
- if (!lst.contains(QStringView(a.value)))
+ const auto lst = QStringView{attrValue}.tokenize(u' ');
+ bool found = false;
+ for (auto s : lst) {
+ if (s == a.value) {
+ found = true;
+ break;
+ }
+ }
+ if (!found)
return false;
break;
}
case QCss::AttributeSelector::MatchDashMatch: {
- const QString dashPrefix = a.value + QLatin1Char('-');
+ const QString dashPrefix = a.value + u'-';
if (attrValue != a.value && !attrValue.startsWith(dashPrefix))
return false;
break;
@@ -2086,14 +2110,14 @@ bool StyleSelector::basicSelectorMatches(const BasicSelector &sel, NodePtr node)
void StyleSelector::matchRule(NodePtr node, const StyleRule &rule, StyleSheetOrigin origin,
int depth, QMultiMap<uint, StyleRule> *weightedRules)
{
- for (int j = 0; j < rule.selectors.count(); ++j) {
+ for (int j = 0; j < rule.selectors.size(); ++j) {
const Selector& selector = rule.selectors.at(j);
if (selectorMatches(selector, node)) {
uint weight = rule.order
+ selector.specificity() *0x100
+ (uint(origin) + depth)*0x100000;
StyleRule newRule = rule;
- if(rule.selectors.count() > 1) {
+ if (rule.selectors.size() > 1) {
newRule.selectors.resize(1);
newRule.selectors[0] = selector;
}
@@ -2114,15 +2138,15 @@ QList<StyleRule> StyleSelector::styleRulesForNode(NodePtr node)
QMultiMap<uint, StyleRule> weightedRules; // (spec, rule) that will be sorted below
//prune using indexed stylesheet
- for (int sheetIdx = 0; sheetIdx < styleSheets.count(); ++sheetIdx) {
+ for (int sheetIdx = 0; sheetIdx < styleSheets.size(); ++sheetIdx) {
const StyleSheet &styleSheet = styleSheets.at(sheetIdx);
- for (int i = 0; i < styleSheet.styleRules.count(); ++i) {
+ for (int i = 0; i < styleSheet.styleRules.size(); ++i) {
matchRule(node, styleSheet.styleRules.at(i), styleSheet.origin, styleSheet.depth, &weightedRules);
}
if (!styleSheet.idIndex.isEmpty()) {
QStringList ids = nodeIds(node);
- for (int i = 0; i < ids.count(); i++) {
+ for (int i = 0; i < ids.size(); i++) {
const QString &key = ids.at(i);
QMultiHash<QString, StyleRule>::const_iterator it = styleSheet.idIndex.constFind(key);
while (it != styleSheet.idIndex.constEnd() && it.key() == key) {
@@ -2133,7 +2157,7 @@ QList<StyleRule> StyleSelector::styleRulesForNode(NodePtr node)
}
if (!styleSheet.nameIndex.isEmpty()) {
QStringList names = nodeNames(node);
- for (int i = 0; i < names.count(); i++) {
+ for (int i = 0; i < names.size(); i++) {
QString name = names.at(i);
if (nameCaseSensitivity == Qt::CaseInsensitive)
name = std::move(name).toLower();
@@ -2145,9 +2169,9 @@ QList<StyleRule> StyleSelector::styleRulesForNode(NodePtr node)
}
}
if (!medium.isEmpty()) {
- for (int i = 0; i < styleSheet.mediaRules.count(); ++i) {
+ for (int i = 0; i < styleSheet.mediaRules.size(); ++i) {
if (styleSheet.mediaRules.at(i).media.contains(medium, Qt::CaseInsensitive)) {
- for (int j = 0; j < styleSheet.mediaRules.at(i).styleRules.count(); ++j) {
+ for (int j = 0; j < styleSheet.mediaRules.at(i).styleRules.size(); ++j) {
matchRule(node, styleSheet.mediaRules.at(i).styleRules.at(j), styleSheet.origin,
styleSheet.depth, &weightedRules);
}
@@ -2156,7 +2180,7 @@ QList<StyleRule> StyleSelector::styleRulesForNode(NodePtr node)
}
}
- rules.reserve(weightedRules.count());
+ rules.reserve(weightedRules.size());
QMultiMap<uint, StyleRule>::const_iterator it = weightedRules.constBegin();
for ( ; it != weightedRules.constEnd() ; ++it)
rules += *it;
@@ -2170,11 +2194,11 @@ QList<Declaration> StyleSelector::declarationsForNode(NodePtr node, const char *
{
QList<Declaration> decls;
QList<StyleRule> rules = styleRulesForNode(node);
- for (int i = 0; i < rules.count(); i++) {
+ for (int i = 0; i < rules.size(); i++) {
const Selector& selector = rules.at(i).selectors.at(0);
const QString pseudoElement = selector.pseudoElement();
- if (extraPseudo && pseudoElement == QLatin1String(extraPseudo)) {
+ if (extraPseudo && pseudoElement == QLatin1StringView(extraPseudo)) {
decls += rules.at(i).declarations;
continue;
}
@@ -2205,7 +2229,7 @@ QString Scanner::preprocess(const QString &input, bool *hasEscapeSequences)
int i = 0;
while (i < output.size()) {
- if (output.at(i) == QLatin1Char('\\')) {
+ if (output.at(i) == u'\\') {
++i;
// test for unicode hex escape
@@ -2242,8 +2266,7 @@ QString Scanner::preprocess(const QString &input, bool *hasEscapeSequences)
int QCssScanner_Generated::handleCommentStart()
{
while (pos < input.size() - 1) {
- if (input.at(pos) == QLatin1Char('*')
- && input.at(pos + 1) == QLatin1Char('/')) {
+ if (input.at(pos) == u'*' && input.at(pos + 1) == u'/') {
pos += 2;
break;
}
@@ -2273,7 +2296,7 @@ QString Symbol::lexem() const
if (len > 0)
result.reserve(len);
for (int i = 0; i < len; ++i) {
- if (text.at(start + i) == QLatin1Char('\\') && i < len - 1)
+ if (text.at(start + i) == u'\\' && i < len - 1)
++i;
result += text.at(start + i);
}
@@ -2298,7 +2321,7 @@ void Parser::init(const QString &css, bool isFile)
if (isFile) {
QFile file(css);
if (file.open(QFile::ReadOnly)) {
- sourcePath = QFileInfo(styleSheet).absolutePath() + QLatin1Char('/');
+ sourcePath = QFileInfo(styleSheet).absolutePath() + u'/';
QTextStream stream(&file);
styleSheet = stream.readAll();
} else {
@@ -2319,7 +2342,7 @@ void Parser::init(const QString &css, bool isFile)
bool Parser::parse(StyleSheet *styleSheet, Qt::CaseSensitivity nameCaseSensitivity)
{
- if (testTokenAndEndsWith(ATKEYWORD_SYM, QLatin1String("charset"))) {
+ if (testTokenAndEndsWith(ATKEYWORD_SYM, "charset"_L1)) {
while (test(S) || test(CDO) || test(CDC)) {}
if (!next(STRING)) return false;
if (!next(SEMICOLON)) return false;
@@ -2366,8 +2389,7 @@ Symbol Parser::errorSymbol()
static inline void removeOptionalQuotes(QString *str)
{
- if (!str->startsWith(QLatin1Char('\''))
- && !str->startsWith(QLatin1Char('\"')))
+ if (!str->startsWith(u'\'') && !str->startsWith(u'\"'))
return;
str->remove(0, 1);
str->chop(1);
@@ -2584,7 +2606,7 @@ bool Parser::parseSimpleSelector(BasicSelector *basicSel)
} else if (testClass()) {
onceMore = true;
AttributeSelector a;
- a.name = QLatin1String("class");
+ a.name = "class"_L1;
a.valueMatchCriterium = AttributeSelector::MatchIncludes;
if (!parseClass(&a.value)) return false;
basicSel->attributeSelectors.append(a);
@@ -2695,7 +2717,7 @@ bool Parser::testPrio()
index = rewind;
return false;
}
- if (lexem().compare(QLatin1String("important"), Qt::CaseInsensitive) != 0) {
+ if (lexem().compare("important"_L1, Qt::CaseInsensitive) != 0) {
index = rewind;
return false;
}
@@ -2798,7 +2820,7 @@ bool Parser::parseTerm(Value *value)
} else if (testFunction()) {
QString name, args;
if (!parseFunction(&name, &args)) return false;
- if (name == QLatin1String("url")) {
+ if (name == "url"_L1) {
value->type = Value::Uri;
removeOptionalQuotes(&args);
if (QFileInfo(args).isRelative() && !sourcePath.isEmpty()) {
@@ -2840,7 +2862,7 @@ bool Parser::parseFunction(QString *name, QString *args)
bool Parser::parseHexColor(QColor *col)
{
- col->setNamedColor(lexem());
+ *col = QColor::fromString(lexem());
if (!col->isValid()) {
qWarning("QCssParser::parseHexColor: Unknown color name '%s'",lexem().toLatin1().constData());
return false;
@@ -2859,7 +2881,7 @@ bool Parser::testAndParseUri(QString *uri)
index = rewind;
return false;
}
- if (name.compare(QLatin1String("url"), Qt::CaseInsensitive) != 0) {
+ if (name.compare("url"_L1, Qt::CaseInsensitive) != 0) {
index = rewind;
return false;
}
@@ -2886,7 +2908,7 @@ bool Parser::next(QCss::TokenType t)
bool Parser::test(QCss::TokenType t)
{
- if (index >= symbols.count())
+ if (index >= symbols.size())
return false;
if (symbols.at(index).token == t) {
++index;
@@ -2953,7 +2975,7 @@ bool Parser::until(QCss::TokenType target, QCss::TokenType target2)
return false;
}
-bool Parser::testTokenAndEndsWith(QCss::TokenType t, QLatin1String str)
+bool Parser::testTokenAndEndsWith(QCss::TokenType t, QLatin1StringView str)
{
if (!test(t)) return false;
if (!lexem().endsWith(str, Qt::CaseInsensitive)) {
diff --git a/src/gui/text/qcssparser_p.h b/src/gui/text/qcssparser_p.h
index 1f71a1c4af..c1cfb1ac9b 100644
--- a/src/gui/text/qcssparser_p.h
+++ b/src/gui/text/qcssparser_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 QCSSPARSER_P_H
#define QCSSPARSER_P_H
@@ -55,7 +19,6 @@
#include <QtCore/QStringList>
#include <QtCore/QList>
#include <QtCore/QVariant>
-#include <QtCore/QPair>
#include <QtCore/QSize>
#include <QtCore/QMultiHash>
#include <QtGui/QFont>
@@ -201,6 +164,12 @@ enum Property {
QtIcon,
LetterSpacing,
WordSpacing,
+ TextDecorationColor,
+ QtPlaceHolderTextColor,
+ QtAccent,
+ QtStrokeWidth,
+ QtStrokeColor,
+ QtForeground,
NumProperties
};
@@ -391,7 +360,7 @@ struct Value
Q_GUI_EXPORT QString toString() const;
};
-QT_CSS_DECLARE_TYPEINFO(Value, Q_MOVABLE_TYPE)
+QT_CSS_DECLARE_TYPEINFO(Value, Q_RELOCATABLE_TYPE)
struct ColorData {
ColorData() : role(QPalette::NoRole), type(Invalid) {}
@@ -401,7 +370,7 @@ struct ColorData {
QPalette::ColorRole role;
enum { Invalid, Color, Role} type;
};
-QT_CSS_DECLARE_TYPEINFO(ColorData, Q_MOVABLE_TYPE)
+QT_CSS_DECLARE_TYPEINFO(ColorData, Q_RELOCATABLE_TYPE)
struct BrushData {
BrushData() : role(QPalette::NoRole), type(Invalid) {}
@@ -411,7 +380,7 @@ struct BrushData {
QPalette::ColorRole role;
enum { Invalid, Brush, Role, DependsOnThePalette } type;
};
-QT_CSS_DECLARE_TYPEINFO(BrushData, Q_MOVABLE_TYPE)
+QT_CSS_DECLARE_TYPEINFO(BrushData, Q_RELOCATABLE_TYPE)
struct BackgroundData {
BrushData brush;
@@ -419,7 +388,7 @@ struct BackgroundData {
Repeat repeat;
Qt::Alignment alignment;
};
-QT_CSS_DECLARE_TYPEINFO(BackgroundData, Q_MOVABLE_TYPE)
+QT_CSS_DECLARE_TYPEINFO(BackgroundData, Q_RELOCATABLE_TYPE)
struct LengthData {
qreal number;
@@ -432,7 +401,7 @@ struct BorderData {
BorderStyle style;
BrushData color;
};
-QT_CSS_DECLARE_TYPEINFO(BorderData, Q_MOVABLE_TYPE)
+QT_CSS_DECLARE_TYPEINFO(BorderData, Q_RELOCATABLE_TYPE)
// 1. StyleRule - x:hover, y:clicked > z:checked { prop1: value1; prop2: value2; }
// 2. QList<Selector> - x:hover, y:clicked z:checked
@@ -483,7 +452,7 @@ struct Q_GUI_EXPORT Declaration
void borderImageValue(QString *image, int *cuts, TileMode *h, TileMode *v) const;
bool borderCollapseValue() const;
};
-QT_CSS_DECLARE_TYPEINFO(Declaration, Q_MOVABLE_TYPE)
+QT_CSS_DECLARE_TYPEINFO(Declaration, Q_RELOCATABLE_TYPE)
const quint64 PseudoClass_Unknown = Q_UINT64_C(0x0000000000000000);
const quint64 PseudoClass_Enabled = Q_UINT64_C(0x0000000000000001);
@@ -543,7 +512,7 @@ struct Pseudo
QString function;
bool negated;
};
-QT_CSS_DECLARE_TYPEINFO(Pseudo, Q_MOVABLE_TYPE)
+QT_CSS_DECLARE_TYPEINFO(Pseudo, Q_RELOCATABLE_TYPE)
struct AttributeSelector
{
@@ -556,13 +525,12 @@ struct AttributeSelector
MatchEndsWith,
MatchContains
};
- inline AttributeSelector() : valueMatchCriterium(NoMatch) {}
QString name;
QString value;
- ValueMatchType valueMatchCriterium;
+ ValueMatchType valueMatchCriterium = NoMatch;
};
-QT_CSS_DECLARE_TYPEINFO(AttributeSelector, Q_MOVABLE_TYPE)
+QT_CSS_DECLARE_TYPEINFO(AttributeSelector, Q_RELOCATABLE_TYPE)
struct BasicSelector
{
@@ -584,7 +552,7 @@ struct BasicSelector
Relation relationToNext;
};
-QT_CSS_DECLARE_TYPEINFO(BasicSelector, Q_MOVABLE_TYPE)
+QT_CSS_DECLARE_TYPEINFO(BasicSelector, Q_RELOCATABLE_TYPE)
struct Q_GUI_EXPORT Selector
{
@@ -593,7 +561,7 @@ struct Q_GUI_EXPORT Selector
quint64 pseudoClass(quint64 *negated = nullptr) const;
QString pseudoElement() const;
};
-QT_CSS_DECLARE_TYPEINFO(Selector, Q_MOVABLE_TYPE)
+QT_CSS_DECLARE_TYPEINFO(Selector, Q_RELOCATABLE_TYPE)
struct StyleRule
{
@@ -602,28 +570,28 @@ struct StyleRule
QList<Declaration> declarations;
int order;
};
-QT_CSS_DECLARE_TYPEINFO(StyleRule, Q_MOVABLE_TYPE)
+QT_CSS_DECLARE_TYPEINFO(StyleRule, Q_RELOCATABLE_TYPE)
struct MediaRule
{
QStringList media;
QList<StyleRule> styleRules;
};
-QT_CSS_DECLARE_TYPEINFO(MediaRule, Q_MOVABLE_TYPE)
+QT_CSS_DECLARE_TYPEINFO(MediaRule, Q_RELOCATABLE_TYPE)
struct PageRule
{
QString selector;
QList<Declaration> declarations;
};
-QT_CSS_DECLARE_TYPEINFO(PageRule, Q_MOVABLE_TYPE)
+QT_CSS_DECLARE_TYPEINFO(PageRule, Q_RELOCATABLE_TYPE)
struct ImportRule
{
QString href;
QStringList media;
};
-QT_CSS_DECLARE_TYPEINFO(ImportRule, Q_MOVABLE_TYPE)
+QT_CSS_DECLARE_TYPEINFO(ImportRule, Q_RELOCATABLE_TYPE)
enum StyleSheetOrigin {
StyleSheetOrigin_Unspecified,
@@ -647,7 +615,7 @@ struct StyleSheet
Q_GUI_EXPORT void buildIndexes(Qt::CaseSensitivity nameCaseSensitivity = Qt::CaseSensitive);
};
-QT_CSS_DECLARE_TYPEINFO(StyleSheet, Q_MOVABLE_TYPE)
+QT_CSS_DECLARE_TYPEINFO(StyleSheet, Q_RELOCATABLE_TYPE)
class Q_GUI_EXPORT StyleSelector
@@ -665,7 +633,7 @@ public:
QList<Declaration> declarationsForNode(NodePtr node, const char *extraPseudo = nullptr);
virtual bool nodeNameEquals(NodePtr node, const QString& nodeName) const;
- virtual QString attribute(NodePtr node, const QString &name) const = 0;
+ virtual QString attributeValue(NodePtr node, const QCss::AttributeSelector &aSelector) const = 0;
virtual bool hasAttributes(NodePtr node) const = 0;
virtual QStringList nodeIds(NodePtr node) const;
virtual QStringList nodeNames(NodePtr node) const = 0;
@@ -745,7 +713,7 @@ struct Symbol
int start, len;
Q_GUI_EXPORT QString lexem() const;
};
-QT_CSS_DECLARE_TYPEINFO(Symbol, Q_MOVABLE_TYPE)
+QT_CSS_DECLARE_TYPEINFO(Symbol, Q_RELOCATABLE_TYPE)
class Q_GUI_EXPORT Scanner
{
@@ -799,9 +767,9 @@ public:
inline bool testMedium() { return test(IDENT); }
inline bool parseNextMedium(QStringList *media) { if (!testMedium()) return recordError(); return parseMedium(media); }
inline bool testPseudoPage() { return test(COLON); }
- inline bool testImport() { return testTokenAndEndsWith(ATKEYWORD_SYM, QLatin1String("import")); }
- inline bool testMedia() { return testTokenAndEndsWith(ATKEYWORD_SYM, QLatin1String("media")); }
- inline bool testPage() { return testTokenAndEndsWith(ATKEYWORD_SYM, QLatin1String("page")); }
+ inline bool testImport() { return testTokenAndEndsWith(ATKEYWORD_SYM, QLatin1StringView("import")); }
+ inline bool testMedia() { return testTokenAndEndsWith(ATKEYWORD_SYM, QLatin1StringView("media")); }
+ inline bool testPage() { return testTokenAndEndsWith(ATKEYWORD_SYM, QLatin1StringView("page")); }
inline bool testCombinator() { return test(PLUS) || test(GREATER) || test(TILDE) || test(S); }
inline bool testProperty() { return test(IDENT); }
bool testTerm();
@@ -821,7 +789,7 @@ public:
inline void skipSpace() { while (test(S)) {}; }
- inline bool hasNext() const { return index < symbols.count(); }
+ inline bool hasNext() const { return index < symbols.size(); }
inline TokenType next() { return symbols.at(index++).token; }
bool next(TokenType t);
bool test(TokenType t);
@@ -832,10 +800,10 @@ public:
QString lexemUntil(TokenType t);
bool until(TokenType target, TokenType target2 = NONE);
inline TokenType lookup() const {
- return (index - 1) < symbols.count() ? symbols.at(index - 1).token : NONE;
+ return (index - 1) < symbols.size() ? symbols.at(index - 1).token : NONE;
}
- bool testTokenAndEndsWith(TokenType t, QLatin1String str);
+ bool testTokenAndEndsWith(TokenType t, QLatin1StringView str);
inline bool recordError() { errorIndex = index; return false; }
@@ -859,7 +827,9 @@ struct Q_GUI_EXPORT ValueExtractor
bool extractBox(int *margins, int *paddings, int *spacing = nullptr);
bool extractBorder(int *borders, QBrush *colors, BorderStyle *Styles, QSize *radii);
bool extractOutline(int *borders, QBrush *colors, BorderStyle *Styles, QSize *radii, int *offsets);
- bool extractPalette(QBrush *fg, QBrush *sfg, QBrush *sbg, QBrush *abg);
+ bool extractPalette(QBrush *foreground, QBrush *selectedForeground, QBrush *selectedBackground,
+ QBrush *alternateBackground, QBrush *placeHolderTextForeground,
+ QBrush *accent);
int extractStyleFeatures();
bool extractImage(QIcon *icon, Qt::Alignment *a, QSize *size);
bool extractIcon(QIcon *icon, QSize *size);
@@ -885,9 +855,9 @@ private:
QT_END_NAMESPACE
-Q_DECLARE_METATYPE( QCss::BackgroundData )
-Q_DECLARE_METATYPE( QCss::LengthData )
-Q_DECLARE_METATYPE( QCss::BorderData )
+QT_DECL_METATYPE_EXTERN_TAGGED(QCss::BackgroundData, QCss__BackgroundData, Q_GUI_EXPORT)
+QT_DECL_METATYPE_EXTERN_TAGGED(QCss::LengthData, QCss__LengthData, Q_GUI_EXPORT)
+QT_DECL_METATYPE_EXTERN_TAGGED(QCss::BorderData, QCss__BorderData, Q_GUI_EXPORT)
#undef QT_CSS_DECLARE_TYPEINFO
diff --git a/src/gui/text/qcssscanner.cpp b/src/gui/text/qcssscanner.cpp
index d48cbb71d7..7bfa797782 100644
--- a/src/gui/text/qcssscanner.cpp
+++ b/src/gui/text/qcssscanner.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2018 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) 2018 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
// auto generated by qtbase/util/lexgen/. DO NOT EDIT.
class QCssScanner_Generated
@@ -44,7 +8,7 @@ public:
QCssScanner_Generated(const QString &inp);
inline QChar next() {
- return (pos < input.length()) ? input.at(pos++) : QChar();
+ return (pos < input.size()) ? input.at(pos++) : QChar();
}
int handleCommentStart();
int lex();
diff --git a/src/gui/text/qdistancefield.cpp b/src/gui/text/qdistancefield.cpp
index c621d7bab8..fe230188c6 100644
--- a/src/gui/text/qdistancefield.cpp
+++ b/src/gui/text/qdistancefield.cpp
@@ -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 "qdistancefield_p.h"
#include <qmath.h>
@@ -45,6 +9,8 @@
QT_BEGIN_NAMESPACE
+using namespace Qt::StringLiterals;
+
Q_LOGGING_CATEGORY(lcDistanceField, "qt.distanceField");
namespace
@@ -564,14 +530,14 @@ static void makeDistanceField(QDistanceFieldData *data, const QPainterPath &path
|| (to.y() < offs << 8) || (to.y() >= (imgHeight - offs) << 8));
}
- isConvex.resize(normals.count());
- for (int next = 0, prev = normals.count() - 1; next < normals.count(); prev = next++) {
+ isConvex.resize(normals.size());
+ for (int next = 0, prev = normals.size() - 1; next < normals.size(); prev = next++) {
isConvex[prev] = normals.at(prev).x() * normals.at(next).y()
- normals.at(prev).y() * normals.at(next).x() < 0;
}
// Draw quads.
- for (int next = 0, prev = normals.count() - 1; next < normals.count(); prev = next++) {
+ for (int next = 0, prev = normals.size() - 1; next < normals.size(); prev = next++) {
QPoint n = normals.at(next);
QPoint intPrev = vertices.at(prev);
QPoint extPrev = vertices.at(prev);
@@ -780,7 +746,7 @@ bool qt_fontHasNarrowOutlines(QFontEngine *fontEngine)
const glyph_t glyph = fe->glyphIndex('O');
if (glyph != 0)
- im = fe->alphaMapForGlyph(glyph, QFixed(), QTransform());
+ im = fe->alphaMapForGlyph(glyph, QFixedPoint(), QTransform());
Q_ASSERT(fe->ref.loadRelaxed() == 0);
delete fe;
@@ -796,7 +762,7 @@ bool qt_fontHasNarrowOutlines(const QRawFont &f)
if (!font.isValid())
return false;
- QList<quint32> glyphIndices = font.glyphIndexesForString(QLatin1String("O"));
+ QList<quint32> glyphIndices = font.glyphIndexesForString("O"_L1);
if (glyphIndices.isEmpty() || glyphIndices[0] == 0)
return false;
@@ -1092,7 +1058,7 @@ QImage QDistanceField::toImage(QImage::Format format) const
}
if (image.format() != format)
- image = image.convertToFormat(format);
+ image = std::move(image).convertToFormat(format);
}
return image;
diff --git a/src/gui/text/qdistancefield_p.h b/src/gui/text/qdistancefield_p.h
index 823bfaf1c6..dbc7a0abb5 100644
--- a/src/gui/text/qdistancefield_p.h
+++ b/src/gui/text/qdistancefield_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 QDISTANCEFIELD_H
#define QDISTANCEFIELD_H
diff --git a/src/gui/text/qfont.cpp b/src/gui/text/qfont.cpp
index 2dd341f7c4..f3b861957e 100644
--- a/src/gui/text/qfont.cpp
+++ b/src/gui/text/qfont.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2019 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) 2019 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 "qfont.h"
#include "qdebug.h"
@@ -67,6 +31,8 @@
#include <QtCore/QMutexLocker>
#include <QtCore/QMutex>
+#include <array>
+
// #define QFONTCACHE_DEBUG
#ifdef QFONTCACHE_DEBUG
# define FC_DEBUG qDebug
@@ -116,19 +82,16 @@ bool QFontDef::exactMatch(const QFontDef &other) const
if (stretch != 0 && other.stretch != 0 && stretch != other.stretch)
return false;
- if (families.size() != other.families.size())
- return false;
-
QString this_family, this_foundry, other_family, other_foundry;
for (int i = 0; i < families.size(); ++i) {
- QFontDatabase::parseFontName(families.at(i), this_foundry, this_family);
- QFontDatabase::parseFontName(other.families.at(i), other_foundry, other_family);
+ QFontDatabasePrivate::parseFontName(families.at(i), this_foundry, this_family);
+ QFontDatabasePrivate::parseFontName(other.families.at(i), other_foundry, other_family);
if (this_family != other_family || this_foundry != other_foundry)
return false;
}
- QFontDatabase::parseFontName(family, this_foundry, this_family);
- QFontDatabase::parseFontName(other.family, other_foundry, other_family);
+ if (variableAxisValues != other.variableAxisValues)
+ return false;
return (styleHint == other.styleHint
&& styleStrategy == other.styleStrategy
@@ -142,14 +105,14 @@ bool QFontDef::exactMatch(const QFontDef &other) const
);
}
-extern bool qt_is_gui_used;
+extern bool qt_is_tty_app;
Q_GUI_EXPORT int qt_defaultDpiX()
{
if (QCoreApplication::instance()->testAttribute(Qt::AA_Use96Dpi))
return 96;
- if (!qt_is_gui_used)
+ if (qt_is_tty_app)
return 75;
if (const QScreen *screen = QGuiApplication::primaryScreen())
@@ -164,7 +127,7 @@ Q_GUI_EXPORT int qt_defaultDpiY()
if (QCoreApplication::instance()->testAttribute(Qt::AA_Use96Dpi))
return 96;
- if (!qt_is_gui_used)
+ if (qt_is_tty_app)
return 75;
if (const QScreen *screen = QGuiApplication::primaryScreen())
@@ -182,7 +145,7 @@ Q_GUI_EXPORT int qt_defaultDpi()
/* Helper function to convert between legacy Qt and OpenType font weights. */
static int convertWeights(int weight, bool inverted)
{
- static const QVarLengthArray<QPair<int, int>, 9> legacyToOpenTypeMap = {
+ static constexpr std::array<int, 2> legacyToOpenTypeMap[] = {
{ 0, QFont::Thin }, { 12, QFont::ExtraLight }, { 25, QFont::Light },
{ 50, QFont::Normal }, { 57, QFont::Medium }, { 63, QFont::DemiBold },
{ 75, QFont::Bold }, { 81, QFont::ExtraBold }, { 87, QFont::Black },
@@ -193,8 +156,8 @@ static int convertWeights(int weight, bool inverted)
// Go through and find the closest mapped value
for (auto mapping : legacyToOpenTypeMap) {
- const int weightOld = inverted ? mapping.second : mapping.first;
- const int weightNew = inverted ? mapping.first : mapping.second;
+ const int weightOld = mapping[ inverted];
+ const int weightNew = mapping[!inverted];
const int dist = qAbs(weightOld - weight);
if (dist < closestDist) {
result = weightNew;
@@ -208,6 +171,26 @@ static int convertWeights(int weight, bool inverted)
return result;
}
+// Splits the family string on a comma and returns the list based on that
+static QStringList splitIntoFamilies(const QString &family)
+{
+ QStringList familyList;
+ if (family.isEmpty())
+ return familyList;
+ const auto list = QStringView{family}.split(u',');
+ const int numFamilies = list.size();
+ familyList.reserve(numFamilies);
+ for (int i = 0; i < numFamilies; ++i) {
+ auto str = list.at(i).trimmed();
+ if ((str.startsWith(u'"') && str.endsWith(u'"'))
+ || (str.startsWith(u'\'') && str.endsWith(u'\''))) {
+ str = str.mid(1, str.size() - 2);
+ }
+ familyList << str.toString();
+ }
+ return familyList;
+}
+
/* Converts from legacy Qt font weight (Qt < 6.0) to OpenType font weight (Qt >= 6.0) */
Q_GUI_EXPORT int qt_legacyToOpenTypeWeight(int weight)
{
@@ -233,7 +216,7 @@ QFontPrivate::QFontPrivate(const QFontPrivate &other)
strikeOut(other.strikeOut), kerning(other.kerning),
capital(other.capital), letterSpacingIsAbsolute(other.letterSpacingIsAbsolute),
letterSpacing(other.letterSpacing), wordSpacing(other.wordSpacing),
- scFont(other.scFont)
+ features(other.features), scFont(other.scFont)
{
if (scFont && scFont != this)
scFont->ref.ref();
@@ -244,8 +227,10 @@ QFontPrivate::~QFontPrivate()
if (engineData && !engineData->ref.deref())
delete engineData;
engineData = nullptr;
- if (scFont && scFont != this)
- scFont->ref.deref();
+ if (scFont && scFont != this) {
+ if (!scFont->ref.deref())
+ delete scFont;
+ }
scFont = nullptr;
}
@@ -265,7 +250,7 @@ QFontEngine *QFontPrivate::engineForScript(int script) const
engineData = nullptr;
}
if (!engineData || !QT_FONT_ENGINE_FROM_DATA(engineData, script))
- QFontDatabase::load(this, script);
+ QFontDatabasePrivate::load(this, script);
return QT_FONT_ENGINE_FROM_DATA(engineData, script);
}
@@ -309,16 +294,8 @@ void QFontPrivate::resolve(uint mask, const QFontPrivate *other)
if ((mask & QFont::AllPropertiesResolved) == QFont::AllPropertiesResolved) return;
// assign the unset-bits with the set-bits of the other font def
- if (! (mask & QFont::FamilyResolved))
- request.family = other->request.family;
-
- if (!(mask & QFont::FamiliesResolved)) {
+ if (!(mask & QFont::FamiliesResolved))
request.families = other->request.families;
- // Prepend the family explicitly set so it will be given
- // preference in this case
- if (mask & QFont::FamilyResolved)
- request.families.prepend(request.family);
- }
if (! (mask & QFont::StyleNameResolved))
request.styleName = other->request.styleName;
@@ -369,9 +346,38 @@ void QFontPrivate::resolve(uint mask, const QFontPrivate *other)
wordSpacing = other->wordSpacing;
if (! (mask & QFont::CapitalizationResolved))
capital = other->capital;
+
+ if (!(mask & QFont::FeaturesResolved))
+ features = other->features;
+
+ if (!(mask & QFont::VariableAxesResolved))
+ request.variableAxisValues = other->request.variableAxisValues;
+}
+
+bool QFontPrivate::hasVariableAxis(QFont::Tag tag, float value) const
+{
+ return request.variableAxisValues.contains(tag) && request.variableAxisValues.value(tag) == value;
+}
+
+void QFontPrivate::setVariableAxis(QFont::Tag tag, float value)
+{
+ request.variableAxisValues.insert(tag, value);
+}
+
+void QFontPrivate::unsetVariableAxis(QFont::Tag tag)
+{
+ request.variableAxisValues.remove(tag);
}
+void QFontPrivate::setFeature(QFont::Tag tag, quint32 value)
+{
+ features.insert(tag, value);
+}
+void QFontPrivate::unsetFeature(QFont::Tag tag)
+{
+ features.remove(tag);
+}
QFontEngineData::QFontEngineData()
@@ -441,7 +447,7 @@ QFontEngineData::~QFontEngineData()
The attributes set in the constructor can also be set later, e.g.
setFamily(), setPointSize(), setPointSizeF(), setWeight() and
setItalic(). The remaining attributes must be set after
- contstruction, e.g. setBold(), setUnderline(), setOverline(),
+ construction, e.g. setBold(), setUnderline(), setOverline(),
setStrikeOut() and setFixedPitch(). QFontInfo objects should be
created \e after the font's attributes have been set. A QFontInfo
object will not change, even if you change the font's
@@ -486,8 +492,6 @@ QFontEngineData::~QFontEngineData()
The font matching algorithm works as follows:
\list 1
\li The specified font families (set by setFamilies()) are searched for.
- \li If not found, then if set the specified font family exists and can be used to represent
- the writing system in use, it will be selected.
\li If not, a replacement font that supports the writing system is
selected. The font matching algorithm will try to find the
best match for all the properties set in the QFont. How this is
@@ -554,10 +558,10 @@ QFontEngineData::~QFontEngineData()
For more general information on fonts, see the
\l{comp.fonts FAQ}{comp.fonts FAQ}.
- Information on encodings can be found from
- \l{Roman Czyborra's} page.
+ Information on encodings can be found from the
+ \l{UTR17} page.
- \sa QFontMetrics, QFontInfo, QFontDatabase, {Character Map Example}
+ \sa QFontMetrics, QFontInfo, QFontDatabase
*/
/*!
@@ -610,16 +614,6 @@ QFontEngineData::~QFontEngineData()
\since 5.2
*/
-#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
-/*
- \obsolete
- Constructs a font from \a font for use on the paint device \a pd.
-*/
-QFont::QFont(const QFont &font, QPaintDevice *pd)
- : QFont(font, static_cast<const QPaintDevice*>(pd))
-{}
-#endif
-
/*!
\since 5.13
Constructs a font from \a font for use on the paint device \a pd.
@@ -654,8 +648,10 @@ void QFont::detach()
if (d->engineData && !d->engineData->ref.deref())
delete d->engineData;
d->engineData = nullptr;
- if (d->scFont && d->scFont != d.data())
- d->scFont->ref.deref();
+ if (d->scFont && d->scFont != d.data()) {
+ if (!d->scFont->ref.deref())
+ delete d->scFont;
+ }
d->scFont = nullptr;
return;
}
@@ -706,11 +702,15 @@ QFont::QFont()
available a family will be set using the \l{QFont}{font matching}
algorithm.
+ This will split the family string on a comma and call setFamilies() with the
+ resulting list. To preserve a font that uses a comma in its name, use
+ the constructor that takes a QStringList.
+
\sa Weight, setFamily(), setPointSize(), setWeight(), setItalic(),
- setStyleHint(), QGuiApplication::font()
+ setStyleHint(), setFamilies(), QGuiApplication::font()
*/
QFont::QFont(const QString &family, int pointSize, int weight, bool italic)
- : d(new QFontPrivate()), resolve_mask(QFont::FamilyResolved)
+ : d(new QFontPrivate()), resolve_mask(QFont::FamiliesResolved)
{
if (pointSize <= 0) {
pointSize = 12;
@@ -727,7 +727,48 @@ QFont::QFont(const QString &family, int pointSize, int weight, bool italic)
if (italic)
resolve_mask |= QFont::StyleResolved;
- d->request.family = family;
+ d->request.families = splitIntoFamilies(family);
+ d->request.pointSize = qreal(pointSize);
+ d->request.pixelSize = -1;
+ d->request.weight = weight;
+ d->request.style = italic ? QFont::StyleItalic : QFont::StyleNormal;
+}
+
+/*!
+ Constructs a font object with the specified \a families, \a
+ pointSize, \a weight and \a italic settings.
+
+ If \a pointSize is zero or negative, the point size of the font
+ is set to a system-dependent default value. Generally, this is
+ 12 points.
+
+ Each family name entry in \a families may optionally also include
+ a foundry name, e.g. "Helvetica [Cronyx]". If the family is
+ available from more than one foundry and the foundry isn't
+ specified, an arbitrary foundry is chosen. If the family isn't
+ available a family will be set using the \l{QFont}{font matching}
+ algorithm.
+
+ \sa Weight, setPointSize(), setWeight(), setItalic(),
+ setStyleHint(), setFamilies(), QGuiApplication::font()
+ */
+QFont::QFont(const QStringList &families, int pointSize, int weight, bool italic)
+ : d(new QFontPrivate()), resolve_mask(QFont::FamiliesResolved)
+{
+ if (pointSize <= 0)
+ pointSize = 12;
+ else
+ resolve_mask |= QFont::SizeResolved;
+
+ if (weight < 0)
+ weight = Normal;
+ else
+ resolve_mask |= QFont::WeightResolved | QFont::StyleResolved;
+
+ if (italic)
+ resolve_mask |= QFont::StyleResolved;
+
+ d->request.families = families;
d->request.pointSize = qreal(pointSize);
d->request.pixelSize = -1;
d->request.weight = weight;
@@ -768,14 +809,14 @@ QFont &QFont::operator=(const QFont &font)
*/
/*!
- Returns the requested font family name, i.e. the name set in the
- constructor or the last setFont() call.
+ Returns the requested font family name. This will always be the same
+ as the first entry in the families() call.
- \sa setFamily(), substitutes(), substitute()
+ \sa setFamily(), substitutes(), substitute(), setFamilies(), families()
*/
QString QFont::family() const
{
- return d->request.family;
+ return d->request.families.isEmpty() ? QString() : d->request.families.constFirst();
}
/*!
@@ -789,18 +830,11 @@ QString QFont::family() const
available a family will be set using the \l{QFont}{font matching}
algorithm.
- \sa family(), setStyleHint(), QFontInfo
+ \sa family(), setStyleHint(), setFamilies(), families(), QFontInfo
*/
void QFont::setFamily(const QString &family)
{
- if ((resolve_mask & QFont::FamilyResolved) && d->request.family == family)
- return;
-
- detach();
-
- d->request.family = family;
-
- resolve_mask |= QFont::FamilyResolved;
+ setFamilies(QStringList(family));
}
/*!
@@ -889,13 +923,7 @@ int QFont::pointSize() const
\li PreferVerticalHinting
\li PreferFullHinting
\row
- \li Windows Vista (w/o Platform Update) and earlier
- \li Full hinting
- \li Full hinting
- \li Full hinting
- \li Full hinting
- \row
- \li Windows 7 and Windows Vista (w/Platform Update) and DirectWrite enabled in Qt
+ \li Windows and DirectWrite enabled in Qt
\li Full hinting
\li Vertical hinting
\li Vertical hinting
@@ -914,12 +942,6 @@ int QFont::pointSize() const
\li No hinting
\endtable
- \note Please be aware that altering the hinting preference on Windows is available through
- the DirectWrite font engine. This is available on Windows Vista after installing the platform
- update, and on Windows 7. In order to use this extension, configure Qt using -directwrite.
- The target application will then depend on the availability of DirectWrite on the target
- system.
-
*/
/*!
@@ -1015,7 +1037,8 @@ qreal QFont::pointSizeF() const
}
/*!
- Sets the font size to \a pixelSize pixels.
+ Sets the font size to \a pixelSize pixels, with a maxiumum size
+ of an unsigned 16-bit integer.
Using this function makes the font device dependent. Use
setPointSize() or setPointSizeF() to set the size of the font
@@ -1131,6 +1154,46 @@ QFont::Weight QFont::weight() const
\value Black 900
*/
+#if QT_DEPRECATED_SINCE(6, 0)
+/*!
+ \deprecated [6.0] Use setWeight() instead.
+
+ Sets the weight of the font to \a legacyWeight using the legacy font
+ weight scale of Qt 5 and previous versions.
+
+ Since Qt 6, the OpenType standard's font weight scale is used instead
+ of a non-standard scale. This requires conversion from values that
+ use the old scale. For convenience, this function may be used when
+ porting from code which uses the old weight scale.
+
+ \note If styleName() is set, this value may be ignored for font selection.
+
+ \sa setWeight(), weight(), QFontInfo
+*/
+void QFont::setLegacyWeight(int legacyWeight)
+{
+ setWeight(QFont::Weight(qt_legacyToOpenTypeWeight(legacyWeight)));
+}
+
+/*!
+ \deprecated [6.0] Use weight() instead.
+
+ Returns the weight of the font converted to the non-standard font
+ weight scale used in Qt 5 and earlier versions.
+
+ Since Qt 6, the OpenType standard's font weight scale is used instead
+ of a non-standard scale. This requires conversion from values that
+ use the old scale. For convenience, this function may be used when
+ porting from code which uses the old weight scale.
+
+ \sa setWeight(), weight(), QFontInfo
+*/
+int QFont::legacyWeight() const
+{
+ return qt_openTypeToLegacyWeight(weight());
+}
+#endif // QT_DEPRECATED_SINCE(6, 0)
+
/*!
Sets the weight of the font to \a weight, using the scale defined by
\l QFont::Weight enumeration.
@@ -1399,6 +1462,14 @@ QFont::StyleHint QFont::styleHint() const
\value NoAntialias don't antialias the fonts.
\value NoSubpixelAntialias avoid subpixel antialiasing on the fonts if possible.
\value PreferAntialias antialias if possible.
+ \value [since 6.8] ContextFontMerging If the selected font does not contain a certain character,
+ then Qt automatically chooses a similar-looking fallback font that contains the
+ character. By default this is done on a character-by-character basis. This means that in
+ certain uncommon cases, multiple fonts may be used to represent one string of text even
+ if it's in the same script. Setting \c ContextFontMerging will try finding the fallback
+ font that matches the largest subset of the input string instead. This will be more
+ expensive for strings where missing glyphs occur, but may give more consistent results.
+ If \c NoFontMerging is set, then \c ContextFontMerging will have no effect.
\value NoFontMerging If the font selected for a certain writing system
does not contain a character requested to draw, then Qt automatically chooses a similar
looking font that contains the character. The NoFontMerging flag disables this feature.
@@ -1471,7 +1542,7 @@ void QFont::setStyleStrategy(StyleStrategy s)
Predefined stretch values that follow the CSS naming convention. The higher
the value, the more stretched the text is.
- \value AnyStretch 0 Accept any stretch matched using the other QFont properties (added in Qt 5.8)
+ \value [since 5.8] AnyStretch 0 Accept any stretch matched using the other QFont properties
\value UltraCondensed 50
\value ExtraCondensed 62
\value Condensed 75
@@ -1709,6 +1780,7 @@ bool QFont::operator==(const QFont &f) const
&& f.d->letterSpacingIsAbsolute == d->letterSpacingIsAbsolute
&& f.d->letterSpacing == d->letterSpacing
&& f.d->wordSpacing == d->wordSpacing
+ && f.d->features == d->features
));
}
@@ -1738,7 +1810,6 @@ bool QFont::operator<(const QFont &f) const
if (r1.styleHint != r2.styleHint) return r1.styleHint < r2.styleHint;
if (r1.styleStrategy != r2.styleStrategy) return r1.styleStrategy < r2.styleStrategy;
if (r1.families != r2.families) return r1.families < r2.families;
- if (r1.family != r2.family) return r1.family < r2.family;
if (f.d->capital != d->capital) return f.d->capital < d->capital;
if (f.d->letterSpacingIsAbsolute != d->letterSpacingIsAbsolute) return f.d->letterSpacingIsAbsolute < d->letterSpacingIsAbsolute;
@@ -1747,7 +1818,37 @@ bool QFont::operator<(const QFont &f) const
int f1attrs = (f.d->underline << 3) + (f.d->overline << 2) + (f.d->strikeOut<<1) + f.d->kerning;
int f2attrs = (d->underline << 3) + (d->overline << 2) + (d->strikeOut<<1) + d->kerning;
- return f1attrs < f2attrs;
+ if (f1attrs != f2attrs) return f1attrs < f2attrs;
+
+ if (d->features.size() != f.d->features.size())
+ return f.d->features.size() < d->features.size();
+
+ {
+ auto it = d->features.constBegin();
+ auto jt = f.d->features.constBegin();
+ for (; it != d->features.constEnd(); ++it, ++jt) {
+ if (it.key() != jt.key())
+ return jt.key() < it.key();
+ if (it.value() != jt.value())
+ return jt.value() < it.value();
+ }
+ }
+
+ if (r1.variableAxisValues.size() != r2.variableAxisValues.size())
+ return r1.variableAxisValues.size() < r2.variableAxisValues.size();
+
+ {
+ auto it = r1.variableAxisValues.constBegin();
+ auto jt = r2.variableAxisValues.constBegin();
+ for (; it != r1.variableAxisValues.constEnd(); ++it, ++jt) {
+ if (it.key() != jt.key())
+ return jt.key() < it.key();
+ if (it.value() != jt.value())
+ return jt.value() < it.value();
+ }
+ }
+
+ return false;
}
@@ -2016,13 +2117,32 @@ QString QFont::key() const
/*!
Returns a description of the font. The description is a
comma-separated list of the attributes, perfectly suited for use
- in QSettings.
+ in QSettings, and consists of the following:
+
+ \list
+ \li Font family
+ \li Point size
+ \li Pixel size
+ \li Style hint
+ \li Font weight
+ \li Font style
+ \li Underline
+ \li Strike out
+ \li Fixed pitch
+ \li Always \e{0}
+ \li Capitalization
+ \li Letter spacing
+ \li Word spacing
+ \li Stretch
+ \li Style strategy
+ \li Font style (omitted when unavailable)
+ \endlist
\sa fromString()
*/
QString QFont::toString() const
{
- const QChar comma(QLatin1Char(','));
+ const QChar comma(u',');
QString fontDescription = family() + comma +
QString::number( pointSizeF()) + comma +
QString::number( pixelSize()) + comma +
@@ -2070,8 +2190,8 @@ size_t qHash(const QFont &font, size_t seed) noexcept
bool QFont::fromString(const QString &descrip)
{
const auto sr = QStringView(descrip).trimmed();
- const auto l = sr.split(QLatin1Char(','));
- const int count = l.count();
+ const auto l = sr.split(u',');
+ const int count = l.size();
if (!count || (count > 2 && count < 9) || count == 9 || count > 17 ||
l.first().isEmpty()) {
qWarning("QFont::fromString: Invalid description '%s'",
@@ -2093,7 +2213,10 @@ bool QFont::fromString(const QString &descrip)
if (l[2].toInt() > 0)
setPixelSize(l[2].toInt());
setStyleHint((StyleHint) l[3].toInt());
- setWeight(QFont::Weight(l[4].toInt()));
+ if (count >= 16)
+ setWeight(QFont::Weight(l[4].toInt()));
+ else
+ setWeight(QFont::Weight(qt_legacyToOpenTypeWeight(l[4].toInt())));
setStyle((QFont::Style)l[5].toInt());
setUnderline(l[6].toInt());
setStrikeOut(l[7].toInt());
@@ -2146,6 +2269,404 @@ void QFont::cacheStatistics()
{
}
+/*!
+ \class QFont::Tag
+ \brief The QFont::Tag type provides access to advanced font features.
+ \since 6.7
+ \inmodule QtGui
+
+ QFont provides access to advanced features when shaping text. A feature is defined
+ by a tag, which can be represented as a four-character string, or as a 32bit integer
+ value. This type represents such a tag in a type-safe way. It can be constructed from
+ a four-character, 8bit string literal, or from a corresponding 32bit integer value.
+ Using a shorter or longer string literal will result in a compile-time error.
+
+ \code
+ QFont font;
+ // Correct
+ font.setFeature("frac");
+
+ // Wrong - won't compile
+ font.setFeature("fraction");
+
+ // Wrong - will produce runtime warning and fail
+ font.setFeature(u"fraction"_s);
+ \endcode
+
+ The named constructors allow to create a tag from an 32bit integer or string value,
+ and will return a \c std::nullopt when the input is invalid.
+
+ \sa QFont::setFeature(), QFont::featureTags()
+*/
+
+/*!
+ \fn QFont::Tag::Tag()
+
+ Default constructor, producing an invalid tag.
+*/
+
+/*!
+ \fn template <size_t N> QFont::Tag::Tag(const char (&str)[N]) noexcept
+
+ Constructs a tag from a string literal, \a str. The literal must be exactly four
+ characters long.
+
+ \code
+ font.setFeature("frac", 1);
+ \endcode
+
+ \sa fromString(), fromValue()
+*/
+
+/*!
+ \fn bool QFont::Tag::comparesEqual(const QFont::Tag &lhs, const QFont::Tag &rhs) noexcept
+ \fn Qt::strong_ordering QFont::Tag::compareThreeWay(const QFont::Tag &lhs, const QFont::Tag &rhs) noexcept
+
+ Compare \a lhs with \a rhs for equality and ordering.
+*/
+
+/*!
+ \fn size_t QFont::Tag::qHash(QFont::Tag key, size_t seed) noexcept
+
+ Returns the hash value for \a key, using \a seed to seed the calculation.
+*/
+
+/*!
+ \fn quint32 QFont::Tag::value() const noexcept
+
+ Returns the numerical value of this tag.
+
+ \sa isValid(), fromValue()
+*/
+
+/*!
+ \fn bool QFont::Tag::isValid() const noexcept
+
+ Returns whether the tag is valid. A tag is valid if its value is not zero.
+
+ \sa value(), fromValue(), fromString()
+*/
+
+/*!
+ \fn QByteArray QFont::Tag::toString() const noexcept
+
+ Returns the string representation of this tag as a byte array.
+
+ \sa fromString()
+*/
+
+/*!
+ \fn std::optional<QFont::Tag> QFont::Tag::fromValue(quint32 value) noexcept
+
+ Returns a tag constructed from \a value, or \c std::nullopt if the tag produced
+ would be invalid.
+
+ \sa isValid()
+*/
+
+/*!
+ Returns a tag constructed from the string in \a view. The string must be exactly
+ four characters long.
+
+ Returns \c std::nullopt if the input is not four characters long, or if the tag
+ produced would be invalid.
+
+ \sa isValid(), fromValue()
+*/
+std::optional<QFont::Tag> QFont::Tag::fromString(QAnyStringView view) noexcept
+{
+ if (view.size() != 4) {
+ qWarning("The tag name must be exactly 4 characters long!");
+ return std::nullopt;
+ }
+ const QFont::Tag maybeTag = view.visit([](auto view) {
+ using CharType = decltype(view.at(0));
+ if constexpr (std::is_same_v<CharType, char>) {
+ const char bytes[5] = { view.at(0), view.at(1), view.at(2), view.at(3), 0 };
+ return Tag(bytes);
+ } else {
+ const char bytes[5] = { view.at(0).toLatin1(), view.at(1).toLatin1(),
+ view.at(2).toLatin1(), view.at(3).toLatin1(), 0 };
+ return Tag(bytes);
+ }
+ });
+ return maybeTag.isValid() ? std::optional<Tag>(maybeTag) : std::nullopt;
+}
+
+/*!
+ \fn QDataStream &operator<<(QDataStream &, QFont::Tag)
+ \fn QDataStream &operator>>(QDataStream &, QFont::Tag &)
+ \relates QFont::Tag
+
+ Data stream operators for QFont::Tag.
+*/
+
+/*!
+ \since 6.7
+
+ Applies a \a value to the variable axis corresponding to \a tag.
+
+ Variable fonts provide a way to store multiple variations (with different weights, widths
+ or styles) in the same font file. The variations are given as floating point values for
+ a pre-defined set of parameters, called "variable axes". Specific instances are typically
+ given names by the font designer, and, in Qt, these can be selected using setStyleName()
+ just like traditional sub-families.
+
+ In some cases, it is also useful to provide arbitrary values for the different axes. For
+ instance, if a font has a Regular and Bold sub-family, you may want a weight in-between these.
+ You could then manually request this by supplying a custom value for the "wght" axis in the
+ font.
+
+ \code
+ QFont font;
+ font.setVariableAxis("wght", (QFont::Normal + QFont::Bold) / 2.0f);
+ \endcode
+
+ If the "wght" axis is supported by the font and the given value is within its defined range,
+ a font corresponding to the weight 550.0 will be provided.
+
+ There are a few standard axes than many fonts provide, such as "wght" (weight), "wdth" (width),
+ "ital" (italic) and "opsz" (optical size). They each have indivdual ranges defined in the font
+ itself. For instance, "wght" may span from 100 to 900 (QFont::Thin to QFont::Black) whereas
+ "ital" can span from 0 to 1 (from not italic to fully italic).
+
+ A font may also choose to define custom axes; the only limitation is that the name has to
+ meet the requirements for a QFont::Tag (sequence of four latin-1 characters.)
+
+ By default, no variable axes are set.
+
+ \note In order to use variable axes on Windows, the application has to run with either the
+ FreeType or DirectWrite font databases. See the documentation for
+ QGuiApplication::QGuiApplication() for more information on how to select these technologies.
+
+ \sa unsetVariableAxis
+ */
+void QFont::setVariableAxis(Tag tag, float value)
+{
+ if (tag.isValid()) {
+ if (resolve_mask & QFont::VariableAxesResolved && d->hasVariableAxis(tag, value))
+ return;
+
+ detach();
+
+ d->setVariableAxis(tag, value);
+ resolve_mask |= QFont::VariableAxesResolved;
+ }
+}
+
+/*!
+ \since 6.7
+
+ Unsets a previously set variable axis value given by \a tag.
+
+ \note If no value has previously been given for this tag, the QFont will still consider its
+ variable axes as set when resolving against other QFont values.
+
+ \sa setVariableAxis
+*/
+void QFont::unsetVariableAxis(Tag tag)
+{
+ if (tag.isValid()) {
+ detach();
+
+ d->unsetVariableAxis(tag);
+ resolve_mask |= QFont::VariableAxesResolved;
+ }
+}
+
+/*!
+ \since 6.7
+
+ Returns a list of tags for all variable axes currently set on this QFont.
+
+ See \l{QFont::}{setVariableAxis()} for more details on variable axes.
+
+ \sa QFont::Tag, setVariableAxis(), unsetVariableAxis(), isVariableAxisSet(), clearVariableAxes()
+*/
+QList<QFont::Tag> QFont::variableAxisTags() const
+{
+ return d->request.variableAxisValues.keys();
+}
+
+/*!
+ \since 6.7
+
+ Returns the value set for a specific variable axis \a tag. If the tag has not been set, 0.0 will
+ be returned instead.
+
+ See \l{QFont::}{setVariableAxis()} for more details on variable axes.
+
+ \sa QFont::Tag, setVariableAxis(), unsetVariableAxis(), isVariableAxisSet(), clearVariableAxes()
+*/
+float QFont::variableAxisValue(Tag tag) const
+{
+ return d->request.variableAxisValues.value(tag);
+}
+
+/*!
+ \since 6.7
+
+ Returns true if a value for the variable axis given by \a tag has been set on the QFont,
+ otherwise returns false.
+
+ See \l{QFont::}{setVariableAxis()} for more details on font variable axes.
+
+ \sa QFont::Tag, setVariableAxis(), unsetVariableAxis(), variableAxisValue(), clearVariableAxes()
+*/
+bool QFont::isVariableAxisSet(Tag tag) const
+{
+ return d->request.variableAxisValues.contains(tag);
+}
+
+/*!
+ \since 6.7
+
+ Clears any previously set variable axis values on the QFont.
+
+ See \l{QFont::}{setVariableAxis()} for more details on variable axes.
+
+ \sa QFont::Tag, setVariableAxis(), unsetVariableAxis(), isVariableAxisSet(), variableAxisValue()
+*/
+void QFont::clearVariableAxes()
+{
+ if (d->request.variableAxisValues.isEmpty())
+ return;
+
+ detach();
+ d->request.variableAxisValues.clear();
+}
+
+
+/*!
+ \since 6.7
+ \overload
+
+ Applies an integer value to the typographical feature specified by \a tag when shaping the
+ text. This provides advanced access to the font shaping process, and can be used to support
+ font features that are otherwise not covered in the API.
+
+ The feature is specified by a \l{QFont::Tag}{tag}, which is typically encoded from the
+ four-character feature name in the font feature map.
+
+ This integer \a value passed along with the tag in most cases represents a boolean value: A zero
+ value means the feature is disabled, and a non-zero value means it is enabled. For certain
+ font features, however, it may have other interpretations. For example, when applied to the
+ \c salt feature, the value is an index that specifies the stylistic alternative to use.
+
+ For example, the \c frac font feature will convert diagonal fractions separated with a slash
+ (such as \c 1/2) with a different representation. Typically this will involve baking the full
+ fraction into a single character width (such as \c ½).
+
+ If a font supports the \c frac feature, then it can be enabled in the shaper by setting
+ \c{features["frac"] = 1} in the font feature map.
+
+ \note By default, Qt will enable and disable certain font features based on other font
+ properties. In particular, the \c kern feature will be enabled/disabled depending on the
+ \l kerning() property of the QFont. In addition, all ligature features
+ (\c liga, \c clig, \c dlig, \c hlig) will be disabled if a \l letterSpacing() is applied,
+ but only for writing systems where the use of ligature is cosmetic. For writing systems where
+ ligatures are required, the features will remain in their default state. The values set using
+ setFeature() and related functions will override the default behavior. If, for instance,
+ the feature "kern" is set to 1, then kerning will always be enabled, regardless of whether the
+ kerning property is set to false. Similarly, if it is set to 0, then it will always be disabled.
+ To reset a font feature to its default behavior, you can unset it using unsetFeature().
+
+ \sa QFont::Tag, clearFeatures(), setFeature(), unsetFeature(), featureTags()
+*/
+void QFont::setFeature(Tag tag, quint32 value)
+{
+ if (tag.isValid()) {
+ d->detachButKeepEngineData(this);
+ d->setFeature(tag, value);
+ resolve_mask |= QFont::FeaturesResolved;
+ }
+}
+
+/*!
+ \since 6.7
+ \overload
+
+ Unsets the \a tag from the map of explicitly enabled/disabled features.
+
+ \note Even if the feature has not previously been added, this will mark the font features map
+ as modified in this QFont, so that it will take precedence when resolving against other fonts.
+
+ Unsetting an existing feature on the QFont reverts behavior to the default.
+
+ See \l setFeature() for more details on font features.
+
+ \sa QFont::Tag, clearFeatures(), setFeature(), featureTags(), featureValue()
+*/
+void QFont::unsetFeature(Tag tag)
+{
+ if (tag.isValid()) {
+ d->detachButKeepEngineData(this);
+ d->unsetFeature(tag);
+ resolve_mask |= QFont::FeaturesResolved;
+ }
+}
+
+/*!
+ \since 6.7
+
+ Returns a list of tags for all font features currently set on this QFont.
+
+ See \l{QFont::}{setFeature()} for more details on font features.
+
+ \sa QFont::Tag, setFeature(), unsetFeature(), isFeatureSet(), clearFeatures()
+*/
+QList<QFont::Tag> QFont::featureTags() const
+{
+ return d->features.keys();
+}
+
+/*!
+ \since 6.7
+
+ Returns the value set for a specific feature \a tag. If the tag has not been set, 0 will be
+ returned instead.
+
+ See \l{QFont::}{setFeature()} for more details on font features.
+
+ \sa QFont::Tag, setFeature(), unsetFeature(), featureTags(), isFeatureSet()
+*/
+quint32 QFont::featureValue(Tag tag) const
+{
+ return d->features.value(tag);
+}
+
+/*!
+ \since 6.7
+
+ Returns true if a value for the feature given by \a tag has been set on the QFont, otherwise
+ returns false.
+
+ See \l{QFont::}{setFeature()} for more details on font features.
+
+ \sa QFont::Tag, setFeature(), unsetFeature(), featureTags(), featureValue()
+*/
+bool QFont::isFeatureSet(Tag tag) const
+{
+ return d->features.contains(tag);
+}
+
+/*!
+ \since 6.7
+
+ Clears any previously set features on the QFont.
+
+ See \l{QFont::}{setFeature()} for more details on font features.
+
+ \sa QFont::Tag, setFeature(), unsetFeature(), featureTags(), featureValue()
+*/
+void QFont::clearFeatures()
+{
+ if (d->features.isEmpty())
+ return;
+
+ d->detachButKeepEngineData(this);
+ d->features.clear();
+}
extern QStringList qt_fallbacksForFamily(const QString &family, QFont::Style style,
QFont::StyleHint styleHint, QChar::Script script);
@@ -2225,9 +2746,9 @@ void QFont::setFamilies(const QStringList &families)
QDataStream &operator<<(QDataStream &s, const QFont &font)
{
if (s.version() == 1) {
- s << font.d->request.family.toLatin1();
+ s << font.d->request.families.constFirst().toLatin1();
} else {
- s << font.d->request.family;
+ s << font.d->request.families.constFirst();
if (s.version() >= QDataStream::Qt_5_4)
s << font.d->request.styleName;
}
@@ -2277,8 +2798,16 @@ QDataStream &operator<<(QDataStream &s, const QFont &font)
s << (quint8)font.d->request.hintingPreference;
if (s.version() >= QDataStream::Qt_5_6)
s << (quint8)font.d->capital;
- if (s.version() >= QDataStream::Qt_5_13)
- s << font.d->request.families;
+ if (s.version() >= QDataStream::Qt_5_13) {
+ if (s.version() < QDataStream::Qt_6_0)
+ s << font.d->request.families.mid(1);
+ else
+ s << font.d->request.families;
+ }
+ if (s.version() >= QDataStream::Qt_6_6)
+ s << font.d->features;
+ if (s.version() >= QDataStream::Qt_6_7)
+ s << font.d->request.variableAxisValues;
return s;
}
@@ -2302,9 +2831,11 @@ QDataStream &operator>>(QDataStream &s, QFont &font)
if (s.version() == 1) {
QByteArray fam;
s >> fam;
- font.d->request.family = QString::fromLatin1(fam);
+ font.d->request.families = QStringList(QString::fromLatin1(fam));
} else {
- s >> font.d->request.family;
+ QString fam;
+ s >> fam;
+ font.d->request.families = QStringList(fam);
if (s.version() >= QDataStream::Qt_5_4)
s >> font.d->request.styleName;
}
@@ -2386,11 +2917,40 @@ QDataStream &operator>>(QDataStream &s, QFont &font)
if (s.version() >= QDataStream::Qt_5_13) {
QStringList value;
s >> value;
- font.d->request.families = value;
+ if (s.version() < QDataStream::Qt_6_0)
+ font.d->request.families.append(value);
+ else
+ font.d->request.families = value;
+ }
+ if (s.version() >= QDataStream::Qt_6_6) {
+ font.d->features.clear();
+ s >> font.d->features;
+ }
+ if (s.version() >= QDataStream::Qt_6_7) {
+ font.d->request.variableAxisValues.clear();
+ s >> font.d->request.variableAxisValues;
}
+
return s;
}
+QDataStream &operator<<(QDataStream &stream, QFont::Tag tag)
+{
+ stream << tag.value();
+ return stream;
+}
+
+QDataStream &operator>>(QDataStream &stream, QFont::Tag &tag)
+{
+ quint32 value;
+ stream >> value;
+ if (const auto maybeTag = QFont::Tag::fromValue(value))
+ tag = *maybeTag;
+ else
+ stream.setStatus(QDataStream::ReadCorruptData);
+ return stream;
+}
+
#endif // QT_NO_DATASTREAM
@@ -2441,6 +3001,35 @@ QDataStream &operator>>(QDataStream &s, QFont &font)
info object is \e not updated.
\endlist
+ \section1 Checking for the existence of a font
+
+ Sometimes it can be useful to check if a font exists before attempting
+ to use it. The most thorough way of doing so is by using \l {exactMatch()}:
+
+ \code
+ const QFont segoeFont(QLatin1String("Segoe UI"));
+ if (QFontInfo(segoeFont).exactMatch()) {
+ // Use the font...
+ }
+ \endcode
+
+ However, this deep search of families can be expensive on some platforms.
+ \c QFontDatabase::families().contains() is a faster, but less thorough
+ alternative:
+
+ \code
+ const QLatin1String segoeUiFamilyName("Segoe UI");
+ if (QFontDatabase::families().contains(segoeUiFamilyName)) {
+ const QFont segoeFont(segoeUiFamilyName);
+ // Use the font...
+ }
+ \endcode
+
+ It's less thorough because it's not a complete search: some font family
+ aliases may be missing from the list. However, this approach results in
+ faster application startup times, and so should always be preferred if
+ possible.
+
\sa QFont, QFontMetrics, QFontDatabase
*/
@@ -2457,6 +3046,8 @@ QDataStream &operator>>(QDataStream &s, QFont &font)
Use QPainter::fontInfo() to get the font info when painting.
This will give correct results also when painting on paint device
that is not screen-compatible.
+
+ \sa {Checking for the existence of a font}
*/
QFontInfo::QFontInfo(const QFont &font)
: d(font.d)
@@ -2498,13 +3089,13 @@ QFontInfo &QFontInfo::operator=(const QFontInfo &fi)
/*!
Returns the family name of the matched window system font.
- \sa QFont::family()
+ \sa QFont::family(), {Checking for the existence of a font}
*/
QString QFontInfo::family() const
{
QFontEngine *engine = d->engineForScript(QChar::Script_Common);
Q_ASSERT(engine != nullptr);
- return engine->fontDef.family;
+ return engine->fontDef.families.isEmpty() ? QString() : engine->fontDef.families.constFirst();
}
/*!
@@ -2582,6 +3173,28 @@ QFont::Style QFontInfo::style() const
return (QFont::Style)engine->fontDef.style;
}
+
+#if QT_DEPRECATED_SINCE(6, 0)
+/*!
+ \deprecated Use weight() instead.
+
+ Returns the weight of the font converted to the non-standard font
+ weight scale used in Qt 5 and earlier versions.
+
+ Since Qt 6, the OpenType standard's font weight scale is used instead
+ of a non-standard scale. This requires conversion from values that
+ use the old scale. For convenience, this function may be used when
+ porting from code which uses the old weight scale.
+
+ \sa QFont::setWeight(), weight(), QFontInfo
+*/
+int QFontInfo::legacyWeight() const
+{
+ return qt_openTypeToLegacyWeight(weight());
+}
+#endif // QT_DEPRECATED_SINCE(6, 0)
+
+
/*!
Returns the weight of the matched window system font.
@@ -2658,10 +3271,10 @@ bool QFontInfo::fixedPitch() const
Q_ASSERT(engine != nullptr);
#ifdef Q_OS_MAC
if (!engine->fontDef.fixedPitchComputed) {
- QChar ch[2] = { QLatin1Char('i'), QLatin1Char('m') };
+ QChar ch[2] = { u'i', u'm' };
QGlyphLayoutArray<2> g;
int l = 2;
- if (!engine->stringToCMap(ch, 2, &g, &l, {}))
+ if (engine->stringToCMap(ch, 2, &g, &l, {}) < 0)
Q_UNREACHABLE();
Q_ASSERT(l == 2);
engine->fontDef.fixedPitch = g.advances[0] == g.advances[1];
@@ -2705,13 +3318,15 @@ bool QFontInfo::exactMatch() const
// QFontCache
// **********************************************************************
+using namespace std::chrono_literals;
+
#ifdef QFONTCACHE_DEBUG
// fast timeouts for debugging
-static const int fast_timeout = 1000; // 1s
-static const int slow_timeout = 5000; // 5s
+static constexpr auto fast_timeout = 1s;
+static constexpr auto slow_timeout = 5s;
#else
-static const int fast_timeout = 10000; // 10s
-static const int slow_timeout = 300000; // 5m
+static constexpr auto fast_timeout = 10s;
+static constexpr auto slow_timeout = 5min;
#endif // QFONTCACHE_DEBUG
#ifndef QFONTCACHE_MIN_COST
@@ -2740,11 +3355,14 @@ void QFontCache::cleanup()
cache->setLocalData(nullptr);
}
-static QBasicAtomicInt font_cache_id = Q_BASIC_ATOMIC_INITIALIZER(0);
+Q_CONSTINIT static QBasicAtomicInt font_cache_id = Q_BASIC_ATOMIC_INITIALIZER(0);
QFontCache::QFontCache()
: QObject(), total_cost(0), max_cost(min_cost),
- current_timestamp(0), fast(false), timer_id(-1),
+ current_timestamp(0), fast(false),
+ autoClean(QGuiApplication::instance()
+ && (QGuiApplication::instance()->thread() == QThread::currentThread())),
+ timer_id(-1),
m_id(font_cache_id.fetchAndAddRelaxed(1) + 1)
{
}
@@ -2914,10 +3532,14 @@ void QFontCache::increaseCost(uint cost)
if (total_cost > max_cost) {
max_cost = total_cost;
+ if (!autoClean)
+ return;
+
if (timer_id == -1 || ! fast) {
- FC_DEBUG(" TIMER: starting fast timer (%d ms)", fast_timeout);
+ FC_DEBUG(" TIMER: starting fast timer (%d s)", static_cast<int>(fast_timeout.count()));
- if (timer_id != -1) killTimer(timer_id);
+ if (timer_id != -1)
+ killTimer(timer_id);
timer_id = startTimer(fast_timeout);
fast = true;
}
@@ -3008,22 +3630,26 @@ void QFontCache::decreaseCache()
FC_DEBUG(" after sweep, in use %u kb, total %u kb, max %u kb, new max %u kb",
in_use_cost, total_cost, max_cost, new_max_cost);
- if (new_max_cost == max_cost) {
- if (fast) {
- FC_DEBUG(" cannot shrink cache, slowing timer");
+ if (autoClean) {
+ if (new_max_cost == max_cost) {
+ if (fast) {
+ FC_DEBUG(" cannot shrink cache, slowing timer");
- killTimer(timer_id);
- timer_id = startTimer(slow_timeout);
- fast = false;
- }
+ if (timer_id != -1) {
+ killTimer(timer_id);
+ timer_id = startTimer(slow_timeout);
+ fast = false;
+ }
- return;
- } else if (! fast) {
- FC_DEBUG(" dropping into passing gear");
+ return;
+ } else if (! fast) {
+ FC_DEBUG(" dropping into passing gear");
- killTimer(timer_id);
- timer_id = startTimer(fast_timeout);
- fast = true;
+ if (timer_id != -1)
+ killTimer(timer_id);
+ timer_id = startTimer(fast_timeout);
+ fast = true; }
+ }
}
max_cost = new_max_cost;
@@ -3120,7 +3746,7 @@ QDebug operator<<(QDebug stream, const QFont &font)
const QFont defaultFont(new QFontPrivate);
- for (int property = QFont::FamilyResolved; property < QFont::AllPropertiesResolved; property <<= 1) {
+ for (int property = QFont::SizeResolved; property < QFont::AllPropertiesResolved; property <<= 1) {
const bool resolved = (font.resolve_mask & property) != 0;
if (!resolved && stream.verbosity() == QDebug::MinimumVerbosity)
continue;
@@ -3132,8 +3758,6 @@ QDebug operator<<(QDebug stream, const QFont &font)
QDebugStateSaver saver(debug);
switch (property) {
- case QFont::FamilyResolved:
- debug << font.family(); break;
case QFont::SizeResolved:
if (font.pointSizeF() >= 0)
debug << font.pointSizeF() << "pt";
@@ -3203,6 +3827,15 @@ QDebug operator<<(QDebug stream, const QFont &font)
return stream;
}
+
+QDebug operator<<(QDebug debug, QFont::Tag tag)
+{
+ QDebugStateSaver saver(debug);
+ debug.noquote() << tag.toString();
+ return debug;
+}
#endif
QT_END_NAMESPACE
+
+#include "moc_qfont.cpp"
diff --git a/src/gui/text/qfont.h b/src/gui/text/qfont.h
index 4079e48dda..66a5f7c155 100644
--- a/src/gui/text/qfont.h
+++ b/src/gui/text/qfont.h
@@ -1,49 +1,15 @@
-/****************************************************************************
-**
-** 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 QFONT_H
#define QFONT_H
+#include <QtCore/qcompare.h>
+#include <QtCore/qendian.h>
+#include <QtCore/qshareddata.h>
#include <QtGui/qtguiglobal.h>
#include <QtGui/qwindowdefs.h>
#include <QtCore/qstring.h>
-#include <QtCore/qsharedpointer.h>
QT_BEGIN_NAMESPACE
@@ -81,6 +47,7 @@ public:
NoAntialias = 0x0100,
NoSubpixelAntialias = 0x0800,
PreferNoShaping = 0x1000,
+ ContextFontMerging = 0x2000,
NoFontMerging = 0x8000
};
Q_ENUM(StyleStrategy)
@@ -162,21 +129,22 @@ public:
HintingPreferenceResolved = 0x8000,
StyleNameResolved = 0x10000,
FamiliesResolved = 0x20000,
- AllPropertiesResolved = 0x3ffff
+ FeaturesResolved = 0x40000,
+ VariableAxesResolved = 0x80000,
+ AllPropertiesResolved = 0xfffff
};
Q_ENUM(ResolveProperties)
QFont();
+
QFont(const QString &family, int pointSize = -1, int weight = -1, bool italic = false);
-#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
- QFont(const QFont &font, QPaintDevice *pd);
-#endif
+ explicit QFont(const QStringList &families, int pointSize = -1, int weight = -1, bool italic = false);
QFont(const QFont &font, const QPaintDevice *pd);
QFont(const QFont &font);
~QFont();
- void swap(QFont &other)
- { qSwap(d, other.d); qSwap(resolve_mask, other.resolve_mask); }
+ void swap(QFont &other) noexcept
+ { d.swap(other.d); std::swap(resolve_mask, other.resolve_mask); }
QString family() const;
void setFamily(const QString &);
@@ -243,6 +211,75 @@ public:
void setHintingPreference(HintingPreference hintingPreference);
HintingPreference hintingPreference() const;
+ struct Tag
+ {
+ constexpr Tag() = default;
+
+ template <size_t N>
+ constexpr Q_IMPLICIT Tag(const char (&str)[N]) noexcept
+ : m_value((quint32(str[0]) << 24) | (quint32(str[1]) << 16)
+ | (quint32(str[2]) << 8) | quint32(str[3]))
+ {
+ static_assert(N == 5, "The tag name must be exactly 4 characters long!");
+ }
+
+ constexpr bool isValid() const noexcept { return m_value != 0; }
+ constexpr quint32 value() const noexcept { return m_value; }
+
+ QByteArray toString() const
+ {
+ const char data[] = {
+ char((m_value & 0xff000000) >> 24),
+ char((m_value & 0x00ff0000) >> 16),
+ char((m_value & 0x0000ff00) >> 8),
+ char((m_value & 0x000000ff)) };
+ return QByteArray(data, sizeof(data));
+ }
+
+ static constexpr std::optional<Tag> fromValue(quint32 value) noexcept
+ {
+ Tag maybeTag;
+ maybeTag.m_value = value;
+ return maybeTag.isValid() ? std::optional<Tag>(maybeTag) : std::nullopt;
+ }
+ Q_GUI_EXPORT static std::optional<Tag> fromString(QAnyStringView view) noexcept;
+
+#ifndef QT_NO_DATASTREAM
+ friend Q_GUI_EXPORT QDataStream &operator<<(QDataStream &, Tag);
+ friend Q_GUI_EXPORT QDataStream &operator>>(QDataStream &, Tag &);
+#endif
+
+#ifndef QT_NO_DEBUG_STREAM
+ friend Q_GUI_EXPORT QDebug operator<<(QDebug debug, Tag tag);
+#endif
+
+ friend constexpr size_t qHash(Tag key, size_t seed = 0) noexcept
+ { return qHash(key.value(), seed); }
+
+ private:
+ friend constexpr bool comparesEqual(const Tag &lhs, const Tag &rhs) noexcept
+ { return lhs.m_value == rhs.m_value; }
+ friend constexpr Qt::strong_ordering compareThreeWay(const Tag &lhs, const Tag &rhs) noexcept
+ { return Qt::compareThreeWay(lhs.m_value, rhs.m_value); }
+ Q_DECLARE_STRONGLY_ORDERED_LITERAL_TYPE(QFont::Tag)
+
+ quint32 m_value = 0;
+ };
+
+ void setFeature(Tag tag, quint32 value);
+ void unsetFeature(Tag tag);
+ quint32 featureValue(Tag tag) const;
+ bool isFeatureSet(Tag tag) const;
+ QList<Tag> featureTags() const;
+ void clearFeatures();
+
+ void setVariableAxis(Tag tag, float value);
+ void unsetVariableAxis(Tag tag);
+ bool isVariableAxisSet(Tag tag) const;
+ float variableAxisValue(Tag tag) const;
+ void clearVariableAxes();
+ QList<Tag> variableAxisTags() const;
+
// dupicated from QFontInfo
bool exactMatch() const;
@@ -252,8 +289,7 @@ public:
bool operator<(const QFont &) const;
operator QVariant() const;
bool isCopyOf(const QFont &) const;
- inline QFont &operator=(QFont &&other) noexcept
- { qSwap(d, other.d); qSwap(resolve_mask, other.resolve_mask); return *this; }
+ QT_MOVE_ASSIGNMENT_OPERATOR_IMPL_VIA_PURE_SWAP(QFont)
QString key() const;
@@ -276,6 +312,11 @@ public:
inline uint resolveMask() const { return resolve_mask; }
inline void setResolveMask(uint mask) { resolve_mask = mask; }
+#if QT_DEPRECATED_SINCE(6, 0)
+ QT_DEPRECATED_VERSION_X_6_0("Use setWeight() instead") void setLegacyWeight(int legacyWeight);
+ QT_DEPRECATED_VERSION_X_6_0("Use weight() instead") int legacyWeight() const;
+#endif
+
private:
explicit QFont(QFontPrivate *);
diff --git a/src/gui/text/qfont_p.h b/src/gui/text/qfont_p.h
index abf6d000e7..b674e71103 100644
--- a/src/gui/text/qfont_p.h
+++ b/src/gui/text/qfont_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 QFONT_P_H
#define QFONT_P_H
@@ -87,11 +51,11 @@ struct QFontDef
{
}
- QString family;
QStringList families;
QString styleName;
QStringList fallBackFamilies;
+ QMap<QFont::Tag, float> variableAxisValues;
qreal pointSize;
qreal pixelSize;
@@ -119,10 +83,10 @@ struct QFontDef
&& styleHint == other.styleHint
&& styleStrategy == other.styleStrategy
&& ignorePitch == other.ignorePitch && fixedPitch == other.fixedPitch
- && family == other.family
&& families == other.families
&& styleName == other.styleName
&& hintingPreference == other.hintingPreference
+ && variableAxisValues == other.variableAxisValues
;
}
inline bool operator<(const QFontDef &other) const
@@ -133,7 +97,6 @@ struct QFontDef
if (stretch != other.stretch) return stretch < other.stretch;
if (styleHint != other.styleHint) return styleHint < other.styleHint;
if (styleStrategy != other.styleStrategy) return styleStrategy < other.styleStrategy;
- if (family != other.family) return family < other.family;
if (families != other.families) return families < other.families;
if (styleName != other.styleName)
return styleName < other.styleName;
@@ -142,6 +105,22 @@ struct QFontDef
if (ignorePitch != other.ignorePitch) return ignorePitch < other.ignorePitch;
if (fixedPitch != other.fixedPitch) return fixedPitch < other.fixedPitch;
+ if (variableAxisValues != other.variableAxisValues) {
+ if (variableAxisValues.size() != other.variableAxisValues.size())
+ return variableAxisValues.size() < other.variableAxisValues.size();
+
+ {
+ auto it = variableAxisValues.constBegin();
+ auto jt = other.variableAxisValues.constBegin();
+ for (; it != variableAxisValues.constEnd(); ++it, ++jt) {
+ if (it.key() != jt.key())
+ return jt.key() < it.key();
+ if (it.value() != jt.value())
+ return jt.value() < it.value();
+ }
+ }
+ }
+
return false;
}
};
@@ -157,10 +136,11 @@ inline size_t qHash(const QFontDef &fd, size_t seed = 0) noexcept
fd.styleStrategy,
fd.ignorePitch,
fd.fixedPitch,
- fd.family,
fd.families,
fd.styleName,
- fd.hintingPreference);
+ fd.hintingPreference,
+ fd.variableAxisValues.keys(),
+ fd.variableAxisValues.values());
}
class QFontEngineData
@@ -204,6 +184,7 @@ public:
QFixed letterSpacing;
QFixed wordSpacing;
+ QHash<QFont::Tag, quint32> features;
mutable QFontPrivate *scFont;
QFont smallCapsFont() const { return QFont(smallCapsFontPrivate()); }
@@ -218,6 +199,13 @@ public:
static void detachButKeepEngineData(QFont *font);
+ void setFeature(QFont::Tag tag, quint32 value);
+ void unsetFeature(QFont::Tag tag);
+
+ void setVariableAxis(QFont::Tag tag, float value);
+ void unsetVariableAxis(QFont::Tag tag);
+ bool hasVariableAxis(QFont::Tag tag, float value) const;
+
private:
QFontPrivate &operator=(const QFontPrivate &) { return *this; }
};
@@ -299,6 +287,7 @@ private:
uint total_cost, max_cost;
uint current_timestamp;
bool fast;
+ const bool autoClean;
int timer_id;
const int m_id;
};
diff --git a/src/gui/text/qfontdatabase.cpp b/src/gui/text/qfontdatabase.cpp
index 811d887134..d3a13d801b 100644
--- a/src/gui/text/qfontdatabase.cpp
+++ b/src/gui/text/qfontdatabase.cpp
@@ -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 "qfontdatabase.h"
#include "qfontdatabase_p.h"
@@ -62,8 +26,14 @@
#include <qtgui_tracepoints_p.h>
+#ifdef Q_OS_WIN
+#include <QtGui/private/qwindowsfontdatabasebase_p.h>
+#endif
+
QT_BEGIN_NAMESPACE
+using namespace Qt::StringLiterals;
+
Q_LOGGING_CATEGORY(lcFontDb, "qt.text.font.db")
Q_LOGGING_CATEGORY(lcFontMatch, "qt.text.font.match")
@@ -78,6 +48,11 @@ Q_AUTOTEST_EXPORT void qt_setQtEnableTestFont(bool value)
}
#endif
+Q_TRACE_POINT(qtgui, QFontDatabase_loadEngine, const QString &families, int pointSize);
+Q_TRACE_POINT(qtgui, QFontDatabasePrivate_addAppFont, const QString &fileName);
+Q_TRACE_POINT(qtgui, QFontDatabase_addApplicationFont, const QString &fileName);
+Q_TRACE_POINT(qtgui, QFontDatabase_load, const QString &family, int pointSize);
+
static int getFontWeight(const QString &weightString)
{
QString s = weightString.toLower();
@@ -90,41 +65,40 @@ static int getFontWeight(const QString &weightString)
//
// A simple string test is the cheapest, so let's do that first.
// Test in decreasing order of commonness
- if (s == QLatin1String("normal") || s == QLatin1String("regular"))
+ if (s == "normal"_L1 || s == "regular"_L1)
return QFont::Normal;
- if (s == QLatin1String("bold"))
+ if (s == "bold"_L1)
return QFont::Bold;
- if (s == QLatin1String("semibold") || s == QLatin1String("semi bold")
- || s == QLatin1String("demibold") || s == QLatin1String("demi bold"))
+ if (s == "semibold"_L1 || s == "semi bold"_L1 || s == "demibold"_L1 || s == "demi bold"_L1)
return QFont::DemiBold;
- if (s == QLatin1String("medium"))
+ if (s == "medium"_L1)
return QFont::Medium;
- if (s == QLatin1String("black"))
+ if (s == "black"_L1)
return QFont::Black;
- if (s == QLatin1String("light"))
+ if (s == "light"_L1)
return QFont::Light;
- if (s == QLatin1String("thin"))
+ if (s == "thin"_L1)
return QFont::Thin;
const QStringView s2 = QStringView{s}.mid(2);
- if (s.startsWith(QLatin1String("ex")) || s.startsWith(QLatin1String("ul"))) {
- if (s2 == QLatin1String("tralight") || s == QLatin1String("tra light"))
+ if (s.startsWith("ex"_L1) || s.startsWith("ul"_L1)) {
+ if (s2 == "tralight"_L1 || s == "tra light"_L1)
return QFont::ExtraLight;
- if (s2 == QLatin1String("trabold") || s2 == QLatin1String("tra bold"))
+ if (s2 == "trabold"_L1 || s2 == "tra bold"_L1)
return QFont::ExtraBold;
}
// Next up, let's see if contains() matches: slightly more expensive, but
// still fast enough.
- if (s.contains(QLatin1String("bold"))) {
- if (s.contains(QLatin1String("demi")))
+ if (s.contains("bold"_L1)) {
+ if (s.contains("demi"_L1))
return QFont::DemiBold;
return QFont::Bold;
}
- if (s.contains(QLatin1String("thin")))
+ if (s.contains("thin"_L1))
return QFont::Thin;
- if (s.contains(QLatin1String("light")))
+ if (s.contains("light"_L1))
return QFont::Light;
- if (s.contains(QLatin1String("black")))
+ if (s.contains("black"_L1))
return QFont::Black;
// Now, we perform string translations & comparisons with those.
@@ -181,9 +155,9 @@ QtFontStyle::Key::Key(const QString &styleString)
if (!styleString.isEmpty()) {
// First the straightforward no-translation checks, these are fast.
- if (styleString.contains(QLatin1String("Italic")))
+ if (styleString.contains("Italic"_L1))
style = QFont::StyleItalic;
- else if (styleString.contains(QLatin1String("Oblique")))
+ else if (styleString.contains("Oblique"_L1))
style = QFont::StyleOblique;
// Then the translation checks. These aren't as fast.
@@ -285,23 +259,40 @@ bool QtFontFamily::matchesFamilyName(const QString &familyName) const
return equalsCaseInsensitive(name, familyName) || aliases.contains(familyName, Qt::CaseInsensitive);
}
-void QtFontFamily::ensurePopulated()
+bool QtFontFamily::ensurePopulated()
{
if (populated)
- return;
+ return true;
QGuiApplicationPrivate::platformIntegration()->fontDatabase()->populateFamily(name);
- Q_ASSERT_X(populated, Q_FUNC_INFO, qPrintable(name));
+ return populated;
+}
+
+void QFontDatabasePrivate::clearFamilies()
+{
+ while (count--)
+ delete families[count];
+ ::free(families);
+ families = nullptr;
+ count = 0;
+
+ for (auto &font : applicationFonts)
+ font.properties.clear(); // Unpopulate
+
+ populated = false;
+ // don't clear the memory fonts!
}
void QFontDatabasePrivate::invalidate()
{
+ qCDebug(lcFontDb) << "Invalidating font database";
+
QFontCache::instance()->clear();
fallbacksCache.clear();
- free();
+ clearFamilies();
QGuiApplicationPrivate::platformIntegration()->fontDatabase()->invalidate();
- emit static_cast<QGuiApplication *>(QCoreApplication::instance())->fontDatabaseChanged();
+ emit qGuiApp->fontDatabaseChanged();
}
QtFontFamily *QFontDatabasePrivate::family(const QString &f, FamilyRequestFlags flags)
@@ -345,8 +336,10 @@ QtFontFamily *QFontDatabasePrivate::family(const QString &f, FamilyRequestFlags
fam = families[pos];
}
- if (fam && (flags & EnsurePopulated))
- fam->ensurePopulated();
+ if (fam && (flags & EnsurePopulated)) {
+ if (!fam->ensurePopulated())
+ return nullptr;
+ }
return fam;
}
@@ -436,11 +429,11 @@ Q_GUI_EXPORT QFontDatabase::WritingSystem qt_writing_system_for_script(int scrip
*/
static void parseFontName(const QString &name, QString &foundry, QString &family)
{
- int i = name.indexOf(QLatin1Char('['));
- int li = name.lastIndexOf(QLatin1Char(']'));
+ int i = name.indexOf(u'[');
+ int li = name.lastIndexOf(u']');
if (i >= 0 && li >= 0 && i < li) {
foundry = name.mid(i + 1, li - i - 1);
- if (i > 0 && name[i - 1] == QLatin1Char(' '))
+ if (i > 0 && name[i - 1] == u' ')
i--;
family = name.left(i);
} else {
@@ -451,7 +444,7 @@ static void parseFontName(const QString &name, QString &foundry, QString &family
// capitalize the family/foundry names
bool space = true;
QChar *s = family.data();
- int len = family.length();
+ int len = family.size();
while(len--) {
if (space) *s = s->toUpper();
space = s->isSpace();
@@ -460,7 +453,7 @@ static void parseFontName(const QString &name, QString &foundry, QString &family
space = true;
s = foundry.data();
- len = foundry.length();
+ len = foundry.size();
while(len--) {
if (space) *s = s->toUpper();
space = s->isSpace();
@@ -480,9 +473,11 @@ struct QtFontDesc
static void initFontDef(const QtFontDesc &desc, const QFontDef &request, QFontDef *fontDef, bool multi)
{
- fontDef->family = desc.family->name;
+ QString family;
+ family = desc.family->name;
if (! desc.foundry->name.isEmpty() && desc.family->count > 1)
- fontDef->family += QLatin1String(" [") + desc.foundry->name + QLatin1Char(']');
+ family += " ["_L1 + desc.foundry->name + u']';
+ fontDef->families = QStringList(family);
if (desc.style->smoothScalable
|| QGuiApplicationPrivate::platformIntegration()->fontDatabase()->fontsAlwaysScalable()
@@ -510,19 +505,6 @@ static QStringList familyList(const QFontDef &req)
QStringList family_list;
family_list << req.families;
- if (!req.family.isEmpty()) {
- const auto list = QStringView{req.family}.split(QLatin1Char(','));
- const int numFamilies = list.size();
- family_list.reserve(numFamilies);
- for (int i = 0; i < numFamilies; ++i) {
- auto str = list.at(i).trimmed();
- if ((str.startsWith(QLatin1Char('"')) && str.endsWith(QLatin1Char('"')))
- || (str.startsWith(QLatin1Char('\'')) && str.endsWith(QLatin1Char('\''))))
- str = str.mid(1, str.length() - 2);
- if (!family_list.contains(str))
- family_list << str.toString();
- }
- }
// append the substitute list for each family in family_list
for (int i = 0, size = family_list.size(); i < size; ++i)
family_list += QFont::substitutes(family_list.at(i));
@@ -530,17 +512,14 @@ static QStringList familyList(const QFontDef &req)
return family_list;
}
-Q_GLOBAL_STATIC(QFontDatabasePrivate, privateDb)
Q_GLOBAL_STATIC(QRecursiveMutex, fontDatabaseMutex)
// used in qguiapplication.cpp
void qt_cleanupFontDatabase()
{
- QFontDatabasePrivate *db = privateDb();
- if (db) {
- db->fallbacksCache.clear();
- db->free();
- }
+ auto *db = QFontDatabasePrivate::instance();
+ db->fallbacksCache.clear();
+ db->clearFamilies();
}
// used in qfont.cpp
@@ -551,7 +530,8 @@ QRecursiveMutex *qt_fontdatabase_mutex()
QFontDatabasePrivate *QFontDatabasePrivate::instance()
{
- return privateDb();
+ static QFontDatabasePrivate instance;
+ return &instance;
}
void qt_registerFont(const QString &familyName, const QString &stylename,
@@ -560,7 +540,7 @@ void qt_registerFont(const QString &familyName, const QString &stylename,
bool scalable, int pixelSize, bool fixedPitch,
const QSupportedWritingSystems &writingSystems, void *handle)
{
- QFontDatabasePrivate *d = privateDb();
+ auto *d = QFontDatabasePrivate::instance();
qCDebug(lcFontDb) << "Adding font: familyName" << familyName << "stylename" << stylename << "weight" << weight
<< "style" << style << "pixelSize" << pixelSize << "antialiased" << antialiased << "fixed" << fixedPitch;
QtFontStyle::Key styleKey;
@@ -591,8 +571,10 @@ void qt_registerFont(const QString &familyName, const QString &stylename,
void qt_registerFontFamily(const QString &familyName)
{
+ qCDebug(lcFontDb) << "Registering family" << familyName;
+
// Create uninitialized/unpopulated family
- privateDb()->family(familyName, QFontDatabasePrivate::EnsureCreated);
+ QFontDatabasePrivate::instance()->family(familyName, QFontDatabasePrivate::EnsureCreated);
}
void qt_registerAliasToFontFamily(const QString &familyName, const QString &alias)
@@ -600,7 +582,9 @@ void qt_registerAliasToFontFamily(const QString &familyName, const QString &alia
if (alias.isEmpty())
return;
- QFontDatabasePrivate *d = privateDb();
+ qCDebug(lcFontDb) << "Registering alias" << alias << "to family" << familyName;
+
+ auto *d = QFontDatabasePrivate::instance();
QtFontFamily *f = d->family(familyName, QFontDatabasePrivate::RequestFamily);
if (!f)
return;
@@ -614,7 +598,7 @@ void qt_registerAliasToFontFamily(const QString &familyName, const QString &alia
QString qt_resolveFontFamilyAlias(const QString &alias)
{
if (!alias.isEmpty()) {
- const QFontDatabasePrivate *d = privateDb();
+ const auto *d = QFontDatabasePrivate::instance();
for (int i = 0; i < d->count; ++i)
if (d->families[i]->matchesFamilyName(alias))
return d->families[i]->name;
@@ -624,7 +608,7 @@ QString qt_resolveFontFamilyAlias(const QString &alias)
bool qt_isFontFamilyPopulated(const QString &familyName)
{
- QFontDatabasePrivate *d = privateDb();
+ auto *d = QFontDatabasePrivate::instance();
QtFontFamily *f = d->family(familyName, QFontDatabasePrivate::RequestFamily);
return f != nullptr && f->populated;
}
@@ -648,7 +632,7 @@ QStringList QPlatformFontDatabase::fallbacksForFamily(const QString &family, QFo
if (writingSystem >= QFontDatabase::WritingSystemsCount)
writingSystem = QFontDatabase::Any;
- QFontDatabasePrivate *db = privateDb();
+ auto *db = QFontDatabasePrivate::instance();
for (int i = 0; i < db->count; ++i) {
QtFontFamily *f = db->families[i];
@@ -663,7 +647,7 @@ QStringList QPlatformFontDatabase::fallbacksForFamily(const QString &family, QFo
for (int k = 0; k < foundry->count; ++k) {
QString name = foundry->name.isEmpty()
? f->name
- : f->name + QLatin1String(" [") + foundry->name + QLatin1Char(']');
+ : f->name + " ["_L1 + foundry->name + u']';
if (style == foundry->styles[k]->key.style)
preferredFallbacks.append(name);
else
@@ -675,13 +659,10 @@ QStringList QPlatformFontDatabase::fallbacksForFamily(const QString &family, QFo
return preferredFallbacks + otherFallbacks;
}
-static void initializeDb();
-
static QStringList fallbacksForFamily(const QString &family, QFont::Style style, QFont::StyleHint styleHint, QChar::Script script)
{
- QFontDatabasePrivate *db = privateDb();
- if (!db->count)
- initializeDb();
+ QMutexLocker locker(fontDatabaseMutex());
+ auto *db = QFontDatabasePrivate::ensureFontDatabase();
const QtFontFallbacksCacheKey cacheKey = { family, style, styleHint, script };
@@ -689,7 +670,8 @@ static QStringList fallbacksForFamily(const QString &family, QFont::Style style,
return *fallbacks;
// make sure that the db has all fallback families
- QStringList retList = QGuiApplicationPrivate::platformIntegration()->fontDatabase()->fallbacksForFamily(family,style,styleHint,script);
+ QStringList userFallbacks = db->applicationFallbackFontFamilies.value(script == QChar::Script_Common ? QChar::Script_Latin : script);
+ QStringList retList = userFallbacks + QGuiApplicationPrivate::platformIntegration()->fontDatabase()->fallbacksForFamily(family,style,styleHint,script);
QStringList::iterator i;
for (i = retList.begin(); i != retList.end(); ++i) {
@@ -717,31 +699,7 @@ QStringList qt_fallbacksForFamily(const QString &family, QFont::Style style, QFo
return fallbacksForFamily(family, style, styleHint, script);
}
-static void registerFont(QFontDatabasePrivate::ApplicationFont *fnt);
-
-static void initializeDb()
-{
- QFontDatabasePrivate *db = privateDb();
-
- // init by asking for the platformfontdb for the first time or after invalidation
- if (!db->count) {
- QGuiApplicationPrivate::platformIntegration()->fontDatabase()->populateFontDatabase();
- for (int i = 0; i < db->applicationFonts.count(); i++) {
- if (!db->applicationFonts.at(i).properties.isEmpty())
- registerFont(&db->applicationFonts[i]);
- }
- }
-}
-
-static inline void load(const QString & = QString(), int = -1)
-{
- // Only initialize the database if it has been cleared or not initialized yet
- if (!privateDb()->count)
- initializeDb();
-}
-
-static
-QFontEngine *loadSingleEngine(int script,
+QFontEngine *QFontDatabasePrivate::loadSingleEngine(int script,
const QFontDef &request,
QtFontFamily *family, QtFontFoundry *foundry,
QtFontStyle *style, QtFontSize *size)
@@ -776,7 +734,7 @@ QFontEngine *loadSingleEngine(int script,
// Also check for OpenType tables when using complex scripts
if (Q_UNLIKELY(!engine->supportsScript(QChar::Script(script)))) {
qWarning(" OpenType support missing for \"%s\", script %d",
- qPrintable(def.family), script);
+ qPrintable(def.families.constFirst()), script);
return nullptr;
}
@@ -792,7 +750,7 @@ QFontEngine *loadSingleEngine(int script,
if (style->key.stretch != 0 && request.stretch != 0
&& (request.styleName.isEmpty() || request.styleName != style->styleName)) {
def.stretch = (request.stretch * 100 + style->key.stretch / 2) / style->key.stretch;
- } else {
+ } else if (request.stretch == QFont::AnyStretch) {
def.stretch = 100;
}
@@ -801,7 +759,7 @@ QFontEngine *loadSingleEngine(int script,
// Also check for OpenType tables when using complex scripts
if (!engine->supportsScript(QChar::Script(script))) {
qWarning(" OpenType support missing for \"%s\", script %d",
-+ qPrintable(def.family), script);
+ +qPrintable(def.families.constFirst()), script);
if (engine->ref.loadRelaxed() == 0)
delete engine;
return nullptr;
@@ -821,15 +779,14 @@ QFontEngine *loadSingleEngine(int script,
return engine;
}
-static
-QFontEngine *loadEngine(int script, const QFontDef &request,
+QFontEngine *QFontDatabasePrivate::loadEngine(int script, const QFontDef &request,
QtFontFamily *family, QtFontFoundry *foundry,
QtFontStyle *style, QtFontSize *size)
{
QFontEngine *engine = loadSingleEngine(script, request, family, foundry, style, size);
if (engine && !(request.styleStrategy & QFont::NoFontMerging) && !engine->symbol) {
- Q_TRACE(QFontDatabase_loadEngine, request.family, request.pointSize);
+ Q_TRACE(QFontDatabase_loadEngine, request.families.join(QLatin1Char(';')), request.pointSize);
QPlatformFontDatabase *pfdb = QGuiApplicationPrivate::platformIntegration()->fontDatabase();
QFontEngineMulti *pfMultiEngine = pfdb->fontEngineMulti(engine, QChar::Script(script));
@@ -868,11 +825,6 @@ QtFontStyle::~QtFontStyle()
free(pixelSizes);
}
-static void registerFont(QFontDatabasePrivate::ApplicationFont *fnt)
-{
- QGuiApplicationPrivate::platformIntegration()->fontDatabase()->addApplicationFont(fnt->data, fnt->fileName, fnt);
-}
-
static QtFontStyle *bestStyle(QtFontFoundry *foundry, const QtFontStyle::Key &styleKey,
const QString &styleName = QString())
{
@@ -913,11 +865,10 @@ static QtFontStyle *bestStyle(QtFontFoundry *foundry, const QtFontStyle::Key &st
}
-static
-unsigned int bestFoundry(int script, unsigned int score, int styleStrategy,
+unsigned int QFontDatabasePrivate::bestFoundry(int script, unsigned int score, int styleStrategy,
const QtFontFamily *family, const QString &foundry_name,
QtFontStyle::Key styleKey, int pixelSize, char pitch,
- QtFontDesc *desc, const QString &styleName = QString())
+ QtFontDesc *desc, const QString &styleName)
{
Q_UNUSED(script);
Q_UNUSED(pitch);
@@ -1060,9 +1011,9 @@ static bool matchFamilyName(const QString &familyName, QtFontFamily *f)
Tries to find the best match for a given request and family/foundry
*/
-static int match(int script, const QFontDef &request,
- const QString &family_name, const QString &foundry_name,
- QtFontDesc *desc, const QList<int> &blacklistedFamilies)
+int QFontDatabasePrivate::match(int script, const QFontDef &request, const QString &family_name,
+ const QString &foundry_name, QtFontDesc *desc, const QList<int> &blacklistedFamilies,
+ unsigned int *resultingScore)
{
int result = -1;
@@ -1074,16 +1025,18 @@ static int match(int script, const QFontDef &request,
char pitch = request.ignorePitch ? '*' : request.fixedPitch ? 'm' : 'p';
- qCDebug(lcFontMatch, "QFontDatabase::match\n"
+ qCDebug(lcFontMatch, "QFontDatabasePrivate::match\n"
" request:\n"
" family: %s [%s], script: %d\n"
+ " styleName: %s\n"
" weight: %d, style: %d\n"
" stretch: %d\n"
" pixelSize: %g\n"
" pitch: %c",
family_name.isEmpty() ? "-- first in script --" : family_name.toLatin1().constData(),
- foundry_name.isEmpty() ? "-- any --" : foundry_name.toLatin1().constData(),
- script, request.weight, request.style, request.stretch, request.pixelSize, pitch);
+ foundry_name.isEmpty() ? "-- any --" : foundry_name.toLatin1().constData(), script,
+ request.styleName.isEmpty() ? "-- any --" : request.styleName.toLatin1().constData(),
+ request.weight, request.style, request.stretch, request.pixelSize, pitch);
desc->family = nullptr;
desc->foundry = nullptr;
@@ -1092,13 +1045,14 @@ static int match(int script, const QFontDef &request,
unsigned int score = ~0u;
- load(family_name, script);
+ QMutexLocker locker(fontDatabaseMutex());
+ QFontDatabasePrivate::ensureFontDatabase();
auto writingSystem = qt_writing_system_for_script(script);
if (writingSystem >= QFontDatabase::WritingSystemsCount)
writingSystem = QFontDatabase::Any;
- QFontDatabasePrivate *db = privateDb();
+ auto *db = QFontDatabasePrivate::instance();
for (int x = 0; x < db->count; ++x) {
if (blacklistedFamilies.contains(x))
continue;
@@ -1107,8 +1061,8 @@ static int match(int script, const QFontDef &request,
if (!matchFamilyName(family_name, test.family))
continue;
-
- test.family->ensurePopulated();
+ if (!test.family->ensurePopulated())
+ continue;
// Check if family is supported in the script we want
if (writingSystem != QFontDatabase::Any && !familySupportsWritingSystem(test.family, writingSystem))
@@ -1136,6 +1090,10 @@ static int match(int script, const QFontDef &request,
if (newscore < 10) // xlfd instead of FT... just accept it
break;
}
+
+ if (resultingScore != nullptr)
+ *resultingScore = score;
+
return result;
}
@@ -1163,9 +1121,9 @@ static QString styleStringHelper(int weight, QFont::Style style)
}
if (style == QFont::StyleItalic)
- result += QLatin1Char(' ') + QCoreApplication::translate("QFontDatabase", "Italic");
+ result += u' ' + QCoreApplication::translate("QFontDatabase", "Italic");
else if (style == QFont::StyleOblique)
- result += QLatin1Char(' ') + QCoreApplication::translate("QFontDatabase", "Oblique");
+ result += u' ' + QCoreApplication::translate("QFontDatabase", "Oblique");
if (result.isEmpty())
result = QCoreApplication::translate("QFontDatabase", "Normal", "The Normal or Regular font weight");
@@ -1230,7 +1188,7 @@ QString QFontDatabase::styleString(const QFontInfo &fontInfo)
Use the styleString() to obtain a text version of a style.
- The QFontDatabase class also supports some static functions, for
+ The QFontDatabase class provides some helper functions, for
example, standardSizes(). You can retrieve the description of a
writing system using writingSystemName(), and a sample of
characters in a writing system with writingSystemSample().
@@ -1244,15 +1202,15 @@ QString QFontDatabase::styleString(const QFontInfo &fontInfo)
each combination of family and style, displaying this information
in a tree view.
- \sa QFont, QFontInfo, QFontMetrics, {Character Map Example}
+ \sa QFont, QFontInfo, QFontMetrics
*/
/*!
+ \fn QFontDatabase::QFontDatabase()
+ \deprecated [6.0] Call the class methods as static functions instead.
+
Creates a font database object.
*/
-QFontDatabase::QFontDatabase()
-{
-}
/*!
\enum QFontDatabase::WritingSystem
@@ -1308,19 +1266,86 @@ QFontDatabase::QFontDatabase()
*/
/*!
+ \class QFontDatabasePrivate
+ \internal
+
+ Singleton implementation of the public QFontDatabase APIs,
+ accessed through QFontDatabasePrivate::instance().
+
+ The database is organized in multiple levels:
+
+ - QFontDatabasePrivate::families
+ - QtFontFamily::foundries
+ - QtFontFoundry::styles
+ - QtFontStyle::sizes
+ - QtFontSize::pixelSize
+
+ The font database is the single source of truth when doing
+ font matching, so the database must be sufficiently filled
+ before attempting a match.
+
+ The database is populated (filled) from two sources:
+
+ 1. The system (platform's) view of the available fonts
+
+ Initiated via QFontDatabasePrivate::populateFontDatabase().
+
+ a. Can be registered lazily by family only, by calling
+ QPlatformFontDatabase::registerFontFamily(), and later
+ populated via QPlatformFontDatabase::populateFamily().
+
+ b. Or fully registered with all styles, by calling
+ QPlatformFontDatabase::registerFont().
+
+ 2. The fonts registered by the application via Qt APIs
+
+ Initiated via QFontDatabase::addApplicationFont() and
+ QFontDatabase::addApplicationFontFromData().
+
+ Application fonts are always fully registered when added.
+
+ Fonts can be added at any time, so the database may grow even
+ after QFontDatabasePrivate::populateFontDatabase() has been
+ completed.
+
+ The database does not support granular removal of fonts,
+ so if the system fonts change, or an application font is
+ removed, the font database will be cleared and then filled
+ from scratch, via QFontDatabasePrivate:invalidate() and
+ QFontDatabasePrivate::ensureFontDatabase().
+*/
+
+/*!
\internal
Initializes the font database if necessary and returns its
pointer. Mutex lock must be held when calling this function.
*/
-QFontDatabasePrivate *QFontDatabase::ensureFontDatabase()
+QFontDatabasePrivate *QFontDatabasePrivate::ensureFontDatabase()
{
- QFontDatabasePrivate *d = privateDb();
- if (d->count == 0) {
+ auto *d = QFontDatabasePrivate::instance();
+ if (!d->populated) {
+ // The font database may have been partially populated, but to ensure
+ // we can answer queries for any platform- or user-provided family we
+ // need to fully populate it now.
+ qCDebug(lcFontDb) << "Populating font database";
+
if (Q_UNLIKELY(qGuiApp == nullptr || QGuiApplicationPrivate::platformIntegration() == nullptr))
qFatal("QFontDatabase: Must construct a QGuiApplication before accessing QFontDatabase");
- initializeDb();
+ auto *platformFontDatabase = QGuiApplicationPrivate::platformIntegration()->fontDatabase();
+ platformFontDatabase->populateFontDatabase();
+
+ for (int i = 0; i < d->applicationFonts.size(); i++) {
+ auto *font = &d->applicationFonts[i];
+ if (!font->isNull() && !font->isPopulated())
+ platformFontDatabase->addApplicationFont(font->data, font->fileName, font);
+ }
+
+ // Note: Both application fonts and platform fonts may be added
+ // after this initial population, so the only thing we are tracking
+ // is whether we've done our part in ensuring a filled font database.
+ d->populated = true;
}
return d;
}
@@ -1335,16 +1360,15 @@ QFontDatabasePrivate *QFontDatabase::ensureFontDatabase()
QList<QFontDatabase::WritingSystem> QFontDatabase::writingSystems()
{
QMutexLocker locker(fontDatabaseMutex());
- QFontDatabasePrivate *d = ensureFontDatabase();
-
- QT_PREPEND_NAMESPACE(load)();
+ QFontDatabasePrivate *d = QFontDatabasePrivate::ensureFontDatabase();
quint64 writingSystemsFound = 0;
static_assert(WritingSystemsCount < 64);
for (int i = 0; i < d->count; ++i) {
QtFontFamily *family = d->families[i];
- family->ensurePopulated();
+ if (!family->ensurePopulated())
+ continue;
if (family->count == 0)
continue;
@@ -1379,9 +1403,7 @@ QList<QFontDatabase::WritingSystem> QFontDatabase::writingSystems(const QString
parseFontName(family, foundryName, familyName);
QMutexLocker locker(fontDatabaseMutex());
- QFontDatabasePrivate *d = ensureFontDatabase();
-
- QT_PREPEND_NAMESPACE(load)();
+ QFontDatabasePrivate *d = QFontDatabasePrivate::ensureFontDatabase();
QList<WritingSystem> list;
QtFontFamily *f = d->family(familyName);
@@ -1410,9 +1432,7 @@ QList<QFontDatabase::WritingSystem> QFontDatabase::writingSystems(const QString
QStringList QFontDatabase::families(WritingSystem writingSystem)
{
QMutexLocker locker(fontDatabaseMutex());
- QFontDatabasePrivate *d = ensureFontDatabase();
-
- QT_PREPEND_NAMESPACE(load)();
+ QFontDatabasePrivate *d = QFontDatabasePrivate::ensureFontDatabase();
QStringList flist;
for (int i = 0; i < d->count; i++) {
@@ -1420,7 +1440,8 @@ QStringList QFontDatabase::families(WritingSystem writingSystem)
if (f->populated && f->count == 0)
continue;
if (writingSystem != Any) {
- f->ensurePopulated();
+ if (!f->ensurePopulated())
+ continue;
if (f->writingSystems[writingSystem] != QtFontFamily::Supported)
continue;
}
@@ -1431,9 +1452,9 @@ QStringList QFontDatabase::families(WritingSystem writingSystem)
QString str = f->name;
QString foundry = f->foundries[j]->name;
if (!foundry.isEmpty()) {
- str += QLatin1String(" [");
+ str += " ["_L1;
str += foundry;
- str += QLatin1Char(']');
+ str += u']';
}
flist.append(str);
}
@@ -1455,9 +1476,7 @@ QStringList QFontDatabase::styles(const QString &family)
parseFontName(family, foundryName, familyName);
QMutexLocker locker(fontDatabaseMutex());
- QFontDatabasePrivate *d = ensureFontDatabase();
-
- QT_PREPEND_NAMESPACE(load)(familyName);
+ QFontDatabasePrivate *d = QFontDatabasePrivate::ensureFontDatabase();
QStringList l;
QtFontFamily *f = d->family(familyName);
@@ -1500,9 +1519,7 @@ bool QFontDatabase::isFixedPitch(const QString &family,
parseFontName(family, foundryName, familyName);
QMutexLocker locker(fontDatabaseMutex());
- QFontDatabasePrivate *d = ensureFontDatabase();
-
- QT_PREPEND_NAMESPACE(load)(familyName);
+ QFontDatabasePrivate *d = QFontDatabasePrivate::ensureFontDatabase();
QtFontFamily *f = d->family(familyName);
return (f && f->fixedPitch);
@@ -1526,9 +1543,7 @@ bool QFontDatabase::isBitmapScalable(const QString &family,
parseFontName(family, foundryName, familyName);
QMutexLocker locker(fontDatabaseMutex());
- QFontDatabasePrivate *d = ensureFontDatabase();
-
- QT_PREPEND_NAMESPACE(load)(familyName);
+ QFontDatabasePrivate *d = QFontDatabasePrivate::ensureFontDatabase();
QtFontFamily *f = d->family(familyName);
if (!f) return bitmapScalable;
@@ -1567,33 +1582,37 @@ bool QFontDatabase::isSmoothlyScalable(const QString &family, const QString &sty
parseFontName(family, foundryName, familyName);
QMutexLocker locker(fontDatabaseMutex());
- QFontDatabasePrivate *d = ensureFontDatabase();
-
- QT_PREPEND_NAMESPACE(load)(familyName);
+ QFontDatabasePrivate *d = QFontDatabasePrivate::ensureFontDatabase();
QtFontFamily *f = d->family(familyName);
if (!f) {
for (int i = 0; i < d->count; i++) {
if (d->families[i]->matchesFamilyName(familyName)) {
f = d->families[i];
- f->ensurePopulated();
- break;
+ if (f->ensurePopulated())
+ break;
}
}
}
if (!f) return smoothScalable;
- QtFontStyle::Key styleKey(style);
+ const QtFontStyle::Key styleKey(style);
for (int j = 0; j < f->count; j++) {
QtFontFoundry *foundry = f->foundries[j];
if (foundryName.isEmpty() || foundry->name.compare(foundryName, Qt::CaseInsensitive) == 0) {
- for (int k = 0; k < foundry->count; k++)
- if ((style.isEmpty() ||
- foundry->styles[k]->styleName == style ||
- foundry->styles[k]->key == styleKey) && foundry->styles[k]->smoothScalable) {
- smoothScalable = true;
+ for (int k = 0; k < foundry->count; k++) {
+ const QtFontStyle *fontStyle = foundry->styles[k];
+ smoothScalable =
+ fontStyle->smoothScalable
+ && ((style.isEmpty()
+ || fontStyle->styleName == style
+ || fontStyle->key == styleKey)
+ || (fontStyle->styleName.isEmpty()
+ && style == styleStringHelper(fontStyle->key.weight,
+ QFont::Style(fontStyle->key.style))));
+ if (smoothScalable)
goto end;
- }
+ }
}
}
end:
@@ -1633,9 +1652,7 @@ QList<int> QFontDatabase::pointSizes(const QString &family,
parseFontName(family, foundryName, familyName);
QMutexLocker locker(fontDatabaseMutex());
- QFontDatabasePrivate *d = ensureFontDatabase();
-
- QT_PREPEND_NAMESPACE(load)(familyName);
+ QFontDatabasePrivate *d = QFontDatabasePrivate::ensureFontDatabase();
QList<int> sizes;
@@ -1660,7 +1677,7 @@ QList<int> QFontDatabase::pointSizes(const QString &family,
const QtFontSize *size = style->pixelSizes + l;
if (size->pixelSize != 0 && size->pixelSize != SMOOTH_SCALABLE) {
- const uint pointSize = qRound(size->pixelSize * 72.0 / dpi);
+ const int pointSize = qRound(size->pixelSize * 72.0 / dpi);
if (! sizes.contains(pointSize))
sizes.append(pointSize);
}
@@ -1686,11 +1703,8 @@ QFont QFontDatabase::font(const QString &family, const QString &style,
{
QString familyName, foundryName;
parseFontName(family, foundryName, familyName);
-
QMutexLocker locker(fontDatabaseMutex());
- QFontDatabasePrivate *d = ensureFontDatabase();
-
- QT_PREPEND_NAMESPACE(load)(familyName);
+ QFontDatabasePrivate *d = QFontDatabasePrivate::ensureFontDatabase();
QtFontFoundry allStyles(foundryName);
QtFontFamily *f = d->family(familyName);
@@ -1710,7 +1724,7 @@ QFont QFontDatabase::font(const QString &family, const QString &style,
if (!s) // no styles found?
return QGuiApplication::font();
- QFont fnt(family, pointSize, s->key.weight);
+ QFont fnt(QStringList{family}, pointSize, s->key.weight);
fnt.setStyle((QFont::Style)s->key.style);
if (!s->styleName.isEmpty())
fnt.setStyleName(s->styleName);
@@ -1737,9 +1751,7 @@ QList<int> QFontDatabase::smoothSizes(const QString &family,
parseFontName(family, foundryName, familyName);
QMutexLocker locker(fontDatabaseMutex());
- QFontDatabasePrivate *d = ensureFontDatabase();
-
- QT_PREPEND_NAMESPACE(load)(familyName);
+ QFontDatabasePrivate *d = QFontDatabasePrivate::ensureFontDatabase();
QList<int> sizes;
@@ -1764,7 +1776,7 @@ QList<int> QFontDatabase::smoothSizes(const QString &family,
const QtFontSize *size = style->pixelSizes + l;
if (size->pixelSize != 0 && size->pixelSize != SMOOTH_SCALABLE) {
- const uint pointSize = qRound(size->pixelSize * 72.0 / dpi);
+ const int pointSize = qRound(size->pixelSize * 72.0 / dpi);
if (! sizes.contains(pointSize))
sizes.append(pointSize);
}
@@ -1803,9 +1815,7 @@ bool QFontDatabase::italic(const QString &family, const QString &style)
parseFontName(family, foundryName, familyName);
QMutexLocker locker(fontDatabaseMutex());
- QFontDatabasePrivate *d = ensureFontDatabase();
-
- QT_PREPEND_NAMESPACE(load)(familyName);
+ QFontDatabasePrivate *d = QFontDatabasePrivate::ensureFontDatabase();
QtFontFoundry allStyles(foundryName);
QtFontFamily *f = d->family(familyName);
@@ -1838,9 +1848,7 @@ bool QFontDatabase::bold(const QString &family,
parseFontName(family, foundryName, familyName);
QMutexLocker locker(fontDatabaseMutex());
- QFontDatabasePrivate *d = ensureFontDatabase();
-
- QT_PREPEND_NAMESPACE(load)(familyName);
+ QFontDatabasePrivate *d = QFontDatabasePrivate::ensureFontDatabase();
QtFontFoundry allStyles(foundryName);
QtFontFamily *f = d->family(familyName);
@@ -1875,9 +1883,7 @@ int QFontDatabase::weight(const QString &family,
parseFontName(family, foundryName, familyName);
QMutexLocker locker(fontDatabaseMutex());
- QFontDatabasePrivate *d = ensureFontDatabase();
-
- QT_PREPEND_NAMESPACE(load)(familyName);
+ QFontDatabasePrivate *d = QFontDatabasePrivate::ensureFontDatabase();
QtFontFoundry allStyles(foundryName);
QtFontFamily *f = d->family(familyName);
@@ -1903,8 +1909,20 @@ bool QFontDatabase::hasFamily(const QString &family)
{
QString parsedFamily, foundry;
parseFontName(family, foundry, parsedFamily);
- const QString familyAlias = resolveFontFamilyAlias(parsedFamily);
- return families().contains(familyAlias, Qt::CaseInsensitive);
+ const QString familyAlias = QFontDatabasePrivate::resolveFontFamilyAlias(parsedFamily);
+
+ QMutexLocker locker(fontDatabaseMutex());
+ QFontDatabasePrivate *d = QFontDatabasePrivate::ensureFontDatabase();
+
+ for (int i = 0; i < d->count; i++) {
+ QtFontFamily *f = d->families[i];
+ if (f->populated && f->count == 0)
+ continue;
+ if (familyAlias.compare(f->name, Qt::CaseInsensitive) == 0)
+ return true;
+ }
+
+ return false;
}
@@ -2043,106 +2061,99 @@ QString QFontDatabase::writingSystemName(WritingSystem writingSystem)
return QCoreApplication::translate("QFontDatabase", name);
}
-static QStringView writing_system_sample(QFontDatabase::WritingSystem writingSystem)
-{
- switch (writingSystem) {
- case QFontDatabase::Any:
- case QFontDatabase::Symbol:
- // show only ascii characters
- return u"AaBbzZ";
- case QFontDatabase::Latin:
- // This is cheating... we only show latin-1 characters so that we don't
- // end up loading lots of fonts - at least on X11...
- return u"Aa\x00C3\x00E1Zz";
- case QFontDatabase::Greek:
- return u"\x0393\x03B1\x03A9\x03C9";
- case QFontDatabase::Cyrillic:
- return u"\x0414\x0434\x0436\x044f";
- case QFontDatabase::Armenian:
- return u"\x053f\x054f\x056f\x057f";
- case QFontDatabase::Hebrew:
- return u"\x05D0\x05D1\x05D2\x05D3";
- case QFontDatabase::Arabic:
- return u"\x0623\x0628\x062C\x062F\x064A\x0629\x0020\x0639\x0631\x0628\x064A\x0629";
- case QFontDatabase::Syriac:
- return u"\x0715\x0725\x0716\x0726";
- case QFontDatabase::Thaana:
- return u"\x0784\x0794\x078c\x078d";
- case QFontDatabase::Devanagari:
- return u"\x0905\x0915\x0925\x0935";
- case QFontDatabase::Bengali:
- return u"\x0986\x0996\x09a6\x09b6";
- case QFontDatabase::Gurmukhi:
- return u"\x0a05\x0a15\x0a25\x0a35";
- case QFontDatabase::Gujarati:
- return u"\x0a85\x0a95\x0aa5\x0ab5";
- case QFontDatabase::Oriya:
- return u"\x0b06\x0b16\x0b2b\x0b36";
- case QFontDatabase::Tamil:
- return u"\x0b89\x0b99\x0ba9\x0bb9";
- case QFontDatabase::Telugu:
- return u"\x0c05\x0c15\x0c25\x0c35";
- case QFontDatabase::Kannada:
- return u"\x0c85\x0c95\x0ca5\x0cb5";
- case QFontDatabase::Malayalam:
- return u"\x0d05\x0d15\x0d25\x0d35";
- case QFontDatabase::Sinhala:
- return u"\x0d90\x0da0\x0db0\x0dc0";
- case QFontDatabase::Thai:
- return u"\x0e02\x0e12\x0e22\x0e32";
- case QFontDatabase::Lao:
- return u"\x0e8d\x0e9d\x0ead\x0ebd";
- case QFontDatabase::Tibetan:
- return u"\x0f00\x0f01\x0f02\x0f03";
- case QFontDatabase::Myanmar:
- return u"\x1000\x1001\x1002\x1003";
- case QFontDatabase::Georgian:
- return u"\x10a0\x10b0\x10c0\x10d0";
- case QFontDatabase::Khmer:
- return u"\x1780\x1790\x17b0\x17c0";
- case QFontDatabase::SimplifiedChinese:
- return u"\x4e2d\x6587\x8303\x4f8b";
- case QFontDatabase::TraditionalChinese:
- return u"\x4e2d\x6587\x7bc4\x4f8b";
- case QFontDatabase::Japanese:
- return u"\x30b5\x30f3\x30d7\x30eb\x3067\x3059";
- case QFontDatabase::Korean:
- return u"\xac00\xac11\xac1a\xac2f";
- case QFontDatabase::Vietnamese:
- return u"\x1ED7\x1ED9\x1ED1\x1ED3";
- case QFontDatabase::Ogham:
- return u"\x1681\x1682\x1683\x1684";
- case QFontDatabase::Runic:
- return u"\x16a0\x16a1\x16a2\x16a3";
- case QFontDatabase::Nko:
- return u"\x7ca\x7cb\x7cc\x7cd";
- default:
- return nullptr;
- }
-}
-
/*!
Returns a string with sample characters from \a writingSystem.
*/
QString QFontDatabase::writingSystemSample(WritingSystem writingSystem)
{
- return writing_system_sample(writingSystem).toString();
+ return [&]() -> QStringView {
+ switch (writingSystem) {
+ case QFontDatabase::Any:
+ case QFontDatabase::Symbol:
+ // show only ascii characters
+ return u"AaBbzZ";
+ case QFontDatabase::Latin:
+ // This is cheating... we only show latin-1 characters so that we don't
+ // end up loading lots of fonts - at least on X11...
+ return u"Aa\x00C3\x00E1Zz";
+ case QFontDatabase::Greek:
+ return u"\x0393\x03B1\x03A9\x03C9";
+ case QFontDatabase::Cyrillic:
+ return u"\x0414\x0434\x0436\x044f";
+ case QFontDatabase::Armenian:
+ return u"\x053f\x054f\x056f\x057f";
+ case QFontDatabase::Hebrew:
+ return u"\x05D0\x05D1\x05D2\x05D3";
+ case QFontDatabase::Arabic:
+ return u"\x0623\x0628\x062C\x062F\x064A\x0629\x0020\x0639\x0631\x0628\x064A\x0629";
+ case QFontDatabase::Syriac:
+ return u"\x0715\x0725\x0716\x0726";
+ case QFontDatabase::Thaana:
+ return u"\x0784\x0794\x078c\x078d";
+ case QFontDatabase::Devanagari:
+ return u"\x0905\x0915\x0925\x0935";
+ case QFontDatabase::Bengali:
+ return u"\x0986\x0996\x09a6\x09b6";
+ case QFontDatabase::Gurmukhi:
+ return u"\x0a05\x0a15\x0a25\x0a35";
+ case QFontDatabase::Gujarati:
+ return u"\x0a85\x0a95\x0aa5\x0ab5";
+ case QFontDatabase::Oriya:
+ return u"\x0b06\x0b16\x0b2b\x0b36";
+ case QFontDatabase::Tamil:
+ return u"\x0b89\x0b99\x0ba9\x0bb9";
+ case QFontDatabase::Telugu:
+ return u"\x0c05\x0c15\x0c25\x0c35";
+ case QFontDatabase::Kannada:
+ return u"\x0c85\x0c95\x0ca5\x0cb5";
+ case QFontDatabase::Malayalam:
+ return u"\x0d05\x0d15\x0d25\x0d35";
+ case QFontDatabase::Sinhala:
+ return u"\x0d90\x0da0\x0db0\x0dc0";
+ case QFontDatabase::Thai:
+ return u"\x0e02\x0e12\x0e22\x0e32";
+ case QFontDatabase::Lao:
+ return u"\x0e8d\x0e9d\x0ead\x0ebd";
+ case QFontDatabase::Tibetan:
+ return u"\x0f00\x0f01\x0f02\x0f03";
+ case QFontDatabase::Myanmar:
+ return u"\x1000\x1001\x1002\x1003";
+ case QFontDatabase::Georgian:
+ return u"\x10a0\x10b0\x10c0\x10d0";
+ case QFontDatabase::Khmer:
+ return u"\x1780\x1790\x17b0\x17c0";
+ case QFontDatabase::SimplifiedChinese:
+ return u"\x4e2d\x6587\x8303\x4f8b";
+ case QFontDatabase::TraditionalChinese:
+ return u"\x4e2d\x6587\x7bc4\x4f8b";
+ case QFontDatabase::Japanese:
+ return u"\x30b5\x30f3\x30d7\x30eb\x3067\x3059";
+ case QFontDatabase::Korean:
+ return u"\xac00\xac11\xac1a\xac2f";
+ case QFontDatabase::Vietnamese:
+ return u"\x1ED7\x1ED9\x1ED1\x1ED3";
+ case QFontDatabase::Ogham:
+ return u"\x1681\x1682\x1683\x1684";
+ case QFontDatabase::Runic:
+ return u"\x16a0\x16a1\x16a2\x16a3";
+ case QFontDatabase::Nko:
+ return u"\x7ca\x7cb\x7cc\x7cd";
+ default:
+ return nullptr;
+ }
+ }().toString();
}
-
-void QFontDatabase::parseFontName(const QString &name, QString &foundry, QString &family)
+void QFontDatabasePrivate::parseFontName(const QString &name, QString &foundry, QString &family)
{
QT_PREPEND_NAMESPACE(parseFontName)(name, foundry, family);
}
-void QFontDatabase::createDatabase()
-{ initializeDb(); }
-
// used from qfontengine_ft.cpp
Q_GUI_EXPORT QByteArray qt_fontdata_from_index(int index)
{
QMutexLocker locker(fontDatabaseMutex());
- return privateDb()->applicationFonts.value(index).data;
+ return QFontDatabasePrivate::instance()->applicationFonts.value(index).data;
}
int QFontDatabasePrivate::addAppFont(const QByteArray &fontData, const QString &fileName)
@@ -2154,35 +2165,36 @@ int QFontDatabasePrivate::addAppFont(const QByteArray &fontData, const QString &
Q_TRACE(QFontDatabasePrivate_addAppFont, fileName);
int i;
- for (i = 0; i < applicationFonts.count(); ++i)
- if (applicationFonts.at(i).properties.isEmpty())
+ for (i = 0; i < applicationFonts.size(); ++i)
+ if (applicationFonts.at(i).isNull())
break;
- if (i >= applicationFonts.count()) {
+ if (i >= applicationFonts.size()) {
applicationFonts.append(ApplicationFont());
- i = applicationFonts.count() - 1;
+ i = applicationFonts.size() - 1;
}
if (font.fileName.isEmpty() && !fontData.isEmpty())
- font.fileName = QLatin1String(":qmemoryfonts/") + QString::number(i);
+ font.fileName = ":qmemoryfonts/"_L1 + QString::number(i);
- bool wasEmpty = privateDb()->count == 0;
- registerFont(&font);
+ auto *platformFontDatabase = QGuiApplicationPrivate::platformIntegration()->fontDatabase();
+ platformFontDatabase->addApplicationFont(font.data, font.fileName, &font);
if (font.properties.isEmpty())
return -1;
applicationFonts[i] = font;
- // If the cache has not yet been populated, we need to reload the application font later
- if (wasEmpty)
- invalidate();
- else
- emit qApp->fontDatabaseChanged();
+ // The font cache may have cached lookups for the font that was now
+ // loaded, so it has to be flushed.
+ QFontCache::instance()->clear();
+
+ emit qApp->fontDatabaseChanged();
+
return i;
}
bool QFontDatabasePrivate::isApplicationFont(const QString &fileName)
{
- for (int i = 0; i < applicationFonts.count(); ++i)
+ for (int i = 0; i < applicationFonts.size(); ++i)
if (applicationFonts.at(i).fileName == fileName)
return true;
return false;
@@ -2196,13 +2208,12 @@ bool QFontDatabasePrivate::isApplicationFont(const QString &fileName)
with removeApplicationFont() or to retrieve the list of family names contained
in the font.
+//! [add-application-font-doc]
The function returns -1 if the font could not be loaded.
Currently only TrueType fonts, TrueType font collections, and OpenType fonts are
supported.
-
- \note Adding application fonts on Unix/X11 platforms without fontconfig is
- currently not supported.
+//! [add-application-font-doc]
\sa addApplicationFontFromData(), applicationFontFamilies(), removeApplicationFont()
*/
@@ -2219,7 +2230,7 @@ int QFontDatabase::addApplicationFont(const QString &fileName)
data = f.readAll();
}
QMutexLocker locker(fontDatabaseMutex());
- return privateDb()->addAppFont(data, fileName);
+ return QFontDatabasePrivate::instance()->addAppFont(data, fileName);
}
/*!
@@ -2230,19 +2241,14 @@ int QFontDatabase::addApplicationFont(const QString &fileName)
with removeApplicationFont() or to retrieve the list of family names contained
in the font.
- The function returns -1 if the font could not be loaded.
-
- Currently only TrueType fonts and TrueType font collections are supported.
-
- \b{Note:} Adding application fonts on Unix/X11 platforms without fontconfig is
- currently not supported.
+ \include qfontdatabase.cpp add-application-font-doc
\sa addApplicationFont(), applicationFontFamilies(), removeApplicationFont()
*/
int QFontDatabase::addApplicationFontFromData(const QByteArray &fontData)
{
QMutexLocker locker(fontDatabaseMutex());
- return privateDb()->addAppFont(fontData, QString() /* fileName */);
+ return QFontDatabasePrivate::instance()->addAppFont(fontData, QString() /* fileName */);
}
/*!
@@ -2256,11 +2262,12 @@ int QFontDatabase::addApplicationFontFromData(const QByteArray &fontData)
QStringList QFontDatabase::applicationFontFamilies(int id)
{
QMutexLocker locker(fontDatabaseMutex());
+ auto *d = QFontDatabasePrivate::instance();
QStringList ret;
- ret.reserve(privateDb()->applicationFonts.value(id).properties.size());
+ ret.reserve(d->applicationFonts.value(id).properties.size());
- for (const auto &properties : privateDb()->applicationFonts.value(id).properties)
+ for (const auto &properties : d->applicationFonts.value(id).properties)
ret.append(properties.familyName);
return ret;
@@ -2318,8 +2325,8 @@ bool QFontDatabase::removeApplicationFont(int handle)
{
QMutexLocker locker(fontDatabaseMutex());
- QFontDatabasePrivate *db = privateDb();
- if (handle < 0 || handle >= db->applicationFonts.count())
+ auto *db = QFontDatabasePrivate::instance();
+ if (handle < 0 || handle >= db->applicationFonts.size())
return false;
db->applicationFonts[handle] = QFontDatabasePrivate::ApplicationFont();
@@ -2344,7 +2351,7 @@ bool QFontDatabase::removeAllApplicationFonts()
{
QMutexLocker locker(fontDatabaseMutex());
- QFontDatabasePrivate *db = privateDb();
+ auto *db = QFontDatabasePrivate::instance();
if (!db || db->applicationFonts.isEmpty())
return false;
@@ -2354,20 +2361,148 @@ bool QFontDatabase::removeAllApplicationFonts()
}
/*!
- \internal
+ \since 6.8
+
+ Adds \a familyName as an application-defined fallback font for \a script.
+
+ When Qt encounters characters that are not supported by the selected font, it will search
+ through a list of fallback fonts to find a match for them. This ensures that combining multiple
+ scripts in a single string is possible, even if the main font does not support them.
+
+ The list of fallback fonts is selected based on the script of the string as well as other
+ conditions, such as system language.
+
+ While the system fallback list is usually sufficient, there are cases where it is useful
+ to override the default behavior. One such case is for using application fonts as fallback to
+ ensure cross-platform consistency.
+
+ In another case the application may be written in a script with regional differences and want
+ to run it untranslated in multiple regions. In this case, it might be useful to override the
+ local region's fallback with one that matches the language of the application.
+
+ By passing \a familyName to addApplicationFallbackFontFamily(), this will become the preferred
+ family when matching missing characters from \a script. The \a script must be a valid script
+ (\c QChar::Script_Latin or higher). When adding multiple fonts for the same script, they will
+ be prioritized in reverse order, so that the last family added will be checked first and so
+ on.
+
+ \sa setApplicationFallbackFontFamilies(), removeApplicationFallbackFontFamily(), applicationFallbackFontFamilies()
*/
-QFontEngine *QFontDatabase::findFont(const QFontDef &request, int script)
+void QFontDatabase::addApplicationFallbackFontFamily(QChar::Script script, const QString &familyName)
{
QMutexLocker locker(fontDatabaseMutex());
- if (!privateDb()->count)
- initializeDb();
+ if (script < QChar::Script_Latin) {
+ qCWarning(lcFontDb) << "Invalid script passed to addApplicationFallbackFontFamily:" << script;
+ return;
+ }
+
+ auto *db = QFontDatabasePrivate::instance();
+ auto it = db->applicationFallbackFontFamilies.find(script);
+ if (it == db->applicationFallbackFontFamilies.end())
+ it = db->applicationFallbackFontFamilies.insert(script, QStringList{});
+
+ it->prepend(familyName);
+ db->fallbacksCache.clear();
+}
+
+/*!
+ \since 6.8
+
+ Removes \a familyName from the list of application-defined fallback fonts for \a script,
+ provided that it has previously been added with \l{addApplicationFallbackFontFamily()}.
+
+ Returns true if the family name was in the list and false if it was not.
+
+ \sa addApplicationFallbackFontFamily(), setApplicationFallbackFontFamilies(), applicationFallbackFontFamilies()
+*/
+bool QFontDatabase::removeApplicationFallbackFontFamily(QChar::Script script, const QString &familyName)
+{
+ QMutexLocker locker(fontDatabaseMutex());
+
+ auto *db = QFontDatabasePrivate::instance();
+ auto it = db->applicationFallbackFontFamilies.find(script);
+ if (it != db->applicationFallbackFontFamilies.end()) {
+ if (it->removeAll(familyName) > 0) {
+ if (it->isEmpty())
+ it = db->applicationFallbackFontFamilies.erase(it);
+ QFontCache::instance()->clear();
+ db->fallbacksCache.clear();
+ return true;
+ }
+ }
+
+ return false;
+}
+
+/*!
+ \since 6.8
+
+ Sets the list of application-defined fallback fonts for \a script to \a familyNames.
+
+ When Qt encounters a character in \a script which is not supported by the current font, it will
+ check the families in \a familyNames, in order from first to last, until it finds a match. See
+ \l{addApplicationFallbackFontFamily()} for more details.
+
+ This function overwrites the current list of application-defined fallback fonts for \a script.
+
+ \sa addApplicationFallbackFontFamily(), removeApplicationFallbackFontFamily(), applicationFallbackFontFamilies()
+*/
+void QFontDatabase::setApplicationFallbackFontFamilies(QChar::Script script, const QStringList &familyNames)
+{
+ QMutexLocker locker(fontDatabaseMutex());
+
+ if (script < QChar::Script_Latin) {
+ qCWarning(lcFontDb) << "Invalid script passed to setApplicationFallbackFontFamilies:" << script;
+ return;
+ }
+
+ auto *db = QFontDatabasePrivate::instance();
+ db->applicationFallbackFontFamilies[script] = familyNames;
+
+ QFontCache::instance()->clear();
+ db->fallbacksCache.clear();
+}
+
+/*!
+ \since 6.8
+
+ Returns the list of application-defined fallback font families previously added for \a script
+ by the \l{addApplicationFallbackFontFamily()} function.
+
+ \sa setApplicationFallbackFontFamilies(), addApplicationFallbackFontFamily(), removeApplicationFallbackFontFamily()
+*/
+QStringList QFontDatabase::applicationFallbackFontFamilies(QChar::Script script)
+{
+ QMutexLocker locker(fontDatabaseMutex());
+
+ auto *db = QFontDatabasePrivate::instance();
+ return db->applicationFallbackFontFamilies.value(script);
+}
+
+/*!
+ \internal
+*/
+QFontEngine *QFontDatabasePrivate::findFont(const QFontDef &req,
+ int script,
+ bool preferScriptOverFamily)
+{
+ QMutexLocker locker(fontDatabaseMutex());
+ ensureFontDatabase();
QFontEngine *engine;
+#ifdef Q_OS_WIN
+ const QFontDef request = static_cast<QWindowsFontDatabaseBase *>(
+ QGuiApplicationPrivate::platformIntegration()->fontDatabase())
+ ->sanitizeRequest(req);
+#else
+ const QFontDef &request = req;
+#endif
+
#if defined(QT_BUILD_INTERNAL)
// For testing purpose only, emulates an exact-matching monospace font
- if (qt_enable_test_font && request.family == QLatin1String("__Qt__Box__Engine__")) {
+ if (qt_enable_test_font && request.families.first() == "__Qt__Box__Engine__"_L1) {
engine = new QTestFontEngine(request.pixelSize);
engine->fontDef = request;
return engine;
@@ -2387,22 +2522,36 @@ QFontEngine *QFontDatabase::findFont(const QFontDef &request, int script)
return engine;
}
+ if (request.pixelSize > 0xffff) {
+ // Stop absurd requests reaching the engines; pixel size is assumed to fit ushort
+ qCDebug(lcFontMatch, "Rejecting request for pixel size %g2, returning box engine", double(request.pixelSize));
+ return new QFontEngineBox(32); // not request.pixelSize, to avoid overflow/DOS
+ }
+
QString family_name, foundry_name;
- const QString requestFamily = request.families.size() > 0 ? request.families.at(0) : request.family;
+ const QString requestFamily = request.families.at(0);
parseFontName(requestFamily, foundry_name, family_name);
QtFontDesc desc;
QList<int> blackListed;
- int index = match(multi ? QChar::Script_Common : script, request, family_name, foundry_name, &desc, blackListed);
- if (index < 0 && QGuiApplicationPrivate::platformIntegration()->fontDatabase()->populateFamilyAliases(family_name)) {
- // We populated familiy aliases (e.g. localized families), so try again
+ unsigned int score = UINT_MAX;
+ int index = match(multi ? QChar::Script_Common : script, request, family_name, foundry_name, &desc, blackListed, &score);
+ if (score > 0 && QGuiApplicationPrivate::platformIntegration()->fontDatabase()->populateFamilyAliases(family_name)) {
+ // We populated family aliases (e.g. localized families), so try again
index = match(multi ? QChar::Script_Common : script, request, family_name, foundry_name, &desc, blackListed);
}
+
+ // If we do not find a match and NoFontMerging is set, use the requested font even if it does
+ // not support the script.
+ //
+ // (we do this at the end to prefer foundries that support the script if they exist)
+ if (index < 0 && !multi && !preferScriptOverFamily)
+ index = match(QChar::Script_Common, request, family_name, foundry_name, &desc, blackListed);
+
if (index >= 0) {
QFontDef fontDef = request;
-
// Don't pass empty family names to the platform font database, since it will then invoke its own matching
// and we will be out of sync with the matched font.
- if (fontDef.families.isEmpty() && fontDef.family.isEmpty())
+ if (fontDef.families.isEmpty())
fontDef.families = QStringList(desc.family->name);
engine = loadEngine(script, fontDef, desc.family, desc.foundry, desc.style, desc.size);
@@ -2431,18 +2580,17 @@ QFontEngine *QFontDatabase::findFont(const QFontDef &request, int script)
for (int i = 0; !engine && i < fallbacks.size(); i++) {
QFontDef def = request;
- def.families.clear();
- def.family = fallbacks.at(i);
+ def.families = QStringList(fallbacks.at(i));
QFontCache::Key key(def, script, multi ? 1 : 0);
engine = fontCache->findEngine(key);
if (!engine) {
QtFontDesc desc;
do {
- index = match(multi ? QChar::Script_Common : script, def, def.family, QLatin1String(""), &desc, blackListed);
+ index = match(multi ? QChar::Script_Common : script, def, def.families.constFirst(), ""_L1, &desc, blackListed);
if (index >= 0) {
QFontDef loadDef = def;
- if (loadDef.families.isEmpty() && loadDef.family.isEmpty())
- loadDef.family = desc.family->name;
+ if (loadDef.families.isEmpty())
+ loadDef.families = QStringList(desc.family->name);
engine = loadEngine(script, loadDef, desc.family, desc.foundry, desc.style, desc.size);
if (engine)
initFontDef(desc, loadDef, &engine->fontDef, multi);
@@ -2463,7 +2611,7 @@ QFontEngine *QFontDatabase::findFont(const QFontDef &request, int script)
return engine;
}
-void QFontDatabase::load(const QFontPrivate *d, int script)
+void QFontDatabasePrivate::load(const QFontPrivate *d, int script)
{
QFontDef req = d->request;
@@ -2482,8 +2630,6 @@ void QFontDatabase::load(const QFontPrivate *d, int script)
// look for the requested font in the engine data cache
// note: fallBackFamilies are not respected in the EngineData cache key;
// join them with the primary selection family to avoid cache misses
- if (!d->request.family.isEmpty())
- req.family = fallBackFamilies.join(QLatin1Char(','));
if (!d->request.families.isEmpty())
req.families = fallBackFamilies;
@@ -2502,7 +2648,7 @@ void QFontDatabase::load(const QFontPrivate *d, int script)
QFontEngine *fe = nullptr;
- Q_TRACE(QFontDatabase_load, req.family, req.pointSize);
+ Q_TRACE(QFontDatabase_load, req.families.join(QLatin1Char(';')), req.pointSize);
req.fallBackFamilies = fallBackFamilies;
if (!req.fallBackFamilies.isEmpty())
@@ -2516,9 +2662,12 @@ void QFontDatabase::load(const QFontPrivate *d, int script)
family_list << req.families.at(0);
// add the default family
- QString defaultFamily = QGuiApplication::font().family();
- if (! family_list.contains(defaultFamily))
- family_list << defaultFamily;
+ const auto families = QGuiApplication::font().families();
+ if (!families.isEmpty()) {
+ QString defaultFamily = families.first();
+ if (! family_list.contains(defaultFamily))
+ family_list << defaultFamily;
+ }
}
@@ -2529,7 +2678,7 @@ void QFontDatabase::load(const QFontPrivate *d, int script)
for (; !fe && it != end; ++it) {
req.families = QStringList(*it);
- fe = QFontDatabase::findFont(req, script);
+ fe = QFontDatabasePrivate::findFont(req, script);
if (fe) {
if (fe->type() == QFontEngine::Box && !req.families.at(0).isEmpty()) {
if (fe->ref.loadRelaxed() == 0)
@@ -2559,7 +2708,7 @@ void QFontDatabase::load(const QFontPrivate *d, int script)
}
}
-QString QFontDatabase::resolveFontFamilyAlias(const QString &family)
+QString QFontDatabasePrivate::resolveFontFamilyAlias(const QString &family)
{
return QGuiApplicationPrivate::platformIntegration()->fontDatabase()->resolveFontFamilyAlias(family);
}
@@ -2572,7 +2721,7 @@ Q_GUI_EXPORT QStringList qt_sort_families_by_writing_system(QChar::Script script
return families;
}
- QFontDatabasePrivate *db = privateDb();
+ auto *db = QFontDatabasePrivate::instance();
QMultiMap<uint, QString> supported;
for (int i = 0; i < families.size(); ++i) {
const QString &family = families.at(i);
@@ -2581,8 +2730,8 @@ Q_GUI_EXPORT QStringList qt_sort_families_by_writing_system(QChar::Script script
for (int x = 0; x < db->count; ++x) {
if (Q_UNLIKELY(matchFamilyName(family, db->families[x]))) {
testFamily = db->families[x];
- testFamily->ensurePopulated();
- break;
+ if (testFamily->ensurePopulated())
+ break;
}
}
@@ -2600,3 +2749,5 @@ Q_GUI_EXPORT QStringList qt_sort_families_by_writing_system(QChar::Script script
QT_END_NAMESPACE
+#include "moc_qfontdatabase.cpp"
+
diff --git a/src/gui/text/qfontdatabase.h b/src/gui/text/qfontdatabase.h
index 67f06b8485..91a534265e 100644
--- a/src/gui/text/qfontdatabase.h
+++ b/src/gui/text/qfontdatabase.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 QFONTDATABASE_H
#define QFONTDATABASE_H
@@ -51,8 +15,6 @@ QT_BEGIN_NAMESPACE
struct QFontDef;
class QFontEngine;
-class QFontDatabasePrivate;
-
class Q_GUI_EXPORT QFontDatabase
{
Q_GADGET
@@ -111,7 +73,11 @@ public:
static QList<int> standardSizes();
- QFontDatabase();
+#if QT_DEPRECATED_SINCE(6, 0) && !defined(QT_BUILD_GUI_LIB)
+ QT_DEPRECATED_VERSION_X_6_0("Call the static functions instead") explicit QFontDatabase() = default;
+#else
+ QFontDatabase() = delete;
+#endif
static QList<WritingSystem> writingSystems();
static QList<WritingSystem> writingSystems(const QString &family);
@@ -146,22 +112,12 @@ public:
static bool removeApplicationFont(int id);
static bool removeAllApplicationFonts();
- static QFont systemFont(SystemFont type);
+ static void addApplicationFallbackFontFamily(QChar::Script script, const QString &familyName);
+ static bool removeApplicationFallbackFontFamily(QChar::Script script, const QString &familyName);
+ static void setApplicationFallbackFontFamilies(QChar::Script, const QStringList &familyNames);
+ static QStringList applicationFallbackFontFamilies(QChar::Script script);
-private:
- static void createDatabase();
- static void parseFontName(const QString &name, QString &foundry, QString &family);
- static QString resolveFontFamilyAlias(const QString &family);
- static QFontEngine *findFont(const QFontDef &request, int script /* QChar::Script */);
- static void load(const QFontPrivate *d, int script /* QChar::Script */);
- static QFontDatabasePrivate *ensureFontDatabase();
-
- friend struct QFontDef;
- friend class QFontPrivate;
- friend class QFontDialog;
- friend class QFontDialogPrivate;
- friend class QFontEngineMulti;
- friend class QRawFont;
+ static QFont systemFont(SystemFont type);
};
QT_END_NAMESPACE
diff --git a/src/gui/text/qfontdatabase_p.h b/src/gui/text/qfontdatabase_p.h
index 6b7d9c2f47..38e1b4ad20 100644
--- a/src/gui/text/qfontdatabase_p.h
+++ b/src/gui/text/qfontdatabase_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2020 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) 2020 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 QFONTDATABASE_P_H
#define QFONTDATABASE_P_H
@@ -54,9 +18,12 @@
#include <QtCore/qcache.h>
#include <QtGui/qfontdatabase.h>
+#include <QtCore/private/qglobal_p.h>
QT_BEGIN_NAMESPACE
+struct QtFontDesc;
+
struct QtFontFallbacksCacheKey
{
QString family;
@@ -207,7 +174,7 @@ struct Q_GUI_EXPORT QtFontFamily
bool matchesFamilyName(const QString &familyName) const;
QtFontFoundry *foundry(const QString &f, bool = false);
- void ensurePopulated();
+ bool ensurePopulated();
};
class Q_GUI_EXPORT QFontDatabasePrivate
@@ -220,9 +187,11 @@ public:
{ }
~QFontDatabasePrivate() {
- free();
+ clearFamilies();
}
+ void clearFamilies();
+
enum FamilyRequestFlags {
RequestFamily = 0,
EnsureCreated,
@@ -230,23 +199,25 @@ public:
};
QtFontFamily *family(const QString &f, FamilyRequestFlags flags = EnsurePopulated);
- void free() {
- while (count--)
- delete families[count];
- ::free(families);
- families = nullptr;
- count = 0;
- // don't clear the memory fonts!
- }
int count;
QtFontFamily **families;
+ bool populated = false;
+
+ QHash<QChar::Script, QStringList> applicationFallbackFontFamilies;
QCache<QtFontFallbacksCacheKey, QStringList> fallbacksCache;
struct ApplicationFont {
QString fileName;
+
+ // Note: The data may be implicitly shared throughout the
+ // font database and platform font database, so be careful
+ // to never detach when accessing this member!
QByteArray data;
+ bool isNull() const { return fileName.isEmpty(); }
+ bool isPopulated() const { return !properties.isEmpty(); }
+
struct Properties {
QString familyName;
QString styleName;
@@ -263,9 +234,36 @@ public:
static QFontDatabasePrivate *instance();
+ static void parseFontName(const QString &name, QString &foundry, QString &family);
+ static QString resolveFontFamilyAlias(const QString &family);
+ static QFontEngine *findFont(const QFontDef &request,
+ int script /* QChar::Script */,
+ bool preferScriptOverFamily = false);
+ static void load(const QFontPrivate *d, int script /* QChar::Script */);
+ static QFontDatabasePrivate *ensureFontDatabase();
+
void invalidate();
+
+private:
+ static int match(int script, const QFontDef &request, const QString &family_name,
+ const QString &foundry_name, QtFontDesc *desc, const QList<int> &blacklistedFamilies,
+ unsigned int *resultingScore = nullptr);
+
+ static unsigned int bestFoundry(int script, unsigned int score, int styleStrategy,
+ const QtFontFamily *family, const QString &foundry_name,
+ QtFontStyle::Key styleKey, int pixelSize, char pitch,
+ QtFontDesc *desc, const QString &styleName = QString());
+
+ static QFontEngine *loadSingleEngine(int script, const QFontDef &request,
+ QtFontFamily *family, QtFontFoundry *foundry,
+ QtFontStyle *style, QtFontSize *size);
+
+ static QFontEngine *loadEngine(int script, const QFontDef &request,
+ QtFontFamily *family, QtFontFoundry *foundry,
+ QtFontStyle *style, QtFontSize *size);
+
};
-Q_DECLARE_TYPEINFO(QFontDatabasePrivate::ApplicationFont, Q_MOVABLE_TYPE);
+Q_DECLARE_TYPEINFO(QFontDatabasePrivate::ApplicationFont, Q_RELOCATABLE_TYPE);
QT_END_NAMESPACE
diff --git a/src/gui/text/qfontengine.cpp b/src/gui/text/qfontengine.cpp
index 07b19d4d19..4e78aaac2e 100644
--- a/src/gui/text/qfontengine.cpp
+++ b/src/gui/text/qfontengine.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2018 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) 2021 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 <qdebug.h>
#include <private/qfontengine_p.h>
@@ -49,6 +13,7 @@
#include "qpainter.h"
#include "qpainterpath.h"
#include "qvarlengtharray.h"
+#include "qtextengine_p.h"
#include <qmath.h>
#include <qendian.h>
#include <private/qstringiterator_p.h>
@@ -90,17 +55,6 @@ static inline bool qSafeFromBigEndian(const uchar *source, const uchar *end, T *
return true;
}
-// Harfbuzz helper functions
-
-#if QT_CONFIG(harfbuzz)
-Q_GLOBAL_STATIC_WITH_ARGS(bool, useHarfbuzzNG,(qgetenv("QT_HARFBUZZ") != "old"))
-
-bool qt_useHarfbuzzNG()
-{
- return *useHarfbuzzNG();
-}
-#endif
-
int QFontEngine::getPointInOutline(glyph_t glyph, int flags, quint32 point, QFixed *xpos, QFixed *ypos, quint32 *nPoints)
{
Q_UNUSED(glyph);
@@ -180,7 +134,7 @@ QFontEngine::~QFontEngine()
QFixed QFontEngine::lineThickness() const
{
// ad hoc algorithm
- int score = fontDef.weight * fontDef.pixelSize;
+ int score = fontDef.weight * fontDef.pixelSize / 10;
int lw = score / 700;
// looks better with thicker line for small pointsizes
@@ -199,20 +153,20 @@ void *QFontEngine::harfbuzzFont() const
{
Q_ASSERT(type() != QFontEngine::Multi);
#if QT_CONFIG(harfbuzz)
- if (qt_useHarfbuzzNG())
- return hb_qt_font_get_for_engine(const_cast<QFontEngine *>(this));
-#endif
+ return hb_qt_font_get_for_engine(const_cast<QFontEngine *>(this));
+#else
return nullptr;
+#endif
}
void *QFontEngine::harfbuzzFace() const
{
Q_ASSERT(type() != QFontEngine::Multi);
#if QT_CONFIG(harfbuzz)
- if (qt_useHarfbuzzNG())
- return hb_qt_face_get_for_engine(const_cast<QFontEngine *>(this));
-#endif
+ return hb_qt_face_get_for_engine(const_cast<QFontEngine *>(this));
+#else
return nullptr;
+#endif
}
bool QFontEngine::supportsScript(QChar::Script script) const
@@ -227,23 +181,23 @@ bool QFontEngine::supportsScript(QChar::Script script) const
return true;
#if QT_CONFIG(harfbuzz)
- if (qt_useHarfbuzzNG()) {
- // in AAT fonts, 'gsub' table is effectively replaced by 'mort'/'morx' table
- uint len;
- if (getSfntTableData(MAKE_TAG('m','o','r','t'), nullptr, &len) || getSfntTableData(MAKE_TAG('m','o','r','x'), nullptr, &len))
- return true;
+ // in AAT fonts, 'gsub' table is effectively replaced by 'mort'/'morx' table
+ uint lenMort = 0, lenMorx = 0;
+ if (getSfntTableData(QFont::Tag("mort").value(), nullptr, &lenMort)
+ || getSfntTableData(QFont::Tag("morx").value(), nullptr, &lenMorx)) {
+ return true;
+ }
- if (hb_face_t *face = hb_qt_face_get_for_engine(const_cast<QFontEngine *>(this))) {
- unsigned int script_count = HB_OT_MAX_TAGS_PER_SCRIPT;
- hb_tag_t script_tags[HB_OT_MAX_TAGS_PER_SCRIPT];
+ if (hb_face_t *face = hb_qt_face_get_for_engine(const_cast<QFontEngine *>(this))) {
+ unsigned int script_count = HB_OT_MAX_TAGS_PER_SCRIPT;
+ hb_tag_t script_tags[HB_OT_MAX_TAGS_PER_SCRIPT];
- hb_ot_tags_from_script_and_language(hb_qt_script_to_script(script), HB_LANGUAGE_INVALID,
- &script_count, script_tags,
- nullptr, nullptr);
+ hb_ot_tags_from_script_and_language(hb_qt_script_to_script(script), HB_LANGUAGE_INVALID,
+ &script_count, script_tags,
+ nullptr, nullptr);
- if (hb_ot_layout_table_select_script(face, HB_OT_TAG_GSUB, script_count, script_tags, nullptr, nullptr))
- return true;
- }
+ if (hb_ot_layout_table_select_script(face, HB_OT_TAG_GSUB, script_count, script_tags, nullptr, nullptr))
+ return true;
}
#endif
return false;
@@ -430,11 +384,16 @@ void QFontEngine::getGlyphBearings(glyph_t glyph, qreal *leftBearing, qreal *rig
bool QFontEngine::processHheaTable() const
{
- QByteArray hhea = getSfntTable(MAKE_TAG('h', 'h', 'e', 'a'));
+ QByteArray hhea = getSfntTable(QFont::Tag("hhea").value());
if (hhea.size() >= 10) {
- qint16 ascent = qFromBigEndian<qint16>(hhea.constData() + 4);
- qint16 descent = qFromBigEndian<qint16>(hhea.constData() + 6);
- qint16 leading = qFromBigEndian<qint16>(hhea.constData() + 8);
+ auto ptr = hhea.constData();
+ qint16 ascent = qFromBigEndian<qint16>(ptr + 4);
+ qint16 descent = qFromBigEndian<qint16>(ptr + 6);
+ qint16 leading = qFromBigEndian<qint16>(ptr + 8);
+
+ // Some fonts may have invalid HHEA data. We detect this and bail out.
+ if (ascent == 0 && descent == 0)
+ return false;
QFixed unitsPerEm = emSquareSize();
m_ascent = QFixed::fromReal(ascent * fontDef.pixelSize) / unitsPerEm;
@@ -450,13 +409,22 @@ bool QFontEngine::processHheaTable() const
void QFontEngine::initializeHeightMetrics() const
{
- bool hasEmbeddedBitmaps = !getSfntTable(MAKE_TAG('E', 'B', 'L', 'C')).isEmpty() || !getSfntTable(MAKE_TAG('C', 'B', 'L', 'C')).isEmpty();
+ bool hasEmbeddedBitmaps =
+ !getSfntTable(QFont::Tag("EBLC").value()).isEmpty()
+ || !getSfntTable(QFont::Tag("CBLC").value()).isEmpty()
+ || !getSfntTable(QFont::Tag("bdat").value()).isEmpty();
if (!hasEmbeddedBitmaps) {
// Get HHEA table values if available
processHheaTable();
// Allow OS/2 metrics to override if present
processOS2Table();
+
+ if (!supportsSubPixelPositions()) {
+ m_ascent = m_ascent.round();
+ m_descent = m_descent.round();
+ m_leading = m_leading.round();
+ }
}
m_heightMetricsQueried = true;
@@ -464,24 +432,32 @@ void QFontEngine::initializeHeightMetrics() const
bool QFontEngine::processOS2Table() const
{
- QByteArray os2 = getSfntTable(MAKE_TAG('O', 'S', '/', '2'));
+ QByteArray os2 = getSfntTable(QFont::Tag("OS/2").value());
if (os2.size() >= 78) {
- quint16 fsSelection = qFromBigEndian<quint16>(os2.constData() + 62);
- qint16 typoAscent = qFromBigEndian<qint16>(os2.constData() + 68);
- qint16 typoDescent = qFromBigEndian<qint16>(os2.constData() + 70);
- qint16 typoLineGap = qFromBigEndian<qint16>(os2.constData() + 72);
- quint16 winAscent = qFromBigEndian<quint16>(os2.constData() + 74);
- quint16 winDescent = qFromBigEndian<quint16>(os2.constData() + 76);
+ auto ptr = os2.constData();
+ quint16 fsSelection = qFromBigEndian<quint16>(ptr + 62);
+ qint16 typoAscent = qFromBigEndian<qint16>(ptr + 68);
+ qint16 typoDescent = qFromBigEndian<qint16>(ptr + 70);
+ qint16 typoLineGap = qFromBigEndian<qint16>(ptr + 72);
+ quint16 winAscent = qFromBigEndian<quint16>(ptr + 74);
+ quint16 winDescent = qFromBigEndian<quint16>(ptr + 76);
enum { USE_TYPO_METRICS = 0x80 };
QFixed unitsPerEm = emSquareSize();
if (fsSelection & USE_TYPO_METRICS) {
+ // Some fonts may have invalid OS/2 data. We detect this and bail out.
+ if (typoAscent == 0 && typoDescent == 0)
+ return false;
m_ascent = QFixed::fromReal(typoAscent * fontDef.pixelSize) / unitsPerEm;
m_descent = -QFixed::fromReal(typoDescent * fontDef.pixelSize) / unitsPerEm;
m_leading = QFixed::fromReal(typoLineGap * fontDef.pixelSize) / unitsPerEm;
} else {
+ // Some fonts may have invalid OS/2 data. We detect this and bail out.
+ if (winAscent == 0 && winDescent == 0)
+ return false;
m_ascent = QFixed::fromReal(winAscent * fontDef.pixelSize) / unitsPerEm;
m_descent = QFixed::fromReal(winDescent * fontDef.pixelSize) / unitsPerEm;
+ m_leading = QFixed{};
}
return true;
@@ -532,7 +508,7 @@ qreal QFontEngine::minRightBearing() const
if (m_minRightBearing == kBearingNotInitialized) {
// Try the 'hhea' font table first, which covers the entire font
- QByteArray hheaTable = getSfntTable(MAKE_TAG('h', 'h', 'e', 'a'));
+ QByteArray hheaTable = getSfntTable(QFont::Tag("hhea").value());
if (hheaTable.size() >= int(kMinRightSideBearingOffset + sizeof(qint16))) {
const uchar *tableData = reinterpret_cast<const uchar *>(hheaTable.constData());
Q_ASSERT(q16Dot16ToFloat(qFromBigEndian<quint32>(tableData)) == 1.0);
@@ -589,12 +565,23 @@ qreal QFontEngine::minRightBearing() const
}
if (m_minLeftBearing == kBearingNotInitialized || m_minRightBearing == kBearingNotInitialized)
- qWarning() << "Failed to compute left/right minimum bearings for" << fontDef.family;
+ qWarning() << "Failed to compute left/right minimum bearings for"
+ << fontDef.families.first();
}
return m_minRightBearing;
}
+glyph_metrics_t QFontEngine::boundingBox(const QGlyphLayout &glyphs)
+{
+ QFixed w;
+ for (int i = 0; i < glyphs.numGlyphs; ++i)
+ w += glyphs.effectiveAdvance(i);
+ const QFixed leftBearing = firstLeftBearing(glyphs);
+ const QFixed rightBearing = lastRightBearing(glyphs);
+ return glyph_metrics_t(leftBearing, -(ascent()), w - leftBearing - rightBearing, ascent() + descent(), w, 0);
+}
+
glyph_metrics_t QFontEngine::tightBoundingBox(const QGlyphLayout &glyphs)
{
glyph_metrics_t overall;
@@ -610,9 +597,9 @@ glyph_metrics_t QFontEngine::tightBoundingBox(const QGlyphLayout &glyphs)
QFixed y = overall.yoff + glyphs.offsets[i].y + bb.y;
overall.x = qMin(overall.x, x);
overall.y = qMin(overall.y, y);
- xmax = qMax(xmax, x + bb.width);
- ymax = qMax(ymax, y + bb.height);
- overall.xoff += bb.xoff;
+ xmax = qMax(xmax, x.ceil() + bb.width);
+ ymax = qMax(ymax, y.ceil() + bb.height);
+ overall.xoff += glyphs.effectiveAdvance(i);
overall.yoff += bb.yoff;
}
overall.height = qMax(overall.height, ymax - overall.y);
@@ -795,7 +782,7 @@ void QFontEngine::addGlyphsToPath(glyph_t *glyphs, QFixedPoint *positions, int n
addBitmapFontToPath(x, y, g, path, flags);
}
-QImage QFontEngine::alphaMapForGlyph(glyph_t glyph, QFixed /*subPixelPosition*/)
+QImage QFontEngine::alphaMapForGlyph(glyph_t glyph, const QFixedPoint &/*subPixelPosition*/)
{
// For font engines don't support subpixel positioning
return alphaMapForGlyph(glyph);
@@ -811,9 +798,9 @@ QImage QFontEngine::alphaMapForGlyph(glyph_t glyph, const QTransform &t)
return i;
}
-QImage QFontEngine::alphaMapForGlyph(glyph_t glyph, QFixed subPixelPosition, const QTransform &t)
+QImage QFontEngine::alphaMapForGlyph(glyph_t glyph, const QFixedPoint &subPixelPosition, const QTransform &t)
{
- if (! supportsSubPixelPositions())
+ if (!supportsHorizontalSubPixelPositions() && !supportsVerticalSubPixelPositions())
return alphaMapForGlyph(glyph, t);
QImage i = alphaMapForGlyph(glyph, subPixelPosition);
@@ -824,7 +811,7 @@ QImage QFontEngine::alphaMapForGlyph(glyph_t glyph, QFixed subPixelPosition, con
return i;
}
-QImage QFontEngine::alphaRGBMapForGlyph(glyph_t glyph, QFixed /*subPixelPosition*/, const QTransform &t)
+QImage QFontEngine::alphaRGBMapForGlyph(glyph_t glyph, const QFixedPoint &/*subPixelPosition*/, const QTransform &t)
{
const QImage alphaMask = alphaMapForGlyph(glyph, t);
QImage rgbMask(alphaMask.width(), alphaMask.height(), QImage::Format_RGB32);
@@ -841,32 +828,37 @@ QImage QFontEngine::alphaRGBMapForGlyph(glyph_t glyph, QFixed /*subPixelPosition
return rgbMask;
}
-QImage QFontEngine::bitmapForGlyph(glyph_t, QFixed subPixelPosition, const QTransform&, const QColor &)
+QImage QFontEngine::bitmapForGlyph(glyph_t, const QFixedPoint &subPixelPosition, const QTransform&, const QColor &)
{
Q_UNUSED(subPixelPosition);
return QImage();
}
-QFixed QFontEngine::subPixelPositionForX(QFixed x) const
+QFixedPoint QFontEngine::subPixelPositionFor(const QFixedPoint &position) const
{
- if (m_subPixelPositionCount <= 1 || !supportsSubPixelPositions())
- return QFixed();
+ if (m_subPixelPositionCount <= 1
+ || (!supportsHorizontalSubPixelPositions()
+ && !supportsVerticalSubPixelPositions())) {
+ return QFixedPoint();
+ }
- QFixed subPixelPosition;
- if (x != 0) {
- subPixelPosition = x - x.floor();
- QFixed fraction = (subPixelPosition / QFixed::fromReal(1.0 / m_subPixelPositionCount)).floor();
+ auto f = [&](QFixed v) {
+ if (v != 0) {
+ v = v - v.floor() + QFixed::fromFixed(1);
+ QFixed fraction = (v * m_subPixelPositionCount).floor();
+ v = fraction / QFixed(m_subPixelPositionCount);
+ }
+ return v;
+ };
- // Compensate for precision loss in fixed point to make sure we are always drawing at a subpixel position over
- // the lower boundary for the selected rasterization by adding 1/64.
- subPixelPosition = fraction / QFixed(m_subPixelPositionCount) + QFixed::fromReal(0.015625);
- }
- return subPixelPosition;
+ return QFixedPoint(f(position.x), f(position.y));
}
-QFontEngine::Glyph *QFontEngine::glyphData(glyph_t, QFixed,
- QFontEngine::GlyphFormat, const QTransform &)
+QFontEngine::Glyph *QFontEngine::glyphData(glyph_t,
+ const QFixedPoint &,
+ QFontEngine::GlyphFormat,
+ const QTransform &)
{
return nullptr;
}
@@ -915,12 +907,9 @@ void QFontEngine::removeGlyphFromCache(glyph_t)
QFontEngine::Properties QFontEngine::properties() const
{
Properties p;
- p.postscriptName
- = QFontEngine::convertToPostscriptFontFamilyName(fontDef.family.toUtf8())
- + '-'
- + QByteArray::number(fontDef.style)
- + '-'
- + QByteArray::number(fontDef.weight);
+ p.postscriptName =
+ QFontEngine::convertToPostscriptFontFamilyName(fontDef.families.first().toUtf8()) + '-'
+ + QByteArray::number(fontDef.style) + '-' + QByteArray::number(fontDef.weight);
p.ascent = ascent();
p.descent = descent();
p.leading = leading();
@@ -1027,7 +1016,7 @@ static inline QFixed kerning(int left, int right, const QFontEngine::KernPair *p
while (left <= right) {
int middle = left + ( ( right - left ) >> 1 );
- if(pairs[middle].left_right == left_right)
+ if (pairs[middle].left_right == left_right)
return pairs[middle].adjust;
if (pairs[middle].left_right < left_right)
@@ -1041,7 +1030,7 @@ static inline QFixed kerning(int left, int right, const QFontEngine::KernPair *p
void QFontEngine::doKerning(QGlyphLayout *glyphs, QFontEngine::ShaperFlags flags) const
{
int numPairs = kerning_pairs.size();
- if(!numPairs)
+ if (!numPairs)
return;
const KernPair *pairs = kerning_pairs.constData();
@@ -1059,7 +1048,7 @@ void QFontEngine::loadKerningPairs(QFixed scalingFactor)
{
kerning_pairs.clear();
- QByteArray tab = getSfntTable(MAKE_TAG('k', 'e', 'r', 'n'));
+ QByteArray tab = getSfntTable(QFont::Tag("kern").value());
if (tab.isEmpty())
return;
@@ -1097,7 +1086,7 @@ void QFontEngine::loadKerningPairs(QFixed scalingFactor)
goto end;
// qDebug("subtable: version=%d, coverage=%x",version, coverage);
- if(version == 0 && coverage == 0x0001) {
+ if (version == 0 && coverage == 0x0001) {
if (offset + length > tab.size()) {
// qDebug("length ouf ot bounds");
goto end;
@@ -1108,7 +1097,7 @@ void QFontEngine::loadKerningPairs(QFixed scalingFactor)
if (!qSafeFromBigEndian(data, end, &nPairs))
goto end;
- if(nPairs * 6 + 8 > length - 6) {
+ if (nPairs * 6 + 8 > length - 6) {
// qDebug("corrupt table!");
// corrupt table
goto end;
@@ -1148,7 +1137,7 @@ end:
int QFontEngine::glyphCount() const
{
- QByteArray maxpTable = getSfntTable(MAKE_TAG('m', 'a', 'x', 'p'));
+ QByteArray maxpTable = getSfntTable(QFont::Tag("maxp").value());
if (maxpTable.size() < 6)
return 0;
@@ -1195,7 +1184,7 @@ const uchar *QFontEngine::getCMap(const uchar *table, uint tableSize, bool *isSy
int tableToUse = -1;
int score = Invalid;
for (int n = 0; n < numTables; ++n) {
- quint16 platformId;
+ quint16 platformId = 0;
if (!qSafeFromBigEndian(maps + 8 * n, endPtr, &platformId))
return nullptr;
@@ -1246,11 +1235,12 @@ const uchar *QFontEngine::getCMap(const uchar *table, uint tableSize, bool *isSy
default:
break;
}
+ break;
default:
break;
}
}
- if(tableToUse < 0)
+ if (tableToUse < 0)
return nullptr;
resolveTable:
@@ -1325,7 +1315,7 @@ resolveTable:
quint32 QFontEngine::getTrueTypeGlyphIndex(const uchar *cmap, int cmapSize, uint unicode)
{
const uchar *end = cmap + cmapSize;
- quint16 format;
+ quint16 format = 0;
if (!qSafeFromBigEndian(cmap, end, &format))
return 0;
@@ -1339,10 +1329,10 @@ quint32 QFontEngine::getTrueTypeGlyphIndex(const uchar *cmap, int cmapSize, uint
Since 0xffff is never a valid Unicode char anyway, we just get rid of the issue
by returning 0 for 0xffff
*/
- if(unicode >= 0xffff)
+ if (unicode >= 0xffff)
return 0;
- quint16 segCountX2;
+ quint16 segCountX2 = 0;
if (!qSafeFromBigEndian(cmap + 6, end, &segCountX2))
return 0;
@@ -1350,7 +1340,7 @@ quint32 QFontEngine::getTrueTypeGlyphIndex(const uchar *cmap, int cmapSize, uint
int i = 0;
for (; i < segCountX2/2; ++i) {
- quint16 codePoint;
+ quint16 codePoint = 0;
if (!qSafeFromBigEndian(ends + 2 * i, end, &codePoint))
return 0;
if (codePoint >= unicode)
@@ -1359,7 +1349,7 @@ quint32 QFontEngine::getTrueTypeGlyphIndex(const uchar *cmap, int cmapSize, uint
const unsigned char *idx = ends + segCountX2 + 2 + 2*i;
- quint16 startIndex;
+ quint16 startIndex = 0;
if (!qSafeFromBigEndian(idx, end, &startIndex))
return 0;
if (startIndex > unicode)
@@ -1367,20 +1357,20 @@ quint32 QFontEngine::getTrueTypeGlyphIndex(const uchar *cmap, int cmapSize, uint
idx += segCountX2;
- quint16 tmp;
+ quint16 tmp = 0;
if (!qSafeFromBigEndian(idx, end, &tmp))
return 0;
qint16 idDelta = qint16(tmp);
idx += segCountX2;
- quint16 idRangeoffset_t;
+ quint16 idRangeoffset_t = 0;
if (!qSafeFromBigEndian(idx, end, &idRangeoffset_t))
return 0;
- quint16 glyphIndex;
+ quint16 glyphIndex = 0;
if (idRangeoffset_t) {
- quint16 id;
+ quint16 id = 0;
if (!qSafeFromBigEndian(idRangeoffset_t + 2 * (unicode - startIndex) + idx, end, &id))
return 0;
@@ -1393,17 +1383,17 @@ quint32 QFontEngine::getTrueTypeGlyphIndex(const uchar *cmap, int cmapSize, uint
}
return glyphIndex;
} else if (format == 6) {
- quint16 tableSize;
+ quint16 tableSize = 0;
if (!qSafeFromBigEndian(cmap + 2, end, &tableSize))
return 0;
- quint16 firstCode6;
+ quint16 firstCode6 = 0;
if (!qSafeFromBigEndian(cmap + 6, end, &firstCode6))
return 0;
if (unicode < firstCode6)
return 0;
- quint16 entryCount6;
+ quint16 entryCount6 = 0;
if (!qSafeFromBigEndian(cmap + 8, end, &entryCount6))
return 0;
if (entryCount6 * 2 + 10 > tableSize)
@@ -1419,7 +1409,7 @@ quint32 QFontEngine::getTrueTypeGlyphIndex(const uchar *cmap, int cmapSize, uint
qSafeFromBigEndian(cmap + 10 + (entryIndex6 * 2), end, &index);
return index;
} else if (format == 12) {
- quint32 nGroups;
+ quint32 nGroups = 0;
if (!qSafeFromBigEndian(cmap + 12, end, &nGroups))
return 0;
@@ -1429,19 +1419,19 @@ quint32 QFontEngine::getTrueTypeGlyphIndex(const uchar *cmap, int cmapSize, uint
while (left <= right) {
int middle = left + ( ( right - left ) >> 1 );
- quint32 startCharCode;
+ quint32 startCharCode = 0;
if (!qSafeFromBigEndian(cmap + 12 * middle, end, &startCharCode))
return 0;
- if(unicode < startCharCode)
+ if (unicode < startCharCode)
right = middle - 1;
else {
- quint32 endCharCode;
+ quint32 endCharCode = 0;
if (!qSafeFromBigEndian(cmap + 12 * middle + 4, end, &endCharCode))
return 0;
if (unicode <= endCharCode) {
- quint32 index;
+ quint32 index = 0;
if (!qSafeFromBigEndian(cmap + 12 * middle + 8, end, &index))
return 0;
@@ -1483,6 +1473,17 @@ bool QFontEngine::hasUnreliableGlyphOutline() const
return glyphFormat == QFontEngine::Format_ARGB;
}
+QFixed QFontEngine::firstLeftBearing(const QGlyphLayout &glyphs)
+{
+ for (int i = 0; i < glyphs.numGlyphs; ++i) {
+ glyph_t glyph = glyphs.glyphs[i];
+ glyph_metrics_t gi = boundingBox(glyph);
+ if (gi.isValid() && gi.width > 0)
+ return gi.leftBearing();
+ }
+ return 0;
+}
+
QFixed QFontEngine::lastRightBearing(const QGlyphLayout &glyphs)
{
if (glyphs.numGlyphs >= 1) {
@@ -1539,22 +1540,22 @@ QFontEngineBox::~QFontEngineBox()
glyph_t QFontEngineBox::glyphIndex(uint ucs4) const
{
Q_UNUSED(ucs4);
- return 0;
+ return 1;
}
-bool QFontEngineBox::stringToCMap(const QChar *str, int len, QGlyphLayout *glyphs, int *nglyphs, QFontEngine::ShaperFlags flags) const
+int QFontEngineBox::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;
}
int ucs4Length = 0;
QStringIterator it(str, str + len);
while (it.hasNext()) {
it.advance();
- glyphs->glyphs[ucs4Length++] = 0;
+ glyphs->glyphs[ucs4Length++] = 1;
}
*nglyphs = ucs4Length;
@@ -1563,7 +1564,7 @@ bool QFontEngineBox::stringToCMap(const QChar *str, int len, QGlyphLayout *glyph
if (!(flags & GlyphIndicesOnly))
recalcAdvances(glyphs, flags);
- return true;
+ return *nglyphs;
}
void QFontEngineBox::recalcAdvances(QGlyphLayout *glyphs, QFontEngine::ShaperFlags) const
@@ -1670,12 +1671,12 @@ QImage QFontEngineBox::alphaMapForGlyph(glyph_t)
QImage image(_size, _size, QImage::Format_Alpha8);
image.fill(0);
- // FIXME: use qpainter
+ uchar *bits = image.bits();
for (int i=2; i <= _size-3; ++i) {
- image.setPixel(i, 2, 255);
- image.setPixel(i, _size-3, 255);
- image.setPixel(2, i, 255);
- image.setPixel(_size-3, i, 255);
+ bits[i + 2 * image.bytesPerLine()] = 255;
+ bits[i + (_size - 3) * image.bytesPerLine()] = 255;
+ bits[2 + i * image.bytesPerLine()] = 255;
+ bits[_size - 3 + i * image.bytesPerLine()] = 255;
}
return image;
}
@@ -1730,7 +1731,9 @@ void QFontEngineMulti::ensureFallbackFamiliesQueried()
if (styleHint == QFont::AnyStyle && fontDef.fixedPitch)
styleHint = QFont::TypeWriter;
- setFallbackFamiliesList(qt_fallbacksForFamily(fontDef.family, QFont::Style(fontDef.style), styleHint, QChar::Script(m_script)));
+ setFallbackFamiliesList(qt_fallbacksForFamily(fontDef.families.constFirst(),
+ QFont::Style(fontDef.style), styleHint,
+ QChar::Script(m_script)));
}
void QFontEngineMulti::setFallbackFamiliesList(const QStringList &fallbackFamilies)
@@ -1744,7 +1747,7 @@ void QFontEngineMulti::setFallbackFamiliesList(const QStringList &fallbackFamili
QFontEngine *engine = m_engines.at(0);
engine->ref.ref();
m_engines[1] = engine;
- m_fallbackFamilies << fontDef.family;
+ m_fallbackFamilies << fontDef.families.constFirst();
} else {
m_engines.resize(m_fallbackFamilies.size() + 1);
}
@@ -1754,7 +1757,7 @@ void QFontEngineMulti::setFallbackFamiliesList(const QStringList &fallbackFamili
void QFontEngineMulti::ensureEngineAt(int at)
{
- if (!m_fallbackFamiliesQueried)
+ if (!m_fallbackFamiliesQueried && at > 0)
ensureFallbackFamiliesQueried();
Q_ASSERT(at < m_engines.size());
if (!m_engines.at(at)) {
@@ -1771,15 +1774,14 @@ QFontEngine *QFontEngineMulti::loadEngine(int at)
{
QFontDef request(fontDef);
request.styleStrategy |= QFont::NoFontMerging;
- request.family = fallbackFamilyAt(at - 1);
- request.families = QStringList(request.family);
+ request.families = QStringList(fallbackFamilyAt(at - 1));
// At this point, the main script of the text has already been considered
// when fetching the list of fallback families from the database, and the
// info about the actual script of the characters may have been discarded,
// so we do not check for writing system support, but instead just load
// the family indiscriminately.
- if (QFontEngine *engine = QFontDatabase::findFont(request, QChar::Script_Common)) {
+ if (QFontEngine *engine = QFontDatabasePrivate::findFont(request, QChar::Script_Common)) {
engine->fontDef.weight = request.weight;
if (request.style > QFont::StyleNormal)
engine->fontDef.style = request.style;
@@ -1792,11 +1794,7 @@ QFontEngine *QFontEngineMulti::loadEngine(int at)
glyph_t QFontEngineMulti::glyphIndex(uint ucs4) const
{
glyph_t glyph = engine(0)->glyphIndex(ucs4);
- if (glyph == 0
- && ucs4 != QChar::LineSeparator
- && ucs4 != QChar::LineFeed
- && ucs4 != QChar::CarriageReturn
- && ucs4 != QChar::ParagraphSeparator) {
+ if (glyph == 0 && !isIgnorableChar(ucs4)) {
if (!m_fallbackFamiliesQueried)
const_cast<QFontEngineMulti *>(this)->ensureFallbackFamiliesQueried();
for (int x = 1, n = qMin(m_engines.size(), 256); x < n; ++x) {
@@ -1823,17 +1821,60 @@ glyph_t QFontEngineMulti::glyphIndex(uint ucs4) const
return glyph;
}
-bool QFontEngineMulti::stringToCMap(const QChar *str, int len,
- QGlyphLayout *glyphs, int *nglyphs,
- QFontEngine::ShaperFlags flags) const
+int QFontEngineMulti::stringToCMap(const QChar *str, int len,
+ QGlyphLayout *glyphs, int *nglyphs,
+ QFontEngine::ShaperFlags flags) const
{
- if (!engine(0)->stringToCMap(str, len, glyphs, nglyphs, flags))
- return false;
+ const int originalNumGlyphs = glyphs->numGlyphs;
+ int mappedGlyphCount = engine(0)->stringToCMap(str, len, glyphs, nglyphs, flags);
+ if (mappedGlyphCount < 0)
+ return -1;
+
+ // If ContextFontMerging is set and the match for the string was incomplete, we try all
+ // fallbacks on the full string until we find the best match.
+ bool contextFontMerging = mappedGlyphCount < *nglyphs && (fontDef.styleStrategy & QFont::ContextFontMerging);
+ if (contextFontMerging) {
+ QVarLengthGlyphLayoutArray tempLayout(len);
+ if (!m_fallbackFamiliesQueried)
+ const_cast<QFontEngineMulti *>(this)->ensureFallbackFamiliesQueried();
+
+ int maxGlyphCount = 0;
+ uchar engineIndex = 0;
+ for (int x = 1, n = qMin(m_engines.size(), 256); x < n; ++x) {
+ int numGlyphs = len;
+ const_cast<QFontEngineMulti *>(this)->ensureEngineAt(x);
+ maxGlyphCount = engine(x)->stringToCMap(str, len, &tempLayout, &numGlyphs, flags);
+
+ // If we found a better match, we copy data into the main QGlyphLayout
+ if (maxGlyphCount > mappedGlyphCount) {
+ *nglyphs = numGlyphs;
+ glyphs->numGlyphs = originalNumGlyphs;
+ glyphs->copy(&tempLayout);
+ engineIndex = x;
+ if (maxGlyphCount == numGlyphs)
+ break;
+ }
+ }
+
+ if (engineIndex > 0) {
+ for (int y = 0; y < glyphs->numGlyphs; ++y) {
+ if (glyphs->glyphs[y] != 0)
+ glyphs->glyphs[y] |= (engineIndex << 24);
+ }
+ } else {
+ contextFontMerging = false;
+ }
+ mappedGlyphCount = maxGlyphCount;
+ }
+
+ // Fill in missing glyphs by going through string one character at the time and finding
+ // the first viable fallback.
int glyph_pos = 0;
QStringIterator it(str, str + len);
int lastFallback = -1;
+ char32_t previousUcs4 = 0;
while (it.hasNext()) {
const char32_t ucs4 = it.peekNext();
@@ -1859,14 +1900,10 @@ bool QFontEngineMulti::stringToCMap(const QChar *str, int len,
lastFallback = -1;
}
- if (glyphs->glyphs[glyph_pos] == 0
- && ucs4 != QChar::LineSeparator
- && ucs4 != QChar::LineFeed
- && ucs4 != QChar::CarriageReturn
- && ucs4 != QChar::ParagraphSeparator) {
+ if (glyphs->glyphs[glyph_pos] == 0 && !isIgnorableChar(ucs4)) {
if (!m_fallbackFamiliesQueried)
const_cast<QFontEngineMulti *>(this)->ensureFallbackFamiliesQueried();
- for (int x = 1, n = qMin(m_engines.size(), 256); x < n; ++x) {
+ for (int x = contextFontMerging ? 0 : 1, n = qMin(m_engines.size(), 256); x < n; ++x) {
QFontEngine *engine = m_engines.at(x);
if (!engine) {
if (!shouldLoadFontEngineForCharacter(x, ucs4))
@@ -1895,16 +1932,55 @@ bool QFontEngineMulti::stringToCMap(const QChar *str, int len,
break;
}
}
+
+ // For variant-selectors, they are modifiers to the previous character. If we
+ // end up with different font selections for the selector and the character it
+ // modifies, we try applying the selector font to the preceding character as well
+ const int variantSelectorBlock = 0xFE00;
+ if ((ucs4 & 0xFFF0) == variantSelectorBlock && glyph_pos > 0) {
+ int selectorFontEngine = glyphs->glyphs[glyph_pos] >> 24;
+ int precedingCharacterFontEngine = glyphs->glyphs[glyph_pos - 1] >> 24;
+
+ if (selectorFontEngine != precedingCharacterFontEngine) {
+ // Emoji variant selectors are specially handled and should affect font
+ // selection. If VS-16 is used, then this means we want to select a color
+ // font. If the selected font is already a color font, we do not need search
+ // again. If the VS-15 is used, then this means we want to select a non-color
+ // font. If the selected font is not a color font, we don't do anything.
+ const QFontEngine *selectedEngine = m_engines.at(precedingCharacterFontEngine);
+ const bool colorFont = selectedEngine->isColorFont();
+ const char32_t vs15 = 0xFE0E;
+ const char32_t vs16 = 0xFE0F;
+ bool adaptVariantSelector = ucs4 < vs15
+ || (ucs4 == vs15 && colorFont)
+ || (ucs4 == vs16 && !colorFont);
+
+ if (adaptVariantSelector) {
+ QFontEngine *engine = m_engines.at(selectorFontEngine);
+ glyph_t glyph = engine->glyphIndex(previousUcs4);
+ if (glyph != 0) {
+ glyphs->glyphs[glyph_pos - 1] = glyph;
+ if (!(flags & GlyphIndicesOnly)) {
+ QGlyphLayout g = glyphs->mid(glyph_pos - 1, 1);
+ engine->recalcAdvances(&g, flags);
+ }
+
+ // set the high byte to indicate which engine the glyph came from
+ glyphs->glyphs[glyph_pos - 1] |= (selectorFontEngine << 24);
+ }
+ }
+ }
+ }
}
it.advance();
++glyph_pos;
+ previousUcs4 = ucs4;
}
*nglyphs = glyph_pos;
glyphs->numGlyphs = glyph_pos;
-
- return true;
+ return mappedGlyphCount;
}
bool QFontEngineMulti::shouldLoadFontEngineForCharacter(int at, uint ucs4) const
@@ -2196,7 +2272,7 @@ bool QFontEngineMulti::canRender(const QChar *string, int len) const
QGlyphLayout g;
g.numGlyphs = nglyphs;
g.glyphs = glyphs.data();
- if (!stringToCMap(string, len, &g, &nglyphs, GlyphIndicesOnly))
+ if (stringToCMap(string, len, &g, &nglyphs, GlyphIndicesOnly) < 0)
Q_UNREACHABLE();
for (int i = 0; i < nglyphs; i++) {
@@ -2216,7 +2292,7 @@ QImage QFontEngineMulti::alphaMapForGlyph(glyph_t glyph)
return engine(which)->alphaMapForGlyph(stripped(glyph));
}
-QImage QFontEngineMulti::alphaMapForGlyph(glyph_t glyph, QFixed subPixelPosition)
+QImage QFontEngineMulti::alphaMapForGlyph(glyph_t glyph, const QFixedPoint &subPixelPosition)
{
const int which = highByte(glyph);
return engine(which)->alphaMapForGlyph(stripped(glyph), subPixelPosition);
@@ -2228,13 +2304,17 @@ QImage QFontEngineMulti::alphaMapForGlyph(glyph_t glyph, const QTransform &t)
return engine(which)->alphaMapForGlyph(stripped(glyph), t);
}
-QImage QFontEngineMulti::alphaMapForGlyph(glyph_t glyph, QFixed subPixelPosition, const QTransform &t)
+QImage QFontEngineMulti::alphaMapForGlyph(glyph_t glyph,
+ const QFixedPoint &subPixelPosition,
+ const QTransform &t)
{
const int which = highByte(glyph);
return engine(which)->alphaMapForGlyph(stripped(glyph), subPixelPosition, t);
}
-QImage QFontEngineMulti::alphaRGBMapForGlyph(glyph_t glyph, QFixed subPixelPosition, const QTransform &t)
+QImage QFontEngineMulti::alphaRGBMapForGlyph(glyph_t glyph,
+ const QFixedPoint &subPixelPosition,
+ const QTransform &t)
{
const int which = highByte(glyph);
return engine(which)->alphaRGBMapForGlyph(stripped(glyph), subPixelPosition, t);
diff --git a/src/gui/text/qfontengine_p.h b/src/gui/text/qfontengine_p.h
index a81e877677..a0e0801354 100644
--- a/src/gui/text/qfontengine_p.h
+++ b/src/gui/text/qfontengine_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) 2021 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_P_H
#define QFONTENGINE_P_H
@@ -65,13 +29,6 @@ class QFontEngineGlyphCache;
struct QGlyphLayout;
-#define MAKE_TAG(ch1, ch2, ch3, ch4) (\
- (((quint32)(ch1)) << 24) | \
- (((quint32)(ch2)) << 16) | \
- (((quint32)(ch3)) << 8) | \
- ((quint32)(ch4)) \
- )
-
// ### this only used in getPointInOutline(), refactor it and then remove these magic numbers
enum HB_Compat_Error {
Err_Ok = 0x0000,
@@ -119,7 +76,8 @@ public:
enum ShaperFlag {
DesignMetrics = 0x0002,
- GlyphIndicesOnly = 0x0004
+ GlyphIndicesOnly = 0x0004,
+ FullStringFallback = 0x008
};
Q_DECLARE_FLAGS(ShaperFlags, ShaperFlag)
@@ -163,11 +121,13 @@ public:
virtual bool getSfntTableData(uint tag, uchar *buffer, uint *length) const;
struct FaceId {
- FaceId() : index(0), encoding(0) {}
+ FaceId() : index(0), instanceIndex(-1), encoding(0) {}
QByteArray filename;
QByteArray uuid;
int index;
+ int instanceIndex;
int encoding;
+ QMap<QFont::Tag, float> variableAxes;
};
virtual FaceId faceId() const { return FaceId(); }
enum SynthesizedFlags {
@@ -176,14 +136,33 @@ public:
SynthesizedStretch = 0x4
};
virtual int synthesized() const { return 0; }
- virtual bool supportsSubPixelPositions() const { return false; }
- virtual QFixed subPixelPositionForX(QFixed x) const;
+ inline bool supportsSubPixelPositions() const
+ {
+ return supportsHorizontalSubPixelPositions() || supportsVerticalSubPixelPositions();
+ }
+ virtual bool supportsHorizontalSubPixelPositions() const { return false; }
+ virtual bool supportsVerticalSubPixelPositions() const { return false; }
+ virtual QFixedPoint subPixelPositionFor(const QFixedPoint &position) const;
+ QFixed subPixelPositionForX(QFixed x) const
+ {
+ return subPixelPositionFor(QFixedPoint(x, 0)).x;
+ }
+
+ bool isColorFont() const { return glyphFormat == Format_ARGB; }
+ static bool isIgnorableChar(char32_t ucs4)
+ {
+ return ucs4 == QChar::LineSeparator
+ || ucs4 == QChar::LineFeed
+ || ucs4 == QChar::CarriageReturn
+ || ucs4 == QChar::ParagraphSeparator
+ || QChar::category(ucs4) == QChar::Other_Control;
+ }
virtual QFixed emSquareSize() const { return ascent(); }
/* returns 0 as glyph index for non existent glyphs */
virtual glyph_t glyphIndex(uint ucs4) const = 0;
- virtual bool stringToCMap(const QChar *str, int len, QGlyphLayout *glyphs, int *nglyphs, ShaperFlags flags) const = 0;
+ virtual int stringToCMap(const QChar *str, int len, QGlyphLayout *glyphs, int *nglyphs, ShaperFlags flags) const = 0;
virtual void recalcAdvances(QGlyphLayout *, ShaperFlags) const {}
virtual void doKerning(QGlyphLayout *, ShaperFlags) const;
@@ -201,22 +180,22 @@ public:
*/
// ### Refactor this into a smaller and more flexible API.
virtual QImage alphaMapForGlyph(glyph_t);
- virtual QImage alphaMapForGlyph(glyph_t glyph, QFixed subPixelPosition);
+ virtual QImage alphaMapForGlyph(glyph_t glyph, const QFixedPoint &subPixelPosition);
virtual QImage alphaMapForGlyph(glyph_t, const QTransform &t);
- virtual QImage alphaMapForGlyph(glyph_t, QFixed subPixelPosition, const QTransform &t);
- virtual QImage alphaRGBMapForGlyph(glyph_t, QFixed subPixelPosition, const QTransform &t);
- virtual QImage bitmapForGlyph(glyph_t, QFixed subPixelPosition, const QTransform &t, const QColor &color = QColor());
- virtual Glyph *glyphData(glyph_t glyph, QFixed subPixelPosition, GlyphFormat neededFormat, const QTransform &t);
+ virtual QImage alphaMapForGlyph(glyph_t, const QFixedPoint &subPixelPosition, const QTransform &t);
+ virtual QImage alphaRGBMapForGlyph(glyph_t, const QFixedPoint &subPixelPosition, const QTransform &t);
+ virtual QImage bitmapForGlyph(glyph_t, const QFixedPoint &subPixelPosition, const QTransform &t, const QColor &color = QColor());
+ virtual Glyph *glyphData(glyph_t glyph, const QFixedPoint &subPixelPosition, GlyphFormat neededFormat, const QTransform &t);
virtual bool hasInternalCaching() const { return false; }
- virtual glyph_metrics_t alphaMapBoundingBox(glyph_t glyph, QFixed /*subPixelPosition*/, const QTransform &matrix, GlyphFormat /*format*/)
+ virtual glyph_metrics_t alphaMapBoundingBox(glyph_t glyph, const QFixedPoint &/*subPixelPosition*/, const QTransform &matrix, GlyphFormat /*format*/)
{
return boundingBox(glyph, matrix);
}
virtual void removeGlyphFromCache(glyph_t);
- virtual glyph_metrics_t boundingBox(const QGlyphLayout &glyphs) = 0;
+ virtual glyph_metrics_t boundingBox(const QGlyphLayout &glyphs);
virtual glyph_metrics_t boundingBox(glyph_t glyph) = 0;
virtual glyph_metrics_t boundingBox(glyph_t glyph, const QTransform &matrix);
glyph_metrics_t tightBoundingBox(const QGlyphLayout &glyphs);
@@ -304,14 +283,11 @@ public:
explicit Holder(void *p, qt_destroy_func_t d) : ptr(p), destroy_func(d) {}
~Holder() { if (ptr && destroy_func) destroy_func(ptr); }
Holder(Holder &&other) noexcept
- : ptr(other.ptr),
- destroy_func(other.destroy_func)
+ : ptr(std::exchange(other.ptr, nullptr)),
+ destroy_func(std::exchange(other.destroy_func, nullptr))
{
- other.ptr = nullptr;
- other.destroy_func = nullptr;
}
- Holder &operator=(Holder &&other) noexcept
- { swap(other); return *this; }
+ QT_MOVE_ASSIGNMENT_OPERATOR_IMPL_VIA_PURE_SWAP(Holder)
void swap(Holder &other) noexcept
{
@@ -359,14 +335,12 @@ public:
GlyphFormat glyphFormat;
int m_subPixelPositionCount; // Number of positions within a single pixel for this cache
- inline QVariant userData() const { return m_userData; }
-
protected:
explicit QFontEngine(Type type);
+ QFixed firstLeftBearing(const QGlyphLayout &glyphs);
QFixed lastRightBearing(const QGlyphLayout &glyphs);
- inline void setUserData(const QVariant &userData) { m_userData = userData; }
QFixed calculatedCapHeight() const;
mutable QFixed m_ascent;
@@ -393,8 +367,6 @@ private:
mutable QHash<const void *, GlyphCaches> m_glyphCaches;
private:
- QVariant m_userData;
-
mutable qreal m_minLeftBearing;
mutable qreal m_minRightBearing;
};
@@ -404,13 +376,18 @@ Q_DECLARE_OPERATORS_FOR_FLAGS(QFontEngine::ShaperFlags)
inline bool operator ==(const QFontEngine::FaceId &f1, const QFontEngine::FaceId &f2)
{
- return f1.index == f2.index && f1.encoding == f2.encoding && f1.filename == f2.filename && f1.uuid == f2.uuid;
+ return f1.index == f2.index
+ && f1.encoding == f2.encoding
+ && f1.filename == f2.filename
+ && f1.uuid == f2.uuid
+ && f1.instanceIndex == f2.instanceIndex
+ && f1.variableAxes == f2.variableAxes;
}
inline size_t qHash(const QFontEngine::FaceId &f, size_t seed = 0)
noexcept(noexcept(qHash(f.filename)))
{
- return qHashMulti(seed, f.filename, f.uuid, f.index, f.encoding);
+ return qHashMulti(seed, f.filename, f.uuid, f.index, f.instanceIndex, f.encoding, f.variableAxes.keys(), f.variableAxes.values());
}
@@ -425,7 +402,7 @@ public:
~QFontEngineBox();
virtual glyph_t glyphIndex(uint ucs4) const override;
- virtual bool stringToCMap(const QChar *str, int len, QGlyphLayout *glyphs, int *nglyphs, ShaperFlags flags) const override;
+ virtual int stringToCMap(const QChar *str, int len, QGlyphLayout *glyphs, int *nglyphs, ShaperFlags flags) const override;
virtual void recalcAdvances(QGlyphLayout *, ShaperFlags) const override;
void draw(QPaintEngine *p, qreal x, qreal y, const QTextItemInt &si);
@@ -463,7 +440,7 @@ public:
~QFontEngineMulti();
virtual glyph_t glyphIndex(uint ucs4) const override;
- virtual bool stringToCMap(const QChar *str, int len, QGlyphLayout *glyphs, int *nglyphs, ShaperFlags flags) const override;
+ virtual int stringToCMap(const QChar *str, int len, QGlyphLayout *glyphs, int *nglyphs, ShaperFlags flags) const override;
virtual glyph_metrics_t boundingBox(const QGlyphLayout &glyphs) override;
virtual glyph_metrics_t boundingBox(glyph_t glyph) override;
@@ -480,10 +457,10 @@ public:
virtual QFixed xHeight() const override;
virtual QFixed averageCharWidth() const override;
virtual QImage alphaMapForGlyph(glyph_t) override;
- virtual QImage alphaMapForGlyph(glyph_t glyph, QFixed subPixelPosition) override;
+ virtual QImage alphaMapForGlyph(glyph_t glyph, const QFixedPoint &subPixelPosition) override;
virtual QImage alphaMapForGlyph(glyph_t, const QTransform &t) override;
- virtual QImage alphaMapForGlyph(glyph_t, QFixed subPixelPosition, const QTransform &t) override;
- virtual QImage alphaRGBMapForGlyph(glyph_t, QFixed subPixelPosition, const QTransform &t) override;
+ virtual QImage alphaMapForGlyph(glyph_t, const QFixedPoint &subPixelPosition, const QTransform &t) override;
+ virtual QImage alphaRGBMapForGlyph(glyph_t, const QFixedPoint &subPixelPosition, const QTransform &t) override;
virtual QFixed lineThickness() const override;
virtual QFixed underlinePosition() const override;
diff --git a/src/gui/text/qfontengineglyphcache.cpp b/src/gui/text/qfontengineglyphcache.cpp
index c6e6499854..3555d56fa8 100644
--- a/src/gui/text/qfontengineglyphcache.cpp
+++ b/src/gui/text/qfontengineglyphcache.cpp
@@ -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 <private/qfontengineglyphcache_p.h>
diff --git a/src/gui/text/qfontengineglyphcache_p.h b/src/gui/text/qfontengineglyphcache_p.h
index 532be10a83..9054ea5950 100644
--- a/src/gui/text/qfontengineglyphcache_p.h
+++ b/src/gui/text/qfontengineglyphcache_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 QFONTENGINEGLYPHCACHE_P_H
#define QFONTENGINEGLYPHCACHE_P_H
@@ -51,7 +15,7 @@
// We mean it.
//
-
+#include <QtCore/qshareddata.h>
#include <QtGui/private/qtguiglobal_p.h>
#include "QtCore/qatomic.h"
#include <QtCore/qvarlengtharray.h>
diff --git a/src/gui/text/qfontinfo.h b/src/gui/text/qfontinfo.h
index dfdac221cb..0edee5abe5 100644
--- a/src/gui/text/qfontinfo.h
+++ b/src/gui/text/qfontinfo.h
@@ -1,48 +1,13 @@
-/****************************************************************************
-**
-** 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 QFONTINFO_H
#define QFONTINFO_H
#include <QtGui/qtguiglobal.h>
#include <QtGui/qfont.h>
-#include <QtCore/qsharedpointer.h>
+
+#include <QtCore/qshareddata.h>
QT_BEGIN_NAMESPACE
@@ -56,7 +21,7 @@ public:
QFontInfo &operator=(const QFontInfo &);
- void swap(QFontInfo &other) { qSwap(d, other.d); }
+ void swap(QFontInfo &other) noexcept { d.swap(other.d); }
QString family() const;
QString styleName() const;
@@ -73,6 +38,10 @@ public:
bool fixedPitch() const;
QFont::StyleHint styleHint() const;
+#if QT_DEPRECATED_SINCE(6, 0)
+ QT_DEPRECATED_VERSION_X_6_0("Use weight() instead") int legacyWeight() const;
+#endif
+
bool exactMatch() const;
private:
diff --git a/src/gui/text/qfontmetrics.cpp b/src/gui/text/qfontmetrics.cpp
index 8f3aa62842..f7e405f0b5 100644
--- a/src/gui/text/qfontmetrics.cpp
+++ b/src/gui/text/qfontmetrics.cpp
@@ -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 "qfont.h"
#include "qpaintdevice.h"
@@ -111,15 +75,23 @@ extern void qt_format_text(const QFont& font, const QRectF &_r,
inFont(). You can also treat the character as a string, and use
the string functions on it.
- The string functions include horizontalAdvance(), to return the width of a
- string in pixels (or points, for a printer), boundingRect(), to
- return a rectangle large enough to contain the rendered string,
+ The string functions include horizontalAdvance(), to return the advance
+ width of a string in pixels (or points, for a printer), boundingRect(),
+ to return a rectangle large enough to contain the rendered string,
and size(), to return the size of that rectangle.
+ \note The advance width can be different from the width of the actual
+ rendered text. It refers to the distance from the origin of the string to
+ where you would append additional characters. As text may have overhang
+ (in the case of an italic font for instance) or padding between
+ characters, the advance width can be either smaller or larger than the
+ actual rendering of the text. This is called the right bearing of the
+ text.
+
Example:
\snippet code/src_gui_text_qfontmetrics.cpp 0
- \sa QFont, QFontInfo, QFontDatabase, {Character Map Example}
+ \sa QFont, QFontInfo, QFontDatabase
*/
/*!
@@ -261,9 +233,8 @@ bool QFontMetrics::operator ==(const QFontMetrics &other) const
The ascent of a font is the distance from the baseline to the
highest position characters extend to. In practice, some font
designers break this rule, e.g. when they put more than one accent
- on top of a character, or to accommodate an unusual character in
- an exotic language, so it is possible (though rare) that this
- value will be too small.
+ on top of a character, or to accommodate a certain character, so it
+ is possible (though rare) that this value will be too small.
\sa descent()
*/
@@ -298,8 +269,8 @@ int QFontMetrics::capHeight() const
The descent is the distance from the base line to the lowest point
characters extend to. In practice, some font designers break this rule,
- e.g. to accommodate an unusual character in an exotic language, so
- it is possible (though rare) that this value will be too small.
+ e.g. to accommodate a certain character, so it is possible (though
+ rare) that this value will be too small.
\sa ascent()
*/
@@ -510,10 +481,13 @@ int QFontMetrics::rightBearing(QChar ch) const
return qRound(rb);
}
+static constexpr QLatin1Char s_variableLengthStringSeparator('\x9c');
+
/*!
Returns the horizontal advance in pixels of the first \a len characters of \a
text. If \a len is negative (the default), the entire string is
- used.
+ used. The entire length of \a text is analysed even if \a len is substantially
+ shorter.
This is the distance appropriate for drawing a subsequent character
after \a text.
@@ -524,16 +498,45 @@ int QFontMetrics::rightBearing(QChar ch) const
*/
int QFontMetrics::horizontalAdvance(const QString &text, int len) const
{
- int pos = text.indexOf(QLatin1Char('\x9c'));
+ int pos = (len >= 0)
+ ? QStringView(text).left(len).indexOf(s_variableLengthStringSeparator)
+ : text.indexOf(s_variableLengthStringSeparator);
if (pos != -1) {
- len = (len < 0) ? pos : qMin(pos, len);
+ len = pos;
} else if (len < 0) {
- len = text.length();
+ len = text.size();
+ }
+ if (len == 0)
+ return 0;
+
+ QStackTextEngine layout(text, QFont(d.data()));
+ return qRound(layout.width(0, len));
+}
+
+/*!
+ Returns the horizontal advance in pixels of \a text laid out using \a option.
+
+ The advance is the distance appropriate for drawing a subsequent
+ character after \a text.
+
+ \since 6.3
+
+ \sa boundingRect()
+*/
+int QFontMetrics::horizontalAdvance(const QString &text, const QTextOption &option) const
+{
+ int pos = text.indexOf(s_variableLengthStringSeparator);
+ int len = -1;
+ if (pos != -1) {
+ len = pos;
+ } else {
+ len = text.size();
}
if (len == 0)
return 0;
QStackTextEngine layout(text, QFont(d.data()));
+ layout.option = option;
return qRound(layout.width(0, len));
}
@@ -614,12 +617,48 @@ int QFontMetrics::horizontalAdvance(QChar ch) const
*/
QRect QFontMetrics::boundingRect(const QString &text) const
{
- if (text.length() == 0)
+ if (text.size() == 0)
+ return QRect();
+
+ QStackTextEngine layout(text, QFont(d.data()));
+ layout.itemize();
+ glyph_metrics_t gm = layout.boundingBox(0, text.size());
+ return QRect(qRound(gm.x), qRound(gm.y), qRound(gm.width), qRound(gm.height));
+}
+
+/*!
+ Returns the bounding rectangle of the characters in the string
+ specified by \a text laid out using \a option. The bounding rectangle always
+ covers at least the set of pixels the text would cover if drawn at (0, 0).
+
+ Note that the bounding rectangle may extend to the left of (0, 0),
+ e.g. for italicized fonts, and that the width of the returned
+ rectangle might be different than what the horizontalAdvance() method
+ returns.
+
+ If you want to know the advance width of the string (to lay out
+ a set of strings next to each other), use horizontalAdvance() instead.
+
+ Newline characters are processed as normal characters, \e not as
+ linebreaks.
+
+ The height of the bounding rectangle is at least as large as the
+ value returned by height().
+
+ \since 6.3
+
+ \sa horizontalAdvance(), height(), QPainter::boundingRect(),
+ tightBoundingRect()
+*/
+QRect QFontMetrics::boundingRect(const QString &text, const QTextOption &option) const
+{
+ if (text.size() == 0)
return QRect();
QStackTextEngine layout(text, QFont(d.data()));
+ layout.option = option;
layout.itemize();
- glyph_metrics_t gm = layout.boundingBox(0, text.length());
+ glyph_metrics_t gm = layout.boundingBox(0, text.size());
return QRect(qRound(gm.x), qRound(gm.y), qRound(gm.width), qRound(gm.height));
}
@@ -759,8 +798,6 @@ QSize QFontMetrics::size(int flags, const QString &text, int tabStops, int *tabA
}
/*!
- \since 4.3
-
Returns a tight bounding rectangle around the characters in the
string specified by \a text. The bounding rectangle always covers
at least the set of pixels the text would cover if drawn at (0,
@@ -777,21 +814,53 @@ QSize QFontMetrics::size(int flags, const QString &text, int tabStops, int *tabA
Newline characters are processed as normal characters, \e not as
linebreaks.
- \warning Calling this method is very slow on Windows.
+ \since 4.3
\sa horizontalAdvance(), height(), boundingRect()
*/
QRect QFontMetrics::tightBoundingRect(const QString &text) const
{
- if (text.length() == 0)
+ if (text.size() == 0)
return QRect();
QStackTextEngine layout(text, QFont(d.data()));
layout.itemize();
- glyph_metrics_t gm = layout.tightBoundingBox(0, text.length());
+ glyph_metrics_t gm = layout.tightBoundingBox(0, text.size());
return QRect(qRound(gm.x), qRound(gm.y), qRound(gm.width), qRound(gm.height));
}
+/*!
+ Returns a tight bounding rectangle around the characters in the
+ string specified by \a text laid out using \a option. The bounding
+ rectangle always covers at least the set of pixels the text would
+ cover if drawn at (0, 0).
+
+ Note that the bounding rectangle may extend to the left of (0, 0),
+ e.g. for italicized fonts, and that the width of the returned
+ rectangle might be different than what the horizontalAdvance() method
+ returns.
+
+ If you want to know the advance width of the string (to lay out
+ a set of strings next to each other), use horizontalAdvance() instead.
+
+ Newline characters are processed as normal characters, \e not as
+ linebreaks.
+
+ \since 6.3
+
+ \sa horizontalAdvance(), height(), boundingRect()
+*/
+QRect QFontMetrics::tightBoundingRect(const QString &text, const QTextOption &option) const
+{
+ if (text.size() == 0)
+ return QRect();
+
+ QStackTextEngine layout(text, QFont(d.data()));
+ layout.option = option;
+ layout.itemize();
+ glyph_metrics_t gm = layout.tightBoundingBox(0, text.size());
+ return QRect(qRound(gm.x), qRound(gm.y), qRound(gm.width), qRound(gm.height));
+}
/*!
\since 4.2
@@ -820,13 +889,13 @@ QString QFontMetrics::elidedText(const QString &text, Qt::TextElideMode mode, in
QString _text = text;
if (!(flags & Qt::TextLongestVariant)) {
int posA = 0;
- int posB = _text.indexOf(QLatin1Char('\x9c'));
+ int posB = _text.indexOf(s_variableLengthStringSeparator);
while (posB >= 0) {
QString portion = _text.mid(posA, posB - posA);
if (size(flags, portion).width() <= width)
return portion;
posA = posB + 1;
- posB = _text.indexOf(QLatin1Char('\x9c'), posA);
+ posB = _text.indexOf(s_variableLengthStringSeparator, posA);
}
_text = _text.mid(posA);
}
@@ -1075,9 +1144,8 @@ bool QFontMetricsF::operator ==(const QFontMetricsF &other) const
The ascent of a font is the distance from the baseline to the
highest position characters extend to. In practice, some font
designers break this rule, e.g. when they put more than one accent
- on top of a character, or to accommodate an unusual character in
- an exotic language, so it is possible (though rare) that this
- value will be too small.
+ on top of a character, or to accommodate a certain character, so
+ it is possible (though rare) that this value will be too small.
\sa descent()
*/
@@ -1113,8 +1181,8 @@ qreal QFontMetricsF::capHeight() const
The descent is the distance from the base line to the lowest point
characters extend to. (Note that this is different from X, which
adds 1 pixel.) In practice, some font designers break this rule,
- e.g. to accommodate an unusual character in an exotic language, so
- it is possible (though rare) that this value will be too small.
+ e.g. to accommodate a certain character, so it is possible (though
+ rare) that this value will be too small.
\sa ascent()
*/
@@ -1332,7 +1400,8 @@ qreal QFontMetricsF::rightBearing(QChar ch) const
/*!
Returns the horizontal advance in pixels of the first \a length characters of \a
text. If \a length is negative (the default), the entire string is
- used.
+ used. The entire length of \a text is analysed even if \a length is substantially
+ shorter.
The advance is the distance appropriate for drawing a subsequent
character after \a text.
@@ -1343,16 +1412,46 @@ qreal QFontMetricsF::rightBearing(QChar ch) const
*/
qreal QFontMetricsF::horizontalAdvance(const QString &text, int length) const
{
- int pos = text.indexOf(QLatin1Char('\x9c'));
+ int pos = (length >= 0)
+ ? QStringView(text).left(length).indexOf(s_variableLengthStringSeparator)
+ : text.indexOf(s_variableLengthStringSeparator);
if (pos != -1)
- length = (length < 0) ? pos : qMin(pos, length);
+ length = pos;
else if (length < 0)
- length = text.length();
+ length = text.size();
+
+ if (length == 0)
+ return 0;
+
+ QStackTextEngine layout(text, QFont(d.data()));
+ layout.itemize();
+ return layout.width(0, length).toReal();
+}
+
+/*!
+ Returns the horizontal advance in pixels of \a text laid out using \a option.
+
+ The advance is the distance appropriate for drawing a subsequent
+ character after \a text.
+
+ \since 6.3
+
+ \sa boundingRect()
+*/
+qreal QFontMetricsF::horizontalAdvance(const QString &text, const QTextOption &option) const
+{
+ int pos = text.indexOf(s_variableLengthStringSeparator);
+ int length = -1;
+ if (pos != -1)
+ length = pos;
+ else
+ length = text.size();
if (length == 0)
return 0;
QStackTextEngine layout(text, QFont(d.data()));
+ layout.option = option;
layout.itemize();
return layout.width(0, length).toReal();
}
@@ -1433,7 +1532,7 @@ qreal QFontMetricsF::horizontalAdvance(QChar ch) const
*/
QRectF QFontMetricsF::boundingRect(const QString &text) const
{
- int len = text.length();
+ int len = text.size();
if (len == 0)
return QRectF();
@@ -1445,6 +1544,42 @@ QRectF QFontMetricsF::boundingRect(const QString &text) const
}
/*!
+ Returns the bounding rectangle of the characters in the string
+ specified by \a text laid out using \a option. The bounding
+ rectangle always covers at least the set of pixels the text
+ would cover if drawn at (0, 0).
+
+ Note that the bounding rectangle may extend to the left of (0, 0),
+ e.g. for italicized fonts, and that the width of the returned
+ rectangle might be different than what the horizontalAdvance() method returns.
+
+ If you want to know the advance width of the string (to lay out
+ a set of strings next to each other), use horizontalAdvance() instead.
+
+ Newline characters are processed as normal characters, \e not as
+ linebreaks.
+
+ The height of the bounding rectangle is at least as large as the
+ value returned height().
+
+ \since 6.3
+ \sa horizontalAdvance(), height(), QPainter::boundingRect()
+*/
+QRectF QFontMetricsF::boundingRect(const QString &text, const QTextOption &option) const
+{
+ if (text.size() == 0)
+ return QRectF();
+
+ QStackTextEngine layout(text, QFont(d.data()));
+ layout.option = option;
+ layout.itemize();
+ glyph_metrics_t gm = layout.boundingBox(0, text.size());
+ return QRectF(gm.x.toReal(), gm.y.toReal(),
+ gm.width.toReal(), gm.height.toReal());
+}
+
+
+/*!
Returns the bounding rectangle of the character \a ch relative to
the left-most point on the base line.
@@ -1480,7 +1615,9 @@ QRectF QFontMetricsF::boundingRect(QChar ch) const
Returns the bounding rectangle of the characters in the given \a text.
This is the set of pixels the text would cover if drawn when constrained
- to the bounding rectangle specified by \a rect.
+ to the bounding rectangle specified by \a rect. If \a rect is a reference
+ to a \nullptr object, e.g. when passing a default constructed QRectF, the
+ bounding rectangle will not constrain itself to the size.
The \a flags argument is the bitwise OR of the following flags:
\list
@@ -1600,18 +1737,49 @@ QSizeF QFontMetricsF::size(int flags, const QString &text, int tabStops, int *ta
Newline characters are processed as normal characters, \e not as
linebreaks.
- \warning Calling this method is very slow on Windows.
-
\sa horizontalAdvance(), height(), boundingRect()
*/
QRectF QFontMetricsF::tightBoundingRect(const QString &text) const
{
- if (text.length() == 0)
- return QRect();
+ if (text.size() == 0)
+ return QRectF();
+
+ QStackTextEngine layout(text, QFont(d.data()));
+ layout.itemize();
+ glyph_metrics_t gm = layout.tightBoundingBox(0, text.size());
+ return QRectF(gm.x.toReal(), gm.y.toReal(), gm.width.toReal(), gm.height.toReal());
+}
+
+/*!
+ Returns a tight bounding rectangle around the characters in the
+ string specified by \a text laid out using \a option. The bounding
+ rectangle always covers at least the set of pixels the text would
+ cover if drawn at (0,0).
+
+ Note that the bounding rectangle may extend to the left of (0, 0),
+ e.g. for italicized fonts, and that the width of the returned
+ rectangle might be different than what the horizontalAdvance() method
+ returns.
+
+ If you want to know the advance width of the string (to lay out
+ a set of strings next to each other), use horizontalAdvance() instead.
+
+ Newline characters are processed as normal characters, \e not as
+ linebreaks.
+
+ \since 6.3
+
+ \sa horizontalAdvance(), height(), boundingRect()
+*/
+QRectF QFontMetricsF::tightBoundingRect(const QString &text, const QTextOption &option) const
+{
+ if (text.size() == 0)
+ return QRectF();
QStackTextEngine layout(text, QFont(d.data()));
+ layout.option = option;
layout.itemize();
- glyph_metrics_t gm = layout.tightBoundingBox(0, text.length());
+ glyph_metrics_t gm = layout.tightBoundingBox(0, text.size());
return QRectF(gm.x.toReal(), gm.y.toReal(), gm.width.toReal(), gm.height.toReal());
}
@@ -1641,13 +1809,13 @@ QString QFontMetricsF::elidedText(const QString &text, Qt::TextElideMode mode, q
QString _text = text;
if (!(flags & Qt::TextLongestVariant)) {
int posA = 0;
- int posB = _text.indexOf(QLatin1Char('\x9c'));
+ int posB = _text.indexOf(s_variableLengthStringSeparator);
while (posB >= 0) {
QString portion = _text.mid(posA, posB - posA);
if (size(flags, portion).width() <= width)
return portion;
posA = posB + 1;
- posB = _text.indexOf(QLatin1Char('\x9c'), posA);
+ posB = _text.indexOf(s_variableLengthStringSeparator, posA);
}
_text = _text.mid(posA);
}
diff --git a/src/gui/text/qfontmetrics.h b/src/gui/text/qfontmetrics.h
index 097d40bf8d..1942d1fa83 100644
--- a/src/gui/text/qfontmetrics.h
+++ b/src/gui/text/qfontmetrics.h
@@ -1,55 +1,21 @@
-/****************************************************************************
-**
-** 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 QFONTMETRICS_H
#define QFONTMETRICS_H
#include <QtGui/qtguiglobal.h>
#include <QtGui/qfont.h>
-#include <QtCore/qsharedpointer.h>
+
#ifndef QT_INCLUDE_COMPAT
#include <QtCore/qrect.h>
#endif
+#include <QtCore/qshareddata.h>
QT_BEGIN_NAMESPACE
class QRect;
+class QTextOption;
class Q_GUI_EXPORT QFontMetrics
{
@@ -60,11 +26,10 @@ public:
~QFontMetrics();
QFontMetrics &operator=(const QFontMetrics &);
- inline QFontMetrics &operator=(QFontMetrics &&other) noexcept
- { qSwap(d, other.d); return *this; }
+ QT_MOVE_ASSIGNMENT_OPERATOR_IMPL_VIA_PURE_SWAP(QFontMetrics)
void swap(QFontMetrics &other) noexcept
- { qSwap(d, other.d); }
+ { d.swap(other.d); }
int ascent() const;
int capHeight() const;
@@ -86,11 +51,13 @@ public:
int rightBearing(QChar) const;
int horizontalAdvance(const QString &, int len = -1) const;
+ int horizontalAdvance(const QString &, const QTextOption &textOption) const;
int horizontalAdvance(QChar) const;
QRect boundingRect(QChar) const;
QRect boundingRect(const QString &text) const;
+ QRect boundingRect(const QString &text, const QTextOption &textOption) const;
QRect boundingRect(const QRect &r, int flags, const QString &text, int tabstops = 0, int *tabarray = nullptr) const;
inline QRect boundingRect(int x, int y, int w, int h, int flags, const QString &text,
int tabstops = 0, int *tabarray = nullptr) const
@@ -98,6 +65,7 @@ public:
QSize size(int flags, const QString& str, int tabstops = 0, int *tabarray = nullptr) const;
QRect tightBoundingRect(const QString &text) const;
+ QRect tightBoundingRect(const QString &text, const QTextOption &textOption) const;
QString elidedText(const QString &text, Qt::TextElideMode mode, int width, int flags = 0) const;
@@ -131,10 +99,9 @@ public:
QFontMetricsF &operator=(const QFontMetricsF &);
QFontMetricsF &operator=(const QFontMetrics &);
- inline QFontMetricsF &operator=(QFontMetricsF &&other) noexcept
- { qSwap(d, other.d); return *this; }
+ QT_MOVE_ASSIGNMENT_OPERATOR_IMPL_VIA_PURE_SWAP(QFontMetricsF)
- void swap(QFontMetricsF &other) noexcept { qSwap(d, other.d); }
+ void swap(QFontMetricsF &other) noexcept { d.swap(other.d); }
qreal ascent() const;
qreal capHeight() const;
@@ -157,13 +124,16 @@ public:
qreal horizontalAdvance(const QString &string, int length = -1) const;
qreal horizontalAdvance(QChar) const;
+ qreal horizontalAdvance(const QString &string, const QTextOption &textOption) const;
QRectF boundingRect(const QString &string) const;
+ QRectF boundingRect(const QString &text, const QTextOption &textOption) const;
QRectF boundingRect(QChar) const;
QRectF boundingRect(const QRectF &r, int flags, const QString& string, int tabstops = 0, int *tabarray = nullptr) const;
QSizeF size(int flags, const QString& str, int tabstops = 0, int *tabarray = nullptr) const;
QRectF tightBoundingRect(const QString &text) const;
+ QRectF tightBoundingRect(const QString &text, const QTextOption &textOption) const;
QString elidedText(const QString &text, Qt::TextElideMode mode, qreal width, int flags = 0) const;
diff --git a/src/gui/text/qfontsubset.cpp b/src/gui/text/qfontsubset.cpp
index 9f1b19e4da..f6c973e522 100644
--- a/src/gui/text/qfontsubset.cpp
+++ b/src/gui/text/qfontsubset.cpp
@@ -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 "qfontsubset_p.h"
#include <qdebug.h>
@@ -49,6 +13,8 @@
QT_BEGIN_NAMESPACE
+using namespace Qt::StringLiterals;
+
#ifndef QT_NO_PDF
// This map is used for symbol fonts to get the correct glyph names for the latin range
@@ -112,24 +78,6 @@ QByteArray QFontSubset::glyphName(unsigned short unicode, bool symbol)
return buffer;
}
-QByteArray QFontSubset::glyphName(unsigned int glyph, const QList<int> &reverseMap) const
-{
- uint glyphIndex = glyph_indices[glyph];
-
- if (glyphIndex == 0)
- return "/.notdef";
-
- QByteArray ba;
- QPdf::ByteStream s(&ba);
- if (reverseMap[glyphIndex] && reverseMap[glyphIndex] < 0x10000) {
- s << '/' << glyphName(reverseMap[glyphIndex], false);
- } else {
- s << "/gl" << (int)glyphIndex;
- }
- return ba;
-}
-
-
QByteArray QFontSubset::widthArray() const
{
Q_ASSERT(!widths.isEmpty());
@@ -142,7 +90,7 @@ QByteArray QFontSubset::widthArray() const
QFixed defWidth = widths[0];
//qDebug("defWidth=%d, scale=%f", defWidth.toInt(), scale.toReal());
- for (int i = 0; i < nGlyphs(); ++i) {
+ for (qsizetype i = 0; i < nGlyphs(); ++i) {
if (defWidth != widths[i])
defWidth = 0;
}
@@ -150,10 +98,10 @@ QByteArray QFontSubset::widthArray() const
s << "/DW " << qRound(defWidth.toInt() * scale);
} else {
s << "/W [";
- for (int g = 0; g < nGlyphs();) {
+ for (qsizetype g = 0; g < nGlyphs();) {
QFixed w = widths[g];
- int start = g;
- int startLinear = 0;
+ qsizetype start = g;
+ qsizetype startLinear = 0;
++g;
while (g < nGlyphs()) {
QFixed nw = widths[g];
@@ -171,11 +119,11 @@ QByteArray QFontSubset::widthArray() const
// qDebug("start=%x startLinear=%x g-1=%x",start,startLinear,g-1);
if (g - startLinear < 10)
startLinear = 0;
- int endnonlinear = startLinear ? startLinear : g;
+ qsizetype endnonlinear = startLinear ? startLinear : g;
// qDebug(" startLinear=%x endnonlinear=%x", startLinear,endnonlinear);
if (endnonlinear > start) {
s << start << '[';
- for (int i = start; i < endnonlinear; ++i)
+ for (qsizetype i = start; i < endnonlinear; ++i)
s << qRound(widths[i].toInt() * scale);
s << "]\n";
}
@@ -201,7 +149,7 @@ QList<int> QFontSubset::getReverseMap() const
{
QList<int> reverseMap(0x10000, 0);
for (uint uc = 0; uc < 0x10000; ++uc) {
- int idx = glyph_indices.indexOf(fontEngine->glyphIndex(uc));
+ auto idx = glyph_indices.indexOf(fontEngine->glyphIndex(uc));
if (idx >= 0 && !reverseMap.at(idx))
reverseMap[idx] = uc;
}
@@ -229,14 +177,14 @@ QByteArray QFontSubset::createToUnicodeMap() const
QPdf::ByteStream s(&ranges);
char buf[5];
- for (int g = 1; g < nGlyphs(); ) {
+ for (qsizetype g = 1; g < nGlyphs(); ) {
int uc0 = reverseMap.at(g);
if (!uc0) {
++g;
continue;
}
- int start = g;
- int startLinear = 0;
+ qsizetype start = g;
+ qsizetype startLinear = 0;
++g;
while (g < nGlyphs()) {
int uc = reverseMap[g];
@@ -257,7 +205,7 @@ QByteArray QFontSubset::createToUnicodeMap() const
// qDebug("start=%x startLinear=%x g-1=%x",start,startLinear,g-1);
if (g - startLinear < 10)
startLinear = 0;
- int endnonlinear = startLinear ? startLinear : g;
+ qsizetype endnonlinear = startLinear ? startLinear : g;
// qDebug(" startLinear=%x endnonlinear=%x", startLinear,endnonlinear);
if (endnonlinear > start) {
s << '<' << QPdf::toHex((ushort)start, buf) << "> <";
@@ -266,7 +214,7 @@ QByteArray QFontSubset::createToUnicodeMap() const
s << '<' << QPdf::toHex((ushort)reverseMap[start], buf) << ">\n";
} else {
s << '[';
- for (int i = start; i < endnonlinear; ++i) {
+ for (qsizetype i = start; i < endnonlinear; ++i) {
s << '<' << QPdf::toHex((ushort)reverseMap[i], buf) << "> ";
}
s << "]\n";
@@ -275,9 +223,9 @@ QByteArray QFontSubset::createToUnicodeMap() const
}
if (startLinear) {
while (startLinear < g) {
- int len = g - startLinear;
- int uc_start = reverseMap[startLinear];
- int uc_end = uc_start + len - 1;
+ qsizetype len = g - startLinear;
+ qsizetype uc_start = reverseMap[startLinear];
+ qsizetype uc_end = uc_start + len - 1;
if ((uc_end >> 8) != (uc_start >> 8))
len = 256 - (uc_start & 0xff);
s << '<' << QPdf::toHex((ushort)startLinear, buf) << "> <";
@@ -300,9 +248,9 @@ QByteArray QFontSubset::createToUnicodeMap() const
return touc;
}
-int QFontSubset::addGlyph(int index)
+qsizetype QFontSubset::addGlyph(uint index)
{
- int idx = glyph_indices.indexOf(index);
+ qsizetype idx = glyph_indices.indexOf(index);
if (idx < 0) {
idx = glyph_indices.size();
glyph_indices.append(index);
@@ -343,7 +291,7 @@ struct QTtfTable {
Tag tag;
QByteArray data;
};
-Q_DECLARE_TYPEINFO(QTtfTable, Q_MOVABLE_TYPE);
+Q_DECLARE_TYPEINFO(QTtfTable, Q_RELOCATABLE_TYPE);
struct qttf_head_table {
@@ -391,7 +339,7 @@ struct qttf_name_table {
QString subfamily;
QString postscript_name;
};
-Q_DECLARE_TYPEINFO(qttf_name_table, Q_MOVABLE_TYPE);
+Q_DECLARE_TYPEINFO(qttf_name_table, Q_RELOCATABLE_TYPE);
static QTtfTable generateHead(const qttf_head_table &head);
@@ -419,7 +367,7 @@ struct QTtfGlyph {
quint16 numPoints;
QByteArray data;
};
-Q_DECLARE_TYPEINFO(QTtfGlyph, Q_MOVABLE_TYPE);
+Q_DECLARE_TYPEINFO(QTtfGlyph, Q_RELOCATABLE_TYPE);
static QTtfGlyph generateGlyph(int index, const QPainterPath &path, qreal advance, qreal lsb, qreal ppem);
// generates glyf, loca and hmtx
@@ -453,7 +401,7 @@ static QTtfTable generateHead(const qttf_head_table &head)
{
const int head_size = 54;
QTtfTable t;
- t.tag = MAKE_TAG('h', 'e', 'a', 'd');
+ t.tag = QFont::Tag("head").value();
t.data.resize(head_size);
QTtfStream s(t.data);
@@ -524,7 +472,7 @@ static QTtfTable generateHhea(const qttf_hhea_table &hhea)
{
const int hhea_size = 36;
QTtfTable t;
- t.tag = MAKE_TAG('h', 'h', 'e', 'a');
+ t.tag = QFont::Tag("hhea").value();
t.data.resize(hhea_size);
QTtfStream s(t.data);
@@ -575,7 +523,7 @@ static QTtfTable generateMaxp(const qttf_maxp_table &maxp)
{
const int maxp_size = 32;
QTtfTable t;
- t.tag = MAKE_TAG('m', 'a', 'x', 'p');
+ t.tag = QFont::Tag("maxp").value();
t.data.resize(maxp_size);
QTtfStream s(t.data);
@@ -619,7 +567,7 @@ struct QTtfNameRecord {
quint16 nameId;
QString value;
};
-Q_DECLARE_TYPEINFO(QTtfNameRecord, Q_MOVABLE_TYPE);
+Q_DECLARE_TYPEINFO(QTtfNameRecord, Q_RELOCATABLE_TYPE);
static QTtfTable generateName(const QList<QTtfNameRecord> &name);
@@ -639,8 +587,8 @@ static QTtfTable generateName(const qttf_name_table &name)
list.append(rec);
rec.nameId = 4;
rec.value = name.family;
- if (name.subfamily != QLatin1String("Regular"))
- rec.value += QLatin1Char(' ') + name.subfamily;
+ if (name.subfamily != "Regular"_L1)
+ rec.value += u' ' + name.subfamily;
list.append(rec);
rec.nameId = 6;
rec.value = name.postscript_name;
@@ -655,12 +603,12 @@ static QTtfTable generateName(const QList<QTtfNameRecord> &name)
const int char_size = 2;
QTtfTable t;
- t.tag = MAKE_TAG('n', 'a', 'm', 'e');
+ t.tag = QFont::Tag("name").value();
const int name_size = 6 + 12*name.size();
int string_size = 0;
for (int i = 0; i < name.size(); ++i) {
- string_size += name.at(i).value.length()*char_size;
+ string_size += name.at(i).value.size()*char_size;
}
t.data.resize(name_size + string_size);
@@ -676,7 +624,7 @@ static QTtfTable generateName(const QList<QTtfNameRecord> &name)
int off = 0;
for (int i = 0; i < name.size(); ++i) {
- int len = name.at(i).value.length()*char_size;
+ int len = name.at(i).value.size()*char_size;
// quint16 platformID Platform ID.
// quint16 encodingID Platform-specific encoding ID.
// quint16 languageID Language ID.
@@ -692,12 +640,8 @@ static QTtfTable generateName(const QList<QTtfNameRecord> &name)
off += len;
}
for (int i = 0; i < name.size(); ++i) {
- const QString &n = name.at(i).value;
- const ushort *uc = n.utf16();
- for (int i = 0; i < n.length(); ++i) {
- s << quint16(*uc);
- ++uc;
- }
+ for (QChar ch : name.at(i).value)
+ s << quint16(ch.unicode());
}
return t;
}
@@ -1014,15 +958,15 @@ static QList<QTtfTable> generateGlyphTables(qttf_font_tables &tables, const QLis
tables.hhea.numberOfHMetrics = nGlyphs;
QTtfTable glyf;
- glyf.tag = MAKE_TAG('g', 'l', 'y', 'f');
+ glyf.tag = QFont::Tag("glyf").value();
QTtfTable loca;
- loca.tag = MAKE_TAG('l', 'o', 'c', 'a');
+ loca.tag = QFont::Tag("loca").value();
loca.data.resize(glyf_size < max_size_small ? (nGlyphs+1)*sizeof(quint16) : (nGlyphs+1)*sizeof(quint32));
QTtfStream ls(loca.data);
QTtfTable hmtx;
- hmtx.tag = MAKE_TAG('h', 'm', 't', 'x');
+ hmtx.tag = QFont::Tag("hmtx").value();
hmtx.data.resize(nGlyphs*4);
QTtfStream hs(hmtx.data);
@@ -1122,12 +1066,12 @@ static QByteArray bindFont(const QList<QTtfTable>& _tables)
for (int i = 0; i < tables.size(); ++i) {
const QTtfTable &t = tables.at(i);
const quint32 size = (t.data.size() + 3) & ~3;
- if (t.tag == MAKE_TAG('h', 'e', 'a', 'd'))
+ if (t.tag == QFont::Tag("head").value())
head_offset = table_offset;
f << t.tag
<< checksum(t.data)
<< table_offset
- << t.data.size();
+ << quint32(t.data.size());
table_offset += size;
#define TAG(x) char(t.tag >> 24) << char((t.tag >> 16) & 0xff) << char((t.tag >> 8) & 0xff) << char(t.tag & 0xff)
//qDebug() << "table " << TAG(t.tag) << "has size " << t.data.size() << "stream at " << f.offset();
@@ -1203,13 +1147,12 @@ QByteArray QFontSubset::toTruetype() const
font.maxp.maxCompositeContours = 0;
font.maxp.maxComponentElements = 0;
font.maxp.maxComponentDepth = 0;
- const int numGlyphs = nGlyphs();
- font.maxp.numGlyphs = numGlyphs;
+ const qsizetype numGlyphs = nGlyphs();
+ font.maxp.numGlyphs = quint16(numGlyphs);
QList<QTtfGlyph> glyphs;
glyphs.reserve(numGlyphs);
- uint sumAdvances = 0;
- for (int i = 0; i < numGlyphs; ++i) {
+ for (qsizetype i = 0; i < numGlyphs; ++i) {
glyph_t g = glyph_indices.at(i);
QPainterPath path;
glyph_metrics_t metric;
@@ -1231,9 +1174,6 @@ QByteArray QFontSubset::toTruetype() const
font.maxp.maxPoints = qMax(font.maxp.maxPoints, glyph.numPoints);
font.maxp.maxContours = qMax(font.maxp.maxContours, glyph.numContours);
- if (glyph.xMax > glyph.xMin)
- sumAdvances += glyph.xMax - glyph.xMin;
-
// qDebug("adding glyph %d size=%d", glyph.index, glyph.data.size());
glyphs.append(glyph);
widths[i] = glyph.advanceWidth;
@@ -1246,25 +1186,25 @@ QByteArray QFontSubset::toTruetype() const
tables.append(generateMaxp(font.maxp));
// name
QTtfTable name_table;
- name_table.tag = MAKE_TAG('n', 'a', 'm', 'e');
+ name_table.tag = QFont::Tag("name").value();
if (!noEmbed)
name_table.data = fontEngine->getSfntTable(name_table.tag);
if (name_table.data.isEmpty()) {
qttf_name_table name;
if (noEmbed)
- name.copyright = QLatin1String("Fake font");
+ name.copyright = "Fake font"_L1;
else
- name.copyright = QLatin1String(properties.copyright);
- name.family = fontEngine->fontDef.family;
- name.subfamily = QLatin1String("Regular"); // ######
- name.postscript_name = QLatin1String(properties.postscriptName);
+ name.copyright = QLatin1StringView(properties.copyright);
+ name.family = fontEngine->fontDef.families.constFirst();
+ name.subfamily = "Regular"_L1; // ######
+ name.postscript_name = QLatin1StringView(properties.postscriptName);
name_table = generateName(name);
}
tables.append(name_table);
if (!noEmbed) {
QTtfTable os2;
- os2.tag = MAKE_TAG('O', 'S', '/', '2');
+ os2.tag = QFont::Tag("OS/2").value();
os2.data = fontEngine->getSfntTable(os2.tag);
if (!os2.data.isEmpty())
tables.append(os2);
diff --git a/src/gui/text/qfontsubset_agl.cpp b/src/gui/text/qfontsubset_agl.cpp
index d96808703a..603f277d3f 100644
--- a/src/gui/text/qfontsubset_agl.cpp
+++ b/src/gui/text/qfontsubset_agl.cpp
@@ -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
/* This file is autogenerated from the Adobe Glyph List database v1.7. Do not edit */
diff --git a/src/gui/text/qfontsubset_p.h b/src/gui/text/qfontsubset_p.h
index 1a3b392601..1ec47f176d 100644
--- a/src/gui/text/qfontsubset_p.h
+++ b/src/gui/text/qfontsubset_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 QFONTSUBSET_P_H
#define QFONTSUBSET_P_H
@@ -59,7 +23,7 @@ QT_BEGIN_NAMESPACE
class QFontSubset
{
public:
- explicit QFontSubset(QFontEngine *fe, int obj_id = 0)
+ explicit QFontSubset(QFontEngine *fe, uint obj_id = 0)
: object_id(obj_id), noEmbed(false), fontEngine(fe), downloaded_glyphs(0), standard_font(false)
{
fontEngine->ref.ref();
@@ -77,19 +41,18 @@ public:
QByteArray widthArray() const;
QByteArray createToUnicodeMap() const;
QList<int> getReverseMap() const;
- QByteArray glyphName(unsigned int glyph, const QList<int> &reverseMap) const;
static QByteArray glyphName(unsigned short unicode, bool symbol);
- int addGlyph(int index);
+ qsizetype addGlyph(uint index);
#endif
- const int object_id;
+ const uint object_id;
bool noEmbed;
QFontEngine *fontEngine;
- QList<int> glyph_indices;
+ QList<uint> glyph_indices;
mutable int downloaded_glyphs;
mutable bool standard_font;
- int nGlyphs() const { return glyph_indices.size(); }
+ qsizetype nGlyphs() const { return glyph_indices.size(); }
mutable QFixed emSquare;
mutable QList<QFixed> widths;
};
diff --git a/src/gui/text/qfragmentmap.cpp b/src/gui/text/qfragmentmap.cpp
index 7dd8999d58..4d55b009d7 100644
--- a/src/gui/text/qfragmentmap.cpp
+++ b/src/gui/text/qfragmentmap.cpp
@@ -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 <private/qtools_p.h>
diff --git a/src/gui/text/qfragmentmap_p.h b/src/gui/text/qfragmentmap_p.h
index 1d781352f8..af884d1f62 100644
--- a/src/gui/text/qfragmentmap_p.h
+++ b/src/gui/text/qfragmentmap_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 QFRAGMENTMAP_P_H
#define QFRAGMENTMAP_P_H
diff --git a/src/gui/text/qglyphrun.cpp b/src/gui/text/qglyphrun.cpp
index 8982e2e1c4..4252d9e7f1 100644
--- a/src/gui/text/qglyphrun.cpp
+++ b/src/gui/text/qglyphrun.cpp
@@ -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 "qglobal.h"
@@ -434,7 +398,7 @@ void QGlyphRun::setFlags(GlyphRunFlags flags)
/*!
Sets the bounding rect of the glyphs in this QGlyphRun to be \a boundingRect. This rectangle
- will be returned by boundingRect() unless it is empty, in which case the bounding rectangle of the
+ will be returned by boundingRect() unless it is null, in which case the bounding rectangle of the
glyphs in the glyph run will be returned instead.
\note Unless you are implementing text shaping, you should not have to use this function.
@@ -468,7 +432,7 @@ void QGlyphRun::setBoundingRect(const QRectF &boundingRect)
*/
QRectF QGlyphRun::boundingRect() const
{
- if (!d->boundingRect.isEmpty() || !d->rawFont.isValid())
+ if (!d->boundingRect.isNull() || !d->rawFont.isValid())
return d->boundingRect;
qreal minX, minY, maxX, maxY;
@@ -501,7 +465,80 @@ QRectF QGlyphRun::boundingRect() const
*/
bool QGlyphRun::isEmpty() const
{
- return d->glyphIndexDataSize == 0;
+ return d->glyphIndexDataSize == 0
+ && d->glyphPositionDataSize == 0
+ && d->stringIndexes.isEmpty()
+ && d->sourceString.isEmpty();
+}
+
+/*!
+ \since 6.5
+
+ Returns the string indexes corresponding to each glyph index, if the glyph run has been
+ constructed from a string and string indexes have been requested from the layout. In this case,
+ the length of the returned vector will correspond to the length of glyphIndexes(). In other
+ cases, it will be empty.
+
+ Since a single glyph may correspond to multiple characters in the source string, there may be
+ gaps in the list of string indexes. For instance, if the string "first" is processed by a font
+ which contains a ligature for the character pair "fi", then the five character string will
+ generate a glyph run consisting of only four glyphs. Then the glyph indexes may in this case be
+ (1, 2, 3, 4) (four arbitrary glyph indexes) whereas the string indexes would be (0, 2, 3, 4).
+ The glyphs are in the logical order of the string, thus it is implied that the first glyphs
+ spans characters 0 and 1 in this case.
+
+ Inversely, a single character may also generate multiple glyphs, in which case there will be
+ duplicate entries in the list of string indexes.
+
+ The string indexes correspond to the string, optionally available through sourceString().
+
+ \sa setStringIndexes(), sourceString(), QTextLayout::glyphRuns()
+*/
+QList<qsizetype> QGlyphRun::stringIndexes() const
+{
+ return d->stringIndexes;
+}
+
+/*!
+ \since 6.5
+
+ Sets the list of string indexes corresponding to the glyph indexes to \a stringIndexes
+
+ See stringIndexes() for more details on the conventions of this list.
+
+ \sa sourceString()
+ */
+void QGlyphRun::setStringIndexes(const QList<qsizetype> &stringIndexes)
+{
+ detach();
+ d->stringIndexes = stringIndexes;
+}
+
+/*!
+ \since 6.5
+
+ Returns the string corresponding to the glyph run, if the glyph run has been created from
+ a string and the string has been requested from the layout.
+
+ \sa setSourceString(), stringIndexes(), QTextLayout::glyphRuns()
+ */
+QString QGlyphRun::sourceString() const
+{
+ return d->sourceString;
+}
+
+/*!
+ \since 6.5
+
+ Set the string corresponding to the glyph run to \a sourceString. If set, the indexes returned
+ by stringIndexes() should be indexes into this string.
+
+ \sa sourceString(), stringIndexes()
+ */
+void QGlyphRun::setSourceString(const QString &sourceString)
+{
+ detach();
+ d->sourceString = sourceString;
}
QT_END_NAMESPACE
diff --git a/src/gui/text/qglyphrun.h b/src/gui/text/qglyphrun.h
index 0da8eddda8..88f9957dd8 100644
--- a/src/gui/text/qglyphrun.h
+++ b/src/gui/text/qglyphrun.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 QGLYPHRUN_H
#define QGLYPHRUN_H
@@ -43,8 +7,8 @@
#include <QtGui/qtguiglobal.h>
#include <QtCore/qlist.h>
#include <QtCore/qpoint.h>
-#include <QtCore/qsharedpointer.h>
#include <QtGui/qrawfont.h>
+#include <QtCore/qshareddata.h>
#if !defined(QT_NO_RAWFONT)
@@ -66,11 +30,11 @@ public:
QGlyphRun();
QGlyphRun(const QGlyphRun &other);
- QGlyphRun &operator=(QGlyphRun &&other) noexcept { swap(other); return *this; }
+ QT_MOVE_ASSIGNMENT_OPERATOR_IMPL_VIA_PURE_SWAP(QGlyphRun)
QGlyphRun &operator=(const QGlyphRun &other);
~QGlyphRun();
- void swap(QGlyphRun &other) noexcept { qSwap(d, other.d); }
+ void swap(QGlyphRun &other) noexcept { d.swap(other.d); }
QRawFont rawFont() const;
void setRawFont(const QRawFont &rawFont);
@@ -110,6 +74,12 @@ public:
void setBoundingRect(const QRectF &boundingRect);
QRectF boundingRect() const;
+ QList<qsizetype> stringIndexes() const;
+ void setStringIndexes(const QList<qsizetype> &stringIndexes);
+
+ void setSourceString(const QString &sourceString);
+ QString sourceString() const;
+
bool isEmpty() const;
private:
diff --git a/src/gui/text/qglyphrun_p.h b/src/gui/text/qglyphrun_p.h
index 77943fc0c0..db160344c6 100644
--- a/src/gui/text/qglyphrun_p.h
+++ b/src/gui/text/qglyphrun_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 QGLYPHRUN_P_H
#define QGLYPHRUN_P_H
@@ -51,6 +15,7 @@
// We mean it.
//
+#include <QtCore/qshareddata.h>
#include <QtGui/private/qtguiglobal_p.h>
#include "qglyphrun.h"
#include "qrawfont.h"
@@ -78,8 +43,10 @@ public:
: QSharedData(other)
, glyphIndexes(other.glyphIndexes)
, glyphPositions(other.glyphPositions)
+ , stringIndexes(other.stringIndexes)
, rawFont(other.rawFont)
, boundingRect(other.boundingRect)
+ , sourceString(other.sourceString)
, flags(other.flags)
, glyphIndexData(other.glyphIndexData)
, glyphIndexDataSize(other.glyphIndexDataSize)
@@ -92,8 +59,10 @@ public:
QList<quint32> glyphIndexes;
QList<QPointF> glyphPositions;
+ QList<qsizetype> stringIndexes;
QRawFont rawFont;
QRectF boundingRect;
+ QString sourceString;
QGlyphRun::GlyphRunFlags flags;
diff --git a/src/gui/text/qharfbuzzng.cpp b/src/gui/text/qharfbuzzng.cpp
index b4b67adc3e..5db6f78319 100644
--- a/src/gui/text/qharfbuzzng.cpp
+++ b/src/gui/text/qharfbuzzng.cpp
@@ -1,42 +1,6 @@
-/****************************************************************************
-**
-** Copyright (C) 2020 The Qt Company Ltd.
-** Copyright (C) 2013 Konstantin Ritt
-** 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) 2020 The Qt Company Ltd.
+// Copyright (C) 2013 Konstantin Ritt
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qharfbuzzng_p.h"
@@ -232,11 +196,40 @@ static const hb_script_t _qtscript_to_hbscript[] = {
HB_SCRIPT_NYIAKENG_PUACHUE_HMONG,
HB_SCRIPT_WANCHO,
- // Unicode 13.0 additions (not present in harfbuzz-ng 2.6.4)
- hb_script_t(HB_TAG('C', 'h', 'o', 'r')), // Script_Chorasmian
- hb_script_t(HB_TAG('D', 'i', 'v', 'e')), // Script_DivesAkuru
- hb_script_t(HB_TAG('K', 'h', 'i', 't')), // Script_KhitanSmallScript
+ // Unicode 13.0 additions (not present in harfbuzz-ng 2.6.6 and earlier)
+#if !HB_VERSION_ATLEAST(2, 6, 7)
+ hb_script_t(HB_TAG('C', 'h', 'r', 's')), // Script_Chorasmian
+ hb_script_t(HB_TAG('D', 'i', 'a', 'k')), // Script_DivesAkuru
+ hb_script_t(HB_TAG('K', 'i', 't', 's')), // Script_KhitanSmallScript
hb_script_t(HB_TAG('Y', 'e', 'z', 'i')), // Script_Yezidi
+#else
+ HB_SCRIPT_CHORASMIAN,
+ HB_SCRIPT_DIVES_AKURU,
+ HB_SCRIPT_KHITAN_SMALL_SCRIPT,
+ HB_SCRIPT_YEZIDI,
+#endif
+ // Unicode 14.0 additions (not present in harfbuzz-ng 2.9.1 and earlier)
+#if !HB_VERSION_ATLEAST(3, 0, 0)
+ hb_script_t(HB_TAG('C','p','m','n')), // Script_CyproMinoan
+ hb_script_t(HB_TAG('O','u','g','r')), // Script_OldUyghur
+ hb_script_t(HB_TAG('T','n','s','a')), // Script_Tangsa
+ hb_script_t(HB_TAG('T','o','t','o')), // Script_Toto
+ hb_script_t(HB_TAG('V','i','t','h')), // Script_Vithkuqi
+#else
+ HB_SCRIPT_CYPRO_MINOAN,
+ HB_SCRIPT_OLD_UYGHUR,
+ HB_SCRIPT_TANGSA,
+ HB_SCRIPT_TOTO,
+ HB_SCRIPT_VITHKUQI,
+#endif
+ // Unicode 15.0 additions (not present in harfbuzz-ng 5.1.0 and earlier)
+#if !HB_VERSION_ATLEAST(5, 2, 0)
+ hb_script_t(HB_TAG('K','a','w','i')), // Script_Kawi
+ hb_script_t(HB_TAG('N','a','g','m')), // Script_NagMundari
+#else
+ HB_SCRIPT_KAWI,
+ HB_SCRIPT_NAG_MUNDARI,
+#endif
};
static_assert(QChar::ScriptCount == sizeof(_qtscript_to_hbscript) / sizeof(_qtscript_to_hbscript[0]));
diff --git a/src/gui/text/qharfbuzzng_p.h b/src/gui/text/qharfbuzzng_p.h
index a7cd84fedc..a46251a4c4 100644
--- a/src/gui/text/qharfbuzzng_p.h
+++ b/src/gui/text/qharfbuzzng_p.h
@@ -1,42 +1,6 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Copyright (C) 2013 Konstantin Ritt
-** 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.
+// Copyright (C) 2013 Konstantin Ritt
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QHARFBUZZNG_P_H
#define QHARFBUZZNG_P_H
diff --git a/src/gui/text/qinputcontrol.cpp b/src/gui/text/qinputcontrol.cpp
index 3381fdb673..4edd46ff0e 100644
--- a/src/gui/text/qinputcontrol.cpp
+++ b/src/gui/text/qinputcontrol.cpp
@@ -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 "qinputcontrol_p.h"
#include <QtGui/qevent.h>
@@ -79,7 +43,10 @@ bool QInputControl::isAcceptableInput(const QKeyEvent *event) const
if (c.category() == QChar::Other_PrivateUse)
return true;
- if (m_type == TextEdit && c == QLatin1Char('\t'))
+ if (c.isHighSurrogate() && text.size() > 1 && text.at(1).isLowSurrogate())
+ return true;
+
+ if (m_type == TextEdit && c == u'\t')
return true;
return false;
@@ -137,3 +104,5 @@ bool QInputControl::isCommonTextEditShortcut(const QKeyEvent *ke)
}
QT_END_NAMESPACE
+
+#include "moc_qinputcontrol_p.cpp"
diff --git a/src/gui/text/qinputcontrol_p.h b/src/gui/text/qinputcontrol_p.h
index b4c1ca8f8f..fec73e1987 100644
--- a/src/gui/text/qinputcontrol_p.h
+++ b/src/gui/text/qinputcontrol_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 QINPUTCONTROL_P_H
#define QINPUTCONTROL_P_H
@@ -53,6 +17,7 @@
#include <QtCore/qobject.h>
#include <qtguiglobal.h>
+#include <private/qglobal_p.h>
QT_BEGIN_NAMESPACE
diff --git a/src/gui/text/qplatformfontdatabase.cpp b/src/gui/text/qplatformfontdatabase.cpp
index 21d43a249f..a146254f68 100644
--- a/src/gui/text/qplatformfontdatabase.cpp
+++ b/src/gui/text/qplatformfontdatabase.cpp
@@ -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 "qplatformfontdatabase.h"
#include <QtGui/private/qfontengine_p.h>
@@ -53,6 +17,8 @@
QT_BEGIN_NAMESPACE
+using namespace Qt::StringLiterals;
+
Q_LOGGING_CATEGORY(lcQpaFonts, "qt.qpa.fonts")
void qt_registerFont(const QString &familyname, const QString &stylename,
@@ -162,11 +128,31 @@ QSupportedWritingSystems &QSupportedWritingSystems::operator=(const QSupportedWr
return *this;
}
+bool operator==(const QSupportedWritingSystems &lhs, const QSupportedWritingSystems &rhs)
+{
+ return !(lhs != rhs);
+}
+
+bool operator!=(const QSupportedWritingSystems &lhs, const QSupportedWritingSystems &rhs)
+{
+ if (lhs.d == rhs.d)
+ return false;
+
+ Q_ASSERT(lhs.d->list.size() == rhs.d->list.size());
+ Q_ASSERT(lhs.d->list.size() == QFontDatabase::WritingSystemsCount);
+ for (int i = 0; i < QFontDatabase::WritingSystemsCount; ++i) {
+ if (lhs.d->list.at(i) != rhs.d->list.at(i))
+ return true;
+ }
+
+ return false;
+}
+
#ifndef QT_NO_DEBUG_STREAM
QDebug operator<<(QDebug debug, const QSupportedWritingSystems &sws)
{
- QMetaObject mo = QFontDatabase::staticMetaObject;
- QMetaEnum me = mo.enumerator(mo.indexOfEnumerator("WritingSystem"));
+ const QMetaObject *mo = &QFontDatabase::staticMetaObject;
+ QMetaEnum me = mo->enumerator(mo->indexOfEnumerator("WritingSystem"));
QDebugStateSaver saver(debug);
debug.nospace() << "QSupportedWritingSystems(";
@@ -293,6 +279,17 @@ QFontEngineMulti *QPlatformFontDatabase::fontEngineMulti(QFontEngine *fontEngine
/*!
Returns the font engine that can be used to render the font described by
the font definition, \a fontDef, in the specified \a script.
+
+ This function is called by QFontDatabase both for system fonts provided
+ by the platform font database, as well as for application fonts added by
+ the application developer.
+
+ The handle is the QPlatformFontDatabase specific handle passed when
+ registering the font family via QPlatformFontDatabase::registerFont.
+
+ The function is called for both fonts added via a filename as well
+ as fonts added from QByteArray data. Subclasses will need to handle
+ both cases via its platform specific handle.
*/
QFontEngine *QPlatformFontDatabase::fontEngine(const QFontDef &fontDef, void *handle)
{
@@ -302,6 +299,13 @@ QFontEngine *QPlatformFontDatabase::fontEngine(const QFontDef &fontDef, void *ha
return nullptr;
}
+/*!
+ Returns the font engine that will be used to back a QRawFont,
+ based on the given \fontData, \a pixelSize, and \a hintingPreference.
+
+ This function is called by QRawFont, and does not play a part in
+ the normal operations of QFontDatabase.
+*/
QFontEngine *QPlatformFontDatabase::fontEngine(const QByteArray &fontData, qreal pixelSize,
QFont::HintingPreference hintingPreference)
{
@@ -357,7 +361,7 @@ QString QPlatformFontDatabase::fontDir() const
{
QString fontpath = QString::fromLocal8Bit(qgetenv("QT_QPA_FONTDIR"));
if (fontpath.isEmpty())
- fontpath = QLibraryInfo::path(QLibraryInfo::LibrariesPath) + QLatin1String("/fonts");
+ fontpath = QLibraryInfo::path(QLibraryInfo::LibrariesPath) + "/fonts"_L1;
return fontpath;
}
@@ -381,7 +385,7 @@ bool QPlatformFontDatabase::isPrivateFontFamily(const QString &family) const
QFont QPlatformFontDatabase::defaultFont() const
{
- return QFont(QLatin1String("Helvetica"));
+ return QFont("Helvetica"_L1);
}
@@ -614,6 +618,24 @@ void QPlatformFontDatabase::registerAliasToFontFamily(const QString &familyName,
}
/*!
+ Requests that the platform font database should be repopulated.
+
+ This will result in invalidating the entire font database.
+
+ The next time the font database is accessed it will be repopulated
+ via a call to QPlatformFontDatabase::populate().
+
+ Application fonts will not be removed, and will be automatically
+ populated when the font database is repopulated.
+
+ \since 6.4
+*/
+void QPlatformFontDatabase::repopulateFontDatabase()
+{
+ QFontDatabasePrivate::instance()->invalidate();
+}
+
+/*!
Helper function that returns true if the font family has already been registered and populated.
\since 5.14
@@ -624,6 +646,18 @@ bool QPlatformFontDatabase::isFamilyPopulated(const QString &familyName)
}
/*!
+ Returns true if this font database supports loading named instances from variable application
+ fonts.
+
+ \since 6.7
+*/
+bool QPlatformFontDatabase::supportsVariableApplicationFonts() const
+{
+ return false;
+}
+
+
+/*!
\class QPlatformFontDatabase
\since 5.0
\internal
diff --git a/src/gui/text/qplatformfontdatabase.h b/src/gui/text/qplatformfontdatabase.h
index e464855bfd..3007a11838 100644
--- a/src/gui/text/qplatformfontdatabase.h
+++ b/src/gui/text/qplatformfontdatabase.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 QPLATFORMFONTDATABASE_H
#define QPLATFORMFONTDATABASE_H
@@ -54,9 +18,6 @@
#include <QtCore/QString>
#include <QtCore/QStringList>
#include <QtCore/QList>
-#if QT_DEPRECATED_SINCE(5, 5)
-#include <QtCore/QHash>
-#endif
#include <QtGui/QFontDatabase>
#include <QtGui/private/qfontengine_p.h>
#include <QtGui/private/qfont_p.h>
@@ -64,7 +25,7 @@
QT_BEGIN_NAMESPACE
-Q_DECLARE_LOGGING_CATEGORY(lcQpaFonts)
+Q_DECLARE_EXPORTED_LOGGING_CATEGORY(lcQpaFonts, Q_GUI_EXPORT)
class QWritingSystemsPrivate;
@@ -111,13 +72,13 @@ public:
virtual void populateFamily(const QString &familyName);
virtual void invalidate();
- virtual QFontEngineMulti *fontEngineMulti(QFontEngine *fontEngine, QChar::Script script);
- virtual QFontEngine *fontEngine(const QFontDef &fontDef, void *handle);
virtual QStringList fallbacksForFamily(const QString &family, QFont::Style style, QFont::StyleHint styleHint, QChar::Script script) const;
virtual QStringList addApplicationFont(const QByteArray &fontData, const QString &fileName, QFontDatabasePrivate::ApplicationFont *font = nullptr);
- virtual void releaseHandle(void *handle);
+ virtual QFontEngine *fontEngine(const QFontDef &fontDef, void *handle);
virtual QFontEngine *fontEngine(const QByteArray &fontData, qreal pixelSize, QFont::HintingPreference hintingPreference);
+ virtual QFontEngineMulti *fontEngineMulti(QFontEngine *fontEngine, QChar::Script script);
+ virtual void releaseHandle(void *handle);
virtual QString fontDir() const;
@@ -128,6 +89,8 @@ public:
virtual bool fontsAlwaysScalable() const;
virtual QList<int> standardSizes() const;
+ virtual bool supportsVariableApplicationFonts() const;
+
// helper
static QSupportedWritingSystems writingSystemsFromTrueTypeBits(quint32 unicodeRange[4], quint32 codePageRange[2]);
static QSupportedWritingSystems writingSystemsFromOS2Table(const char *os2Table, size_t length);
@@ -142,6 +105,8 @@ public:
static void registerFontFamily(const QString &familyName);
static void registerAliasToFontFamily(const QString &familyName, const QString &alias);
+ static void repopulateFontDatabase();
+
static bool isFamilyPopulated(const QString &familyName);
};
diff --git a/src/gui/text/qrawfont.cpp b/src/gui/text/qrawfont.cpp
index 36c94a080a..54676b3560 100644
--- a/src/gui/text/qrawfont.cpp
+++ b/src/gui/text/qrawfont.cpp
@@ -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 "qglobal.h"
@@ -281,12 +245,12 @@ QImage QRawFont::alphaMapForGlyph(quint32 glyphIndex, AntialiasingType antialias
return QImage();
if (d->fontEngine->glyphFormat == QFontEngine::Format_ARGB)
- return d->fontEngine->bitmapForGlyph(glyphIndex, QFixed(), transform);
+ return d->fontEngine->bitmapForGlyph(glyphIndex, QFixedPoint(), transform);
if (antialiasingType == SubPixelAntialiasing)
- return d->fontEngine->alphaRGBMapForGlyph(glyphIndex, QFixed(), transform);
+ return d->fontEngine->alphaRGBMapForGlyph(glyphIndex, QFixedPoint(), transform);
- return d->fontEngine->alphaMapForGlyph(glyphIndex, QFixed(), transform);
+ return d->fontEngine->alphaMapForGlyph(glyphIndex, QFixedPoint(), transform);
}
/*!
@@ -476,7 +440,7 @@ qreal QRawFont::underlinePosition() const
*/
QString QRawFont::familyName() const
{
- return d->isValid() ? d->fontEngine->fontDef.family : QString();
+ return d->isValid() ? d->fontEngine->fontDef.families.constFirst() : QString();
}
/*!
@@ -534,7 +498,7 @@ QList<quint32> QRawFont::glyphIndexesForString(const QString &text) const
QGlyphLayout glyphs;
glyphs.numGlyphs = numGlyphs;
glyphs.glyphs = glyphIndexes.data();
- if (!d->fontEngine->stringToCMap(text.data(), text.size(), &glyphs, &numGlyphs, QFontEngine::GlyphIndicesOnly))
+ if (d->fontEngine->stringToCMap(text.data(), text.size(), &glyphs, &numGlyphs, QFontEngine::GlyphIndicesOnly) < 0)
Q_UNREACHABLE();
glyphIndexes.resize(numGlyphs);
@@ -567,7 +531,7 @@ bool QRawFont::glyphIndexesForChars(const QChar *chars, int numChars, quint32 *g
QGlyphLayout glyphs;
glyphs.numGlyphs = *numGlyphs;
glyphs.glyphs = glyphIndexes;
- return d->fontEngine->stringToCMap(chars, numChars, &glyphs, numGlyphs, QFontEngine::GlyphIndicesOnly);
+ return d->fontEngine->stringToCMap(chars, numChars, &glyphs, numGlyphs, QFontEngine::GlyphIndicesOnly) >= 0;
}
/*!
@@ -579,7 +543,12 @@ bool QRawFont::glyphIndexesForChars(const QChar *chars, int numChars, quint32 *g
to make it appear as if the two glyphs are unspaced. How the advances are calculated is
controlled by \a layoutFlags.
- \sa QTextLine::horizontalAdvance(), QFontMetricsF::horizontalAdvance()
+ \note When \c KernedAdvances is requested, this function will apply kerning rules from the
+ TrueType table \c{KERN}, if this is available in the font. In many modern fonts, kerning is
+ handled through OpenType rules or AAT rules, which requires a full shaping step to be applied.
+ To get the results of fully shaping the text, use \l{QTextLayout}.
+
+ \sa QTextLine::horizontalAdvance(), QFontMetricsF::horizontalAdvance(), QTextLayout::glyphRuns()
*/
/*!
@@ -604,7 +573,12 @@ bool QRawFont::glyphIndexesForChars(const QChar *chars, int numChars, quint32 *g
array \a glyphIndexes while the results are returned through \a advances, both of them must
have \a numGlyphs elements. How the advances are calculated is controlled by \a layoutFlags.
- \sa QTextLine::horizontalAdvance(), QFontMetricsF::horizontalAdvance()
+ \note When \c KernedAdvances is requested, this function will apply kerning rules from the
+ TrueType table \c{KERN}, if this is available in the font. In many modern fonts, kerning is
+ handled through OpenType rules or AAT rules, which requires a full shaping step to be applied.
+ To get the results of fully shaping the text, use \l{QTextLayout}.
+
+ \sa QTextLine::horizontalAdvance(), QFontMetricsF::horizontalAdvance(), QTextLayout::glyphRuns()
*/
bool QRawFont::advancesForGlyphIndexes(const quint32 *glyphIndexes, QPointF *advances, int numGlyphs, LayoutFlags layoutFlags) const
{
@@ -658,17 +632,33 @@ QFont::HintingPreference QRawFont::hintingPreference() const
}
/*!
- Retrieves the sfnt table named \a tagName from the underlying physical font, or an empty
- byte array if no such table was found. The returned font table's byte order is Big Endian, like
- the sfnt format specifies. The \a tagName must be four characters long and should be formatted
- in the default endianness of the current platform.
+ \fn QByteArray QRawFont::fontTable(const char *tag) const
+ \overload fontTable(QFont::Tag)
+
+ The name must be a four-character string.
+*/
+
+/*!
+ \fn QByteArray QRawFont::fontTable(QFont::Tag tag) const
+ \since 6.7
+
+ Retrieves the sfnt table specified by \a tag from the underlying physical font,
+ or an empty byte array if no such table was found. The returned font table's byte order is
+ Big Endian, like the sfnt format specifies.
*/
-QByteArray QRawFont::fontTable(const char *tagName) const
+QByteArray QRawFont::fontTable(const char *tag) const
+{
+ if (auto maybeTag = QFont::Tag::fromString(tag))
+ return fontTable(*maybeTag);
+ return QByteArray();
+}
+
+QByteArray QRawFont::fontTable(QFont::Tag tag) const
{
if (!d->isValid())
return QByteArray();
- return d->fontEngine->getSfntTable(MAKE_TAG(tagName[0], tagName[1], tagName[2], tagName[3]));
+ return d->fontEngine->getSfntTable(tag.value());
}
/*!
@@ -760,7 +750,7 @@ QRawFont QRawFont::fromFont(const QFont &font, QFontDatabase::WritingSystem writ
QFontDef request(multiEngine->fontDef);
request.styleStrategy |= QFont::NoFontMerging;
- if (QFontEngine *engine = QFontDatabase::findFont(request, script)) {
+ if (QFontEngine *engine = QFontDatabasePrivate::findFont(request, script, true)) {
if (request.weight > QFont::Normal)
engine->fontDef.weight = request.weight;
if (request.style > QFont::StyleNormal)
diff --git a/src/gui/text/qrawfont.h b/src/gui/text/qrawfont.h
index e7e12df040..d23d0c1493 100644
--- a/src/gui/text/qrawfont.h
+++ b/src/gui/text/qrawfont.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 QRAWFONT_H
#define QRAWFONT_H
@@ -79,11 +43,11 @@ public:
qreal pixelSize,
QFont::HintingPreference hintingPreference = QFont::PreferDefaultHinting);
QRawFont(const QRawFont &other);
- QRawFont &operator=(QRawFont &&other) noexcept { swap(other); return *this; }
+ QT_MOVE_ASSIGNMENT_OPERATOR_IMPL_VIA_PURE_SWAP(QRawFont)
QRawFont &operator=(const QRawFont &other);
~QRawFont();
- void swap(QRawFont &other) noexcept { qSwap(d, other.d); }
+ void swap(QRawFont &other) noexcept { d.swap(other.d); }
bool isValid() const;
@@ -141,6 +105,7 @@ public:
QList<QFontDatabase::WritingSystem> supportedWritingSystems() const;
QByteArray fontTable(const char *tagName) const;
+ QByteArray fontTable(QFont::Tag tag) const;
static QRawFont fromFont(const QFont &font,
QFontDatabase::WritingSystem writingSystem = QFontDatabase::Any);
@@ -163,7 +128,7 @@ inline QList<QPointF> QRawFont::advancesForGlyphIndexes(const QList<quint32> &gl
QRawFont::LayoutFlags layoutFlags) const
{
QList<QPointF> advances(glyphIndexes.size());
- if (advancesForGlyphIndexes(glyphIndexes.constData(), advances.data(), glyphIndexes.size(), layoutFlags))
+ if (advancesForGlyphIndexes(glyphIndexes.constData(), advances.data(), int(glyphIndexes.size()), layoutFlags))
return advances;
return QList<QPointF>();
}
diff --git a/src/gui/text/qrawfont_p.h b/src/gui/text/qrawfont_p.h
index 03259a94ed..c69ea6dad2 100644
--- a/src/gui/text/qrawfont_p.h
+++ b/src/gui/text/qrawfont_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 QRAWFONTPRIVATE_P_H
#define QRAWFONTPRIVATE_P_H
diff --git a/src/gui/text/qstatictext.cpp b/src/gui/text/qstatictext.cpp
index 2d4fd5594d..eb7d19d64e 100644
--- a/src/gui/text/qstatictext.cpp
+++ b/src/gui/text/qstatictext.cpp
@@ -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 "qstatictext.h"
#include "qstatictext_p.h"
@@ -46,6 +10,8 @@
QT_BEGIN_NAMESPACE
+QT_IMPL_METATYPE_EXTERN(QStaticText)
+
QStaticTextUserData::~QStaticTextUserData()
{
}
@@ -84,7 +50,7 @@ QStaticTextUserData::~QStaticTextUserData()
class MyWidget: public QWidget
{
public:
- MyWidget(QWidget *parent = 0) : QWidget(parent), m_staticText("This is static text")
+ MyWidget(QWidget *parent = nullptr) : QWidget(parent), m_staticText("This is static text")
protected:
void paintEvent(QPaintEvent *)
diff --git a/src/gui/text/qstatictext.h b/src/gui/text/qstatictext.h
index e8c94a6add..7c8d8f8734 100644
--- a/src/gui/text/qstatictext.h
+++ b/src/gui/text/qstatictext.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 QSTATICTEXT_H
#define QSTATICTEXT_H
@@ -64,11 +28,11 @@ public:
QStaticText();
explicit QStaticText(const QString &text);
QStaticText(const QStaticText &other);
- QStaticText &operator=(QStaticText &&other) noexcept { swap(other); return *this; }
+ QT_MOVE_ASSIGNMENT_OPERATOR_IMPL_VIA_PURE_SWAP(QStaticText)
QStaticText &operator=(const QStaticText &);
~QStaticText();
- void swap(QStaticText &other) noexcept { qSwap(data, other.data); }
+ void swap(QStaticText &other) noexcept { data.swap(other.data); }
void setText(const QString &text);
QString text() const;
@@ -103,6 +67,6 @@ Q_DECLARE_SHARED(QStaticText)
QT_END_NAMESPACE
-Q_DECLARE_METATYPE(QStaticText)
+QT_DECL_METATYPE_EXTERN(QStaticText, Q_GUI_EXPORT)
#endif // QSTATICTEXT_H
diff --git a/src/gui/text/qstatictext_p.h b/src/gui/text/qstatictext_p.h
index 3760d3bf0f..cbf13e7b06 100644
--- a/src/gui/text/qstatictext_p.h
+++ b/src/gui/text/qstatictext_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 QSTATICTEXT_P_H
#define QSTATICTEXT_P_H
@@ -119,7 +83,7 @@ private: // private to avoid abuse
// ================
// 43 bytes per item
};
-Q_DECLARE_TYPEINFO(QStaticTextItem, Q_MOVABLE_TYPE);
+Q_DECLARE_TYPEINFO(QStaticTextItem, Q_RELOCATABLE_TYPE);
class QStaticText;
class Q_AUTOTEST_EXPORT QStaticTextPrivate
diff --git a/src/gui/text/qsyntaxhighlighter.cpp b/src/gui/text/qsyntaxhighlighter.cpp
index 5a837b29e3..6c1e2e74fc 100644
--- a/src/gui/text/qsyntaxhighlighter.cpp
+++ b/src/gui/text/qsyntaxhighlighter.cpp
@@ -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 "qsyntaxhighlighter.h"
@@ -102,41 +66,37 @@ void QSyntaxHighlighterPrivate::applyFormatChanges()
QList<QTextLayout::FormatRange> ranges = layout->formats();
const int preeditAreaStart = layout->preeditAreaPosition();
- const int preeditAreaLength = layout->preeditAreaText().length();
+ const int preeditAreaLength = layout->preeditAreaText().size();
if (preeditAreaLength != 0) {
auto isOutsidePreeditArea = [=](const QTextLayout::FormatRange &range) {
return range.start < preeditAreaStart
|| range.start + range.length > preeditAreaStart + preeditAreaLength;
};
- const auto it = std::remove_if(ranges.begin(), ranges.end(),
- isOutsidePreeditArea);
- if (it != ranges.end()) {
- ranges.erase(it, ranges.end());
+ if (ranges.removeIf(isOutsidePreeditArea) > 0)
formatsChanged = true;
- }
} else if (!ranges.isEmpty()) {
ranges.clear();
formatsChanged = true;
}
int i = 0;
- while (i < formatChanges.count()) {
+ while (i < formatChanges.size()) {
QTextLayout::FormatRange r;
- while (i < formatChanges.count() && formatChanges.at(i) == r.format)
+ while (i < formatChanges.size() && formatChanges.at(i) == r.format)
++i;
- if (i == formatChanges.count())
+ if (i == formatChanges.size())
break;
r.start = i;
r.format = formatChanges.at(i);
- while (i < formatChanges.count() && formatChanges.at(i) == r.format)
+ while (i < formatChanges.size() && formatChanges.at(i) == r.format)
++i;
- Q_ASSERT(i <= formatChanges.count());
+ Q_ASSERT(i <= formatChanges.size());
r.length = i - r.start;
if (preeditAreaLength != 0) {
@@ -259,7 +219,7 @@ void QSyntaxHighlighterPrivate::reformatBlock(const QTextBlock &block)
an int value. If no state is set, the returned value is -1. You
can designate any other value to identify any given state using
the setCurrentBlockState() function. Once the state is set the
- QTextBlock keeps that value until it is set set again or until the
+ QTextBlock keeps that value until it is set again or until the
corresponding paragraph of text is deleted.
For example, if you're writing a simple C++ syntax highlighter,
@@ -443,10 +403,10 @@ void QSyntaxHighlighter::rehighlightBlock(const QTextBlock &block)
void QSyntaxHighlighter::setFormat(int start, int count, const QTextCharFormat &format)
{
Q_D(QSyntaxHighlighter);
- if (start < 0 || start >= d->formatChanges.count())
+ if (start < 0 || start >= d->formatChanges.size())
return;
- const int end = qMin(start + count, d->formatChanges.count());
+ const int end = qMin(start + count, d->formatChanges.size());
for (int i = start; i < end; ++i)
d->formatChanges[i] = format;
}
@@ -496,7 +456,7 @@ void QSyntaxHighlighter::setFormat(int start, int count, const QFont &font)
QTextCharFormat QSyntaxHighlighter::format(int pos) const
{
Q_D(const QSyntaxHighlighter);
- if (pos < 0 || pos >= d->formatChanges.count())
+ if (pos < 0 || pos >= d->formatChanges.size())
return QTextCharFormat();
return d->formatChanges.at(pos);
}
diff --git a/src/gui/text/qsyntaxhighlighter.h b/src/gui/text/qsyntaxhighlighter.h
index ad0d50a4c0..3ce0e60e15 100644
--- a/src/gui/text/qsyntaxhighlighter.h
+++ b/src/gui/text/qsyntaxhighlighter.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 QSYNTAXHIGHLIGHTER_H
#define QSYNTAXHIGHLIGHTER_H
diff --git a/src/gui/text/qt_attribution.json b/src/gui/text/qt_attribution.json
index c3a57267e2..f4998da6ea 100644
--- a/src/gui/text/qt_attribution.json
+++ b/src/gui/text/qt_attribution.json
@@ -5,7 +5,7 @@
"QDocModule": "qtgui",
"Description": "Provides standardized names for glyphs.",
"QtUsage": "Used by PDF generator to make it easier for reader applications to resolve the original contents of rendered text.",
- "Path": "qfontsubset_agl.cpp",
+ "Files": "qfontsubset_agl.cpp",
"Homepage": "https://github.com/adobe-type-tools/agl-aglfn",
"Version": "1.7",
diff --git a/src/gui/text/qtextcursor.cpp b/src/gui/text/qtextcursor.cpp
index c579eca1ff..5730f55e6a 100644
--- a/src/gui/text/qtextcursor.cpp
+++ b/src/gui/text/qtextcursor.cpp
@@ -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 "qtextcursor.h"
#include "qtextcursor_p.h"
@@ -612,7 +576,7 @@ bool QTextCursorPrivate::movePosition(QTextCursor::MoveOperation op, QTextCursor
++row;
}
cell = table->cellAt(row, column);
- // note we also continue while we have not reached a cell thats not merged with one above us
+ // note we also continue while we have not reached a cell that's not merged with one above us
} while (cell.isValid()
&& ((op == QTextCursor::NextRow && currentRow == cell.row())
|| cell.row() < row));
@@ -625,7 +589,7 @@ bool QTextCursorPrivate::movePosition(QTextCursor::MoveOperation op, QTextCursor
--row;
}
cell = table->cellAt(row, column);
- // note we also continue while we have not reached a cell thats not merged with one above us
+ // note we also continue while we have not reached a cell that's not merged with one above us
} while (cell.isValid()
&& ((op == QTextCursor::PreviousRow && currentRow == cell.row())
|| cell.row() < row));
@@ -1459,28 +1423,28 @@ void QTextCursor::insertText(const QString &text, const QTextCharFormat &_format
QTextBlockFormat blockFmt = blockFormat();
- int textStart = d->priv->text.length();
+ int textStart = d->priv->text.size();
int blockStart = 0;
d->priv->text += text;
- int textEnd = d->priv->text.length();
+ int textEnd = d->priv->text.size();
- for (int i = 0; i < text.length(); ++i) {
+ for (int i = 0; i < text.size(); ++i) {
QChar ch = text.at(i);
const int blockEnd = i;
- if (ch == QLatin1Char('\r')
- && (i + 1) < text.length()
- && text.at(i + 1) == QLatin1Char('\n')) {
+ if (ch == u'\r'
+ && (i + 1) < text.size()
+ && text.at(i + 1) == u'\n') {
++i;
ch = text.at(i);
}
- if (ch == QLatin1Char('\n')
+ if (ch == u'\n'
|| ch == QChar::ParagraphSeparator
|| ch == QTextBeginningOfFrame
|| ch == QTextEndOfFrame
- || ch == QLatin1Char('\r')) {
+ || ch == u'\r') {
if (!hasEditBlock) {
hasEditBlock = true;
@@ -1715,7 +1679,7 @@ static void getText(QString &text, QTextDocumentPrivate *priv, const QString &do
const int offsetInFragment = qMax(0, pos - fragIt.position());
const int len = qMin(int(frag->size_array[0] - offsetInFragment), end - pos);
- text += QString(docText.constData() + frag->stringPosition + offsetInFragment, len);
+ text += QStringView(docText.constData() + frag->stringPosition + offsetInFragment, len);
pos += len;
}
}
@@ -2185,7 +2149,7 @@ QTextTable *QTextCursor::insertTable(int rows, int cols)
*/
QTextTable *QTextCursor::insertTable(int rows, int cols, const QTextTableFormat &format)
{
- if(!d || !d->priv || rows == 0 || cols == 0)
+ if (!d || !d->priv || rows == 0 || cols == 0)
return nullptr;
int pos = d->position;
@@ -2205,7 +2169,7 @@ QTextTable *QTextCursor::insertTable(int rows, int cols, const QTextTableFormat
*/
QTextTable *QTextCursor::currentTable() const
{
- if(!d || !d->priv)
+ if (!d || !d->priv)
return nullptr;
QTextFrame *frame = d->priv->frameAt(d->position);
@@ -2242,7 +2206,7 @@ QTextFrame *QTextCursor::insertFrame(const QTextFrameFormat &format)
*/
QTextFrame *QTextCursor::currentFrame() const
{
- if(!d || !d->priv)
+ if (!d || !d->priv)
return nullptr;
return d->priv->frameAt(d->position);
@@ -2291,6 +2255,28 @@ void QTextCursor::insertHtml(const QString &html)
#endif // QT_NO_TEXTHTMLPARSER
/*!
+ \since 6.4
+ Inserts the \a markdown text at the current position(),
+ with the specified Markdown \a features. The default is GitHub dialect.
+*/
+
+#if QT_CONFIG(textmarkdownreader)
+
+void QTextCursor::insertMarkdown(const QString &markdown, QTextDocument::MarkdownFeatures features)
+{
+ if (!d || !d->priv)
+ return;
+ QTextDocumentFragment fragment = QTextDocumentFragment::fromMarkdown(markdown, features);
+ if (markdown.startsWith(QLatin1Char('\n')))
+ insertBlock(fragment.d->doc->firstBlock().blockFormat());
+ insertFragment(fragment);
+ if (!atEnd() && markdown.endsWith(QLatin1Char('\n')))
+ insertText(QLatin1String("\n"));
+}
+
+#endif // textmarkdownreader
+
+/*!
\overload
\since 4.2
@@ -2314,7 +2300,7 @@ void QTextCursor::insertImage(const QTextImageFormat &format, QTextFrameFormat::
d->priv->beginEditBlock();
d->remove();
const int idx = d->priv->formatCollection()->indexForFormat(fmt);
- d->priv->insert(d->position, QString(QChar(QChar::ObjectReplacementCharacter)), idx);
+ d->priv->insert(d->position, QChar(QChar::ObjectReplacementCharacter), idx);
d->priv->endEditBlock();
}
diff --git a/src/gui/text/qtextcursor.h b/src/gui/text/qtextcursor.h
index 7cad3cc5e8..92c00c0189 100644
--- a/src/gui/text/qtextcursor.h
+++ b/src/gui/text/qtextcursor.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 QTEXTCURSOR_H
#define QTEXTCURSOR_H
@@ -43,12 +7,11 @@
#include <QtGui/qtguiglobal.h>
#include <QtCore/qstring.h>
#include <QtCore/qshareddata.h>
+#include <QtGui/qtextdocument.h>
#include <QtGui/qtextformat.h>
QT_BEGIN_NAMESPACE
-
-class QTextDocument;
class QTextCursorPrivate;
class QTextDocumentFragment;
class QTextCharFormat;
@@ -73,11 +36,11 @@ public:
explicit QTextCursor(QTextFrame *frame);
explicit QTextCursor(const QTextBlock &block);
QTextCursor(const QTextCursor &cursor);
- QTextCursor &operator=(QTextCursor &&other) noexcept { swap(other); return *this; }
+ QT_MOVE_ASSIGNMENT_OPERATOR_IMPL_VIA_PURE_SWAP(QTextCursor)
QTextCursor &operator=(const QTextCursor &other);
~QTextCursor();
- void swap(QTextCursor &other) noexcept { qSwap(d, other.d); }
+ void swap(QTextCursor &other) noexcept { d.swap(other.d); }
bool isNull() const;
@@ -201,6 +164,10 @@ public:
#ifndef QT_NO_TEXTHTMLPARSER
void insertHtml(const QString &html);
#endif // QT_NO_TEXTHTMLPARSER
+#if QT_CONFIG(textmarkdownreader)
+ void insertMarkdown(const QString &markdown,
+ QTextDocument::MarkdownFeatures features = QTextDocument::MarkdownDialectGitHub);
+#endif // textmarkdownreader
void insertImage(const QTextImageFormat &format, QTextFrameFormat::Position alignment);
void insertImage(const QTextImageFormat &format);
diff --git a/src/gui/text/qtextcursor_p.h b/src/gui/text/qtextcursor_p.h
index 3569317b6d..f035ba6acf 100644
--- a/src/gui/text/qtextcursor_p.h
+++ b/src/gui/text/qtextcursor_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 QTEXTCURSOR_P_H
#define QTEXTCURSOR_P_H
diff --git a/src/gui/text/qtextdocument.cpp b/src/gui/text/qtextdocument.cpp
index ff142794f6..15a313e13d 100644
--- a/src/gui/text/qtextdocument.cpp
+++ b/src/gui/text/qtextdocument.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2019 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) 2019 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 "qtextdocument.h"
#include <qtextformat.h>
@@ -47,6 +11,7 @@
#include "qtexttable.h"
#include "qtextlist.h"
#include <qdebug.h>
+#include <qloggingcategory.h>
#if QT_CONFIG(regularexpression)
#include <qregularexpression.h>
#endif
@@ -78,10 +43,19 @@
QT_BEGIN_NAMESPACE
+Q_DECLARE_LOGGING_CATEGORY(lcLayout);
+
+using namespace Qt::StringLiterals;
+
Q_CORE_EXPORT Q_DECL_CONST_FUNCTION unsigned int qt_int_sqrt(unsigned int n);
+namespace {
+ QTextDocument::ResourceProvider qt_defaultResourceProvider;
+};
/*!
+ \fn bool Qt::mightBeRichText(QAnyStringView text)
+
Returns \c true if the string \a text is likely to be rich text;
otherwise returns \c false.
@@ -91,57 +65,61 @@ Q_CORE_EXPORT Q_DECL_CONST_FUNCTION unsigned int qt_int_sqrt(unsigned int n);
for common cases, there is no guarantee.
This function is defined in the \c <QTextDocument> header file.
-*/
-bool Qt::mightBeRichText(const QString& text)
+
+ \note In Qt versions prior to 6.7, this function took QString only.
+ */
+template <typename T>
+static bool mightBeRichTextImpl(T text)
{
if (text.isEmpty())
return false;
- int start = 0;
+ qsizetype start = 0;
- while (start < text.length() && text.at(start).isSpace())
+ while (start < text.size() && QChar(text.at(start)).isSpace())
++start;
// skip a leading <?xml ... ?> as for example with xhtml
- if (QStringView{text}.mid(start, 5).compare(QLatin1String("<?xml")) == 0) {
- while (start < text.length()) {
- if (text.at(start) == QLatin1Char('?')
- && start + 2 < text.length()
- && text.at(start + 1) == QLatin1Char('>')) {
+ if (text.mid(start, 5).compare("<?xml"_L1) == 0) {
+ while (start < text.size()) {
+ if (text.at(start) == u'?'
+ && start + 2 < text.size()
+ && text.at(start + 1) == u'>') {
start += 2;
break;
}
++start;
}
- while (start < text.length() && text.at(start).isSpace())
+ while (start < text.size() && QChar(text.at(start)).isSpace())
++start;
}
- if (QStringView{text}.mid(start, 5).compare(QLatin1String("<!doc"), Qt::CaseInsensitive) == 0)
+ if (text.mid(start, 5).compare("<!doc"_L1, Qt::CaseInsensitive) == 0)
return true;
- int open = start;
- while (open < text.length() && text.at(open) != QLatin1Char('<')
- && text.at(open) != QLatin1Char('\n')) {
- if (text.at(open) == QLatin1Char('&') && QStringView{text}.mid(open + 1, 3) == QLatin1String("lt;"))
+ qsizetype open = start;
+ while (open < text.size() && text.at(open) != u'<'
+ && text.at(open) != u'\n') {
+ if (text.at(open) == u'&' && text.mid(open + 1, 3) == "lt;"_L1)
return true; // support desperate attempt of user to see <...>
++open;
}
- if (open < text.length() && text.at(open) == QLatin1Char('<')) {
- const int close = text.indexOf(QLatin1Char('>'), open);
+ if (open < text.size() && text.at(open) == u'<') {
+ const qsizetype close = text.indexOf(u'>', open);
if (close > -1) {
- QString tag;
- for (int i = open+1; i < close; ++i) {
- if (text[i].isDigit() || text[i].isLetter())
- tag += text[i];
- else if (!tag.isEmpty() && text[i].isSpace())
+ QVarLengthArray<char16_t> tag;
+ for (qsizetype i = open + 1; i < close; ++i) {
+ const auto current = QChar(text[i]);
+ if (current.isDigit() || current.isLetter())
+ tag.append(current.toLower().unicode());
+ else if (!tag.isEmpty() && current.isSpace())
break;
- else if (!tag.isEmpty() && text[i] == QLatin1Char('/') && i + 1 == close)
+ else if (!tag.isEmpty() && current == u'/' && i + 1 == close)
break;
- else if (!text[i].isSpace() && (!tag.isEmpty() || text[i] != QLatin1Char('!')))
+ else if (!current.isSpace() && (!tag.isEmpty() || current != u'!'))
return false; // that's not a tag
}
#ifndef QT_NO_TEXTHTMLPARSER
- return QTextHtmlParser::lookupElement(std::move(tag).toLower()) != -1;
+ return QTextHtmlParser::lookupElement(tag) != -1;
#else
return false;
#endif // QT_NO_TEXTHTMLPARSER
@@ -150,6 +128,16 @@ bool Qt::mightBeRichText(const QString& text)
return false;
}
+static bool mightBeRichTextImpl(QUtf8StringView text)
+{
+ return mightBeRichTextImpl(QLatin1StringView(QByteArrayView(text)));
+}
+
+bool Qt::mightBeRichText(QAnyStringView text)
+{
+ return text.visit([](auto text) { return mightBeRichTextImpl(text); });
+}
+
/*!
Converts the plain text string \a plain to an HTML-formatted
paragraph while preserving most of its look.
@@ -162,27 +150,27 @@ bool Qt::mightBeRichText(const QString& text)
*/
QString Qt::convertFromPlainText(const QString &plain, Qt::WhiteSpaceMode mode)
{
- int col = 0;
+ qsizetype col = 0;
QString rich;
- rich += QLatin1String("<p>");
- for (int i = 0; i < plain.length(); ++i) {
- if (plain[i] == QLatin1Char('\n')){
- int c = 1;
- while (i+1 < plain.length() && plain[i+1] == QLatin1Char('\n')) {
+ rich += "<p>"_L1;
+ for (qsizetype i = 0; i < plain.size(); ++i) {
+ if (plain[i] == u'\n'){
+ qsizetype c = 1;
+ while (i+1 < plain.size() && plain[i+1] == u'\n') {
i++;
c++;
}
if (c == 1)
- rich += QLatin1String("<br>\n");
+ rich += "<br>\n"_L1;
else {
- rich += QLatin1String("</p>\n");
+ rich += "</p>\n"_L1;
while (--c > 1)
- rich += QLatin1String("<br>\n");
- rich += QLatin1String("<p>");
+ rich += "<br>\n"_L1;
+ rich += "<p>"_L1;
}
col = 0;
} else {
- if (mode == Qt::WhiteSpacePre && plain[i] == QLatin1Char('\t')){
+ if (mode == Qt::WhiteSpacePre && plain[i] == u'\t'){
rich += QChar::Nbsp;
++col;
while (col % 8) {
@@ -192,19 +180,19 @@ QString Qt::convertFromPlainText(const QString &plain, Qt::WhiteSpaceMode mode)
}
else if (mode == Qt::WhiteSpacePre && plain[i].isSpace())
rich += QChar::Nbsp;
- else if (plain[i] == QLatin1Char('<'))
- rich += QLatin1String("&lt;");
- else if (plain[i] == QLatin1Char('>'))
- rich += QLatin1String("&gt;");
- else if (plain[i] == QLatin1Char('&'))
- rich += QLatin1String("&amp;");
+ else if (plain[i] == u'<')
+ rich += "&lt;"_L1;
+ else if (plain[i] == u'>')
+ rich += "&gt;"_L1;
+ else if (plain[i] == u'&')
+ rich += "&amp;"_L1;
else
rich += plain[i];
++col;
}
}
if (col != 0)
- rich += QLatin1String("</p>");
+ rich += "</p>"_L1;
return rich;
}
@@ -266,7 +254,7 @@ QString Qt::convertFromPlainText(const QString &plain, Qt::WhiteSpaceMode mode)
\li Text block group format changes.
\endlist
- \sa QTextCursor, QTextEdit, {Rich Text Processing}, {Text Object Example}
+ \sa QTextCursor, QTextEdit, {Rich Text Processing}
*/
/*!
@@ -348,12 +336,14 @@ QTextDocument *QTextDocument::clone(QObject *parent) const
QTextDocumentPrivate *priv = doc->d_func();
priv->title = d->title;
priv->url = d->url;
+ priv->cssMedia = d->cssMedia;
priv->pageSize = d->pageSize;
priv->indentWidth = d->indentWidth;
priv->defaultTextOption = d->defaultTextOption;
priv->setDefaultFont(d->defaultFont());
priv->resources = d->resources;
priv->cachedResources.clear();
+ priv->resourceProvider = d->resourceProvider;
#ifndef QT_NO_CSSPARSER
priv->defaultStyleSheet = d->defaultStyleSheet;
priv->parsedDefaultStyleSheet = d->parsedDefaultStyleSheet;
@@ -664,6 +654,40 @@ bool QTextDocument::useDesignMetrics() const
}
/*!
+ \property QTextDocument::layoutEnabled
+ \since 6.4
+ \brief whether QTextDocument should recalculate the layout after every change
+
+ If this property is set to true, any change to the document triggers a layout,
+ which makes everything work as expected but takes time.
+
+ Temporarily disabling the layout can save time when making multiple changes
+ (not just text content, but also default font, default text option....)
+ so that the document is only laid out once at the end. This can be useful when
+ the text width or page size isn't yet known, for instance.
+
+ By default, this property is \c true.
+
+ \sa setTextWidth
+*/
+
+void QTextDocument::setLayoutEnabled(bool b)
+{
+ Q_D(QTextDocument);
+ if (d->layoutEnabled == b)
+ return;
+ d->layoutEnabled = b;
+ if (b && d->lout)
+ d->lout->documentChanged(0, 0, d->length());
+}
+
+bool QTextDocument::isLayoutEnabled() const
+{
+ Q_D(const QTextDocument);
+ return d->layoutEnabled;
+}
+
+/*!
\since 4.2
Draws the content of the document with painter \a p, clipped to \a rect.
@@ -706,6 +730,8 @@ void QTextDocument::setTextWidth(qreal width)
{
Q_D(QTextDocument);
QSizeF sz = d->pageSize;
+
+ qCDebug(lcLayout) << "page size" << sz << "-> width" << width;
sz.setWidth(width);
sz.setHeight(-1);
setPageSize(sz);
@@ -812,7 +838,7 @@ void QTextDocument::adjustSize()
// Pull this private function in from qglobal.cpp
QFont f = defaultFont();
QFontMetrics fm(f);
- int mw = fm.horizontalAdvance(QLatin1Char('x')) * 80;
+ int mw = fm.horizontalAdvance(u'x') * 80;
int w = mw;
setTextWidth(w);
QSizeF size = documentLayout()->documentSize();
@@ -833,7 +859,7 @@ void QTextDocument::adjustSize()
\property QTextDocument::size
\since 4.2
- Returns the actual size of the document.
+ \brief the actual size of the document.
This is equivalent to documentLayout()->documentSize();
The size of the document can be changed either by setting
@@ -855,7 +881,7 @@ QSizeF QTextDocument::size() const
\property QTextDocument::blockCount
\since 4.2
- Returns the number of text blocks in the document.
+ \brief the number of text blocks in the document.
The value of this property is undefined in documents with tables or frames.
@@ -1131,6 +1157,10 @@ QString QTextDocument::metaInformation(MetaInformation info) const
return d->title;
case DocumentUrl:
return d->url;
+ case CssMedia:
+ return d->cssMedia;
+ case FrontMatter:
+ return d->frontMatter;
}
return QString();
}
@@ -1151,6 +1181,12 @@ void QTextDocument::setMetaInformation(MetaInformation info, const QString &stri
case DocumentUrl:
d->url = string;
break;
+ case CssMedia:
+ d->cssMedia = string;
+ break;
+ case FrontMatter:
+ d->frontMatter = string;
+ break;
}
}
@@ -1190,19 +1226,27 @@ QString QTextDocument::toPlainText() const
Q_D(const QTextDocument);
QString txt = d->plainText();
+ constexpr char16_t delims[] = { 0xfdd0, 0xfdd1,
+ QChar::ParagraphSeparator, QChar::LineSeparator, QChar::Nbsp };
+
+ const size_t pos = std::u16string_view(txt).find_first_of(
+ std::u16string_view(delims, std::size(delims)));
+ if (pos == std::u16string_view::npos)
+ return txt;
+
QChar *uc = txt.data();
- QChar *e = uc + txt.size();
+ QChar *const e = uc + txt.size();
- for (; uc != e; ++uc) {
+ for (uc += pos; uc != e; ++uc) {
switch (uc->unicode()) {
case 0xfdd0: // QTextBeginningOfFrame
case 0xfdd1: // QTextEndOfFrame
case QChar::ParagraphSeparator:
case QChar::LineSeparator:
- *uc = QLatin1Char('\n');
+ *uc = u'\n';
break;
case QChar::Nbsp:
- *uc = QLatin1Char(' ');
+ *uc = u' ';
break;
default:
;
@@ -1238,11 +1282,14 @@ void QTextDocument::setPlainText(const QString &text)
"<b>bold</b> text" will produce text where the first word has a font
weight that gives it a bold appearance: "\b{bold} text".
+ To select a css media rule other than the default "screen" rule,
+ use setMetaInformation() with 'CssMedia' as "info" parameter.
+
\note It is the responsibility of the caller to make sure that the
text is correctly decoded when a QString containing HTML is created
and passed to setHtml().
- \sa setPlainText(), {Supported HTML Subset}
+ \sa setPlainText(), {Supported HTML Subset}, setMetaInformation()
*/
#ifndef QT_NO_TEXTHTMLPARSER
@@ -1254,6 +1301,8 @@ void QTextDocument::setHtml(const QString &html)
d->enableUndoRedo(false);
d->beginEditBlock();
d->clear();
+ // ctor calls parse() to build up QTextHtmlParser::nodes list
+ // then import() populates the QTextDocument from those
QTextHtmlImporter(this, html, QTextHtmlImporter::ImportToDocument).import();
d->endEditBlock();
d->enableUndoRedo(previousState);
@@ -1282,19 +1331,26 @@ void QTextDocument::setHtml(const QString &html)
\value DocumentTitle The title of the document.
\value DocumentUrl The url of the document. The loadResource() function uses
this url as the base when loading relative resources.
+ \value CssMedia This value is used to select the corresponding '@media'
+ rule, if any, from a specified CSS stylesheet when setHtml()
+ is called. This enum value has been introduced in Qt 6.3.
+ \value FrontMatter This value is used to select header material, if any was
+ extracted during parsing of the source file (currently
+ only from Markdown format). This enum value has been
+ introduced in Qt 6.8.
- \sa metaInformation(), setMetaInformation()
+ \sa metaInformation(), setMetaInformation(), setHtml()
*/
static bool findInBlock(const QTextBlock &block, const QString &expression, int offset,
QTextDocument::FindFlags options, QTextCursor *cursor)
{
QString text = block.text();
- text.replace(QChar::Nbsp, QLatin1Char(' '));
+ text.replace(QChar::Nbsp, u' ');
Qt::CaseSensitivity sensitivity = options & QTextDocument::FindCaseSensitively ? Qt::CaseSensitive : Qt::CaseInsensitive;
int idx = -1;
- while (offset >= 0 && offset <= text.length()) {
+ while (offset >= 0 && offset <= text.size()) {
idx = (options & QTextDocument::FindBackward) ?
text.lastIndexOf(expression, offset, sensitivity) : text.indexOf(expression, offset, sensitivity);
if (idx == -1)
@@ -1302,9 +1358,9 @@ static bool findInBlock(const QTextBlock &block, const QString &expression, int
if (options & QTextDocument::FindWholeWords) {
const int start = idx;
- const int end = start + expression.length();
+ const int end = start + expression.size();
if ((start != 0 && text.at(start - 1).isLetterOrNumber())
- || (end != text.length() && text.at(end).isLetterOrNumber())) {
+ || (end != text.size() && text.at(end).isLetterOrNumber())) {
//if this is not a whole word, continue the search in the string
offset = (options & QTextDocument::FindBackward) ? idx-1 : end+1;
idx = -1;
@@ -1314,7 +1370,7 @@ static bool findInBlock(const QTextBlock &block, const QString &expression, int
//we have a hit, return the cursor for that.
*cursor = QTextCursorPrivate::fromPosition(const_cast<QTextDocumentPrivate *>(QTextDocumentPrivate::get(block)),
block.position() + idx);
- cursor->setPosition(cursor->position() + expression.length(), QTextCursor::KeepAnchor);
+ cursor->setPosition(cursor->position() + expression.size(), QTextCursor::KeepAnchor);
return true;
}
return false;
@@ -1410,11 +1466,11 @@ static bool findInBlock(const QTextBlock &block, const QRegularExpression &expr,
QTextDocument::FindFlags options, QTextCursor *cursor)
{
QString text = block.text();
- text.replace(QChar::Nbsp, QLatin1Char(' '));
+ text.replace(QChar::Nbsp, u' ');
QRegularExpressionMatch match;
int idx = -1;
- while (offset >= 0 && offset <= text.length()) {
+ while (offset >= 0 && offset <= text.size()) {
idx = (options & QTextDocument::FindBackward) ?
text.lastIndexOf(expr, offset, &match) : text.indexOf(expr, offset, &match);
if (idx == -1)
@@ -1424,7 +1480,7 @@ static bool findInBlock(const QTextBlock &block, const QRegularExpression &expr,
const int start = idx;
const int end = start + match.capturedLength();
if ((start != 0 && text.at(start - 1).isLetterOrNumber())
- || (end != text.length() && text.at(end).isLetterOrNumber())) {
+ || (end != text.size() && text.at(end).isLetterOrNumber())) {
//if this is not a whole word, continue the search in the string
offset = (options & QTextDocument::FindBackward) ? idx-1 : end+1;
idx = -1;
@@ -1455,6 +1511,10 @@ static bool findInBlock(const QTextBlock &block, const QRegularExpression &expr,
If the \a from position is 0 (the default) the search begins from the beginning
of the document; otherwise it begins at the specified position.
+
+ \warning For historical reasons, the case sensitivity option set on
+ \a expr is ignored. Instead, the \a options are used to determine
+ if the search is case sensitive or not.
*/
QTextCursor QTextDocument::find(const QRegularExpression &expr, int from, FindFlags options) const
{
@@ -1810,9 +1870,10 @@ void QTextDocument::setBaselineOffset(qreal baseline)
\fn qreal QTextDocument::baselineOffset() const
\since 6.0
- Returns the the baseline offset in % used in the document layout.
+ Returns the baseline offset in % used in the document layout.
- \sa setBaselineOffset(), setSubScriptBaseline(), subScriptBaseline(), setSuperScriptBaseline(), superScriptBaseline()
+ \sa setBaselineOffset(), setSubScriptBaseline(), subScriptBaseline(), setSuperScriptBaseline(),
+ superScriptBaseline()
*/
qreal QTextDocument::baselineOffset() const
{
@@ -1916,8 +1977,6 @@ void QTextDocument::print(QPagedPaintDevice *printer) const
bool documentPaginated = d->pageSize.isValid() && !d->pageSize.isNull()
&& d->pageSize.height() != INT_MAX;
- QPagedPaintDevicePrivate *pd = QPagedPaintDevicePrivate::get(printer);
-
// ### set page size to paginated size?
QMarginsF m = printer->pageLayout().margins(QPageLayout::Millimeter);
if (!documentPaginated && m.left() == 0. && m.right() == 0. && m.top() == 0. && m.bottom() == 0.) {
@@ -2003,9 +2062,9 @@ void QTextDocument::print(QPagedPaintDevice *printer) const
clonedDoc->setPageSize(body.size());
}
- QRangeCollection *rangeCollection = pd->rangeCollection;
- int fromPage = rangeCollection->firstPage();
- int toPage = rangeCollection->lastPage();
+ const QPageRanges pageRanges = printer->pageRanges();
+ int fromPage = pageRanges.firstPage();
+ int toPage = pageRanges.lastPage();
if (fromPage == 0 && toPage == 0) {
fromPage = 1;
@@ -2031,7 +2090,7 @@ void QTextDocument::print(QPagedPaintDevice *printer) const
int page = fromPage;
while (true) {
- if (rangeCollection->isEmpty() || rangeCollection->contains(page))
+ if (pageRanges.isEmpty() || pageRanges.contains(page))
printPage(page, &p, doc, body, pageNumberPos);
if (page == toPage)
@@ -2052,10 +2111,10 @@ void QTextDocument::print(QPagedPaintDevice *printer) const
\value UnknownResource No resource is loaded, or the resource type is not known.
\value HtmlResource The resource contains HTML.
\value ImageResource The resource contains image data.
- Currently supported data types are QVariant::Pixmap and
- QVariant::Image. If the corresponding variant is of type
- QVariant::ByteArray then Qt attempts to load the image using
- QImage::loadFromData. QVariant::Icon is currently not supported.
+ Currently supported data types are QMetaType::QPixmap and
+ QMetaType::QImage. If the corresponding variant is of type
+ QMetaType::QByteArray then Qt attempts to load the image using
+ QImage::loadFromData. QMetaType::QIcon is currently not supported.
The icon needs to be converted to one of the supported types first,
for example using QIcon::pixmap.
\value StyleSheetResource The resource contains CSS.
@@ -2080,7 +2139,11 @@ void QTextDocument::print(QPagedPaintDevice *printer) const
the resource. loadResource should then use addResource to add the
resource to the cache.
- \sa QTextDocument::ResourceType
+ If loadResource does not load the resource, then the resourceProvider and
+ lastly the defaultResourceProvider will be called, if set. Note that the
+ result from the provider will not be added automatically to the cache.
+
+ \sa QTextDocument::ResourceType, resourceProvider()
*/
QVariant QTextDocument::resource(int type, const QUrl &name) const
{
@@ -2089,8 +2152,15 @@ QVariant QTextDocument::resource(int type, const QUrl &name) const
QVariant r = d->resources.value(url);
if (!r.isValid()) {
r = d->cachedResources.value(url);
- if (!r.isValid())
+ if (!r.isValid()) {
r = const_cast<QTextDocument *>(this)->loadResource(type, url);
+ if (!r.isValid()) {
+ if (d->resourceProvider)
+ r = d->resourceProvider(url);
+ else if (auto defaultProvider = defaultResourceProvider())
+ r = defaultProvider(url);
+ }
+ }
}
return r;
}
@@ -2121,6 +2191,66 @@ void QTextDocument::addResource(int type, const QUrl &name, const QVariant &reso
}
/*!
+ \since 6.1
+
+ Returns the resource provider for this text document.
+
+ \sa setResourceProvider(), defaultResourceProvider(), loadResource()
+*/
+QTextDocument::ResourceProvider QTextDocument::resourceProvider() const
+{
+ Q_D(const QTextDocument);
+ return d->resourceProvider;
+}
+
+/*!
+ \since 6.1
+ \typealias QTextDocument::ResourceProvider
+
+ Type alias for std::function\<QVariant(const QUrl&)\>.
+*/
+
+/*!
+ \since 6.1
+
+ Sets the provider of resources for the text document to \a provider.
+
+ \sa resourceProvider(), loadResource()
+*/
+void QTextDocument::setResourceProvider(const ResourceProvider &provider)
+{
+ Q_D(QTextDocument);
+ d->resourceProvider = provider;
+}
+
+/*!
+ \since 6.1
+
+ Sets the default resource provider to \a provider.
+
+ The default provider will be used by all QTextDocuments that don't have an
+ explicit provider set.
+
+ \sa setResourceProvider(), loadResource()
+*/
+void QTextDocument::setDefaultResourceProvider(const ResourceProvider &provider)
+{
+ qt_defaultResourceProvider = provider;
+}
+
+/*!
+ \since 6.1
+
+ Returns the default resource provider.
+
+ \sa resourceProvider(), loadResource()
+*/
+QTextDocument::ResourceProvider QTextDocument::defaultResourceProvider()
+{
+ return qt_defaultResourceProvider;
+}
+
+/*!
Loads data of the specified \a type from the resource with the
given \a name.
@@ -2136,6 +2266,8 @@ void QTextDocument::addResource(int type, const QUrl &name, const QVariant &reso
loadResource method such as QTextEdit, QTextBrowser
or a QTextDocument itself then the default implementation tries
to retrieve the data from the parent.
+
+ \sa QTextDocument::ResourceProvider
*/
QVariant QTextDocument::loadResource(int type, const QUrl &name)
{
@@ -2148,12 +2280,13 @@ QVariant QTextDocument::loadResource(int type, const QUrl &name)
int index = me->indexOfMethod("loadResource(int,QUrl)");
if (index >= 0) {
QMetaMethod loader = me->method(index);
- loader.invoke(p, Q_RETURN_ARG(QVariant, r), Q_ARG(int, type), Q_ARG(QUrl, name));
+ // don't invoke() via a queued connection: this function needs to return a value
+ loader.invoke(p, Qt::DirectConnection, Q_RETURN_ARG(QVariant, r), Q_ARG(int, type), Q_ARG(QUrl, name));
}
}
// handle data: URLs
- if (r.isNull() && name.scheme().compare(QLatin1String("data"), Qt::CaseInsensitive) == 0) {
+ if (r.isNull() && name.scheme().compare("data"_L1, Qt::CaseInsensitive) == 0) {
QString mimetype;
QByteArray payload;
if (qDecodeDataUrl(name, mimetype, payload))
@@ -2169,7 +2302,7 @@ QVariant QTextDocument::loadResource(int type, const QUrl &name)
// For the second case QUrl can merge "#someanchor" with "foo.html"
// correctly to "foo.html#someanchor"
if (!(currentURL.isRelative()
- || (currentURL.scheme() == QLatin1String("file")
+ || (currentURL.scheme() == "file"_L1
&& !QFileInfo(currentURL.toLocalFile()).isAbsolute()))
|| (name.hasFragment() && name.path().isEmpty())) {
resourceUrl = currentURL.resolved(name);
@@ -2182,7 +2315,7 @@ QVariant QTextDocument::loadResource(int type, const QUrl &name)
resourceUrl =
QUrl::fromLocalFile(fi.absolutePath() + QDir::separator()).resolved(name);
} else if (currentURL.isEmpty()) {
- resourceUrl.setScheme(QLatin1String("file"));
+ resourceUrl.setScheme("file"_L1);
}
}
}
@@ -2236,16 +2369,16 @@ static QString colorValue(QColor color)
result = color.name();
} else if (color.alpha()) {
QString alphaValue = QString::number(color.alphaF(), 'f', 6);
- while (alphaValue.length() > 1 && alphaValue.at(alphaValue.size() - 1) == QLatin1Char('0'))
+ while (alphaValue.size() > 1 && alphaValue.at(alphaValue.size() - 1) == u'0')
alphaValue.chop(1);
- if (alphaValue.at(alphaValue.size() - 1) == QLatin1Char('.'))
+ if (alphaValue.at(alphaValue.size() - 1) == u'.')
alphaValue.chop(1);
result = QString::fromLatin1("rgba(%1,%2,%3,%4)").arg(color.red())
.arg(color.green())
.arg(color.blue())
.arg(alphaValue);
} else {
- result = QLatin1String("transparent");
+ result = "transparent"_L1;
}
return result;
@@ -2256,20 +2389,11 @@ QTextHtmlExporter::QTextHtmlExporter(const QTextDocument *_doc)
{
const QFont defaultFont = doc->defaultFont();
defaultCharFormat.setFont(defaultFont);
- // don't export those for the default font since we cannot turn them off with CSS
- defaultCharFormat.clearProperty(QTextFormat::FontUnderline);
- defaultCharFormat.clearProperty(QTextFormat::FontOverline);
- defaultCharFormat.clearProperty(QTextFormat::FontStrikeOut);
- defaultCharFormat.clearProperty(QTextFormat::TextUnderlineStyle);
}
static QStringList resolvedFontFamilies(const QTextCharFormat &format)
{
- QStringList fontFamilies = format.fontFamilies().toStringList();
- const QString mainFontFamily = format.fontFamily();
- if (!mainFontFamily.isEmpty() && !fontFamilies.contains(mainFontFamily))
- fontFamilies.append(mainFontFamily);
- return fontFamilies;
+ return format.fontFamilies().toStringList();
}
/*!
@@ -2279,68 +2403,94 @@ static QStringList resolvedFontFamilies(const QTextCharFormat &format)
*/
QString QTextHtmlExporter::toHtml(ExportMode mode)
{
- html = QLatin1String("<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0//EN\" "
- "\"http://www.w3.org/TR/REC-html40/strict.dtd\">\n"
- "<html><head><meta name=\"qrichtext\" content=\"1\" />");
+ html = "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0//EN\" "
+ "\"http://www.w3.org/TR/REC-html40/strict.dtd\">\n"
+ "<html><head><meta name=\"qrichtext\" content=\"1\" />"_L1;
html.reserve(QTextDocumentPrivate::get(doc)->length());
fragmentMarkers = (mode == ExportFragment);
- html += QString::fromLatin1("<meta charset=\"utf-8\" />");
+ html += "<meta charset=\"utf-8\" />"_L1;
QString title = doc->metaInformation(QTextDocument::DocumentTitle);
- if (!title.isEmpty())
- html += QString::fromLatin1("<title>") + title + QString::fromLatin1("</title>");
- html += QLatin1String("<style type=\"text/css\">\n");
- html += QLatin1String("p, li { white-space: pre-wrap; }\n");
- html += QLatin1String("</style>");
- html += QLatin1String("</head><body");
+ if (!title.isEmpty()) {
+ html += "<title>"_L1;
+ html += title;
+ html += "</title>"_L1;
+ }
+ html += "<style type=\"text/css\">\n"_L1;
+ html += "p, li { white-space: pre-wrap; }\n"_L1;
+ html += "hr { height: 1px; border-width: 0; }\n"_L1;
+ html += "li.unchecked::marker { content: \"\\2610\"; }\n"_L1;
+ html += "li.checked::marker { content: \"\\2612\"; }\n"_L1;
+ html += "</style>"_L1;
+ html += "</head><body"_L1;
if (mode == ExportEntireDocument) {
- html += QLatin1String(" style=\"");
+ html += " style=\""_L1;
emitFontFamily(resolvedFontFamilies(defaultCharFormat));
if (defaultCharFormat.hasProperty(QTextFormat::FontPointSize)) {
- html += QLatin1String(" font-size:");
+ html += " font-size:"_L1;
html += QString::number(defaultCharFormat.fontPointSize());
- html += QLatin1String("pt;");
+ html += "pt;"_L1;
} else if (defaultCharFormat.hasProperty(QTextFormat::FontPixelSize)) {
- html += QLatin1String(" font-size:");
+ html += " font-size:"_L1;
html += QString::number(defaultCharFormat.intProperty(QTextFormat::FontPixelSize));
- html += QLatin1String("px;");
+ html += "px;"_L1;
}
- html += QLatin1String(" font-weight:");
+ html += " font-weight:"_L1;
html += QString::number(defaultCharFormat.fontWeight());
- html += QLatin1Char(';');
+ html += u';';
- html += QLatin1String(" font-style:");
- html += (defaultCharFormat.fontItalic() ? QLatin1String("italic") : QLatin1String("normal"));
- html += QLatin1Char(';');
+ html += " font-style:"_L1;
+ html += (defaultCharFormat.fontItalic() ? "italic"_L1 : "normal"_L1);
+ html += u';';
const bool percentSpacing = (defaultCharFormat.fontLetterSpacingType() == QFont::PercentageSpacing);
if (defaultCharFormat.hasProperty(QTextFormat::FontLetterSpacing) &&
(!percentSpacing || defaultCharFormat.fontLetterSpacing() != 0.0)) {
- html += QLatin1String(" letter-spacing:");
+ html += " letter-spacing:"_L1;
qreal value = defaultCharFormat.fontLetterSpacing();
if (percentSpacing) // Map to em (100% == 0em)
value = (value / 100) - 1;
html += QString::number(value);
- html += percentSpacing ? QLatin1String("em;") : QLatin1String("px;");
+ html += percentSpacing ? "em;"_L1 : "px;"_L1;
}
if (defaultCharFormat.hasProperty(QTextFormat::FontWordSpacing) &&
defaultCharFormat.fontWordSpacing() != 0.0) {
- html += QLatin1String(" word-spacing:");
+ html += " word-spacing:"_L1;
html += QString::number(defaultCharFormat.fontWordSpacing());
- html += QLatin1String("px;");
+ html += "px;"_L1;
}
- // do not set text-decoration on the default font since those values are /always/ propagated
- // and cannot be turned off with CSS
+ QString decorationTag(" text-decoration:"_L1);
+ bool atLeastOneDecorationSet = false;
+ if (defaultCharFormat.hasProperty(QTextFormat::FontUnderline) || defaultCharFormat.hasProperty(QTextFormat::TextUnderlineStyle)) {
+ if (defaultCharFormat.fontUnderline()) {
+ decorationTag += " underline"_L1;
+ atLeastOneDecorationSet = true;
+ }
+ }
+ if (defaultCharFormat.hasProperty(QTextFormat::FontOverline)) {
+ if (defaultCharFormat.fontOverline()) {
+ decorationTag += " overline"_L1;
+ atLeastOneDecorationSet = true;
+ }
+ }
+ if (defaultCharFormat.hasProperty(QTextFormat::FontStrikeOut)) {
+ if (defaultCharFormat.fontStrikeOut()) {
+ decorationTag += " line-through"_L1;
+ atLeastOneDecorationSet = true;
+ }
+ }
+ if (atLeastOneDecorationSet)
+ html += decorationTag + u';';
- html += QLatin1Char('\"');
+ html += u'\"';
const QTextFrameFormat fmt = doc->rootFrame()->frameFormat();
emitBackgroundAttribute(fmt);
@@ -2348,7 +2498,7 @@ QString QTextHtmlExporter::toHtml(ExportMode mode)
} else {
defaultCharFormat = QTextCharFormat();
}
- html += QLatin1Char('>');
+ html += u'>';
QTextFrameFormat rootFmt = doc->rootFrame()->frameFormat();
rootFmt.clearProperty(QTextFormat::BackgroundBrush);
@@ -2361,17 +2511,17 @@ QString QTextHtmlExporter::toHtml(ExportMode mode)
else
emitTextFrame(doc->rootFrame());
- html += QLatin1String("</body></html>");
+ html += "</body></html>"_L1;
return html;
}
void QTextHtmlExporter::emitAttribute(const char *attribute, const QString &value)
{
- html += QLatin1Char(' ');
- html += QLatin1String(attribute);
- html += QLatin1String("=\"");
+ html += u' ';
+ html += QLatin1StringView(attribute);
+ html += "=\""_L1;
html += value.toHtmlEscaped();
- html += QLatin1Char('"');
+ html += u'"';
}
bool QTextHtmlExporter::emitCharFormatStyle(const QTextCharFormat &format)
@@ -2388,9 +2538,9 @@ bool QTextHtmlExporter::emitCharFormatStyle(const QTextCharFormat &format)
if (format.hasProperty(QTextFormat::FontPointSize)
&& format.fontPointSize() != defaultCharFormat.fontPointSize()) {
- html += QLatin1String(" font-size:");
+ html += " font-size:"_L1;
html += QString::number(format.fontPointSize());
- html += QLatin1String("pt;");
+ html += "pt;"_L1;
attributesEmitted = true;
} else if (format.hasProperty(QTextFormat::FontSizeAdjustment)) {
static const char sizeNameData[] =
@@ -2410,35 +2560,37 @@ bool QTextHtmlExporter::emitCharFormatStyle(const QTextCharFormat &format)
name = sizeNameData + sizeNameOffsets[idx];
}
if (name) {
- html += QLatin1String(" font-size:");
- html += QLatin1String(name);
- html += QLatin1Char(';');
+ html += " font-size:"_L1;
+ html += QLatin1StringView(name);
+ html += u';';
attributesEmitted = true;
}
- } else if (format.hasProperty(QTextFormat::FontPixelSize)) {
- html += QLatin1String(" font-size:");
+ } else if (format.hasProperty(QTextFormat::FontPixelSize)
+ && format.property(QTextFormat::FontPixelSize)
+ != defaultCharFormat.property(QTextFormat::FontPixelSize)) {
+ html += " font-size:"_L1;
html += QString::number(format.intProperty(QTextFormat::FontPixelSize));
- html += QLatin1String("px;");
+ html += "px;"_L1;
attributesEmitted = true;
}
if (format.hasProperty(QTextFormat::FontWeight)
&& format.fontWeight() != defaultCharFormat.fontWeight()) {
- html += QLatin1String(" font-weight:");
+ html += " font-weight:"_L1;
html += QString::number(format.fontWeight());
- html += QLatin1Char(';');
+ html += u';';
attributesEmitted = true;
}
if (format.hasProperty(QTextFormat::FontItalic)
&& format.fontItalic() != defaultCharFormat.fontItalic()) {
- html += QLatin1String(" font-style:");
- html += (format.fontItalic() ? QLatin1String("italic") : QLatin1String("normal"));
- html += QLatin1Char(';');
+ html += " font-style:"_L1;
+ html += (format.fontItalic() ? "italic"_L1 : "normal"_L1);
+ html += u';';
attributesEmitted = true;
}
- QLatin1String decorationTag(" text-decoration:");
+ const auto decorationTag = " text-decoration:"_L1;
html += decorationTag;
bool hasDecoration = false;
bool atLeastOneDecorationSet = false;
@@ -2447,7 +2599,7 @@ bool QTextHtmlExporter::emitCharFormatStyle(const QTextCharFormat &format)
&& format.fontUnderline() != defaultCharFormat.fontUnderline()) {
hasDecoration = true;
if (format.fontUnderline()) {
- html += QLatin1String(" underline");
+ html += " underline"_L1;
atLeastOneDecorationSet = true;
}
}
@@ -2456,7 +2608,7 @@ bool QTextHtmlExporter::emitCharFormatStyle(const QTextCharFormat &format)
&& format.fontOverline() != defaultCharFormat.fontOverline()) {
hasDecoration = true;
if (format.fontOverline()) {
- html += QLatin1String(" overline");
+ html += " overline"_L1;
atLeastOneDecorationSet = true;
}
}
@@ -2465,15 +2617,20 @@ bool QTextHtmlExporter::emitCharFormatStyle(const QTextCharFormat &format)
&& format.fontStrikeOut() != defaultCharFormat.fontStrikeOut()) {
hasDecoration = true;
if (format.fontStrikeOut()) {
- html += QLatin1String(" line-through");
+ html += " line-through"_L1;
atLeastOneDecorationSet = true;
}
}
if (hasDecoration) {
if (!atLeastOneDecorationSet)
- html += QLatin1String("none");
- html += QLatin1Char(';');
+ html += "none"_L1;
+ html += u';';
+ if (format.hasProperty(QTextFormat::TextUnderlineColor)) {
+ html += " text-decoration-color:"_L1;
+ html += colorValue(format.underlineColor());
+ html += u';';
+ }
attributesEmitted = true;
} else {
html.chop(decorationTag.size());
@@ -2486,61 +2643,120 @@ bool QTextHtmlExporter::emitCharFormatStyle(const QTextCharFormat &format)
const bool isPixmap = qHasPixmapTexture(brush);
const qint64 cacheKey = isPixmap ? brush.texture().cacheKey() : brush.textureImage().cacheKey();
- html += QLatin1String(" -qt-fg-texture-cachekey:");
+ html += " -qt-fg-texture-cachekey:"_L1;
html += QString::number(cacheKey);
- html += QLatin1String(";");
+ html += ";"_L1;
+ } else if (brush.style() == Qt::LinearGradientPattern
+ || brush.style() == Qt::RadialGradientPattern
+ || brush.style() == Qt::ConicalGradientPattern) {
+ const QGradient *gradient = brush.gradient();
+ if (gradient->type() == QGradient::LinearGradient) {
+ const QLinearGradient *linearGradient = static_cast<const QLinearGradient *>(brush.gradient());
+
+ html += " -qt-foreground: qlineargradient("_L1;
+ html += "x1:"_L1 + QString::number(linearGradient->start().x()) + u',';
+ html += "y1:"_L1 + QString::number(linearGradient->start().y()) + u',';
+ html += "x2:"_L1 + QString::number(linearGradient->finalStop().x()) + u',';
+ html += "y2:"_L1 + QString::number(linearGradient->finalStop().y()) + u',';
+ } else if (gradient->type() == QGradient::RadialGradient) {
+ const QRadialGradient *radialGradient = static_cast<const QRadialGradient *>(brush.gradient());
+
+ html += " -qt-foreground: qradialgradient("_L1;
+ html += "cx:"_L1 + QString::number(radialGradient->center().x()) + u',';
+ html += "cy:"_L1 + QString::number(radialGradient->center().y()) + u',';
+ html += "fx:"_L1 + QString::number(radialGradient->focalPoint().x()) + u',';
+ html += "fy:"_L1 + QString::number(radialGradient->focalPoint().y()) + u',';
+ html += "radius:"_L1 + QString::number(radialGradient->radius()) + u',';
+ } else {
+ const QConicalGradient *conicalGradient = static_cast<const QConicalGradient *>(brush.gradient());
+
+ html += " -qt-foreground: qconicalgradient("_L1;
+ html += "cx:"_L1 + QString::number(conicalGradient->center().x()) + u',';
+ html += "cy:"_L1 + QString::number(conicalGradient->center().y()) + u',';
+ html += "angle:"_L1 + QString::number(conicalGradient->angle()) + u',';
+ }
+
+ const QStringList coordinateModes = { "logical"_L1, "stretchtodevice"_L1, "objectbounding"_L1, "object"_L1 };
+ html += "coordinatemode:"_L1;
+ html += coordinateModes.at(int(gradient->coordinateMode()));
+ html += u',';
+
+ const QStringList spreads = { "pad"_L1, "reflect"_L1, "repeat"_L1 };
+ html += "spread:"_L1;
+ html += spreads.at(int(gradient->spread()));
+
+ for (const QGradientStop &stop : gradient->stops()) {
+ html += ",stop:"_L1;
+ html += QString::number(stop.first);
+ html += u' ';
+ html += colorValue(stop.second);
+ }
+
+ html += ");"_L1;
} else {
- html += QLatin1String(" color:");
+ html += " color:"_L1;
html += colorValue(brush.color());
- html += QLatin1Char(';');
+ html += u';';
}
attributesEmitted = true;
}
if (format.background() != defaultCharFormat.background()
&& format.background().style() == Qt::SolidPattern) {
- html += QLatin1String(" background-color:");
+ html += " background-color:"_L1;
html += colorValue(format.background().color());
- html += QLatin1Char(';');
+ html += u';';
attributesEmitted = true;
}
if (format.verticalAlignment() != defaultCharFormat.verticalAlignment()
&& format.verticalAlignment() != QTextCharFormat::AlignNormal)
{
- html += QLatin1String(" vertical-align:");
+ html += " vertical-align:"_L1;
QTextCharFormat::VerticalAlignment valign = format.verticalAlignment();
if (valign == QTextCharFormat::AlignSubScript)
- html += QLatin1String("sub");
+ html += "sub"_L1;
else if (valign == QTextCharFormat::AlignSuperScript)
- html += QLatin1String("super");
+ html += "super"_L1;
else if (valign == QTextCharFormat::AlignMiddle)
- html += QLatin1String("middle");
+ html += "middle"_L1;
else if (valign == QTextCharFormat::AlignTop)
- html += QLatin1String("top");
+ html += "top"_L1;
else if (valign == QTextCharFormat::AlignBottom)
- html += QLatin1String("bottom");
+ html += "bottom"_L1;
- html += QLatin1Char(';');
+ html += u';';
attributesEmitted = true;
}
if (format.fontCapitalization() != QFont::MixedCase) {
const QFont::Capitalization caps = format.fontCapitalization();
if (caps == QFont::AllUppercase)
- html += QLatin1String(" text-transform:uppercase;");
+ html += " text-transform:uppercase;"_L1;
else if (caps == QFont::AllLowercase)
- html += QLatin1String(" text-transform:lowercase;");
+ html += " text-transform:lowercase;"_L1;
else if (caps == QFont::SmallCaps)
- html += QLatin1String(" font-variant:small-caps;");
+ html += " font-variant:small-caps;"_L1;
attributesEmitted = true;
}
if (format.fontWordSpacing() != 0.0) {
- html += QLatin1String(" word-spacing:");
+ html += " word-spacing:"_L1;
html += QString::number(format.fontWordSpacing());
- html += QLatin1String("px;");
+ html += "px;"_L1;
+ attributesEmitted = true;
+ }
+
+ if (format.hasProperty(QTextFormat::TextOutline)) {
+ QPen outlinePen = format.textOutline();
+ html += " -qt-stroke-color:"_L1;
+ html += colorValue(outlinePen.color());
+ html += u';';
+
+ html += " -qt-stroke-width:"_L1;
+ html += QString::number(outlinePen.widthF());
+ html += "px;"_L1;
attributesEmitted = true;
}
@@ -2552,15 +2768,15 @@ void QTextHtmlExporter::emitTextLength(const char *attribute, const QTextLength
if (length.type() == QTextLength::VariableLength) // default
return;
- html += QLatin1Char(' ');
- html += QLatin1String(attribute);
- html += QLatin1String("=\"");
+ html += u' ';
+ html += QLatin1StringView(attribute);
+ html += "=\""_L1;
html += QString::number(length.rawValue());
if (length.type() == QTextLength::PercentageLength)
- html += QLatin1String("%\"");
+ html += "%\""_L1;
else
- html += QLatin1Char('\"');
+ html += u'\"';
}
void QTextHtmlExporter::emitAlignment(Qt::Alignment align)
@@ -2568,11 +2784,11 @@ void QTextHtmlExporter::emitAlignment(Qt::Alignment align)
if (align & Qt::AlignLeft)
return;
else if (align & Qt::AlignRight)
- html += QLatin1String(" align=\"right\"");
+ html += " align=\"right\""_L1;
else if (align & Qt::AlignHCenter)
- html += QLatin1String(" align=\"center\"");
+ html += " align=\"center\""_L1;
else if (align & Qt::AlignJustify)
- html += QLatin1String(" align=\"justify\"");
+ html += " align=\"justify\""_L1;
}
void QTextHtmlExporter::emitFloatStyle(QTextFrameFormat::Position pos, StyleMode mode)
@@ -2581,108 +2797,108 @@ void QTextHtmlExporter::emitFloatStyle(QTextFrameFormat::Position pos, StyleMode
return;
if (mode == EmitStyleTag)
- html += QLatin1String(" style=\"float:");
+ html += " style=\"float:"_L1;
else
- html += QLatin1String(" float:");
+ html += " float:"_L1;
if (pos == QTextFrameFormat::FloatLeft)
- html += QLatin1String(" left;");
+ html += " left;"_L1;
else if (pos == QTextFrameFormat::FloatRight)
- html += QLatin1String(" right;");
+ html += " right;"_L1;
else
Q_ASSERT_X(0, "QTextHtmlExporter::emitFloatStyle()", "pos should be a valid enum type");
if (mode == EmitStyleTag)
- html += QLatin1Char('\"');
+ html += u'\"';
}
-static QLatin1String richtextBorderStyleToHtmlBorderStyle(QTextFrameFormat::BorderStyle style)
+static QLatin1StringView richtextBorderStyleToHtmlBorderStyle(QTextFrameFormat::BorderStyle style)
{
switch (style) {
case QTextFrameFormat::BorderStyle_None:
- return QLatin1String("none");
+ return "none"_L1;
case QTextFrameFormat::BorderStyle_Dotted:
- return QLatin1String("dotted");
+ return "dotted"_L1;
case QTextFrameFormat::BorderStyle_Dashed:
- return QLatin1String("dashed");
+ return "dashed"_L1;
case QTextFrameFormat::BorderStyle_Solid:
- return QLatin1String("solid");
+ return "solid"_L1;
case QTextFrameFormat::BorderStyle_Double:
- return QLatin1String("double");
+ return "double"_L1;
case QTextFrameFormat::BorderStyle_DotDash:
- return QLatin1String("dot-dash");
+ return "dot-dash"_L1;
case QTextFrameFormat::BorderStyle_DotDotDash:
- return QLatin1String("dot-dot-dash");
+ return "dot-dot-dash"_L1;
case QTextFrameFormat::BorderStyle_Groove:
- return QLatin1String("groove");
+ return "groove"_L1;
case QTextFrameFormat::BorderStyle_Ridge:
- return QLatin1String("ridge");
+ return "ridge"_L1;
case QTextFrameFormat::BorderStyle_Inset:
- return QLatin1String("inset");
+ return "inset"_L1;
case QTextFrameFormat::BorderStyle_Outset:
- return QLatin1String("outset");
+ return "outset"_L1;
default:
Q_UNREACHABLE();
};
- return QLatin1String("");
+ return ""_L1;
}
void QTextHtmlExporter::emitBorderStyle(QTextFrameFormat::BorderStyle style)
{
Q_ASSERT(style <= QTextFrameFormat::BorderStyle_Outset);
- html += QLatin1String(" border-style:");
+ html += " border-style:"_L1;
html += richtextBorderStyleToHtmlBorderStyle(style);
- html += QLatin1Char(';');
+ html += u';';
}
void QTextHtmlExporter::emitPageBreakPolicy(QTextFormat::PageBreakFlags policy)
{
if (policy & QTextFormat::PageBreak_AlwaysBefore)
- html += QLatin1String(" page-break-before:always;");
+ html += " page-break-before:always;"_L1;
if (policy & QTextFormat::PageBreak_AlwaysAfter)
- html += QLatin1String(" page-break-after:always;");
+ html += " page-break-after:always;"_L1;
}
void QTextHtmlExporter::emitFontFamily(const QStringList &families)
{
- html += QLatin1String(" font-family:");
+ html += " font-family:"_L1;
bool first = true;
for (const QString &family : families) {
- QLatin1String quote("\'");
- if (family.contains(QLatin1Char('\'')))
- quote = QLatin1String("&quot;");
+ auto quote = "\'"_L1;
+ if (family.contains(u'\''))
+ quote = "&quot;"_L1;
if (!first)
- html += QLatin1String(",");
+ html += ","_L1;
else
first = false;
html += quote;
html += family.toHtmlEscaped();
html += quote;
}
- html += QLatin1Char(';');
+ html += u';';
}
void QTextHtmlExporter::emitMargins(const QString &top, const QString &bottom, const QString &left, const QString &right)
{
- html += QLatin1String(" margin-top:");
+ html += " margin-top:"_L1;
html += top;
- html += QLatin1String("px;");
+ html += "px;"_L1;
- html += QLatin1String(" margin-bottom:");
+ html += " margin-bottom:"_L1;
html += bottom;
- html += QLatin1String("px;");
+ html += "px;"_L1;
- html += QLatin1String(" margin-left:");
+ html += " margin-left:"_L1;
html += left;
- html += QLatin1String("px;");
+ html += "px;"_L1;
- html += QLatin1String(" margin-right:");
+ html += " margin-right:"_L1;
html += right;
- html += QLatin1String("px;");
+ html += "px;"_L1;
}
void QTextHtmlExporter::emitFragment(const QTextFragment &fragment)
@@ -2694,15 +2910,15 @@ void QTextHtmlExporter::emitFragment(const QTextFragment &fragment)
if (format.isAnchor()) {
const auto names = format.anchorNames();
if (!names.isEmpty()) {
- html += QLatin1String("<a name=\"");
+ html += "<a name=\""_L1;
html += names.constFirst().toHtmlEscaped();
- html += QLatin1String("\"></a>");
+ html += "\"></a>"_L1;
}
const QString href = format.anchorHref();
if (!href.isEmpty()) {
- html += QLatin1String("<a href=\"");
+ html += "<a href=\""_L1;
html += href.toHtmlEscaped();
- html += QLatin1String("\">");
+ html += "\">"_L1;
closeAnchor = true;
}
}
@@ -2711,22 +2927,22 @@ void QTextHtmlExporter::emitFragment(const QTextFragment &fragment)
const bool isObject = txt.contains(QChar::ObjectReplacementCharacter);
const bool isImage = isObject && format.isImageFormat();
- QLatin1String styleTag("<span style=\"");
+ const auto styleTag = "<span style=\""_L1;
html += styleTag;
bool attributesEmitted = false;
if (!isImage)
attributesEmitted = emitCharFormatStyle(format);
if (attributesEmitted)
- html += QLatin1String("\">");
+ html += "\">"_L1;
else
html.chop(styleTag.size());
if (isObject) {
- for (int i = 0; isImage && i < txt.length(); ++i) {
+ for (int i = 0; isImage && i < txt.size(); ++i) {
QTextImageFormat imgFmt = format.toImageFormat();
- html += QLatin1String("<img");
+ html += "<img"_L1;
if (imgFmt.hasProperty(QTextFormat::ImageName))
emitAttribute("src", imgFmt.name());
@@ -2744,14 +2960,14 @@ void QTextHtmlExporter::emitFragment(const QTextFragment &fragment)
emitAttribute("height", QString::number(imgFmt.height()));
if (imgFmt.verticalAlignment() == QTextCharFormat::AlignMiddle)
- html += QLatin1String(" style=\"vertical-align: middle;\"");
+ html += " style=\"vertical-align: middle;\""_L1;
else if (imgFmt.verticalAlignment() == QTextCharFormat::AlignTop)
- html += QLatin1String(" style=\"vertical-align: top;\"");
+ html += " style=\"vertical-align: top;\""_L1;
if (QTextFrame *imageFrame = qobject_cast<QTextFrame *>(doc->objectForFormat(imgFmt)))
emitFloatStyle(imageFrame->frameFormat().position());
- html += QLatin1String(" />");
+ html += " />"_L1;
}
} else {
Q_ASSERT(!txt.contains(QChar::ObjectReplacementCharacter));
@@ -2760,16 +2976,16 @@ void QTextHtmlExporter::emitFragment(const QTextFragment &fragment)
// split for [\n{LineSeparator}]
// space in BR on purpose for compatibility with old-fashioned browsers
- txt.replace(QLatin1Char('\n'), QLatin1String("<br />"));
- txt.replace(QChar::LineSeparator, QLatin1String("<br />"));
+ txt.replace(u'\n', "<br />"_L1);
+ txt.replace(QChar::LineSeparator, "<br />"_L1);
html += txt;
}
if (attributesEmitted)
- html += QLatin1String("</span>");
+ html += "</span>"_L1;
if (closeAnchor)
- html += QLatin1String("</a>");
+ html += "</a>"_L1;
}
static bool isOrderedList(int style)
@@ -2787,16 +3003,16 @@ void QTextHtmlExporter::emitBlockAttributes(const QTextBlock &block)
emitAlignment(format.alignment());
// assume default to not bloat the html too much
- // html += QLatin1String(" dir='ltr'");
+ // html += " dir='ltr'"_L1;
if (block.textDirection() == Qt::RightToLeft)
- html += QLatin1String(" dir='rtl'");
+ html += " dir='rtl'"_L1;
- QLatin1String style(" style=\"");
+ const auto style = " style=\""_L1;
html += style;
const bool emptyBlock = block.begin().atEnd();
if (emptyBlock) {
- html += QLatin1String("-qt-paragraph-type:empty;");
+ html += "-qt-paragraph-type:empty;"_L1;
}
emitMargins(QString::number(format.topMargin()),
@@ -2804,38 +3020,38 @@ void QTextHtmlExporter::emitBlockAttributes(const QTextBlock &block)
QString::number(format.leftMargin()),
QString::number(format.rightMargin()));
- html += QLatin1String(" -qt-block-indent:");
+ html += " -qt-block-indent:"_L1;
html += QString::number(format.indent());
- html += QLatin1Char(';');
+ html += u';';
- html += QLatin1String(" text-indent:");
+ html += " text-indent:"_L1;
html += QString::number(format.textIndent());
- html += QLatin1String("px;");
+ html += "px;"_L1;
if (block.userState() != -1) {
- html += QLatin1String(" -qt-user-state:");
+ html += " -qt-user-state:"_L1;
html += QString::number(block.userState());
- html += QLatin1Char(';');
+ html += u';';
}
if (format.lineHeightType() != QTextBlockFormat::SingleHeight) {
- html += QLatin1String(" line-height:")
+ html += " line-height:"_L1
+ QString::number(format.lineHeight());
switch (format.lineHeightType()) {
case QTextBlockFormat::ProportionalHeight:
- html += QLatin1String("%;");
+ html += "%;"_L1;
break;
case QTextBlockFormat::FixedHeight:
- html += QLatin1String("; -qt-line-height-type: fixed;");
+ html += "; -qt-line-height-type: fixed;"_L1;
break;
case QTextBlockFormat::MinimumHeight:
- html += QLatin1String("px;");
+ html += "px;"_L1;
break;
case QTextBlockFormat::LineDistanceHeight:
- html += QLatin1String("; -qt-line-height-type: line-distance;");
+ html += "; -qt-line-height-type: line-distance;"_L1;
break;
default:
- html += QLatin1String(";");
+ html += ";"_L1;
break; // Should never reach here
}
}
@@ -2858,7 +3074,7 @@ void QTextHtmlExporter::emitBlockAttributes(const QTextBlock &block)
if (!diff.properties().isEmpty())
emitCharFormatStyle(diff);
- html += QLatin1Char('"');
+ html += u'"';
}
@@ -2877,7 +3093,7 @@ void QTextHtmlExporter::emitBlock(const QTextBlock &block)
return;
}
- html += QLatin1Char('\n');
+ html += u'\n';
// save and later restore, in case we 'change' the default format by
// emitting block char format information
@@ -2888,128 +3104,178 @@ void QTextHtmlExporter::emitBlock(const QTextBlock &block)
if (list->itemNumber(block) == 0) { // first item? emit <ul> or appropriate
const QTextListFormat format = list->format();
const int style = format.style();
+ bool ordered = false;
switch (style) {
- case QTextListFormat::ListDecimal: html += QLatin1String("<ol"); break;
- case QTextListFormat::ListDisc: html += QLatin1String("<ul"); break;
- case QTextListFormat::ListCircle: html += QLatin1String("<ul type=\"circle\""); break;
- case QTextListFormat::ListSquare: html += QLatin1String("<ul type=\"square\""); break;
- case QTextListFormat::ListLowerAlpha: html += QLatin1String("<ol type=\"a\""); break;
- case QTextListFormat::ListUpperAlpha: html += QLatin1String("<ol type=\"A\""); break;
- case QTextListFormat::ListLowerRoman: html += QLatin1String("<ol type=\"i\""); break;
- case QTextListFormat::ListUpperRoman: html += QLatin1String("<ol type=\"I\""); break;
- default: html += QLatin1String("<ul"); // ### should not happen
+ case QTextListFormat::ListDisc: html += "<ul"_L1; break;
+ case QTextListFormat::ListCircle: html += "<ul type=\"circle\""_L1; break;
+ case QTextListFormat::ListSquare: html += "<ul type=\"square\""_L1; break;
+ case QTextListFormat::ListDecimal: html += "<ol"_L1; ordered = true; break;
+ case QTextListFormat::ListLowerAlpha: html += "<ol type=\"a\""_L1; ordered = true; break;
+ case QTextListFormat::ListUpperAlpha: html += "<ol type=\"A\""_L1; ordered = true; break;
+ case QTextListFormat::ListLowerRoman: html += "<ol type=\"i\""_L1; ordered = true; break;
+ case QTextListFormat::ListUpperRoman: html += "<ol type=\"I\""_L1; ordered = true; break;
+ default: html += "<ul"_L1; // ### should not happen
}
- QString styleString = QString::fromLatin1("margin-top: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px;");
+ if (ordered && format.start() != 1) {
+ html += " start=\""_L1;
+ html += QString::number(format.start());
+ html += u'"';
+ }
+
+ QString styleString;
+ styleString += "margin-top: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px;"_L1;
if (format.hasProperty(QTextFormat::ListIndent)) {
- styleString += QLatin1String(" -qt-list-indent: ");
+ styleString += " -qt-list-indent: "_L1;
styleString += QString::number(format.indent());
- styleString += QLatin1Char(';');
+ styleString += u';';
}
if (format.hasProperty(QTextFormat::ListNumberPrefix)) {
QString numberPrefix = format.numberPrefix();
- numberPrefix.replace(QLatin1Char('"'), QLatin1String("\\22"));
- numberPrefix.replace(QLatin1Char('\''), QLatin1String("\\27")); // FIXME: There's a problem in the CSS parser the prevents this from being correctly restored
- styleString += QLatin1String(" -qt-list-number-prefix: ");
- styleString += QLatin1Char('\'');
+ numberPrefix.replace(u'"', "\\22"_L1);
+ numberPrefix.replace(u'\'', "\\27"_L1); // FIXME: There's a problem in the CSS parser the prevents this from being correctly restored
+ styleString += " -qt-list-number-prefix: "_L1;
+ styleString += u'\'';
styleString += numberPrefix;
- styleString += QLatin1Char('\'');
- styleString += QLatin1Char(';');
+ styleString += u'\'';
+ styleString += u';';
}
if (format.hasProperty(QTextFormat::ListNumberSuffix)) {
- if (format.numberSuffix() != QLatin1String(".")) { // this is our default
+ if (format.numberSuffix() != "."_L1) { // this is our default
QString numberSuffix = format.numberSuffix();
- numberSuffix.replace(QLatin1Char('"'), QLatin1String("\\22"));
- numberSuffix.replace(QLatin1Char('\''), QLatin1String("\\27")); // see above
- styleString += QLatin1String(" -qt-list-number-suffix: ");
- styleString += QLatin1Char('\'');
+ numberSuffix.replace(u'"', "\\22"_L1);
+ numberSuffix.replace(u'\'', "\\27"_L1); // see above
+ styleString += " -qt-list-number-suffix: "_L1;
+ styleString += u'\'';
styleString += numberSuffix;
- styleString += QLatin1Char('\'');
- styleString += QLatin1Char(';');
+ styleString += u'\'';
+ styleString += u';';
}
}
- html += QLatin1String(" style=\"");
+ html += " style=\""_L1;
html += styleString;
- html += QLatin1String("\">");
+ html += "\">\n"_L1;
}
- html += QLatin1String("<li");
+ html += "<li"_L1;
const QTextCharFormat blockFmt = formatDifference(defaultCharFormat, block.charFormat()).toCharFormat();
if (!blockFmt.properties().isEmpty()) {
- html += QLatin1String(" style=\"");
+ html += " style=\""_L1;
emitCharFormatStyle(blockFmt);
- html += QLatin1Char('\"');
+ html += u'\"';
defaultCharFormat.merge(block.charFormat());
}
+ if (block.blockFormat().hasProperty(QTextFormat::BlockMarker)) {
+ switch (block.blockFormat().marker()) {
+ case QTextBlockFormat::MarkerType::Checked:
+ html += " class=\"checked\""_L1;
+ break;
+ case QTextBlockFormat::MarkerType::Unchecked:
+ html += " class=\"unchecked\""_L1;
+ break;
+ case QTextBlockFormat::MarkerType::NoMarker:
+ break;
+ }
+ }
}
const QTextBlockFormat blockFormat = block.blockFormat();
if (blockFormat.hasProperty(QTextFormat::BlockTrailingHorizontalRulerWidth)) {
- html += QLatin1String("<hr");
+ html += "<hr"_L1;
QTextLength width = blockFormat.lengthProperty(QTextFormat::BlockTrailingHorizontalRulerWidth);
if (width.type() != QTextLength::VariableLength)
emitTextLength("width", width);
- else
- html += QLatin1Char(' ');
+ html += u' ';
+
+ if (blockFormat.hasProperty(QTextFormat::BackgroundBrush)) {
+ html += "style=\""_L1;
+ html += "background-color:"_L1;
+ html += colorValue(qvariant_cast<QBrush>(blockFormat.property(QTextFormat::BackgroundBrush)).color());
+ html += u';';
+ html += u'\"';
+ }
- html += QLatin1String("/>");
+ html += "/>"_L1;
return;
}
const bool pre = blockFormat.nonBreakableLines();
if (pre) {
if (list)
- html += QLatin1Char('>');
- html += QLatin1String("<pre");
+ html += u'>';
+ html += "<pre"_L1;
} else if (!list) {
int headingLevel = blockFormat.headingLevel();
if (headingLevel > 0 && headingLevel <= 6)
- html += QLatin1String("<h") + QString::number(headingLevel);
+ html += "<h"_L1 + QString::number(headingLevel);
else
- html += QLatin1String("<p");
+ html += "<p"_L1;
}
emitBlockAttributes(block);
- html += QLatin1Char('>');
+ html += u'>';
if (block.begin().atEnd())
- html += QLatin1String("<br />");
+ html += "<br />"_L1;
QTextBlock::Iterator it = block.begin();
if (fragmentMarkers && !it.atEnd() && block == doc->begin())
- html += QLatin1String("<!--StartFragment-->");
+ html += "<!--StartFragment-->"_L1;
for (; !it.atEnd(); ++it)
emitFragment(it.fragment());
if (fragmentMarkers && block.position() + block.length() == QTextDocumentPrivate::get(doc)->length())
- html += QLatin1String("<!--EndFragment-->");
+ html += "<!--EndFragment-->"_L1;
+
+ QString closeTags;
if (pre)
- html += QLatin1String("</pre>");
+ html += "</pre>"_L1;
else if (list)
- html += QLatin1String("</li>");
+ closeTags += "</li>"_L1;
else {
int headingLevel = blockFormat.headingLevel();
if (headingLevel > 0 && headingLevel <= 6)
- html += QLatin1String("</h") + QString::number(headingLevel) + QLatin1Char('>');
+ html += QString::asprintf("</h%d>", headingLevel);
else
- html += QLatin1String("</p>");
+ html += "</p>"_L1;
}
if (list) {
if (list->itemNumber(block) == list->count() - 1) { // last item? close list
if (isOrderedList(list->format().style()))
- html += QLatin1String("</ol>");
+ closeTags += "</ol>"_L1;
else
- html += QLatin1String("</ul>");
+ closeTags += "</ul>"_L1;
+ }
+ const QTextBlock nextBlock = block.next();
+ // If the next block is the beginning of a new deeper nested list, then we don't
+ // want to close the current list item just yet. This should be closed when this
+ // item is fully finished
+ if (nextBlock.isValid() && nextBlock.textList() &&
+ nextBlock.textList()->itemNumber(nextBlock) == 0 &&
+ nextBlock.textList()->format().indent() > list->format().indent()) {
+ QString lastTag;
+ if (!closingTags.isEmpty() && list->itemNumber(block) == list->count() - 1)
+ lastTag = closingTags.takeLast();
+ lastTag.prepend(closeTags);
+ closingTags << lastTag;
+ } else if (list->itemNumber(block) == list->count() - 1) {
+ // If we are at the end of the list now then we can add in the closing tags for that
+ // current block
+ html += closeTags;
+ if (!closingTags.isEmpty())
+ html += closingTags.takeLast();
+ } else {
+ html += closeTags;
}
}
@@ -3084,7 +3350,7 @@ void QTextHtmlExporter::emitTable(const QTextTable *table)
{
QTextTableFormat format = table->format();
- html += QLatin1String("\n<table");
+ html += "\n<table"_L1;
if (format.hasProperty(QTextFormat::FrameBorder))
emitAttribute("border", QString::number(format.border()));
@@ -3101,7 +3367,7 @@ void QTextHtmlExporter::emitTable(const QTextTable *table)
emitBackgroundAttribute(format);
- html += QLatin1Char('>');
+ html += u'>';
const int rows = table->rows();
const int columns = table->columns();
@@ -3111,7 +3377,7 @@ void QTextHtmlExporter::emitTable(const QTextTable *table)
columnWidths.resize(columns);
columnWidths.fill(QTextLength());
}
- Q_ASSERT(columnWidths.count() == columns);
+ Q_ASSERT(columnWidths.size() == columns);
QVarLengthArray<bool> widthEmittedForColumn(columns);
for (int i = 0; i < columns; ++i)
@@ -3119,10 +3385,10 @@ void QTextHtmlExporter::emitTable(const QTextTable *table)
const int headerRowCount = qMin(format.headerRowCount(), rows);
if (headerRowCount > 0)
- html += QLatin1String("<thead>");
+ html += "<thead>"_L1;
for (int row = 0; row < rows; ++row) {
- html += QLatin1String("\n<tr>");
+ html += "\n<tr>"_L1;
for (int col = 0; col < columns; ++col) {
const QTextTableCell cell = table->cellAt(row, col);
@@ -3134,7 +3400,7 @@ void QTextHtmlExporter::emitTable(const QTextTable *table)
if (cell.column() != col)
continue;
- html += QLatin1String("\n<td");
+ html += "\n<td"_L1;
if (!widthEmittedForColumn[col] && cell.columnSpan() == 1) {
emitTextLength("width", columnWidths.at(col));
@@ -3156,21 +3422,21 @@ void QTextHtmlExporter::emitTable(const QTextTable *table)
QString styleString;
if (valign >= QTextCharFormat::AlignMiddle && valign <= QTextCharFormat::AlignBottom) {
- styleString += QLatin1String(" vertical-align:");
+ styleString += " vertical-align:"_L1;
switch (valign) {
case QTextCharFormat::AlignMiddle:
- styleString += QLatin1String("middle");
+ styleString += "middle"_L1;
break;
case QTextCharFormat::AlignTop:
- styleString += QLatin1String("top");
+ styleString += "top"_L1;
break;
case QTextCharFormat::AlignBottom:
- styleString += QLatin1String("bottom");
+ styleString += "bottom"_L1;
break;
default:
break;
}
- styleString += QLatin1Char(';');
+ styleString += u';';
QTextCharFormat temp;
temp.setVerticalAlignment(valign);
@@ -3178,59 +3444,59 @@ void QTextHtmlExporter::emitTable(const QTextTable *table)
}
if (cellFormat.hasProperty(QTextFormat::TableCellLeftPadding))
- styleString += QLatin1String(" padding-left:") + QString::number(cellFormat.leftPadding()) + QLatin1Char(';');
+ styleString += " padding-left:"_L1 + QString::number(cellFormat.leftPadding()) + u';';
if (cellFormat.hasProperty(QTextFormat::TableCellRightPadding))
- styleString += QLatin1String(" padding-right:") + QString::number(cellFormat.rightPadding()) + QLatin1Char(';');
+ styleString += " padding-right:"_L1 + QString::number(cellFormat.rightPadding()) + u';';
if (cellFormat.hasProperty(QTextFormat::TableCellTopPadding))
- styleString += QLatin1String(" padding-top:") + QString::number(cellFormat.topPadding()) + QLatin1Char(';');
+ styleString += " padding-top:"_L1 + QString::number(cellFormat.topPadding()) + u';';
if (cellFormat.hasProperty(QTextFormat::TableCellBottomPadding))
- styleString += QLatin1String(" padding-bottom:") + QString::number(cellFormat.bottomPadding()) + QLatin1Char(';');
+ styleString += " padding-bottom:"_L1 + QString::number(cellFormat.bottomPadding()) + u';';
if (cellFormat.hasProperty(QTextFormat::TableCellTopBorder))
- styleString += QLatin1String(" border-top:") + QString::number(cellFormat.topBorder()) + QLatin1String("px;");
+ styleString += " border-top:"_L1 + QString::number(cellFormat.topBorder()) + "px;"_L1;
if (cellFormat.hasProperty(QTextFormat::TableCellRightBorder))
- styleString += QLatin1String(" border-right:") + QString::number(cellFormat.rightBorder()) + QLatin1String("px;");
+ styleString += " border-right:"_L1 + QString::number(cellFormat.rightBorder()) + "px;"_L1;
if (cellFormat.hasProperty(QTextFormat::TableCellBottomBorder))
- styleString += QLatin1String(" border-bottom:") + QString::number(cellFormat.bottomBorder()) + QLatin1String("px;");
+ styleString += " border-bottom:"_L1 + QString::number(cellFormat.bottomBorder()) + "px;"_L1;
if (cellFormat.hasProperty(QTextFormat::TableCellLeftBorder))
- styleString += QLatin1String(" border-left:") + QString::number(cellFormat.leftBorder()) + QLatin1String("px;");
+ styleString += " border-left:"_L1 + QString::number(cellFormat.leftBorder()) + "px;"_L1;
if (cellFormat.hasProperty(QTextFormat::TableCellTopBorderBrush))
- styleString += QLatin1String(" border-top-color:") + cellFormat.topBorderBrush().color().name() + QLatin1Char(';');
+ styleString += " border-top-color:"_L1 + cellFormat.topBorderBrush().color().name() + u';';
if (cellFormat.hasProperty(QTextFormat::TableCellRightBorderBrush))
- styleString += QLatin1String(" border-right-color:") + cellFormat.rightBorderBrush().color().name() + QLatin1Char(';');
+ styleString += " border-right-color:"_L1 + cellFormat.rightBorderBrush().color().name() + u';';
if (cellFormat.hasProperty(QTextFormat::TableCellBottomBorderBrush))
- styleString += QLatin1String(" border-bottom-color:") + cellFormat.bottomBorderBrush().color().name() + QLatin1Char(';');
+ styleString += " border-bottom-color:"_L1 + cellFormat.bottomBorderBrush().color().name() + u';';
if (cellFormat.hasProperty(QTextFormat::TableCellLeftBorderBrush))
- styleString += QLatin1String(" border-left-color:") + cellFormat.leftBorderBrush().color().name() + QLatin1Char(';');
+ styleString += " border-left-color:"_L1 + cellFormat.leftBorderBrush().color().name() + u';';
if (cellFormat.hasProperty(QTextFormat::TableCellTopBorderStyle))
- styleString += QLatin1String(" border-top-style:") + richtextBorderStyleToHtmlBorderStyle(cellFormat.topBorderStyle()) + QLatin1Char(';');
+ styleString += " border-top-style:"_L1 + richtextBorderStyleToHtmlBorderStyle(cellFormat.topBorderStyle()) + u';';
if (cellFormat.hasProperty(QTextFormat::TableCellRightBorderStyle))
- styleString += QLatin1String(" border-right-style:") + richtextBorderStyleToHtmlBorderStyle(cellFormat.rightBorderStyle()) + QLatin1Char(';');
+ styleString += " border-right-style:"_L1 + richtextBorderStyleToHtmlBorderStyle(cellFormat.rightBorderStyle()) + u';';
if (cellFormat.hasProperty(QTextFormat::TableCellBottomBorderStyle))
- styleString += QLatin1String(" border-bottom-style:") + richtextBorderStyleToHtmlBorderStyle(cellFormat.bottomBorderStyle()) + QLatin1Char(';');
+ styleString += " border-bottom-style:"_L1 + richtextBorderStyleToHtmlBorderStyle(cellFormat.bottomBorderStyle()) + u';';
if (cellFormat.hasProperty(QTextFormat::TableCellLeftBorderStyle))
- styleString += QLatin1String(" border-left-style:") + richtextBorderStyleToHtmlBorderStyle(cellFormat.leftBorderStyle()) + QLatin1Char(';');
+ styleString += " border-left-style:"_L1 + richtextBorderStyleToHtmlBorderStyle(cellFormat.leftBorderStyle()) + u';';
if (!styleString.isEmpty())
- html += QLatin1String(" style=\"") + styleString + QLatin1Char('\"');
+ html += " style=\""_L1 + styleString + u'\"';
- html += QLatin1Char('>');
+ html += u'>';
emitFrame(cell.begin());
- html += QLatin1String("</td>");
+ html += "</td>"_L1;
defaultCharFormat = oldDefaultCharFormat;
}
- html += QLatin1String("</tr>");
+ html += "</tr>"_L1;
if (headerRowCount > 0 && row == headerRowCount - 1)
- html += QLatin1String("</thead>");
+ html += "</thead>"_L1;
}
- html += QLatin1String("</table>");
+ html += "</table>"_L1;
}
void QTextHtmlExporter::emitFrame(const QTextFrame::Iterator &frameIt)
@@ -3263,7 +3529,7 @@ void QTextHtmlExporter::emitTextFrame(const QTextFrame *f)
{
FrameType frameType = f->parentFrame() ? TextFrame : RootFrame;
- html += QLatin1String("\n<table");
+ html += "\n<table"_L1;
QTextFrameFormat format = f->frameFormat();
if (format.hasProperty(QTextFormat::FrameBorder))
@@ -3278,22 +3544,22 @@ void QTextHtmlExporter::emitTextFrame(const QTextFrame *f)
if (frameType != RootFrame)
emitBackgroundAttribute(format);
- html += QLatin1Char('>');
- html += QLatin1String("\n<tr>\n<td style=\"border: none;\">");
+ html += u'>';
+ html += "\n<tr>\n<td style=\"border: none;\">"_L1;
emitFrame(f->begin());
- html += QLatin1String("</td></tr></table>");
+ html += "</td></tr></table>"_L1;
}
void QTextHtmlExporter::emitFrameStyle(const QTextFrameFormat &format, FrameType frameType)
{
- QLatin1String styleAttribute(" style=\"");
+ const auto styleAttribute = " style=\""_L1;
html += styleAttribute;
- const int originalHtmlLength = html.length();
+ const qsizetype originalHtmlLength = html.size();
if (frameType == TextFrame)
- html += QLatin1String("-qt-table-type: frame;");
+ html += "-qt-table-type: frame;"_L1;
else if (frameType == RootFrame)
- html += QLatin1String("-qt-table-type: root;");
+ html += "-qt-table-type: root;"_L1;
const QTextFrameFormat defaultFormat;
@@ -3301,9 +3567,9 @@ void QTextHtmlExporter::emitFrameStyle(const QTextFrameFormat &format, FrameType
emitPageBreakPolicy(format.pageBreakPolicy());
if (format.borderBrush() != defaultFormat.borderBrush()) {
- html += QLatin1String(" border-color:");
+ html += " border-color:"_L1;
html += colorValue(format.borderBrush().color());
- html += QLatin1Char(';');
+ html += u';';
}
if (format.borderStyle() != defaultFormat.borderStyle())
@@ -3320,12 +3586,12 @@ void QTextHtmlExporter::emitFrameStyle(const QTextFrameFormat &format, FrameType
QString::number(format.rightMargin()));
if (format.property(QTextFormat::TableBorderCollapse).toBool())
- html += QLatin1String(" border-collapse:collapse;");
+ html += " border-collapse:collapse;"_L1;
- if (html.length() == originalHtmlLength) // nothing emitted?
+ if (html.size() == originalHtmlLength) // nothing emitted?
html.chop(styleAttribute.size());
else
- html += QLatin1Char('\"');
+ html += u'\"';
}
/*!
@@ -3397,7 +3663,7 @@ QString QTextDocument::toMarkdown(QTextDocument::MarkdownFeatures features) cons
#if QT_CONFIG(textmarkdownreader)
void QTextDocument::setMarkdown(const QString &markdown, QTextDocument::MarkdownFeatures features)
{
- QTextMarkdownImporter(features).import(this, markdown);
+ QTextMarkdownImporter(this, features).import(markdown);
}
#endif
@@ -3418,3 +3684,5 @@ QList<QTextFormat> QTextDocument::allFormats() const
*/
QT_END_NAMESPACE
+
+#include "moc_qtextdocument.cpp"
diff --git a/src/gui/text/qtextdocument.h b/src/gui/text/qtextdocument.h
index 0470ad52e9..b6253bfa46 100644
--- a/src/gui/text/qtextdocument.h
+++ b/src/gui/text/qtextdocument.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2019 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) 2019 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 QTEXTDOCUMENT_H
#define QTEXTDOCUMENT_H
@@ -69,10 +33,12 @@ class QRectF;
class QTextOption;
class QTextCursor;
-
namespace Qt
{
+#if QT_GUI_REMOVED_SINCE(6, 7)
Q_GUI_EXPORT bool mightBeRichText(const QString&);
+#endif
+ Q_GUI_EXPORT bool mightBeRichText(QAnyStringView);
Q_GUI_EXPORT QString convertFromPlainText(const QString &plain, WhiteSpaceMode mode = WhiteSpacePre);
}
@@ -99,6 +65,7 @@ class Q_GUI_EXPORT QTextDocument : public QObject
Q_PROPERTY(QSizeF pageSize READ pageSize WRITE setPageSize)
Q_PROPERTY(QFont defaultFont READ defaultFont WRITE setDefaultFont)
Q_PROPERTY(bool useDesignMetrics READ useDesignMetrics WRITE setUseDesignMetrics)
+ Q_PROPERTY(bool layoutEnabled READ isLayoutEnabled WRITE setLayoutEnabled)
Q_PROPERTY(QSizeF size READ size)
Q_PROPERTY(qreal textWidth READ textWidth WRITE setTextWidth)
Q_PROPERTY(int blockCount READ blockCount)
@@ -137,7 +104,9 @@ public:
enum MetaInformation {
DocumentTitle,
- DocumentUrl
+ DocumentUrl,
+ CssMedia,
+ FrontMatter,
};
void setMetaInformation(MetaInformation info, const QString &);
QString metaInformation(MetaInformation info) const;
@@ -151,7 +120,7 @@ public:
enum MarkdownFeature {
MarkdownNoHTML = 0x0020 | 0x0040,
MarkdownDialectCommonMark = 0,
- MarkdownDialectGitHub = 0x0004 | 0x0008 | 0x0400 | 0x0100 | 0x0200 | 0x0800
+ MarkdownDialectGitHub = 0x0004 | 0x0008 | 0x0400 | 0x0100 | 0x0200 | 0x0800 | 0x4000 | 0x100000
};
Q_DECLARE_FLAGS(MarkdownFeatures, MarkdownFeature)
Q_FLAG(MarkdownFeatures)
@@ -239,6 +208,14 @@ public:
QVariant resource(int type, const QUrl &name) const;
void addResource(int type, const QUrl &name, const QVariant &resource);
+ using ResourceProvider = std::function<QVariant(const QUrl&)>;
+
+ QTextDocument::ResourceProvider resourceProvider() const;
+ void setResourceProvider(const ResourceProvider &provider);
+
+ static QTextDocument::ResourceProvider defaultResourceProvider();
+ static void setDefaultResourceProvider(const ResourceProvider &provider);
+
QList<QTextFormat> allFormats() const;
void markContentsDirty(int from, int length);
@@ -246,6 +223,9 @@ public:
void setUseDesignMetrics(bool b);
bool useDesignMetrics() const;
+ void setLayoutEnabled(bool b);
+ bool isLayoutEnabled() const;
+
void drawContents(QPainter *painter, const QRectF &rect = QRectF());
void setTextWidth(qreal width);
diff --git a/src/gui/text/qtextdocument_p.cpp b/src/gui/text/qtextdocument_p.cpp
index d6da0f38d2..3c1fc04d4b 100644
--- a/src/gui/text/qtextdocument_p.cpp
+++ b/src/gui/text/qtextdocument_p.cpp
@@ -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 <private/qtools_p.h>
#include <qdebug.h>
@@ -59,7 +23,7 @@ QT_BEGIN_NAMESPACE
#define PMDEBUG if(0) qDebug
-// The VxWorks DIAB compiler crashes when initializing the anonymouse union with { a7 }
+// The VxWorks DIAB compiler crashes when initializing the anonymous union with { a7 }
#if !defined(Q_CC_DIAB)
# define QT_INIT_TEXTUNDOCOMMAND(c, a1, a2, a3, a4, a5, a6, a7, a8) \
QTextUndoCommand c = { a1, a2, 0, 0, quint8(a3), a4, quint32(a5), quint32(a6), { int(a7) }, quint32(a8) }
@@ -186,7 +150,9 @@ QTextDocumentPrivate::QTextDocumentPrivate()
docChangeLength(0),
framesDirty(true),
rtFrame(nullptr),
- initialBlockCharFormatIndex(-1) // set correctly later in init()
+ initialBlockCharFormatIndex(-1), // set correctly later in init()
+ resourceProvider(nullptr),
+ cssMedia(QStringLiteral("screen"))
{
editBlock = 0;
editBlockCursorPosition = -1;
@@ -236,7 +202,7 @@ void QTextDocumentPrivate::clear()
{
Q_Q(QTextDocument);
- for (QTextCursorPrivate *curs : qAsConst(cursors)) {
+ for (QTextCursorPrivate *curs : std::as_const(cursors)) {
curs->setPosition(0);
curs->currentCharFormat = -1;
curs->anchor = 0;
@@ -289,7 +255,7 @@ void QTextDocumentPrivate::clear()
QTextDocumentPrivate::~QTextDocumentPrivate()
{
- for (QTextCursorPrivate *curs : qAsConst(cursors))
+ for (QTextCursorPrivate *curs : std::as_const(cursors))
curs->priv = nullptr;
cursors.clear();
undoState = 0;
@@ -381,8 +347,13 @@ int QTextDocumentPrivate::insert_block(int pos, uint strPos, int format, int blo
Q_ASSERT(blocks.length() == fragments.length());
QTextBlockGroup *group = qobject_cast<QTextBlockGroup *>(objectForFormat(blockFormat));
- if (group)
+ if (group) {
group->blockInserted(QTextBlock(this, b));
+ if (command != QTextUndoCommand::BlockDeleted) {
+ docChangeOldLength--;
+ docChangeLength--;
+ }
+ }
QTextFrame *frame = qobject_cast<QTextFrame *>(objectForFormat(formats.format(format)));
if (frame) {
@@ -404,7 +375,7 @@ int QTextDocumentPrivate::insertBlock(QChar blockSeparator,
beginEditBlock();
- int strPos = text.length();
+ int strPos = text.size();
text.append(blockSeparator);
int ob = blocks.findNode(pos);
@@ -474,16 +445,16 @@ void QTextDocumentPrivate::insert(int pos, int strPos, int strLength, int format
finishEdit();
}
-void QTextDocumentPrivate::insert(int pos, const QString &str, int format)
+void QTextDocumentPrivate::insert(int pos, QStringView str, int format)
{
if (str.size() == 0)
return;
Q_ASSERT(noBlockInString(str));
- int strPos = text.length();
+ int strPos = text.size();
text.append(str);
- insert(pos, strPos, str.length(), format);
+ insert(pos, strPos, str.size(), format);
}
int QTextDocumentPrivate::remove_string(int pos, uint length, QTextUndoCommand::Operation op)
@@ -677,7 +648,7 @@ void QTextDocumentPrivate::remove(int pos, int length, QTextUndoCommand::Operati
blockCursorAdjustment = true;
move(pos, -1, length, op);
blockCursorAdjustment = false;
- for (QTextCursorPrivate *curs : qAsConst(cursors)) {
+ for (QTextCursorPrivate *curs : std::as_const(cursors)) {
if (curs->adjustPosition(pos, -length, op) == QTextCursorPrivate::CursorMoved) {
curs->changed = true;
}
@@ -1235,13 +1206,13 @@ void QTextDocumentPrivate::finishEdit()
}
QList<QTextCursor> changedCursors;
- for (QTextCursorPrivate *curs : qAsConst(cursors)) {
+ for (QTextCursorPrivate *curs : std::as_const(cursors)) {
if (curs->changed) {
curs->changed = false;
changedCursors.append(QTextCursor(curs));
}
}
- for (const QTextCursor &cursor : qAsConst(changedCursors))
+ for (const QTextCursor &cursor : std::as_const(changedCursors))
emit q->cursorPositionChanged(cursor);
contentsChanged();
@@ -1287,7 +1258,7 @@ void QTextDocumentPrivate::adjustDocumentChangesAndCursors(int from, int addedOr
if (blockCursorAdjustment) {
; // postpone, will be called again from QTextDocumentPrivate::remove()
} else {
- for (QTextCursorPrivate *curs : qAsConst(cursors)) {
+ for (QTextCursorPrivate *curs : std::as_const(cursors)) {
if (curs->adjustPosition(from, addedOrRemoved, op) == QTextCursorPrivate::CursorMoved) {
curs->changed = true;
}
@@ -1450,6 +1421,16 @@ QTextFrame *QTextDocumentPrivate::rootFrame() const
return rtFrame;
}
+void QTextDocumentPrivate::addCursor(QTextCursorPrivate *c)
+{
+ cursors.insert(c);
+}
+
+void QTextDocumentPrivate::removeCursor(QTextCursorPrivate *c)
+{
+ cursors.remove(c);
+}
+
QTextFrame *QTextDocumentPrivate::frameAt(int pos) const
{
QTextFrame *f = rootFrame();
@@ -1464,7 +1445,7 @@ QTextFrame *QTextDocumentPrivate::frameAt(int pos) const
void QTextDocumentPrivate::clearFrame(QTextFrame *f)
{
- for (int i = 0; i < f->d_func()->childFrames.count(); ++i)
+ for (int i = 0; i < f->d_func()->childFrames.size(); ++i)
clearFrame(f->d_func()->childFrames.at(i));
f->d_func()->childFrames.clear();
f->d_func()->parentFrame = nullptr;
@@ -1734,7 +1715,7 @@ bool QTextDocumentPrivate::ensureMaximumBlockCount()
void QTextDocumentPrivate::aboutToRemoveCell(int from, int to)
{
Q_ASSERT(from <= to);
- for (QTextCursorPrivate *curs : qAsConst(cursors))
+ for (QTextCursorPrivate *curs : std::as_const(cursors))
curs->aboutToRemoveCell(from, to);
}
diff --git a/src/gui/text/qtextdocument_p.h b/src/gui/text/qtextdocument_p.h
index b4e0f526d0..1c4edc4329 100644
--- a/src/gui/text/qtextdocument_p.h
+++ b/src/gui/text/qtextdocument_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 QTEXTDOCUMENT_P_H
#define QTEXTDOCUMENT_P_H
@@ -65,7 +29,9 @@
#include "QtCore/qurl.h"
#include "QtCore/qvariant.h"
+#if QT_CONFIG(cssparser)
#include "private/qcssparser_p.h"
+#endif
#include "private/qfragmentmap_p.h"
#include "private/qobject_p.h"
#include "private/qtextformat_p.h"
@@ -176,7 +142,9 @@ public:
void setLayout(QAbstractTextDocumentLayout *layout);
- void insert(int pos, const QString &text, int format);
+ void insert(int pos, QStringView text, int format);
+ void insert(int pos, QChar c, int format)
+ { insert(pos, QStringView(&c, 1), format); }
void insert(int pos, int strPos, int strLength, int format);
int insertBlock(int pos, int blockFormat, int charFormat, QTextUndoCommand::Operation = QTextUndoCommand::MoveCursor);
int insertBlock(QChar blockSeparator, int pos, int blockFormat, int charFormat,
@@ -279,8 +247,8 @@ private:
public:
void documentChange(int from, int length);
- inline void addCursor(QTextCursorPrivate *c) { cursors.insert(c); }
- inline void removeCursor(QTextCursorPrivate *c) { cursors.remove(c); }
+ void addCursor(QTextCursorPrivate *c);
+ void removeCursor(QTextCursorPrivate *c);
QTextFrame *frameAt(int pos) const;
QTextFrame *rootFrame() const;
@@ -327,6 +295,8 @@ public:
return get(object->document());
}
+ bool canLayout() const { return layoutEnabled && !pageSize.isNull(); }
+
private:
QTextDocumentPrivate(const QTextDocumentPrivate& m);
QTextDocumentPrivate& operator= (const QTextDocumentPrivate& m);
@@ -366,12 +336,14 @@ private:
QMap<int, QTextObject *> objects;
QMap<QUrl, QVariant> resources;
QMap<QUrl, QVariant> cachedResources;
+ QTextDocument::ResourceProvider resourceProvider;
QString defaultStyleSheet;
int lastBlockCount;
public:
bool inContentsChange;
+ bool layoutEnabled = true;
QTextOption defaultTextOption;
Qt::CursorMoveStyle defaultCursorMoveStyle;
#ifndef QT_NO_CSSPARSER
@@ -383,6 +355,8 @@ public:
QSizeF pageSize;
QString title;
QString url;
+ QString cssMedia;
+ QString frontMatter;
qreal indentWidth;
qreal documentMargin;
QUrl baseUrl;
@@ -437,6 +411,7 @@ private:
QTextCharFormat defaultCharFormat;
const QTextDocument *doc;
bool fragmentMarkers;
+ QStringList closingTags;
};
QT_END_NAMESPACE
diff --git a/src/gui/text/qtextdocumentfragment.cpp b/src/gui/text/qtextdocumentfragment.cpp
index 6e7c5f1ed9..1b6e76c201 100644
--- a/src/gui/text/qtextdocumentfragment.cpp
+++ b/src/gui/text/qtextdocumentfragment.cpp
@@ -1,46 +1,16 @@
-/****************************************************************************
-**
-** 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 "qtextdocumentfragment.h"
#include "qtextdocumentfragment_p.h"
#include "qtextcursor_p.h"
#include "qtextlist.h"
+#if QT_CONFIG(textmarkdownreader)
+#include "qtextmarkdownimporter_p.h"
+#endif
+#if QT_CONFIG(textmarkdownwriter)
+#include "qtextmarkdownwriter_p.h"
+#endif
#include <qdebug.h>
#include <qbytearray.h>
@@ -49,6 +19,8 @@
QT_BEGIN_NAMESPACE
+using namespace Qt::StringLiterals;
+
QTextCopyHelper::QTextCopyHelper(const QTextCursor &_source, const QTextCursor &_destination, bool forceCharFormat, const QTextCharFormat &fmt)
#if defined(Q_CC_DIAB) // compiler bug
: formatCollection(*_destination.d->priv->formatCollection()), originalText((const QString)_source.d->priv->buffer())
@@ -112,7 +84,7 @@ int QTextCopyHelper::appendFragment(int pos, int endPos, int objectIndex)
}
QString txtToInsert(originalText.constData() + frag->stringPosition + inFragmentOffset, charsToCopy);
- if (txtToInsert.length() == 1
+ if (txtToInsert.size() == 1
&& (txtToInsert.at(0) == QChar::ParagraphSeparator
|| txtToInsert.at(0) == QTextBeginningOfFrame
|| txtToInsert.at(0) == QTextEndOfFrame
@@ -137,7 +109,7 @@ int QTextCopyHelper::appendFragment(int pos, int endPos, int objectIndex)
const int userState = nextBlock.userState();
if (userState != -1)
dst->blocksFind(insertPos).setUserState(userState);
- insertPos += txtToInsert.length();
+ insertPos += txtToInsert.size();
}
return charsToCopy;
@@ -266,12 +238,11 @@ void QTextDocumentFragmentPrivate::insert(QTextCursor &_cursor) const
document fragment. Document fragments can also be created by the
static functions, fromPlainText() and fromHtml().
- The contents of a document fragment can be obtained as plain text
- by using the toPlainText() function, or it can be obtained as HTML
- with toHtml().
+ The contents of a document fragment can be obtained as raw text
+ by using the toRawText() function, as ASCII with toPlainText(),
+ as HTML with toHtml(), or as Markdown with toMarkdown().
*/
-
/*!
Constructs an empty QTextDocumentFragment.
@@ -358,10 +329,15 @@ bool QTextDocumentFragment::isEmpty() const
}
/*!
- Returns the document fragment's text as plain text (i.e. with no
- formatting information).
-
- \sa toHtml()
+ This function returns the same as toRawText(), but will replace
+ some unicode characters with ASCII alternatives.
+ In particular, no-break space (U+00A0) is replaced by a regular
+ space (U+0020), and both paragraph (U+2029) and line (U+2028)
+ separators are replaced by line feed (U+000A).
+ If you need the precise contents of the document, use toRawText()
+ instead.
+
+ \sa toHtml(), toMarkdown(), toRawText()
*/
QString QTextDocumentFragment::toPlainText() const
{
@@ -371,6 +347,21 @@ QString QTextDocumentFragment::toPlainText() const
return d->doc->toPlainText();
}
+/*!
+ Returns the document fragment's text as raw text (i.e. with no
+ formatting information).
+
+ \since 6.4
+ \sa toHtml(), toMarkdown(), toPlainText()
+*/
+QString QTextDocumentFragment::toRawText() const
+{
+ if (!d)
+ return QString();
+
+ return d->doc->toRawText();
+}
+
#ifndef QT_NO_TEXTHTMLPARSER
/*!
@@ -378,7 +369,7 @@ QString QTextDocumentFragment::toPlainText() const
Returns the contents of the document fragment as HTML.
- \sa toPlainText(), QTextDocument::toHtml()
+ \sa toPlainText(), toMarkdown(), QTextDocument::toHtml()
*/
QString QTextDocumentFragment::toHtml() const
{
@@ -390,6 +381,26 @@ QString QTextDocumentFragment::toHtml() const
#endif // QT_NO_TEXTHTMLPARSER
+#if QT_CONFIG(textmarkdownwriter)
+
+/*!
+ \since 6.4
+
+ Returns the contents of the document fragment as Markdown,
+ with the specified \a features. The default is GitHub dialect.
+
+ \sa toPlainText(), QTextDocument::toMarkdown()
+*/
+QString QTextDocumentFragment::toMarkdown(QTextDocument::MarkdownFeatures features) const
+{
+ if (!d)
+ return QString();
+
+ return d->doc->toMarkdown(features);
+}
+
+#endif // textmarkdownwriter
+
/*!
Returns a document fragment that contains the given \a plainText.
@@ -425,14 +436,14 @@ QTextHtmlImporter::QTextHtmlImporter(QTextDocument *_doc, const QString &_html,
wsm = QTextHtmlParserNode::WhiteSpaceNormal;
QString html = _html;
- const int startFragmentPos = html.indexOf(QLatin1String("<!--StartFragment-->"));
+ const int startFragmentPos = html.indexOf("<!--StartFragment-->"_L1);
if (startFragmentPos != -1) {
- const QLatin1String qt3RichTextHeader("<meta name=\"qrichtext\" content=\"1\" />");
+ const auto qt3RichTextHeader = "<meta name=\"qrichtext\" content=\"1\" />"_L1;
// Hack for Qt3
const bool hasQtRichtextMetaTag = html.contains(qt3RichTextHeader);
- const int endFragmentPos = html.indexOf(QLatin1String("<!--EndFragment-->"));
+ const int endFragmentPos = html.indexOf("<!--EndFragment-->"_L1);
if (startFragmentPos < endFragmentPos)
html = html.mid(startFragmentPos, endFragmentPos - startFragmentPos);
else
@@ -477,7 +488,8 @@ void QTextHtmlImporter::import()
* means there was a tag closing in the input html
*/
if (currentNodeIdx > 0 && (currentNode->parent != currentNodeIdx - 1)) {
- blockTagClosed = closeTag();
+ const bool lastBlockTagClosed = closeTag();
+ blockTagClosed = blockTagClosed || lastBlockTagClosed;
// visually collapse subsequent block tags, but if the element after the closed block tag
// is for example an inline element (!isBlock) we have to make sure we start a new paragraph by setting
// hasBlock to false.
@@ -529,6 +541,7 @@ void QTextHtmlImporter::import()
appendBlock(block, currentNode->charFormat);
+ blockTagClosed = false;
hasBlock = true;
}
@@ -561,56 +574,54 @@ bool QTextHtmlImporter::appendNodeText()
const int initialCursorPosition = cursor.position();
QTextCharFormat format = currentNode->charFormat;
- if(wsm == QTextHtmlParserNode::WhiteSpacePre || wsm == QTextHtmlParserNode::WhiteSpacePreWrap)
+ if (wsm == QTextHtmlParserNode::WhiteSpacePre || wsm == QTextHtmlParserNode::WhiteSpacePreWrap)
compressNextWhitespace = PreserveWhiteSpace;
- QString text = currentNode->text;
+ const QString text = currentNode->text;
QString textToInsert;
textToInsert.reserve(text.size());
- for (int i = 0; i < text.length(); ++i) {
- QChar ch = text.at(i);
-
+ for (QChar ch : text) {
if (ch.isSpace()
&& ch != QChar::Nbsp
&& ch != QChar::ParagraphSeparator) {
- if (wsm == QTextHtmlParserNode::WhiteSpacePreLine && (ch == QLatin1Char('\n') || ch == QLatin1Char('\r')))
+ if (wsm == QTextHtmlParserNode::WhiteSpacePreLine && (ch == u'\n' || ch == u'\r'))
compressNextWhitespace = PreserveWhiteSpace;
if (compressNextWhitespace == CollapseWhiteSpace)
compressNextWhitespace = RemoveWhiteSpace; // allow this one, and remove the ones coming next.
- else if(compressNextWhitespace == RemoveWhiteSpace)
+ else if (compressNextWhitespace == RemoveWhiteSpace)
continue;
if (wsm == QTextHtmlParserNode::WhiteSpacePre
|| textEditMode
) {
- if (ch == QLatin1Char('\n')) {
+ if (ch == u'\n') {
if (textEditMode)
continue;
- } else if (ch == QLatin1Char('\r')) {
+ } else if (ch == u'\r') {
continue;
}
} else if (wsm != QTextHtmlParserNode::WhiteSpacePreWrap) {
compressNextWhitespace = RemoveWhiteSpace;
- if (wsm == QTextHtmlParserNode::WhiteSpacePreLine && (ch == QLatin1Char('\n') || ch == QLatin1Char('\r')))
+ if (wsm == QTextHtmlParserNode::WhiteSpacePreLine && (ch == u'\n' || ch == u'\r'))
{ }
else if (wsm == QTextHtmlParserNode::WhiteSpaceNoWrap)
ch = QChar::Nbsp;
else
- ch = QLatin1Char(' ');
+ ch = u' ';
}
} else {
compressNextWhitespace = PreserveWhiteSpace;
}
- if (ch == QLatin1Char('\n')
+ if (ch == u'\n'
|| ch == QChar::ParagraphSeparator) {
if (!textToInsert.isEmpty()) {
- if (wsm == QTextHtmlParserNode::WhiteSpacePreLine && textToInsert.at(textToInsert.length() - 1) == QLatin1Char(' '))
+ if (wsm == QTextHtmlParserNode::WhiteSpacePreLine && textToInsert.at(textToInsert.size() - 1) == u' ')
textToInsert = textToInsert.chopped(1);
cursor.insertText(textToInsert, format);
textToInsert.clear();
@@ -688,6 +699,8 @@ QTextHtmlImporter::ProcessNodeResult QTextHtmlImporter::processSpecialNodes()
listFmt.setNumberPrefix(currentNode->textListNumberPrefix);
if (!currentNode->textListNumberSuffix.isNull())
listFmt.setNumberSuffix(currentNode->textListNumberSuffix);
+ if (currentNode->listStart != 1)
+ listFmt.setStart(currentNode->listStart);
++indent;
if (currentNode->hasCssListIndent)
@@ -895,7 +908,7 @@ QTextHtmlImporter::Table QTextHtmlImporter::scanTable(int tableNodeIdx)
int tableHeaderRowCount = 0;
QList<int> rowNodes;
- rowNodes.reserve(at(tableNodeIdx).children.count());
+ rowNodes.reserve(at(tableNodeIdx).children.size());
for (int row : at(tableNodeIdx).children) {
switch (at(row).id) {
case Html_tr:
@@ -920,7 +933,7 @@ QTextHtmlImporter::Table QTextHtmlImporter::scanTable(int tableNodeIdx)
QList<RowColSpanInfo> rowColSpanForColumn;
int effectiveRow = 0;
- for (int row : qAsConst(rowNodes)) {
+ for (int row : std::as_const(rowNodes)) {
int colsInRow = 0;
for (int cell : at(row).children) {
@@ -948,7 +961,7 @@ QTextHtmlImporter::Table QTextHtmlImporter::scanTable(int tableNodeIdx)
if (spanInfo.colSpan > 1 || spanInfo.rowSpan > 1)
rowColSpans.append(spanInfo);
- columnWidths.resize(qMax(columnWidths.count(), colsInRow));
+ columnWidths.resize(qMax(columnWidths.size(), colsInRow));
rowColSpanForColumn.resize(columnWidths.size());
for (int i = currentColumn; i < currentColumn + c.tableCellColSpan; ++i) {
if (columnWidths.at(i).type() == QTextLength::VariableLength) {
@@ -1030,7 +1043,7 @@ QTextHtmlImporter::Table QTextHtmlImporter::scanTable(int tableNodeIdx)
QTextTable *textTable = cursor.insertTable(table.rows, table.columns, fmt.toTableFormat());
table.frame = textTable;
- for (int i = 0; i < rowColSpans.count(); ++i) {
+ for (int i = 0; i < rowColSpans.size(); ++i) {
const RowColSpanInfo &nfo = rowColSpans.at(i);
textTable->mergeCells(nfo.row, nfo.col, nfo.rowSpan, nfo.colSpan);
}
@@ -1255,9 +1268,6 @@ void QTextHtmlImporter::appendBlock(const QTextBlockFormat &format, QTextCharFor
compressNextWhitespace = RemoveWhiteSpace;
}
-#endif // QT_NO_TEXTHTMLPARSER
-
-#ifndef QT_NO_TEXTHTMLPARSER
/*!
\fn QTextDocumentFragment QTextDocumentFragment::fromHtml(const QString &text, const QTextDocument *resourceProvider)
\since 4.2
@@ -1281,5 +1291,32 @@ QTextDocumentFragment QTextDocumentFragment::fromHtml(const QString &html, const
return res;
}
-QT_END_NAMESPACE
#endif // QT_NO_TEXTHTMLPARSER
+
+#if QT_CONFIG(textmarkdownreader)
+
+/*!
+ \fn QTextDocumentFragment QTextDocumentFragment::fromMarkdown(const QString &markdown, QTextDocument::MarkdownFeatures features)
+ \since 6.4
+
+ Returns a QTextDocumentFragment based on the given \a markdown text with
+ the specified \a features. The default is GitHub dialect.
+
+ The formatting is preserved as much as possible; for example, \c {**bold**}
+ will become a document fragment containing the text "bold" with a bold
+ character style.
+
+ \note Loading external resources is not supported.
+*/
+QTextDocumentFragment QTextDocumentFragment::fromMarkdown(const QString &markdown, QTextDocument::MarkdownFeatures features)
+{
+ QTextDocumentFragment res;
+ res.d = new QTextDocumentFragmentPrivate;
+
+ QTextMarkdownImporter(res.d->doc, features).import(markdown);
+ return res;
+}
+
+#endif // textmarkdownreader
+
+QT_END_NAMESPACE
diff --git a/src/gui/text/qtextdocumentfragment.h b/src/gui/text/qtextdocumentfragment.h
index 2283b06c6c..a618ff15ff 100644
--- a/src/gui/text/qtextdocumentfragment.h
+++ b/src/gui/text/qtextdocumentfragment.h
@@ -1,53 +1,17 @@
-/****************************************************************************
-**
-** 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 QTEXTDOCUMENTFRAGMENT_H
#define QTEXTDOCUMENTFRAGMENT_H
#include <QtGui/qtguiglobal.h>
+#include <QtGui/qtextdocument.h>
#include <QtCore/qstring.h>
QT_BEGIN_NAMESPACE
class QTextStream;
-class QTextDocument;
class QTextDocumentFragmentPrivate;
class QTextCursor;
@@ -64,14 +28,22 @@ public:
bool isEmpty() const;
QString toPlainText() const;
+ QString toRawText() const;
#ifndef QT_NO_TEXTHTMLPARSER
QString toHtml() const;
#endif // QT_NO_TEXTHTMLPARSER
+#if QT_CONFIG(textmarkdownwriter)
+ QString toMarkdown(QTextDocument::MarkdownFeatures features = QTextDocument::MarkdownDialectGitHub) const;
+#endif
static QTextDocumentFragment fromPlainText(const QString &plainText);
#ifndef QT_NO_TEXTHTMLPARSER
static QTextDocumentFragment fromHtml(const QString &html, const QTextDocument *resourceProvider = nullptr);
#endif // QT_NO_TEXTHTMLPARSER
+#if QT_CONFIG(textmarkdownreader)
+ static QTextDocumentFragment fromMarkdown(const QString &markdown,
+ QTextDocument::MarkdownFeatures features = QTextDocument::MarkdownDialectGitHub);
+#endif
private:
QTextDocumentFragmentPrivate *d;
diff --git a/src/gui/text/qtextdocumentfragment_p.h b/src/gui/text/qtextdocumentfragment_p.h
index 3683cd8d4f..0d130003bc 100644
--- a/src/gui/text/qtextdocumentfragment_p.h
+++ b/src/gui/text/qtextdocumentfragment_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 QTEXTDOCUMENTFRAGMENT_P_H
#define QTEXTDOCUMENTFRAGMENT_P_H
@@ -233,9 +197,9 @@ private:
int currentNodeIdx;
const QTextHtmlParserNode *currentNode;
};
-Q_DECLARE_TYPEINFO(QTextHtmlImporter::List, Q_MOVABLE_TYPE);
+Q_DECLARE_TYPEINFO(QTextHtmlImporter::List, Q_RELOCATABLE_TYPE);
Q_DECLARE_TYPEINFO(QTextHtmlImporter::TableCellIterator, Q_PRIMITIVE_TYPE);
-Q_DECLARE_TYPEINFO(QTextHtmlImporter::Table, Q_MOVABLE_TYPE);
+Q_DECLARE_TYPEINFO(QTextHtmlImporter::Table, Q_RELOCATABLE_TYPE);
Q_DECLARE_TYPEINFO(QTextHtmlImporter::RowColSpanInfo, Q_PRIMITIVE_TYPE);
QT_END_NAMESPACE
diff --git a/src/gui/text/qtextdocumentlayout.cpp b/src/gui/text/qtextdocumentlayout.cpp
index 8ea3a655b7..452f814231 100644
--- a/src/gui/text/qtextdocumentlayout.cpp
+++ b/src/gui/text/qtextdocumentlayout.cpp
@@ -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 "qtextdocumentlayout_p.h"
#include "qtextdocument_p.h"
@@ -43,7 +7,9 @@
#include "qtexttable.h"
#include "qtextlist.h"
#include "qtextengine_p.h"
+#if QT_CONFIG(cssparser)
#include "private/qcssutil_p.h"
+#endif
#include "private/qguiapplication_p.h"
#include "qabstracttextdocumentlayout_p.h"
@@ -59,6 +25,7 @@
#include <qbasictimer.h>
#include "private/qfunctions_p.h"
#include <qloggingcategory.h>
+#include <QtCore/qpointer.h>
#include <algorithm>
@@ -105,14 +72,13 @@ public:
bool sizeDirty;
bool layoutDirty;
- bool fullLayoutCompleted;
QList<QPointer<QTextFrame>> floats;
};
QTextFrameData::QTextFrameData()
: maximumWidth(QFIXED_MAX),
- currentLayoutStruct(nullptr), sizeDirty(true), layoutDirty(true), fullLayoutCompleted(false)
+ currentLayoutStruct(nullptr), sizeDirty(true), layoutDirty(true)
{
}
@@ -582,7 +548,7 @@ public:
QTextLayoutStruct *layoutStruct, int layoutFrom, int layoutTo, const QTextBlockFormat *previousBlockFormat);
void layoutFlow(QTextFrame::Iterator it, QTextLayoutStruct *layoutStruct, int layoutFrom, int layoutTo, QFixed width = 0);
- void floatMargins(const QFixed &y, const QTextLayoutStruct *layoutStruct, QFixed *left, QFixed *right) const;
+ void floatMargins(QFixed y, const QTextLayoutStruct *layoutStruct, QFixed *left, QFixed *right) const;
QFixed findY(QFixed yFrom, const QTextLayoutStruct *layoutStruct, QFixed requiredWidth) const;
QList<QCheckPoint> checkPoints;
@@ -831,7 +797,7 @@ QTextDocumentLayoutPrivate::hitTest(const QTextBlock &bl, const QFixedPoint &poi
textrect.translate(tl->position());
qCDebug(lcHit) << " checking block" << bl.position() << "point=" << point.toPointF() << " tlrect" << textrect;
*position = bl.position();
- if (point.y.toReal() < textrect.top()) {
+ if (point.y.toReal() < textrect.top() - bl.blockFormat().topMargin()) {
qCDebug(lcHit) << " before pos=" << *position;
return PointBefore;
} else if (point.y.toReal() > textrect.bottom()) {
@@ -1212,7 +1178,7 @@ void QTextDocumentLayoutPrivate::drawFrame(const QPointF &offset, QPainter *pain
it = frameIteratorForYPosition(QFixed::fromReal(context.clip.top()));
QList<QTextFrame *> floats;
- const int numFloats = fd->floats.count();
+ const int numFloats = fd->floats.size();
floats.reserve(numFloats);
for (int i = 0; i < numFloats; ++i)
floats.append(fd->floats.at(i));
@@ -1246,8 +1212,7 @@ static inline QTextFormat::Property borderPropertyForEdge(QCss::Edge edge)
case QCss::RightEdge:
return QTextFormat::TableCellRightBorder;
default:
- Q_UNREACHABLE();
- return QTextFormat::UserProperty;
+ Q_UNREACHABLE_RETURN(QTextFormat::UserProperty);
}
}
@@ -1263,8 +1228,7 @@ static inline QTextFormat::Property borderStylePropertyForEdge(QCss::Edge edge)
case QCss::RightEdge:
return QTextFormat::TableCellRightBorderStyle;
default:
- Q_UNREACHABLE();
- return QTextFormat::UserProperty;
+ Q_UNREACHABLE_RETURN(QTextFormat::UserProperty);
}
}
@@ -1280,8 +1244,7 @@ static inline QCss::Edge adjacentEdge(QCss::Edge edge)
case QCss::LeftEdge:
return QCss::RightEdge;
default:
- Q_UNREACHABLE();
- return QCss::NumEdges;
+ Q_UNREACHABLE_RETURN(QCss::NumEdges);
}
}
@@ -1360,8 +1323,7 @@ static inline bool sharesAxis(const QTextTableCell &cell, QCss::Edge edge,
return cell.column() + cell.columnSpan() ==
competingCell.column() + (competingCellEdge == QCss::LeftEdge ? 0 : competingCell.columnSpan());
default:
- Q_UNREACHABLE();
- return false;
+ Q_UNREACHABLE_RETURN(false);
}
}
@@ -1520,7 +1482,7 @@ static inline qreal collapseOffset(const QTextDocumentLayoutPrivate *p, const Ed
// there was no edge B (due to a rowspan or the axis C-D being the table's right
// border).
//
-// ignoreEdgesAbove: true if an egde (left, right or top) for the first row
+// ignoreEdgesAbove: true if an edge (left, right or top) for the first row
// after a table page break should be painted. In this case the edges of the
// row above must be ignored.
static inline double prioritizedEdgeAnchorOffset(const QTextDocumentLayoutPrivate *p,
@@ -1616,7 +1578,7 @@ void drawCellBorder(const QTextDocumentLayoutPrivate *p, QPainter *painter,
QBrush brush;
if (edgeData.edgeClass != EdgeData::ClassExplicit && td->drawGrid) {
- borderStyle = QTextFrameFormat::BorderStyle_Solid;
+ borderStyle = table->format().borderStyle();
brush = table->format().borderBrush();
}
else {
@@ -1856,11 +1818,20 @@ void QTextDocumentLayoutPrivate::drawTableCell(const QRectF &cellRect, QPainter
if (r >= headerRowCount)
topMargin += td->headerHeight.toReal();
- if (!td->borderCollapse && td->border != 0) {
+ // If cell border configured, don't draw default border for cells. It will be taken care later by
+ // drawTableCellBorder().
+ bool cellBorderConfigured = (cell.format().hasProperty(QTextFormat::TableCellLeftBorder) ||
+ cell.format().hasProperty(QTextFormat::TableCellTopBorder) ||
+ cell.format().hasProperty(QTextFormat::TableCellRightBorder) ||
+ cell.format().hasProperty(QTextFormat::TableCellBottomBorder));
+
+ if (!td->borderCollapse && td->border != 0 && !cellBorderConfigured) {
const QBrush oldBrush = painter->brush();
const QPen oldPen = painter->pen();
- const qreal border = td->border.toReal();
+ // If border is configured for the table (and not explicitly for the cell), then
+ // always draw 1px border around the cell
+ const qreal border = 1;
QRectF borderRect(cellRect.left() - border, cellRect.top() - border, cellRect.width() + border, cellRect.height() + border);
@@ -1923,7 +1894,8 @@ void QTextDocumentLayoutPrivate::drawTableCell(const QRectF &cellRect, QPainter
}
// paint over the background - otherwise we would have to adjust the background paint cellRect for the border values
- drawTableCellBorder(cellRect, painter, table, td, cell);
+ if (cellBorderConfigured)
+ drawTableCellBorder(cellRect, painter, table, td, cell);
const QFixed verticalOffset = td->cellVerticalOffsets.at(c + r * table->columns());
@@ -2004,7 +1976,7 @@ void QTextDocumentLayoutPrivate::drawFlow(const QPointF &offset, QPainter *paint
previousFrame = c;
}
- for (int i = 0; i < floats.count(); ++i) {
+ for (int i = 0; i < floats.size(); ++i) {
QTextFrame *frame = floats.at(i);
if (!isFrameFromInlineObject(frame)
|| frame->frameFormat().position() == QTextFrameFormat::InFlow)
@@ -2045,7 +2017,9 @@ void QTextDocumentLayoutPrivate::drawBlock(const QPointF &offset, QPainter *pain
rect.setRight((fd->size.width - fd->rightMargin).toReal());
}
- fillBackground(painter, rect, bg, r.topLeft());
+ // in the case of <hr>, the background-color CSS style fills only the rule's thickness instead of the whole line
+ if (!blockFormat.hasProperty(QTextFormat::BlockTrailingHorizontalRulerWidth))
+ fillBackground(painter, rect, bg, r.topLeft());
}
QList<QTextLayout::FormatRange> selections;
@@ -2105,7 +2079,10 @@ void QTextDocumentLayoutPrivate::drawBlock(const QPointF &offset, QPainter *pain
if (blockFormat.hasProperty(QTextFormat::BlockTrailingHorizontalRulerWidth)) {
const qreal width = blockFormat.lengthProperty(QTextFormat::BlockTrailingHorizontalRulerWidth).value(r.width());
- painter->setPen(context.palette.color(QPalette::Dark));
+ const auto color = blockFormat.hasProperty(QTextFormat::BackgroundBrush)
+ ? qvariant_cast<QBrush>(blockFormat.property(QTextFormat::BackgroundBrush)).color()
+ : context.palette.color(QPalette::Inactive, QPalette::WindowText);
+ painter->setPen(color);
qreal y = r.bottom();
if (bl.length() == 1)
y = r.top() + r.height() / 2;
@@ -2124,7 +2101,7 @@ void QTextDocumentLayoutPrivate::drawListItem(const QPointF &offset, QPainter *p
{
Q_Q(const QTextDocumentLayout);
const QTextBlockFormat blockFormat = bl.blockFormat();
- const QTextCharFormat charFormat = QTextCursor(bl).charFormat();
+ const QTextCharFormat charFormat = bl.charFormat();
QFont font(charFormat.font());
if (q->paintDevice())
font = QFont(font, q->paintDevice());
@@ -2178,7 +2155,7 @@ void QTextDocumentLayoutPrivate::drawListItem(const QPointF &offset, QPainter *p
QRectF r(pos, size);
- qreal xoff = fontMetrics.horizontalAdvance(QLatin1Char(' '));
+ qreal xoff = fontMetrics.horizontalAdvance(u' ');
if (dir == Qt::LeftToRight)
xoff = -xoff - size.width();
r.translate( xoff, (fontMetrics.height() / 2) - (size.height() / 2));
@@ -2187,9 +2164,11 @@ void QTextDocumentLayoutPrivate::drawListItem(const QPointF &offset, QPainter *p
painter->setRenderHint(QPainter::Antialiasing);
+ const bool marker = bl.blockFormat().marker() != QTextBlockFormat::MarkerType::NoMarker;
if (selectionFormat) {
painter->setPen(QPen(selectionFormat->foreground(), 0));
- painter->fillRect(r, selectionFormat->background());
+ if (!marker)
+ painter->fillRect(r, selectionFormat->background());
} else {
QBrush fg = charFormat.foreground();
if (fg == Qt::NoBrush)
@@ -2199,19 +2178,21 @@ void QTextDocumentLayoutPrivate::drawListItem(const QPointF &offset, QPainter *p
QBrush brush = context.palette.brush(QPalette::Text);
- bool marker = bl.blockFormat().marker() != QTextBlockFormat::MarkerType::NoMarker;
if (marker) {
int adj = fontMetrics.lineSpacing() / 6;
r.adjust(-adj, 0, -adj, 0);
+ const QRectF outer = r.adjusted(-adj, -adj, adj, adj);
+ if (selectionFormat)
+ painter->fillRect(outer, selectionFormat->background());
if (bl.blockFormat().marker() == QTextBlockFormat::MarkerType::Checked) {
- // ### Qt6: render with QStyle / PE_IndicatorCheckBox. We don't currently
+ // ### Qt7: render with QStyle / PE_IndicatorCheckBox. We don't currently
// have access to that here, because it would be a widget dependency.
painter->setPen(QPen(painter->pen().color(), 2));
painter->drawLine(r.topLeft(), r.bottomRight());
painter->drawLine(r.topRight(), r.bottomLeft());
painter->setPen(QPen(painter->pen().color(), 0));
}
- painter->drawRect(r.adjusted(-adj, -adj, adj, adj));
+ painter->drawRect(outer);
}
switch (style) {
@@ -2235,17 +2216,15 @@ void QTextDocumentLayoutPrivate::drawListItem(const QPointF &offset, QPainter *p
}
case QTextListFormat::ListSquare:
if (!marker)
- painter->fillRect(r, brush);
+ painter->fillRect(r, painter->pen().brush());
break;
case QTextListFormat::ListCircle:
- if (!marker) {
- painter->setPen(QPen(brush, 0));
+ if (!marker)
painter->drawEllipse(r.translated(0.5, 0.5)); // pixel align for sharper rendering
- }
break;
case QTextListFormat::ListDisc:
if (!marker) {
- painter->setBrush(brush);
+ painter->setBrush(painter->pen().brush());
painter->setPen(Qt::NoPen);
painter->drawEllipse(r);
}
@@ -2358,9 +2337,10 @@ QTextLayoutStruct QTextDocumentLayoutPrivate::layoutCell(QTextTable *t, const QT
floatMinWidth = qMax(floatMinWidth, cd->minimumWidth);
}
- // constraint the maximumWidth by the minimum width of the fixed size floats, to
- // keep them visible
+ // constraint the maximum/minimumWidth by the minimum width of the fixed size floats,
+ // to keep them visible
layoutStruct.maximumWidth = qMax(layoutStruct.maximumWidth, floatMinWidth);
+ layoutStruct.minimumWidth = qMax(layoutStruct.minimumWidth, floatMinWidth);
// as floats in cells get added to the table's float list but must not affect
// floats in other cells we must clear the list here.
@@ -2395,7 +2375,7 @@ QRectF QTextDocumentLayoutPrivate::layoutTable(QTextTable *table, int layoutFrom
td->childFrameMap.clear();
{
const QList<QTextFrame *> children = table->childFrames();
- for (int i = 0; i < children.count(); ++i) {
+ for (int i = 0; i < children.size(); ++i) {
QTextFrame *frame = children.at(i);
QTextTableCell cell = table->cellAt(frame->firstPosition());
td->childFrameMap.insert(cell.row() + cell.column() * rows, frame);
@@ -2405,7 +2385,7 @@ QRectF QTextDocumentLayoutPrivate::layoutTable(QTextTable *table, int layoutFrom
QList<QTextLength> columnWidthConstraints = fmt.columnWidthConstraints();
if (columnWidthConstraints.size() != columns)
columnWidthConstraints.resize(columns);
- Q_ASSERT(columnWidthConstraints.count() == columns);
+ Q_ASSERT(columnWidthConstraints.size() == columns);
// borderCollapse will disable drawing the html4 style table cell borders
// and draw a 1px grid instead. This also sets a fixed cellspacing
@@ -2537,6 +2517,8 @@ recalc_minmax_widths:
for (int n = 0; n < cspan; ++n) {
const int col = i + n;
QFixed w = widthToDistribute / (cspan - n);
+ if (td->maxWidths[col] != QFIXED_MAX)
+ w = qMax(td->maxWidths[col], w);
td->maxWidths[col] = qMax(td->minWidths.at(col), w);
widthToDistribute -= td->maxWidths.at(col);
if (widthToDistribute <= 0)
@@ -2578,8 +2560,9 @@ recalc_minmax_widths:
const QFixed allottedPercentage = QFixed::fromReal(columnWidthConstraints.at(i).rawValue());
const QFixed percentWidth = totalPercentagedWidth * allottedPercentage / totalPercentage;
- if (percentWidth >= td->minWidths.at(i)) {
- td->widths[i] = qBound(td->minWidths.at(i), percentWidth, remainingWidth - remainingMinWidths);
+ QFixed maxWidth = remainingWidth - remainingMinWidths;
+ if (percentWidth >= td->minWidths.at(i) && maxWidth > td->minWidths.at(i)) {
+ td->widths[i] = qBound(td->minWidths.at(i), percentWidth, maxWidth);
} else {
td->widths[i] = td->minWidths.at(i);
}
@@ -2600,9 +2583,9 @@ recalc_minmax_widths:
QFixed lastRemainingWidth = remainingWidth;
while (remainingWidth > 0) {
- for (int k = 0; k < columnsWithProperMaxSize.count(); ++k) {
+ for (int k = 0; k < columnsWithProperMaxSize.size(); ++k) {
const int col = columnsWithProperMaxSize[k];
- const int colsLeft = columnsWithProperMaxSize.count() - k;
+ const int colsLeft = columnsWithProperMaxSize.size() - k;
const QFixed w = qMin(td->maxWidths.at(col) - td->widths.at(col), remainingWidth / colsLeft);
td->widths[col] += w;
remainingWidth -= w;
@@ -2944,7 +2927,7 @@ QRectF QTextDocumentLayoutPrivate::layoutFrame(QTextFrame *f, int layoutFrom, in
QTextFrameData *fd = data(f);
QFixed newContentsWidth;
- bool fullLayout = (f == document->rootFrame() && !fd->fullLayoutCompleted);
+ bool fullLayout = false;
{
QTextFrameFormat fformat = f->frameFormat();
// set sizes of this frame from the format
@@ -3137,7 +3120,7 @@ void QTextDocumentLayoutPrivate::layoutFlow(QTextFrame::Iterator it, QTextLayout
QTextBlockFormat previousBlockFormat = previousIt.currentBlock().blockFormat();
QFixed maximumBlockWidth = 0;
- while (!it.atEnd()) {
+ while (!it.atEnd() && layoutStruct->absoluteY() < QFIXED_MAX) {
QTextFrame *c = it.currentFrame();
int docPos;
@@ -3374,7 +3357,7 @@ void QTextDocumentLayoutPrivate::layoutFlow(QTextFrame::Iterator it, QTextLayout
// and not per cell and layoutCell already takes care of doing the same as we do here
if (!qobject_cast<QTextTable *>(layoutStruct->frame)) {
QList<QTextFrame *> children = layoutStruct->frame->childFrames();
- for (int i = 0; i < children.count(); ++i) {
+ for (int i = 0; i < children.size(); ++i) {
QTextFrameData *fd = data(children.at(i));
if (!fd->layoutDirty && children.at(i)->frameFormat().position() != QTextFrameFormat::InFlow)
layoutStruct->y = qMax(layoutStruct->y, fd->position.y + fd->size.height);
@@ -3387,7 +3370,7 @@ void QTextDocumentLayoutPrivate::layoutFlow(QTextFrame::Iterator it, QTextLayout
if (!fd->floats.isEmpty())
contentHasAlignment = true;
- if (it.atEnd()) {
+ if (it.atEnd() || layoutStruct->absoluteY() >= QFIXED_MAX) {
//qDebug("layout done!");
currentLazyLayoutPosition = -1;
QCheckPoint cp;
@@ -3398,7 +3381,6 @@ void QTextDocumentLayoutPrivate::layoutFlow(QTextFrame::Iterator it, QTextLayout
cp.contentsWidth = layoutStruct->contentsWidth;
checkPoints.append(cp);
checkPoints.reserve(checkPoints.size());
- fd->fullLayoutCompleted = true;
} else {
currentLazyLayoutPosition = checkPoints.constLast().positionInFrame;
// #######
@@ -3413,19 +3395,21 @@ void QTextDocumentLayoutPrivate::layoutFlow(QTextFrame::Iterator it, QTextLayout
static inline void getLineHeightParams(const QTextBlockFormat &blockFormat, const QTextLine &line, qreal scaling,
QFixed *lineAdjustment, QFixed *lineBreakHeight, QFixed *lineHeight, QFixed *lineBottom)
{
+ const qreal height = line.height();
+ const int lineHeightType = blockFormat.lineHeightType();
qreal rawHeight = qCeil(line.ascent() + line.descent() + line.leading());
*lineHeight = QFixed::fromReal(blockFormat.lineHeight(rawHeight, scaling));
- *lineBottom = QFixed::fromReal(blockFormat.lineHeight(line.height(), scaling));
+ *lineBottom = QFixed::fromReal(blockFormat.lineHeight(height, scaling));
- if (blockFormat.lineHeightType() == QTextBlockFormat::FixedHeight || blockFormat.lineHeightType() == QTextBlockFormat::MinimumHeight) {
+ if (lineHeightType == QTextBlockFormat::FixedHeight || lineHeightType == QTextBlockFormat::MinimumHeight) {
*lineBreakHeight = *lineBottom;
- if (blockFormat.lineHeightType() == QTextBlockFormat::FixedHeight)
+ if (lineHeightType == QTextBlockFormat::FixedHeight)
*lineAdjustment = QFixed::fromReal(line.ascent() + qMax(line.leading(), qreal(0.0))) - ((*lineHeight * 4) / 5);
else
- *lineAdjustment = QFixed::fromReal(line.height()) - *lineHeight;
+ *lineAdjustment = QFixed::fromReal(height) - *lineHeight;
}
else {
- *lineBreakHeight = QFixed::fromReal(line.height());
+ *lineBreakHeight = QFixed::fromReal(height);
*lineAdjustment = 0;
}
}
@@ -3572,6 +3556,11 @@ void QTextDocumentLayoutPrivate::layoutBlock(const QTextBlock &bl, int blockPosi
while (layoutStruct->pageHeight > 0 && layoutStruct->absoluteY() + lineBreakHeight > layoutStruct->pageBottom &&
layoutStruct->contentHeight() >= lineBreakHeight) {
+ if (layoutStruct->pageHeight == QFIXED_MAX) {
+ layoutStruct->y = QFIXED_MAX - layoutStruct->frameY;
+ break;
+ }
+
layoutStruct->newPage();
floatMargins(layoutStruct->y, layoutStruct, &left, &right);
@@ -3655,7 +3644,7 @@ void QTextDocumentLayoutPrivate::layoutBlock(const QTextBlock &bl, int blockPosi
}
}
-void QTextDocumentLayoutPrivate::floatMargins(const QFixed &y, const QTextLayoutStruct *layoutStruct,
+void QTextDocumentLayoutPrivate::floatMargins(QFixed y, const QTextLayoutStruct *layoutStruct,
QFixed *left, QFixed *right) const
{
// qDebug() << "floatMargins y=" << y;
@@ -3719,7 +3708,7 @@ void QTextDocumentLayout::draw(QPainter *painter, const PaintContext &context)
QTextFrame *frame = d->document->rootFrame();
QTextFrameData *fd = data(frame);
- if(fd->sizeDirty)
+ if (fd->sizeDirty)
return;
if (context.clip.isValid()) {
@@ -3756,11 +3745,7 @@ static void markFrames(QTextFrame *current, int from, int oldLength, int length)
QTextFrameData *fd = data(current);
// float got removed in editing operation
- QTextFrame *null = nullptr; // work-around for (at least) MSVC 2012 emitting
- // warning C4100 for its own header <algorithm>
- // when passing nullptr directly to std::remove
- fd->floats.erase(std::remove(fd->floats.begin(), fd->floats.end(), null),
- fd->floats.end());
+ fd->floats.removeAll(nullptr);
fd->layoutDirty = true;
fd->sizeDirty = true;
@@ -3782,7 +3767,7 @@ void QTextDocumentLayout::documentChanged(int from, int oldLength, int length)
for (; blockIt.isValid() && blockIt != endIt; blockIt = blockIt.next())
blockIt.clearLayout();
- if (d->docPrivate->pageSize.isNull())
+ if (!d->docPrivate->canLayout())
return;
QRectF updateRect;
@@ -3810,7 +3795,6 @@ void QTextDocumentLayout::documentChanged(int from, int oldLength, int length)
d->contentHasAlignment = false;
d->currentLazyLayoutPosition = 0;
d->checkPoints.clear();
- data(d->docPrivate->rootFrame())->fullLayoutCompleted = false;
d->layoutStep();
} else {
d->ensureLayoutedByPosition(from);
@@ -3850,7 +3834,7 @@ QRectF QTextDocumentLayout::doLayout(int from, int oldLength, int length)
QRectF updateRect;
QTextFrame *root = d->docPrivate->rootFrame();
- if(data(root)->sizeDirty)
+ if (data(root)->sizeDirty)
updateRect = d->layoutFrame(root, from, from + length);
data(root)->layoutDirty = false;
@@ -3879,7 +3863,7 @@ int QTextDocumentLayout::hitTest(const QPointF &point, Qt::HitTestAccuracy accur
// ensure we stay within document bounds
int lastPos = f->lastPosition();
if (l && !l->preeditAreaText().isEmpty())
- lastPos += l->preeditAreaText().length();
+ lastPos += l->preeditAreaText().size();
if (position > lastPos)
position = lastPos;
else if (position < 0)
@@ -4061,7 +4045,7 @@ QRectF QTextDocumentLayout::tableCellBoundingRect(QTextTable *table, const QText
QRectF QTextDocumentLayout::tableBoundingRect(QTextTable *table) const
{
Q_D(const QTextDocumentLayout);
- if (d->docPrivate->pageSize.isNull())
+ if (!d->docPrivate->canLayout())
return QRectF();
d->ensureLayoutFinished();
@@ -4088,7 +4072,7 @@ QRectF QTextDocumentLayout::tableBoundingRect(QTextTable *table) const
QRectF QTextDocumentLayout::frameBoundingRect(QTextFrame *frame) const
{
Q_D(const QTextDocumentLayout);
- if (d->docPrivate->pageSize.isNull())
+ if (!d->docPrivate->canLayout())
return QRectF();
d->ensureLayoutFinished();
return d->frameBoundingRectInternal(frame);
@@ -4117,7 +4101,7 @@ QRectF QTextDocumentLayoutPrivate::frameBoundingRectInternal(QTextFrame *frame)
QRectF QTextDocumentLayout::blockBoundingRect(const QTextBlock &block) const
{
Q_D(const QTextDocumentLayout);
- if (d->docPrivate->pageSize.isNull() || !block.isValid() || !block.isVisible())
+ if (!d->docPrivate->canLayout() || !block.isValid() || !block.isVisible())
return QRectF();
d->ensureLayoutedByPosition(block.position() + block.length());
QTextFrame *frame = d->document->frameAt(block.position());
diff --git a/src/gui/text/qtextdocumentlayout_p.h b/src/gui/text/qtextdocumentlayout_p.h
index 2054ebaa35..9f21d8ed39 100644
--- a/src/gui/text/qtextdocumentlayout_p.h
+++ b/src/gui/text/qtextdocumentlayout_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 QTEXTDOCUMENTLAYOUT_P_H
#define QTEXTDOCUMENTLAYOUT_P_H
diff --git a/src/gui/text/qtextdocumentwriter.cpp b/src/gui/text/qtextdocumentwriter.cpp
index 31dfb9436f..55f8414bd5 100644
--- a/src/gui/text/qtextdocumentwriter.cpp
+++ b/src/gui/text/qtextdocumentwriter.cpp
@@ -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 "qtextdocumentwriter.h"
#include <QtCore/qfile.h>
@@ -77,7 +41,6 @@ public:
\inmodule QtGui
\ingroup richtext-processing
- \ingroup io
To write a document, construct a QTextDocumentWriter object with either a
file name or a device object, and specify the document format to be
@@ -182,7 +145,7 @@ QByteArray QTextDocumentWriter::format () const
unchanged.
If the device is not already open, QTextDocumentWriter will attempt to
- open the device in \l QIODevice::WriteOnly mode by calling open().
+ open the device in \l {QIODeviceBase::}{WriteOnly} mode by calling open().
\note This will not work for certain devices, such as QProcess,
QTcpSocket and QUdpSocket, where some configuration is required before
@@ -211,7 +174,8 @@ QIODevice *QTextDocumentWriter::device () const
/*!
Sets the name of the file to be written to \a fileName. Internally,
QTextDocumentWriter will create a QFile and open it in \l
- QIODevice::WriteOnly mode, and use this file when writing the document.
+ {QIODeviceBase::}{WriteOnly} mode, and use this file when writing the
+ document.
\sa fileName(), setDevice()
*/
@@ -240,9 +204,11 @@ QString QTextDocumentWriter::fileName () const
*/
bool QTextDocumentWriter::write(const QTextDocument *document)
{
- QByteArray suffix;
+ if (!d->device)
+ return false;
- if (d->device && d->format.isEmpty()) {
+ QByteArray suffix;
+ if (d->format.isEmpty()) {
// if there's no format, see if device is a file, and if so, find
// the file suffix
if (QFile *file = qobject_cast<QFile *>(d->device))
diff --git a/src/gui/text/qtextdocumentwriter.h b/src/gui/text/qtextdocumentwriter.h
index cb34da7e8e..c52c681f7d 100644
--- a/src/gui/text/qtextdocumentwriter.h
+++ b/src/gui/text/qtextdocumentwriter.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 QTEXTDOCUMENTWRITER_H
#define QTEXTDOCUMENTWRITER_H
diff --git a/src/gui/text/qtextengine.cpp b/src/gui/text/qtextengine.cpp
index 074771beaa..a18157ab9b 100644
--- a/src/gui/text/qtextengine.cpp
+++ b/src/gui/text/qtextengine.cpp
@@ -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) 2021 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 <QtGui/private/qtguiglobal_p.h>
#include "qdebug.h"
@@ -43,6 +7,7 @@
#include "qtextformat_p.h"
#include "qtextengine_p.h"
#include "qabstracttextdocumentlayout.h"
+#include "qabstracttextdocumentlayout_p.h"
#include "qtextlayout.h"
#include "qtextboundaryfinder.h"
#include <QtCore/private/qunicodetables_p.h>
@@ -71,24 +36,19 @@ public:
Itemizer(const QString &string, const QScriptAnalysis *analysis, QScriptItemArray &items)
: m_string(string),
m_analysis(analysis),
- m_items(items),
- m_splitter(nullptr)
+ m_items(items)
{
}
- ~Itemizer()
- {
- delete m_splitter;
- }
-
+ ~Itemizer() = default;
/// generate the script items
- /// The caps parameter is used to choose the algoritm of splitting text and assiging roles to the textitems
+ /// The caps parameter is used to choose the algorithm of splitting text and assigning roles to the textitems
void generate(int start, int length, QFont::Capitalization caps)
{
if (caps == QFont::SmallCaps)
generateScriptItemsSmallCaps(reinterpret_cast<const ushort *>(m_string.unicode()), start, length);
- else if(caps == QFont::Capitalize)
+ else if (caps == QFont::Capitalize)
generateScriptItemsCapitalize(start, length);
- else if(caps != QFont::MixedCase) {
+ else if (caps != QFont::MixedCase) {
generateScriptItemsAndChangeCase(start, length,
caps == QFont::AllLowercase ? QScriptAnalysis::Lowercase : QScriptAnalysis::Uppercase);
}
@@ -120,7 +80,7 @@ private:
for (int i = start + 1; i < end; ++i) {
if (m_analysis[i].bidiLevel == m_analysis[start].bidiLevel
&& m_analysis[i].flags == m_analysis[start].flags
- && (m_analysis[i].script == m_analysis[start].script || m_string[i] == QLatin1Char('.'))
+ && (m_analysis[i].script == m_analysis[start].script || m_string[i] == u'.')
&& m_analysis[i].flags < QScriptAnalysis::SpaceTabOrObject
&& i - start < MaxItemLength)
continue;
@@ -136,8 +96,8 @@ private:
return;
if (!m_splitter)
- m_splitter = new QTextBoundaryFinder(QTextBoundaryFinder::Word,
- m_string.constData(), m_string.length(),
+ m_splitter = std::make_unique<QTextBoundaryFinder>(QTextBoundaryFinder::Word,
+ m_string.constData(), m_string.size(),
/*buffer*/nullptr, /*buffer size*/0);
m_splitter->setPosition(start);
@@ -206,7 +166,7 @@ private:
const QString &m_string;
const QScriptAnalysis * const m_analysis;
QScriptItemArray &m_items;
- QTextBoundaryFinder *m_splitter;
+ std::unique_ptr<QTextBoundaryFinder> m_splitter;
};
// -----------------------------------------------------------------------------------------------------
@@ -258,7 +218,7 @@ struct QBidiAlgorithm {
for (int i = 0; i < length; ++i) {
int pos = i;
char32_t uc = text[i].unicode();
- if (QChar::isHighSurrogate(uc) && i < length - 1) {
+ if (QChar::isHighSurrogate(uc) && i < length - 1 && text[i + 1].isLowSurrogate()) {
++i;
analysis[i].bidiDirection = QChar::DirNSM;
uc = QChar::surrogateToUcs4(ushort(uc), text[i].unicode());
@@ -1192,7 +1152,7 @@ void QTextEngine::bidiReorder(int numItems, const quint8 *levels, int *visualOrd
// reverse any contiguous sequence of characters that are at that level or higher.
// reversing is only done up to the lowest odd level
- if(!(levelLow%2)) levelLow++;
+ if (!(levelLow%2)) levelLow++;
BIDI_DEBUG() << "reorderLine: lineLow = " << (uint)levelLow << ", lineHigh = " << (uint)levelHigh;
@@ -1208,7 +1168,7 @@ void QTextEngine::bidiReorder(int numItems, const quint8 *levels, int *visualOrd
while(i <= count && levels[i] >= levelHigh) i++;
int end = i-1;
- if(start != end) {
+ if (start != end) {
//qDebug() << "reversing from " << start << " to " << end;
for(int j = 0; j < (end-start+1)/2; j++) {
int tmp = visualOrder[start+j];
@@ -1247,9 +1207,9 @@ enum JustificationClass {
Adds an inter character justification opportunity after the number or letter
character and a space justification opportunity after the space character.
*/
-static inline void qt_getDefaultJustificationOpportunities(const ushort *string, int length, const QGlyphLayout &g, ushort *log_clusters, int spaceAs)
+static inline void qt_getDefaultJustificationOpportunities(const ushort *string, qsizetype length, const QGlyphLayout &g, ushort *log_clusters, int spaceAs)
{
- int str_pos = 0;
+ qsizetype str_pos = 0;
while (str_pos < length) {
int glyph_pos = log_clusters[str_pos];
@@ -1281,7 +1241,7 @@ static inline void qt_getDefaultJustificationOpportunities(const ushort *string,
}
}
-static inline void qt_getJustificationOpportunities(const ushort *string, int length, const QScriptItem &si, const QGlyphLayout &g, ushort *log_clusters)
+static inline void qt_getJustificationOpportunities(const ushort *string, qsizetype length, const QScriptItem &si, const QGlyphLayout &g, ushort *log_clusters)
{
Q_ASSERT(length > 0 && g.numGlyphs > 0);
@@ -1354,10 +1314,6 @@ void QTextEngine::shapeLine(const QScriptLine &line)
}
}
-#if QT_CONFIG(harfbuzz)
-extern bool qt_useHarfbuzzNG(); // defined in qfontengine.cpp
-#endif
-
static void applyVisibilityRules(ushort ucs, QGlyphLayout *glyphs, uint glyphPosition, QFontEngine *fontEngine)
{
// hide characters that should normally be invisible
@@ -1373,9 +1329,15 @@ static void applyVisibilityRules(ushort ucs, QGlyphLayout *glyphs, uint glyphPos
if (!fontEngine->symbol) {
// U+00AD [SOFT HYPHEN] is a default ignorable codepoint,
// so we replace its glyph and metrics with ones for
- // U+002D [HYPHEN-MINUS] and make it visible if it appears at line-break
+ // U+002D [HYPHEN-MINUS] or U+2010 [HYPHEN] and make
+ // it visible if it appears at line-break
const uint engineIndex = glyphs->glyphs[glyphPosition] & 0xff000000;
- glyphs->glyphs[glyphPosition] = fontEngine->glyphIndex('-');
+ glyph_t glyph = fontEngine->glyphIndex(0x002d);
+ if (glyph == 0)
+ glyph = fontEngine->glyphIndex(0x2010);
+ if (glyph == 0)
+ glyph = fontEngine->glyphIndex(0x00ad);
+ glyphs->glyphs[glyphPosition] = glyph;
if (Q_LIKELY(glyphs->glyphs[glyphPosition] != 0)) {
glyphs->glyphs[glyphPosition] |= engineIndex;
QGlyphLayout tmp = glyphs->mid(glyphPosition, 1);
@@ -1429,36 +1391,42 @@ void QTextEngine::shapeText(int item) const
}
if (Q_UNLIKELY(!ensureSpace(itemLength))) {
- Q_UNREACHABLE(); // ### report OOM error somehow
- return;
+ Q_UNREACHABLE_RETURN(); // ### report OOM error somehow
}
QFontEngine *fontEngine = this->fontEngine(si, &si.ascent, &si.descent, &si.leading);
bool kerningEnabled;
bool letterSpacingIsAbsolute;
- bool shapingEnabled;
+ bool shapingEnabled = false;
+ QHash<QFont::Tag, quint32> features;
QFixed letterSpacing, wordSpacing;
#ifndef QT_NO_RAWFONT
if (useRawFont) {
QTextCharFormat f = format(&si);
QFont font = f.font();
kerningEnabled = font.kerning();
+# if QT_CONFIG(harfbuzz)
shapingEnabled = QFontEngine::scriptRequiresOpenType(QChar::Script(si.analysis.script))
|| (font.styleStrategy() & QFont::PreferNoShaping) == 0;
+# endif
wordSpacing = QFixed::fromReal(font.wordSpacing());
letterSpacing = QFixed::fromReal(font.letterSpacing());
letterSpacingIsAbsolute = true;
+ features = font.d->features;
} else
#endif
{
QFont font = this->font(si);
kerningEnabled = font.d->kerning;
+#if QT_CONFIG(harfbuzz)
shapingEnabled = QFontEngine::scriptRequiresOpenType(QChar::Script(si.analysis.script))
|| (font.d->request.styleStrategy & QFont::PreferNoShaping) == 0;
+#endif
letterSpacingIsAbsolute = font.d->letterSpacingIsAbsolute;
letterSpacing = font.d->letterSpacing;
wordSpacing = font.d->wordSpacing;
+ features = font.d->features;
if (letterSpacingIsAbsolute && letterSpacing.value())
letterSpacing *= font.d->dpi / qt_defaultDpiY();
@@ -1466,8 +1434,7 @@ void QTextEngine::shapeText(int item) const
// split up the item into parts that come from different font engines
// k * 3 entries, array[k] == index in string, array[k + 1] == index in glyphs, array[k + 2] == engine index
- QList<uint> itemBoundaries;
- itemBoundaries.reserve(24);
+ QVarLengthArray<uint, 24> itemBoundaries;
QGlyphLayout initialGlyphs = availableGlyphs(&si);
int nGlyphs = initialGlyphs.numGlyphs;
@@ -1478,7 +1445,7 @@ void QTextEngine::shapeText(int item) const
shapingEnabled
? QFontEngine::GlyphIndicesOnly
: QFontEngine::ShaperFlag(0);
- if (!fontEngine->stringToCMap(reinterpret_cast<const QChar *>(string), itemLength, &initialGlyphs, &nGlyphs, shaperFlags))
+ if (fontEngine->stringToCMap(reinterpret_cast<const QChar *>(string), itemLength, &initialGlyphs, &nGlyphs, shaperFlags) < 0)
Q_UNREACHABLE();
}
@@ -1487,9 +1454,9 @@ void QTextEngine::shapeText(int item) const
for (int i = 0, glyph_pos = 0; i < itemLength; ++i, ++glyph_pos) {
const uint engineIdx = initialGlyphs.glyphs[glyph_pos] >> 24;
if (lastEngine != engineIdx) {
- itemBoundaries.append(i);
- itemBoundaries.append(glyph_pos);
- itemBoundaries.append(engineIdx);
+ itemBoundaries.push_back(i);
+ itemBoundaries.push_back(glyph_pos);
+ itemBoundaries.push_back(engineIdx);
if (engineIdx != 0) {
QFontEngine *actualFontEngine = static_cast<QFontEngineMulti *>(fontEngine)->engine(engineIdx);
@@ -1505,14 +1472,21 @@ void QTextEngine::shapeText(int item) const
++i;
}
} else {
- itemBoundaries.append(0);
- itemBoundaries.append(0);
- itemBoundaries.append(0);
+ itemBoundaries.push_back(0);
+ itemBoundaries.push_back(0);
+ itemBoundaries.push_back(0);
}
#if QT_CONFIG(harfbuzz)
- if (Q_LIKELY(shapingEnabled && qt_useHarfbuzzNG())) {
- si.num_glyphs = shapeTextWithHarfbuzzNG(si, string, itemLength, fontEngine, itemBoundaries, kerningEnabled, letterSpacing != 0);
+ if (Q_LIKELY(shapingEnabled)) {
+ si.num_glyphs = shapeTextWithHarfbuzzNG(si,
+ string,
+ itemLength,
+ fontEngine,
+ itemBoundaries,
+ kerningEnabled,
+ letterSpacing != 0,
+ features);
} else
#endif
{
@@ -1546,19 +1520,33 @@ void QTextEngine::shapeText(int item) const
si.num_glyphs = glyph_pos;
}
+
if (Q_UNLIKELY(si.num_glyphs == 0)) {
- Q_UNREACHABLE(); // ### report shaping errors somehow
+ if (Q_UNLIKELY(!ensureSpace(si.glyph_data_offset + 1))) {
+ qWarning() << "Unable to allocate space for place-holder glyph";
+ return;
+ }
+
+ si.num_glyphs = 1;
+
+ // Overwrite with 0 token to indicate failure
+ QGlyphLayout g = availableGlyphs(&si);
+ g.glyphs[0] = 0;
+ g.attributes[0].clusterStart = true;
+
+ ushort *log_clusters = logClusters(&si);
+ for (int i = 0; i < itemLength; ++i)
+ log_clusters[i] = 0;
+
return;
}
-
layoutData->used += si.num_glyphs;
QGlyphLayout glyphs = shapedGlyphs(&si);
#if QT_CONFIG(harfbuzz)
- if (Q_LIKELY(qt_useHarfbuzzNG()))
- qt_getJustificationOpportunities(string, itemLength, si, glyphs, logClusters(&si));
+ qt_getJustificationOpportunities(string, itemLength, si, glyphs, logClusters(&si));
#endif
if (letterSpacing != 0) {
@@ -1608,9 +1596,10 @@ int QTextEngine::shapeTextWithHarfbuzzNG(const QScriptItem &si,
const ushort *string,
int itemLength,
QFontEngine *fontEngine,
- const QList<uint> &itemBoundaries,
+ QSpan<uint> itemBoundaries,
bool kerningEnabled,
- bool hasLetterSpacing) const
+ bool hasLetterSpacing,
+ const QHash<QFont::Tag, quint32> &fontFeatures) const
{
uint glyphs_shaped = 0;
@@ -1629,7 +1618,7 @@ int QTextEngine::shapeTextWithHarfbuzzNG(const QScriptItem &si,
// ### TODO get_default_for_script?
props.language = hb_language_get_default(); // use default language from locale
- for (int k = 0; k < itemBoundaries.size(); k += 3) {
+ for (qsizetype k = 0; k < itemBoundaries.size(); k += 3) {
const uint item_pos = itemBoundaries[k];
const uint item_length = (k + 4 < itemBoundaries.size() ? itemBoundaries[k + 3] : itemLength) - item_pos;
const uint engineIdx = itemBoundaries[k + 2];
@@ -1664,14 +1653,24 @@ int QTextEngine::shapeTextWithHarfbuzzNG(const QScriptItem &si,
|| script == QChar::Script_Khmer || script == QChar::Script_Nko);
bool dontLigate = hasLetterSpacing && !scriptRequiresOpenType;
- const hb_feature_t features[5] = {
- { HB_TAG('k','e','r','n'), !!kerningEnabled, HB_FEATURE_GLOBAL_START, HB_FEATURE_GLOBAL_END },
- { HB_TAG('l','i','g','a'), false, HB_FEATURE_GLOBAL_START, HB_FEATURE_GLOBAL_END },
- { HB_TAG('c','l','i','g'), false, HB_FEATURE_GLOBAL_START, HB_FEATURE_GLOBAL_END },
- { HB_TAG('d','l','i','g'), false, HB_FEATURE_GLOBAL_START, HB_FEATURE_GLOBAL_END },
- { HB_TAG('h','l','i','g'), false, HB_FEATURE_GLOBAL_START, HB_FEATURE_GLOBAL_END }
- };
- const int num_features = dontLigate ? 5 : 1;
+
+ QHash<QFont::Tag, quint32> features;
+ features.insert(QFont::Tag("kern"), !!kerningEnabled);
+ if (dontLigate) {
+ features.insert(QFont::Tag("liga"), false);
+ features.insert(QFont::Tag("clig"), false);
+ features.insert(QFont::Tag("dlig"), false);
+ features.insert(QFont::Tag("hlig"), false);
+ }
+ features.insert(fontFeatures);
+
+ QVarLengthArray<hb_feature_t, 16> featureArray;
+ for (auto it = features.constBegin(); it != features.constEnd(); ++it) {
+ featureArray.append({ it.key().value(),
+ it.value(),
+ HB_FEATURE_GLOBAL_START,
+ HB_FEATURE_GLOBAL_END });
+ }
// whitelist cross-platforms shapers only
static const char *shaper_list[] = {
@@ -1681,7 +1680,11 @@ int QTextEngine::shapeTextWithHarfbuzzNG(const QScriptItem &si,
nullptr
};
- bool shapedOk = hb_shape_full(hb_font, buffer, features, num_features, shaper_list);
+ bool shapedOk = hb_shape_full(hb_font,
+ buffer,
+ featureArray.constData(),
+ features.size(),
+ shaper_list);
if (Q_UNLIKELY(!shapedOk)) {
hb_buffer_destroy(buffer);
return 0;
@@ -1691,9 +1694,14 @@ int QTextEngine::shapeTextWithHarfbuzzNG(const QScriptItem &si,
hb_buffer_reverse(buffer);
}
- const uint num_glyphs = hb_buffer_get_length(buffer);
+ uint num_glyphs = hb_buffer_get_length(buffer);
+ const bool has_glyphs = num_glyphs > 0;
+ // If Harfbuzz returns zero glyphs, we have to manually add a missing glyph
+ if (Q_UNLIKELY(!has_glyphs))
+ num_glyphs = 1;
+
// ensure we have enough space for shaped glyphs and metrics
- if (Q_UNLIKELY(num_glyphs == 0 || !ensureSpace(glyphs_shaped + num_glyphs))) {
+ if (Q_UNLIKELY(!ensureSpace(glyphs_shaped + num_glyphs))) {
hb_buffer_destroy(buffer);
return 0;
}
@@ -1701,44 +1709,55 @@ int QTextEngine::shapeTextWithHarfbuzzNG(const QScriptItem &si,
// fetch the shaped glyphs and metrics
QGlyphLayout g = availableGlyphs(&si).mid(glyphs_shaped, num_glyphs);
ushort *log_clusters = logClusters(&si) + item_pos;
-
- hb_glyph_info_t *infos = hb_buffer_get_glyph_infos(buffer, nullptr);
- hb_glyph_position_t *positions = hb_buffer_get_glyph_positions(buffer, nullptr);
- uint str_pos = 0;
- uint last_cluster = ~0u;
- uint last_glyph_pos = glyphs_shaped;
- for (uint i = 0; i < num_glyphs; ++i, ++infos, ++positions) {
- g.glyphs[i] = infos->codepoint;
-
- g.advances[i] = QFixed::fromFixed(positions->x_advance);
- g.offsets[i].x = QFixed::fromFixed(positions->x_offset);
- g.offsets[i].y = QFixed::fromFixed(positions->y_offset);
-
- uint cluster = infos->cluster;
- if (Q_LIKELY(last_cluster != cluster)) {
- g.attributes[i].clusterStart = true;
-
- // fix up clusters so that the cluster indices will be monotonic
- // and thus we never return out-of-order indices
- while (last_cluster++ < cluster && str_pos < item_length)
- log_clusters[str_pos++] = last_glyph_pos;
- last_glyph_pos = i + glyphs_shaped;
- last_cluster = cluster;
-
- applyVisibilityRules(string[item_pos + str_pos], &g, i, actualFontEngine);
+ if (Q_LIKELY(has_glyphs)) {
+ hb_glyph_info_t *infos = hb_buffer_get_glyph_infos(buffer, nullptr);
+ hb_glyph_position_t *positions = hb_buffer_get_glyph_positions(buffer, nullptr);
+ uint str_pos = 0;
+ uint last_cluster = ~0u;
+ uint last_glyph_pos = glyphs_shaped;
+ for (uint i = 0; i < num_glyphs; ++i, ++infos, ++positions) {
+ g.glyphs[i] = infos->codepoint;
+
+ g.advances[i] = QFixed::fromFixed(positions->x_advance);
+ g.offsets[i].x = QFixed::fromFixed(positions->x_offset);
+ g.offsets[i].y = QFixed::fromFixed(positions->y_offset);
+
+ uint cluster = infos->cluster;
+ if (Q_LIKELY(last_cluster != cluster)) {
+ g.attributes[i].clusterStart = true;
+
+ // fix up clusters so that the cluster indices will be monotonic
+ // and thus we never return out-of-order indices
+ while (last_cluster++ < cluster && str_pos < item_length)
+ log_clusters[str_pos++] = last_glyph_pos;
+ last_glyph_pos = i + glyphs_shaped;
+ last_cluster = cluster;
+
+ applyVisibilityRules(string[item_pos + str_pos], &g, i, actualFontEngine);
+ }
}
+ while (str_pos < item_length)
+ log_clusters[str_pos++] = last_glyph_pos;
+ } else { // Harfbuzz did not return a glyph for the character, so we add a placeholder
+ g.glyphs[0] = 0;
+ g.advances[0] = QFixed{};
+ g.offsets[0].x = QFixed{};
+ g.offsets[0].y = QFixed{};
+ g.attributes[0].clusterStart = true;
+ g.attributes[0].dontPrint = true;
+ log_clusters[0] = glyphs_shaped;
}
- while (str_pos < item_length)
- log_clusters[str_pos++] = last_glyph_pos;
if (Q_UNLIKELY(engineIdx != 0)) {
for (quint32 i = 0; i < num_glyphs; ++i)
g.glyphs[i] |= (engineIdx << 24);
}
- if (!actualFontEngine->supportsSubPixelPositions()) {
- for (uint i = 0; i < num_glyphs; ++i)
+ if (!actualFontEngine->supportsHorizontalSubPixelPositions()) {
+ for (uint i = 0; i < num_glyphs; ++i) {
g.advances[i] = g.advances[i].round();
+ g.offsets[i].x = g.offsets[i].x.round();
+ }
}
glyphs_shaped += num_glyphs;
@@ -1797,7 +1816,7 @@ const QCharAttributes *QTextEngine::attributes() const
return (QCharAttributes *) layoutData->memory;
itemize();
- if (! ensureSpace(layoutData->string.length()))
+ if (! ensureSpace(layoutData->string.size()))
return nullptr;
QVarLengthArray<QUnicodeTools::ScriptItem> scriptItems(layoutData->items.size());
@@ -1904,7 +1923,7 @@ void QTextEngine::itemize() const
if (layoutData->items.size())
return;
- int length = layoutData->string.length();
+ int length = layoutData->string.size();
if (!length)
return;
@@ -1921,9 +1940,9 @@ void QTextEngine::itemize() const
{
QUnicodeTools::ScriptItemArray scriptItems;
QUnicodeTools::initScripts(layoutData->string, &scriptItems);
- for (int i = 0; i < scriptItems.length(); ++i) {
+ for (int i = 0; i < scriptItems.size(); ++i) {
const auto &item = scriptItems.at(i);
- int end = i < scriptItems.length() - 1 ? scriptItems.at(i + 1).position : length;
+ int end = i < scriptItems.size() - 1 ? scriptItems.at(i + 1).position : length;
for (int j = item.position; j < end; ++j)
analysis[j].script = item.script;
}
@@ -1934,7 +1953,17 @@ void QTextEngine::itemize() const
while (uc < e) {
switch (*uc) {
case QChar::ObjectReplacementCharacter:
- analysis->flags = QScriptAnalysis::Object;
+ {
+ const QTextDocumentPrivate *doc_p = QTextDocumentPrivate::get(block);
+ if (doc_p != nullptr
+ && doc_p->layout() != nullptr
+ && QAbstractTextDocumentLayoutPrivate::get(doc_p->layout()) != nullptr
+ && QAbstractTextDocumentLayoutPrivate::get(doc_p->layout())->hasHandlers()) {
+ analysis->flags = QScriptAnalysis::Object;
+ } else {
+ analysis->flags = QScriptAnalysis::None;
+ }
+ }
break;
case QChar::LineSeparator:
analysis->flags = QScriptAnalysis::LineOrParagraphSeparator;
@@ -1979,20 +2008,35 @@ void QTextEngine::itemize() const
QTextDocumentPrivate::FragmentIterator end = p->find(block.position() + block.length() - 1); // -1 to omit the block separator char
int format = it.value()->format;
+ int preeditPosition = s ? s->preeditPosition : INT_MAX;
int prevPosition = 0;
int position = prevPosition;
while (1) {
const QTextFragmentData * const frag = it.value();
if (it == end || format != frag->format) {
- if (s && position >= s->preeditPosition) {
- position += s->preeditText.length();
- s = nullptr;
+ if (s && position >= preeditPosition) {
+ position += s->preeditText.size();
+ preeditPosition = INT_MAX;
}
Q_ASSERT(position <= length);
QFont::Capitalization capitalization =
formatCollection()->charFormat(format).hasProperty(QTextFormat::FontCapitalization)
? formatCollection()->charFormat(format).fontCapitalization()
: formatCollection()->defaultFont().capitalization();
+ if (s) {
+ for (const auto &range : std::as_const(s->formats)) {
+ if (range.start + range.length <= prevPosition || range.start >= position)
+ continue;
+ if (range.format.hasProperty(QTextFormat::FontCapitalization)) {
+ if (range.start > prevPosition)
+ itemizer.generate(prevPosition, range.start - prevPosition, capitalization);
+ int newStart = std::max(prevPosition, range.start);
+ int newEnd = std::min(position, range.start + range.length);
+ itemizer.generate(newStart, newEnd - newStart, range.format.fontCapitalization());
+ prevPosition = newEnd;
+ }
+ }
+ }
itemizer.generate(prevPosition, position - prevPosition, capitalization);
if (it == end) {
if (position < length)
@@ -2068,35 +2112,30 @@ int QTextEngine::findItem(int strPos, int firstItem) const
return right;
}
-QFixed QTextEngine::width(int from, int len) const
+namespace {
+template<typename InnerFunc>
+void textIterator(const QTextEngine *textEngine, int from, int len, QFixed &width, InnerFunc &&innerFunc)
{
- itemize();
-
- QFixed w = 0;
-
-// qDebug("QTextEngine::width(from = %d, len = %d), numItems=%d, strleng=%d", from, len, items.size(), string.length());
- for (int i = 0; i < layoutData->items.size(); i++) {
- const QScriptItem *si = layoutData->items.constData() + i;
+ for (int i = 0; i < textEngine->layoutData->items.size(); i++) {
+ const QScriptItem *si = textEngine->layoutData->items.constData() + i;
int pos = si->position;
- int ilen = length(i);
+ int ilen = textEngine->length(i);
// qDebug("item %d: from %d len %d", i, pos, ilen);
if (pos >= from + len)
break;
if (pos + ilen > from) {
if (!si->num_glyphs)
- shape(i);
+ textEngine->shape(i);
if (si->analysis.flags == QScriptAnalysis::Object) {
- w += si->width;
+ width += si->width;
continue;
} else if (si->analysis.flags == QScriptAnalysis::Tab) {
- w += calculateTabWidth(i, w);
+ width += textEngine->calculateTabWidth(i, width);
continue;
}
-
- QGlyphLayout glyphs = shapedGlyphs(si);
- unsigned short *logClusters = this->logClusters(si);
+ unsigned short *logClusters = textEngine->logClusters(si);
// fprintf(stderr, " logclusters:");
// for (int k = 0; k < ilen; k++)
@@ -2121,11 +2160,24 @@ QFixed QTextEngine::width(int from, int len) const
glyphEnd = (charEnd == ilen) ? si->num_glyphs : logClusters[charEnd];
// qDebug("char: start=%d end=%d / glyph: start = %d, end = %d", charFrom, charEnd, glyphStart, glyphEnd);
- for (int i = glyphStart; i < glyphEnd; i++)
- w += glyphs.advances[i] * !glyphs.attributes[i].dontPrint;
+ innerFunc(glyphStart, glyphEnd, si);
}
}
}
+}
+} // namespace
+
+QFixed QTextEngine::width(int from, int len) const
+{
+ itemize();
+
+ QFixed w = 0;
+// qDebug("QTextEngine::width(from = %d, len = %d), numItems=%d, strleng=%d", from, len, items.size(), string.length());
+ textIterator(this, from, len, w, [this, &w](int glyphStart, int glyphEnd, const QScriptItem *si) {
+ QGlyphLayout glyphs = this->shapedGlyphs(si);
+ for (int j = glyphStart; j < glyphEnd; j++)
+ w += glyphs.advances[j] * !glyphs.attributes[j].dontPrint;
+ });
// qDebug(" --> w= %d ", w);
return w;
}
@@ -2136,58 +2188,20 @@ glyph_metrics_t QTextEngine::boundingBox(int from, int len) const
glyph_metrics_t gm;
- for (int i = 0; i < layoutData->items.size(); i++) {
- const QScriptItem *si = layoutData->items.constData() + i;
-
- int pos = si->position;
- int ilen = length(i);
- if (pos > from + len)
- break;
- if (pos + ilen > from) {
- if (!si->num_glyphs)
- shape(i);
-
- if (si->analysis.flags == QScriptAnalysis::Object) {
- gm.width += si->width;
- continue;
- } else if (si->analysis.flags == QScriptAnalysis::Tab) {
- gm.width += calculateTabWidth(i, gm.width);
- continue;
- }
-
- unsigned short *logClusters = this->logClusters(si);
- QGlyphLayout glyphs = shapedGlyphs(si);
-
- // do the simple thing for now and give the first glyph in a cluster the full width, all other ones 0.
- int charFrom = from - pos;
- if (charFrom < 0)
- charFrom = 0;
- int glyphStart = logClusters[charFrom];
- if (charFrom > 0 && logClusters[charFrom-1] == glyphStart)
- while (charFrom < ilen && logClusters[charFrom] == glyphStart)
- charFrom++;
- if (charFrom < ilen) {
- QFontEngine *fe = fontEngine(*si);
- glyphStart = logClusters[charFrom];
- int charEnd = from + len - 1 - pos;
- if (charEnd >= ilen)
- charEnd = ilen-1;
- int glyphEnd = logClusters[charEnd];
- while (charEnd < ilen && logClusters[charEnd] == glyphEnd)
- charEnd++;
- glyphEnd = (charEnd == ilen) ? si->num_glyphs : logClusters[charEnd];
- if (glyphStart <= glyphEnd ) {
- glyph_metrics_t m = fe->boundingBox(glyphs.mid(glyphStart, glyphEnd - glyphStart));
- gm.x = qMin(gm.x, m.x + gm.xoff);
- gm.y = qMin(gm.y, m.y + gm.yoff);
- gm.width = qMax(gm.width, m.width+gm.xoff);
- gm.height = qMax(gm.height, m.height+gm.yoff);
- gm.xoff += m.xoff;
- gm.yoff += m.yoff;
- }
- }
+ textIterator(this, from, len, gm.width, [this, &gm](int glyphStart, int glyphEnd, const QScriptItem *si) {
+ if (glyphStart <= glyphEnd) {
+ QGlyphLayout glyphs = this->shapedGlyphs(si);
+ QFontEngine *fe = this->fontEngine(*si);
+ glyph_metrics_t m = fe->boundingBox(glyphs.mid(glyphStart, glyphEnd - glyphStart));
+ gm.x = qMin(gm.x, m.x + gm.xoff);
+ gm.y = qMin(gm.y, m.y + gm.yoff);
+ gm.width = qMax(gm.width, m.width + gm.xoff);
+ gm.height = qMax(gm.height, m.height + gm.yoff);
+ gm.xoff += m.xoff;
+ gm.yoff += m.yoff;
}
- }
+ });
+
return gm;
}
@@ -2197,48 +2211,19 @@ glyph_metrics_t QTextEngine::tightBoundingBox(int from, int len) const
glyph_metrics_t gm;
- for (int i = 0; i < layoutData->items.size(); i++) {
- const QScriptItem *si = layoutData->items.constData() + i;
- int pos = si->position;
- int ilen = length(i);
- if (pos > from + len)
- break;
- if (pos + len > from) {
- if (!si->num_glyphs)
- shape(i);
- unsigned short *logClusters = this->logClusters(si);
- QGlyphLayout glyphs = shapedGlyphs(si);
-
- // do the simple thing for now and give the first glyph in a cluster the full width, all other ones 0.
- int charFrom = from - pos;
- if (charFrom < 0)
- charFrom = 0;
- int glyphStart = logClusters[charFrom];
- if (charFrom > 0 && logClusters[charFrom-1] == glyphStart)
- while (charFrom < ilen && logClusters[charFrom] == glyphStart)
- charFrom++;
- if (charFrom < ilen) {
- glyphStart = logClusters[charFrom];
- int charEnd = from + len - 1 - pos;
- if (charEnd >= ilen)
- charEnd = ilen-1;
- int glyphEnd = logClusters[charEnd];
- while (charEnd < ilen && logClusters[charEnd] == glyphEnd)
- charEnd++;
- glyphEnd = (charEnd == ilen) ? si->num_glyphs : logClusters[charEnd];
- if (glyphStart <= glyphEnd ) {
- QFontEngine *fe = fontEngine(*si);
- glyph_metrics_t m = fe->tightBoundingBox(glyphs.mid(glyphStart, glyphEnd - glyphStart));
- gm.x = qMin(gm.x, m.x + gm.xoff);
- gm.y = qMin(gm.y, m.y + gm.yoff);
- gm.width = qMax(gm.width, m.width+gm.xoff);
- gm.height = qMax(gm.height, m.height+gm.yoff);
- gm.xoff += m.xoff;
- gm.yoff += m.yoff;
- }
- }
- }
- }
+ textIterator(this, from, len, gm.width, [this, &gm](int glyphStart, int glyphEnd, const QScriptItem *si) {
+ if (glyphStart <= glyphEnd) {
+ QGlyphLayout glyphs = this->shapedGlyphs(si);
+ QFontEngine *fe = fontEngine(*si);
+ glyph_metrics_t m = fe->tightBoundingBox(glyphs.mid(glyphStart, glyphEnd - glyphStart));
+ gm.x = qMin(gm.x, m.x + gm.xoff);
+ gm.y = qMin(gm.y, m.y + gm.yoff);
+ gm.width = qMax(gm.width, m.width + gm.xoff);
+ gm.height = qMax(gm.height, m.height + gm.yoff);
+ gm.xoff += m.xoff;
+ gm.yoff += m.yoff;
+ }
+ });
return gm;
}
@@ -2305,6 +2290,12 @@ QFontEngine *QTextEngine::fontEngine(const QScriptItem &si, QFixed *ascent, QFix
if (feCache.prevScaledFontEngine) {
scaledEngine = feCache.prevScaledFontEngine;
} else {
+ // GCC 12 gets confused about QFontEngine::ref, for some non-obvious reason
+ // warning: ‘unsigned int __atomic_or_fetch_4(volatile void*, unsigned int, int)’ writing 4 bytes
+ // into a region of size 0 overflows the destination [-Wstringop-overflow=]
+ QT_WARNING_PUSH
+ QT_WARNING_DISABLE_GCC("-Wstringop-overflow")
+
QFontEngine *scEngine = rawFont.d->fontEngine->cloneWithSize(smallCapsFraction * rawFont.pixelSize());
scEngine->ref.ref();
scaledEngine = QFontEngineMulti::createMultiFontEngine(scEngine, script);
@@ -2314,6 +2305,7 @@ QFontEngine *QTextEngine::fontEngine(const QScriptItem &si, QFixed *ascent, QFix
if (!scEngine->ref.deref())
delete scEngine;
+ QT_WARNING_POP
}
}
} else
@@ -2336,8 +2328,8 @@ QFontEngine *QTextEngine::fontEngine(const QScriptItem &si, QFixed *ascent, QFix
font = font.resolve(fnt);
}
engine = font.d->engineForScript(script);
- if (engine)
- engine->ref.ref();
+ Q_ASSERT(engine);
+ engine->ref.ref();
QTextCharFormat::VerticalAlignment valign = f.verticalAlignment();
if (valign == QTextCharFormat::AlignSuperScript || valign == QTextCharFormat::AlignSubScript) {
@@ -2363,13 +2355,12 @@ QFontEngine *QTextEngine::fontEngine(const QScriptItem &si, QFixed *ascent, QFix
feCache.prevLength = length(&si);
}
} else {
- if (feCache.prevFontEngine && feCache.prevScript == script && feCache.prevPosition == -1)
+ if (feCache.prevFontEngine && feCache.prevScript == script && feCache.prevPosition == -1) {
engine = feCache.prevFontEngine;
- else {
+ } else {
engine = font.d->engineForScript(script);
-
- if (engine)
- engine->ref.ref();
+ Q_ASSERT(engine);
+ engine->ref.ref();
if (feCache.prevFontEngine)
releaseCachedFontEngine(feCache.prevFontEngine);
feCache.prevFontEngine = engine;
@@ -2387,7 +2378,10 @@ QFontEngine *QTextEngine::fontEngine(const QScriptItem &si, QFixed *ascent, QFix
}
}
- if (ascent) {
+ if (leading) {
+ Q_ASSERT(engine);
+ Q_ASSERT(ascent);
+ Q_ASSERT(descent);
*ascent = engine->ascent();
*descent = engine->descent();
*leading = engine->leading();
@@ -2450,7 +2444,7 @@ void QTextEngine::justify(const QScriptLine &line)
if (!forceJustification) {
int end = line.from + (int)line.length + line.trailingSpaces;
- if (end == layoutData->string.length())
+ if (end == layoutData->string.size())
return; // no justification at end of paragraph
if (end && layoutData->items.at(findItem(end - 1)).analysis.flags == QScriptAnalysis::LineOrParagraphSeparator)
return; // no justification at the end of an explicitly separated line
@@ -2656,18 +2650,20 @@ QTextEngine::LayoutData::LayoutData()
haveCharAttributes = false;
logClustersPtr = nullptr;
available_glyphs = 0;
+ currentMaxWidth = 0;
}
-QTextEngine::LayoutData::LayoutData(const QString &str, void **stack_memory, int _allocated)
+QTextEngine::LayoutData::LayoutData(const QString &str, void **stack_memory, qsizetype _allocated)
: string(str)
{
allocated = _allocated;
- int space_charAttributes = int(sizeof(QCharAttributes) * string.length() / sizeof(void*) + 1);
- int space_logClusters = int(sizeof(unsigned short) * string.length() / sizeof(void*) + 1);
- available_glyphs = ((int)allocated - space_charAttributes - space_logClusters)*(int)sizeof(void*)/(int)QGlyphLayout::SpaceNeeded;
+ constexpr qsizetype voidSize = sizeof(void*);
+ qsizetype space_charAttributes = sizeof(QCharAttributes) * string.size() / voidSize + 1;
+ qsizetype space_logClusters = sizeof(unsigned short) * string.size() / voidSize + 1;
+ available_glyphs = (allocated - space_charAttributes - space_logClusters) * voidSize / QGlyphLayout::SpaceNeeded;
- if (available_glyphs < str.length()) {
+ if (available_glyphs < str.size()) {
// need to allocate on the heap
allocated = 0;
@@ -2680,7 +2676,7 @@ QTextEngine::LayoutData::LayoutData(const QString &str, void **stack_memory, int
logClustersPtr = (unsigned short *)(memory + space_charAttributes);
void *m = memory + space_charAttributes + space_logClusters;
- glyphLayout = QGlyphLayout(reinterpret_cast<char *>(m), str.length());
+ glyphLayout = QGlyphLayout(reinterpret_cast<char *>(m), str.size());
glyphLayout.clear();
memset(memory, 0, space_charAttributes*sizeof(void *));
}
@@ -2688,6 +2684,7 @@ QTextEngine::LayoutData::LayoutData(const QString &str, void **stack_memory, int
hasBidi = false;
layoutState = LayoutEmpty;
haveCharAttributes = false;
+ currentMaxWidth = 0;
}
QTextEngine::LayoutData::~LayoutData()
@@ -2705,15 +2702,16 @@ bool QTextEngine::LayoutData::reallocate(int totalGlyphs)
return true;
}
- int space_charAttributes = int(sizeof(QCharAttributes) * string.length() / sizeof(void*) + 1);
- int space_logClusters = int(sizeof(unsigned short) * string.length() / sizeof(void*) + 1);
- int space_glyphs = (totalGlyphs * QGlyphLayout::SpaceNeeded) / sizeof(void *) + 2;
+ const qsizetype space_charAttributes = (sizeof(QCharAttributes) * string.size() / sizeof(void*) + 1);
+ const qsizetype space_logClusters = (sizeof(unsigned short) * string.size() / sizeof(void*) + 1);
+ const qsizetype space_glyphs = qsizetype(totalGlyphs) * QGlyphLayout::SpaceNeeded / sizeof(void *) + 2;
- int newAllocated = space_charAttributes + space_glyphs + space_logClusters;
- // These values can be negative if the length of string/glyphs causes overflow,
+ const qsizetype newAllocated = space_charAttributes + space_glyphs + space_logClusters;
+ // Check if the length of string/glyphs causes int overflow,
// we can't layout such a long string all at once, so return false here to
// indicate there is a failure
- if (space_charAttributes < 0 || space_logClusters < 0 || space_glyphs < 0 || newAllocated < allocated) {
+ if (size_t(space_charAttributes) > INT_MAX || size_t(space_logClusters) > INT_MAX || totalGlyphs < 0
+ || size_t(space_glyphs) > INT_MAX || size_t(newAllocated) > INT_MAX || newAllocated < allocated) {
layoutState = LayoutFailed;
return false;
}
@@ -2733,7 +2731,7 @@ bool QTextEngine::LayoutData::reallocate(int totalGlyphs)
logClustersPtr = (unsigned short *) m;
m += space_logClusters;
- const int space_preGlyphLayout = space_charAttributes + space_logClusters;
+ const qsizetype space_preGlyphLayout = space_charAttributes + space_logClusters;
if (allocated < space_preGlyphLayout)
memset(memory + allocated, 0, (space_preGlyphLayout - allocated)*sizeof(void *));
@@ -2743,6 +2741,21 @@ bool QTextEngine::LayoutData::reallocate(int totalGlyphs)
return true;
}
+void QGlyphLayout::copy(QGlyphLayout *oldLayout)
+{
+ Q_ASSERT(offsets != oldLayout->offsets);
+
+ int n = std::min(numGlyphs, oldLayout->numGlyphs);
+
+ memcpy(offsets, oldLayout->offsets, n * sizeof(QFixedPoint));
+ memcpy(attributes, oldLayout->attributes, n * sizeof(QGlyphAttributes));
+ memcpy(justifications, oldLayout->justifications, n * sizeof(QGlyphJustification));
+ memcpy(advances, oldLayout->advances, n * sizeof(QFixed));
+ memcpy(glyphs, oldLayout->glyphs, n * sizeof(glyph_t));
+
+ numGlyphs = n;
+}
+
// grow to the new size, copying the existing data to the new layout
void QGlyphLayout::grow(char *address, int totalGlyphs)
{
@@ -2773,6 +2786,7 @@ void QTextEngine::freeMemory()
layoutData->hasBidi = false;
layoutData->layoutState = LayoutEmpty;
layoutData->haveCharAttributes = false;
+ layoutData->currentMaxWidth = 0;
layoutData->items.clear();
}
if (specialData)
@@ -2796,10 +2810,10 @@ int QTextEngine::formatIndex(const QScriptItem *si) const
return -1;
int pos = si->position;
if (specialData && si->position >= specialData->preeditPosition) {
- if (si->position < specialData->preeditPosition + specialData->preeditText.length())
+ if (si->position < specialData->preeditPosition + specialData->preeditText.size())
pos = qMax(qMin(block.length(), specialData->preeditPosition) - 1, 0);
else
- pos -= specialData->preeditText.length();
+ pos -= specialData->preeditText.size();
}
QTextDocumentPrivate::FragmentIterator it = p->find(block.position() + pos);
return it.value()->format;
@@ -2934,9 +2948,9 @@ void QTextEngine::indexFormats()
*/
static inline bool nextCharJoins(const QString &string, int pos)
{
- while (pos < string.length() && string.at(pos).category() == QChar::Mark_NonSpacing)
+ while (pos < string.size() && string.at(pos).category() == QChar::Mark_NonSpacing)
++pos;
- if (pos == string.length())
+ if (pos == string.size())
return false;
QChar::JoiningType joining = string.at(pos).joiningType();
return joining != QChar::Joining_None && joining != QChar::Joining_Transparent;
@@ -2952,11 +2966,11 @@ static inline bool prevCharJoins(const QString &string, int pos)
return joining == QChar::Joining_Dual || joining == QChar::Joining_Causing;
}
-static inline bool isRetainableControlCode(QChar c)
+static constexpr bool isRetainableControlCode(char16_t c) noexcept
{
- return (c.unicode() >= 0x202a && c.unicode() <= 0x202e) // LRE, RLE, PDF, LRO, RLO
- || (c.unicode() >= 0x200e && c.unicode() <= 0x200f) // LRM, RLM
- || (c.unicode() >= 0x2066 && c.unicode() <= 0x2069); // LRI, RLI, FSI, PDI
+ return (c >= 0x202a && c <= 0x202e) // LRE, RLE, PDF, LRO, RLO
+ || (c >= 0x200e && c <= 0x200f) // LRM, RLM
+ || (c >= 0x2066 && c <= 0x2069); // LRI, RLI, FSI, PDI
}
static QString stringMidRetainingBidiCC(const QString &string,
@@ -2969,14 +2983,14 @@ static QString stringMidRetainingBidiCC(const QString &string,
{
QString prefix;
for (int i=subStringFrom; i<midStart; ++i) {
- QChar c = string.at(i);
+ char16_t c = string.at(i).unicode();
if (isRetainableControlCode(c))
prefix += c;
}
QString suffix;
for (int i=midStart + midLength; i<subStringTo; ++i) {
- QChar c = string.at(i);
+ char16_t c = string.at(i).unicode();
if (isRetainableControlCode(c))
suffix += c;
}
@@ -2984,7 +2998,7 @@ static QString stringMidRetainingBidiCC(const QString &string,
return prefix + ellidePrefix + QStringView{string}.mid(midStart, midLength) + ellideSuffix + suffix;
}
-QString QTextEngine::elidedText(Qt::TextElideMode mode, const QFixed &width, int flags, int from, int count) const
+QString QTextEngine::elidedText(Qt::TextElideMode mode, QFixed width, int flags, int from, int count) const
{
// qDebug() << "elidedText; available width" << width.toReal() << "text width:" << this->width(0, layoutData->string.length()).toReal();
@@ -3003,14 +3017,14 @@ QString QTextEngine::elidedText(Qt::TextElideMode mode, const QFixed &width, int
const int end = si.position + length(&si);
for (int i = si.position; i < end - 1; ++i) {
- if (layoutData->string.at(i) == QLatin1Char('&')
+ if (layoutData->string.at(i) == u'&'
&& !attributes[i + 1].whiteSpace && attributes[i + 1].graphemeBoundary) {
const int gp = logClusters[i - si.position];
glyphs.attributes[gp].dontPrint = true;
// emulate grapheme cluster
attributes[i] = attributes[i + 1];
memset(attributes + i + 1, 0, sizeof(QCharAttributes));
- if (layoutData->string.at(i + 1) == QLatin1Char('&'))
+ if (layoutData->string.at(i + 1) == u'&')
++i;
}
}
@@ -3019,12 +3033,12 @@ QString QTextEngine::elidedText(Qt::TextElideMode mode, const QFixed &width, int
validate();
- const int to = count >= 0 && count <= layoutData->string.length() - from
+ const int to = count >= 0 && count <= layoutData->string.size() - from
? from + count
- : layoutData->string.length();
+ : layoutData->string.size();
if (mode == Qt::ElideNone
- || this->width(from, layoutData->string.length()) <= width
+ || this->width(from, layoutData->string.size()) <= width
|| to - from <= 1)
return layoutData->string.mid(from, from - to);
@@ -3033,7 +3047,7 @@ QString QTextEngine::elidedText(Qt::TextElideMode mode, const QFixed &width, int
{
QFontEngine *engine = fnt.d->engineForScript(QChar::Script_Common);
- QChar ellipsisChar = u'\x2026';
+ constexpr char16_t ellipsisChar = u'\x2026';
// We only want to use the ellipsis character if it is from the main
// font (not one of the fallbacks), since using a fallback font
@@ -3045,7 +3059,7 @@ QString QTextEngine::elidedText(Qt::TextElideMode mode, const QFixed &width, int
engine = multiEngine->engine(0);
}
- glyph_t glyph = engine->glyphIndex(ellipsisChar.unicode());
+ glyph_t glyph = engine->glyphIndex(ellipsisChar);
QGlyphLayout glyphs;
glyphs.numGlyphs = 1;
@@ -3063,6 +3077,11 @@ QString QTextEngine::elidedText(Qt::TextElideMode mode, const QFixed &width, int
ellipsisWidth *= 3;
ellipsisText = QStringLiteral("...");
+ } else {
+ engine = fnt.d->engineForScript(QChar::Script_Common);
+ glyph = engine->glyphIndex(ellipsisChar);
+ engine->recalcAdvances(&glyphs, { });
+ ellipsisText = ellipsisChar;
}
}
}
@@ -3086,7 +3105,7 @@ QString QTextEngine::elidedText(Qt::TextElideMode mode, const QFixed &width, int
pos = nextBreak;
++nextBreak;
- while (nextBreak < layoutData->string.length() && !attributes[nextBreak].graphemeBoundary)
+ while (nextBreak < layoutData->string.size() && !attributes[nextBreak].graphemeBoundary)
++nextBreak;
currentWidth += this->width(pos, nextBreak - pos);
@@ -3138,7 +3157,7 @@ QString QTextEngine::elidedText(Qt::TextElideMode mode, const QFixed &width, int
rightPos = nextRightBreak;
++nextLeftBreak;
- while (nextLeftBreak < layoutData->string.length() && !attributes[nextLeftBreak].graphemeBoundary)
+ while (nextLeftBreak < layoutData->string.size() && !attributes[nextLeftBreak].graphemeBoundary)
++nextLeftBreak;
--nextRightBreak;
@@ -3177,7 +3196,7 @@ void QTextEngine::setBoundary(int strPos) const
QFixed QTextEngine::calculateTabWidth(int item, QFixed x) const
{
- const QScriptItem &si = layoutData->items[item];
+ const QScriptItem &si = layoutData->items.at(item);
QFixed dpiScale = 1;
if (QTextDocumentPrivate::get(block) != nullptr && QTextDocumentPrivate::get(block)->layout() != nullptr) {
@@ -3211,15 +3230,15 @@ QFixed QTextEngine::calculateTabWidth(int item, QFixed x) const
}
}
}
- for (const QTextOption::Tab &tabSpec : qAsConst(tabArray)) {
+ for (const QTextOption::Tab &tabSpec : std::as_const(tabArray)) {
QFixed tab = QFixed::fromReal(tabSpec.position) * dpiScale;
if (tab > x) { // this is the tab we need.
- int tabSectionEnd = layoutData->string.count();
+ int tabSectionEnd = layoutData->string.size();
if (tabSpec.type == QTextOption::RightTab || tabSpec.type == QTextOption::CenterTab) {
// find next tab to calculate the width required.
tab = QFixed::fromReal(tabSpec.position);
- for (int i=item + 1; i < layoutData->items.count(); i++) {
- const QScriptItem &item = layoutData->items[i];
+ for (int i=item + 1; i < layoutData->items.size(); i++) {
+ const QScriptItem &item = layoutData->items.at(i);
if (item.analysis.flags == QScriptAnalysis::TabOrObject) { // found it.
tabSectionEnd = item.position;
break;
@@ -3227,13 +3246,13 @@ QFixed QTextEngine::calculateTabWidth(int item, QFixed x) const
}
}
else if (tabSpec.type == QTextOption::DelimiterTab)
- // find delimitor character to calculate the width required
+ // find delimiter character to calculate the width required
tabSectionEnd = qMax(si.position, layoutData->string.indexOf(tabSpec.delimiter, si.position) + 1);
if (tabSectionEnd > si.position) {
QFixed length;
// Calculate the length of text between this tab and the tabSectionEnd
- for (int i=item; i < layoutData->items.count(); i++) {
+ for (int i=item; i < layoutData->items.size(); i++) {
const QScriptItem &item = layoutData->items.at(i);
if (item.position > tabSectionEnd || item.position <= si.position)
continue;
@@ -3305,7 +3324,7 @@ void QTextEngine::resolveFormats() const
QTextFormatCollection *collection = formatCollection();
- QList<QTextCharFormat> resolvedFormats(layoutData->items.count());
+ QList<QTextCharFormat> resolvedFormats(layoutData->items.size());
QVarLengthArray<int, 64> formatsSortedByStart;
formatsSortedByStart.reserve(specialData->formats.size());
@@ -3323,7 +3342,7 @@ void QTextEngine::resolveFormats() const
const int *startIt = formatsSortedByStart.constBegin();
const int *endIt = formatsSortedByEnd.constBegin();
- for (int i = 0; i < layoutData->items.count(); ++i) {
+ for (int i = 0; i < layoutData->items.size(); ++i) {
const QScriptItem *si = &layoutData->items.at(i);
int end = si->position + length(si);
@@ -3499,8 +3518,8 @@ int QTextEngine::previousLogicalPosition(int oldPos) const
{
const QCharAttributes *attrs = attributes();
int len = block.isValid() ? block.length() - 1
- : layoutData->string.length();
- Q_ASSERT(len <= layoutData->string.length());
+ : layoutData->string.size();
+ Q_ASSERT(len <= layoutData->string.size());
if (!attrs || oldPos <= 0 || oldPos > len)
return oldPos;
@@ -3514,8 +3533,8 @@ int QTextEngine::nextLogicalPosition(int oldPos) const
{
const QCharAttributes *attrs = attributes();
int len = block.isValid() ? block.length() - 1
- : layoutData->string.length();
- Q_ASSERT(len <= layoutData->string.length());
+ : layoutData->string.size();
+ Q_ASSERT(len <= layoutData->string.size());
if (!attrs || oldPos < 0 || oldPos >= len)
return oldPos;
@@ -3529,7 +3548,7 @@ int QTextEngine::lineNumberForTextPosition(int pos)
{
if (!layoutData)
itemize();
- if (pos == layoutData->string.length() && lines.size())
+ if (pos == layoutData->string.size() && lines.size())
return lines.size() - 1;
for (int i = 0; i < lines.size(); ++i) {
const QScriptLine& line = lines[i];
@@ -3661,12 +3680,6 @@ void QTextEngine::drawDecorations(QPainter *painter)
{
QPen oldPen = painter->pen();
- bool wasCompatiblePainting = painter->renderHints()
- & QPainter::Qt4CompatiblePainting;
-
- if (wasCompatiblePainting)
- painter->setRenderHint(QPainter::Qt4CompatiblePainting, false);
-
adjustUnderlines();
drawItemDecorationList(painter, underlineList);
drawItemDecorationList(painter, strikeOutList);
@@ -3674,9 +3687,6 @@ void QTextEngine::drawDecorations(QPainter *painter)
clearDecorations();
- if (wasCompatiblePainting)
- painter->setRenderHint(QPainter::Qt4CompatiblePainting);
-
painter->setPen(oldPen);
}
@@ -3862,10 +3872,12 @@ QTextLineItemIterator::QTextLineItemIterator(QTextEngine *_eng, int _lineNum, co
x += eng->alignLine(line);
- QVarLengthArray<uchar> levels(nItems);
- for (int i = 0; i < nItems; ++i)
- levels[i] = eng->layoutData->items.at(i + firstItem).analysis.bidiLevel;
- QTextEngine::bidiReorder(nItems, levels.data(), visualOrder.data());
+ if (nItems > 0) {
+ QVarLengthArray<uchar> levels(nItems);
+ for (int i = 0; i < nItems; ++i)
+ levels[i] = eng->layoutData->items.at(i + firstItem).analysis.bidiLevel;
+ QTextEngine::bidiReorder(nItems, levels.data(), visualOrder.data());
+ }
eng->shapeLine(line);
}
diff --git a/src/gui/text/qtextengine_p.h b/src/gui/text/qtextengine_p.h
index 0e80510058..c01d3a0711 100644
--- a/src/gui/text/qtextengine_p.h
+++ b/src/gui/text/qtextengine_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 QTEXTENGINE_P_H
#define QTEXTENGINE_P_H
@@ -62,6 +26,7 @@
#include "QtCore/qlist.h"
#include "QtCore/qnamespace.h"
#include "QtCore/qset.h"
+#include <QtCore/qspan.h>
#include "QtCore/qstring.h"
#include "QtCore/qvarlengtharray.h"
@@ -194,10 +159,8 @@ Q_DECLARE_TYPEINFO(QGlyphAttributes, Q_PRIMITIVE_TYPE);
struct QGlyphLayout
{
- enum {
- SpaceNeeded = sizeof(glyph_t) + sizeof(QFixed) + sizeof(QFixedPoint)
- + sizeof(QGlyphAttributes) + sizeof(QGlyphJustification)
- };
+ static constexpr qsizetype SpaceNeeded = sizeof(glyph_t) + sizeof(QFixed) + sizeof(QFixedPoint)
+ + sizeof(QGlyphAttributes) + sizeof(QGlyphJustification);
// init to 0 not needed, done when shaping
QFixedPoint *offsets; // 8 bytes per element
@@ -213,7 +176,7 @@ struct QGlyphLayout
inline explicit QGlyphLayout(char *address, int totalGlyphs)
{
offsets = reinterpret_cast<QFixedPoint *>(address);
- int offset = totalGlyphs * sizeof(QFixedPoint);
+ qsizetype offset = totalGlyphs * sizeof(QFixedPoint);
glyphs = reinterpret_cast<glyph_t *>(address + offset);
offset += totalGlyphs * sizeof(glyph_t);
advances = reinterpret_cast<QFixed *>(address + offset);
@@ -246,7 +209,7 @@ struct QGlyphLayout
last = numGlyphs;
if (first == 0 && last == numGlyphs
&& reinterpret_cast<char *>(offsets + numGlyphs) == reinterpret_cast<char *>(glyphs)) {
- memset(static_cast<void *>(offsets), 0, (numGlyphs * SpaceNeeded));
+ memset(static_cast<void *>(offsets), 0, qsizetype(numGlyphs) * SpaceNeeded);
} else {
const int num = last - first;
memset(static_cast<void *>(offsets + first), 0, num * sizeof(QFixedPoint));
@@ -261,6 +224,7 @@ struct QGlyphLayout
return reinterpret_cast<char *>(offsets);
}
+ void copy(QGlyphLayout *other);
void grow(char *address, int totalGlyphs);
};
@@ -407,12 +371,12 @@ public:
LayoutFailed
};
struct Q_GUI_EXPORT LayoutData {
- LayoutData(const QString &str, void **stack_memory, int mem_size);
+ LayoutData(const QString &str, void **stack_memory, qsizetype mem_size);
LayoutData();
~LayoutData();
mutable QScriptItemArray items;
- int allocated;
- int available_glyphs;
+ qsizetype allocated;
+ qsizetype available_glyphs;
void **memory;
unsigned short *logClustersPtr;
QGlyphLayout glyphLayout;
@@ -421,6 +385,7 @@ public:
uint layoutState : 2;
uint memory_on_stack : 1;
uint haveCharAttributes : 1;
+ QFixed currentMaxWidth;
QString string;
bool reallocate(int totalGlyphs);
};
@@ -470,14 +435,14 @@ public:
const QScriptItem &si = layoutData->items[item];
int from = si.position;
item++;
- return (item < layoutData->items.size() ? layoutData->items[item].position : layoutData->string.length()) - from;
+ return (item < layoutData->items.size() ? layoutData->items[item].position : layoutData->string.size()) - from;
}
int length(const QScriptItem *si) const {
int end;
if (si + 1 < layoutData->items.constData()+ layoutData->items.size())
end = (si+1)->position;
else
- end = layoutData->string.length();
+ end = layoutData->string.size();
return end - si->position;
}
@@ -622,7 +587,7 @@ private:
public:
bool atWordSeparator(int position) const;
- QString elidedText(Qt::TextElideMode mode, const QFixed &width, int flags = 0, int from = 0, int count = -1) const;
+ QString elidedText(Qt::TextElideMode mode, QFixed width, int flags = 0, int from = 0, int count = -1) const;
void shapeLine(const QScriptLine &line);
QFixed leadingSpaceWidth(const QScriptLine &line);
@@ -656,9 +621,14 @@ private:
void addRequiredBoundaries() const;
void shapeText(int item) const;
#if QT_CONFIG(harfbuzz)
- int shapeTextWithHarfbuzzNG(const QScriptItem &si, const ushort *string, int itemLength,
- QFontEngine *fontEngine, const QList<uint> &itemBoundaries,
- bool kerningEnabled, bool hasLetterSpacing) const;
+ int shapeTextWithHarfbuzzNG(const QScriptItem &si,
+ const ushort *string,
+ int itemLength,
+ QFontEngine *fontEngine,
+ QSpan<uint> itemBoundaries,
+ bool kerningEnabled,
+ bool hasLetterSpacing,
+ const QHash<QFont::Tag, quint32> &features) const;
#endif
int endOfLine(int lineNum);
@@ -673,7 +643,7 @@ public:
LayoutData _layoutData;
void *_memory[MemSize];
};
-Q_DECLARE_TYPEINFO(QTextEngine::ItemDecoration, Q_MOVABLE_TYPE);
+Q_DECLARE_TYPEINFO(QTextEngine::ItemDecoration, Q_RELOCATABLE_TYPE);
struct QTextLineItemIterator
{
diff --git a/src/gui/text/qtextformat.cpp b/src/gui/text/qtextformat.cpp
index af1fdfa653..daa79a55d2 100644
--- a/src/gui/text/qtextformat.cpp
+++ b/src/gui/text/qtextformat.cpp
@@ -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) 2021 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 "qtextformat.h"
#include "qtextformat_p.h"
@@ -177,7 +141,7 @@ struct Property
{ return key == other.key && value == other.value; }
};
}
-Q_DECLARE_TYPEINFO(Property, Q_MOVABLE_TYPE);
+Q_DECLARE_TYPEINFO(Property, Q_RELOCATABLE_TYPE);
class QTextFormatPrivate : public QSharedData
{
@@ -204,7 +168,7 @@ public:
if (key >= QTextFormat::FirstFontProperty && key <= QTextFormat::LastFontProperty)
fontDirty = true;
- for (int i = 0; i < props.count(); ++i)
+ for (int i = 0; i < props.size(); ++i)
if (props.at(i).key == key) {
props[i].value = value;
return;
@@ -214,7 +178,7 @@ public:
inline void clearProperty(qint32 key)
{
- for (int i = 0; i < props.count(); ++i)
+ for (int i = 0; i < props.size(); ++i)
if (props.at(i).key == key) {
hashDirty = true;
if (key >= QTextFormat::FirstFontProperty && key <= QTextFormat::LastFontProperty)
@@ -226,7 +190,7 @@ public:
inline int propertyIndex(qint32 key) const
{
- for (int i = 0; i < props.count(); ++i)
+ for (int i = 0; i < props.size(); ++i)
if (props.at(i).key == key)
return i;
return -1;
@@ -293,7 +257,7 @@ static inline size_t variantHash(const QVariant &variant)
case QMetaType::Bool: return 0x371818 + variant.toBool();
case QMetaType::QPen: return 0x02020202 + hash(qvariant_cast<QPen>(variant));
case QMetaType::QVariantList:
- return 0x8377U + qvariant_cast<QVariantList>(variant).count();
+ return 0x8377U + qvariant_cast<QVariantList>(variant).size();
case QMetaType::QColor: return hash(qvariant_cast<QColor>(variant));
case QMetaType::QTextLength:
return 0x377 + hash(qvariant_cast<QTextLength>(variant).rawValue());
@@ -354,11 +318,8 @@ void QTextFormatPrivate::recalcFont() const
QFont::SpacingType spacingType = QFont::PercentageSpacing;
qreal letterSpacing = 0.0;
- for (int i = 0; i < props.count(); ++i) {
+ for (int i = 0; i < props.size(); ++i) {
switch (props.at(i).key) {
- case QTextFormat::FontFamily:
- f.setFamily(props.at(i).value.toString());
- break;
case QTextFormat::FontFamilies:
f.setFamilies(props.at(i).value.toStringList());
break;
@@ -444,17 +405,29 @@ Q_GUI_EXPORT QDataStream &operator<<(QDataStream &stream, const QTextFormat &fmt
{
QMap<int, QVariant> properties = fmt.properties();
if (stream.version() < QDataStream::Qt_6_0) {
- auto it = properties.find(QTextFormat::FontLetterSpacingType);
- if (it != properties.end()) {
+ auto it = properties.constFind(QTextFormat::FontLetterSpacingType);
+ if (it != properties.cend()) {
properties[QTextFormat::OldFontLetterSpacingType] = it.value();
properties.erase(it);
}
- it = properties.find(QTextFormat::FontStretch);
- if (it != properties.end()) {
+ it = properties.constFind(QTextFormat::FontStretch);
+ if (it != properties.cend()) {
properties[QTextFormat::OldFontStretch] = it.value();
properties.erase(it);
}
+
+ it = properties.constFind(QTextFormat::TextUnderlineColor);
+ if (it != properties.cend()) {
+ properties[QTextFormat::OldTextUnderlineColor] = it.value();
+ properties.erase(it);
+ }
+
+ it = properties.constFind(QTextFormat::FontFamilies);
+ if (it != properties.cend()) {
+ properties[QTextFormat::OldFontFamily] = QVariant(it.value().toStringList().constFirst());
+ properties.erase(it);
+ }
}
stream << fmt.format_type << properties;
@@ -468,7 +441,7 @@ Q_GUI_EXPORT QDataStream &operator>>(QDataStream &stream, QTextFormat &fmt)
// QTextFormat's default constructor doesn't allocate the private structure, so
// we have to do this, in case fmt is a default constructed value.
- if(!fmt.d)
+ if (!fmt.d)
fmt.d = new QTextFormatPrivate();
for (QMap<qint32, QVariant>::ConstIterator it = properties.constBegin();
@@ -478,11 +451,65 @@ Q_GUI_EXPORT QDataStream &operator>>(QDataStream &stream, QTextFormat &fmt)
key = QTextFormat::FontLetterSpacingType;
else if (key == QTextFormat::OldFontStretch)
key = QTextFormat::FontStretch;
+ else if (key == QTextFormat::OldTextUnderlineColor)
+ key = QTextFormat::TextUnderlineColor;
+ else if (key == QTextFormat::OldFontFamily)
+ key = QTextFormat::FontFamilies;
fmt.d->insertProperty(key, it.value());
}
return stream;
}
+
+Q_GUI_EXPORT QDataStream &operator<<(QDataStream &stream, const QTextCharFormat &fmt)
+{
+ return stream << static_cast<const QTextFormat &>(fmt);
+}
+
+Q_GUI_EXPORT QDataStream &operator>>(QDataStream &stream, QTextCharFormat &fmt)
+{
+ return stream >> static_cast<QTextFormat &>(fmt);
+}
+
+Q_GUI_EXPORT QDataStream &operator<<(QDataStream &stream, const QTextBlockFormat &fmt)
+{
+ return stream << static_cast<const QTextFormat &>(fmt);
+}
+
+Q_GUI_EXPORT QDataStream &operator>>(QDataStream &stream, QTextBlockFormat &fmt)
+{
+ return stream >> static_cast<QTextFormat &>(fmt);
+}
+
+Q_GUI_EXPORT QDataStream &operator<<(QDataStream &stream, const QTextListFormat &fmt)
+{
+ return stream << static_cast<const QTextFormat &>(fmt);
+}
+
+Q_GUI_EXPORT QDataStream &operator>>(QDataStream &stream, QTextListFormat &fmt)
+{
+ return stream >> static_cast<QTextFormat &>(fmt);
+}
+
+Q_GUI_EXPORT QDataStream &operator<<(QDataStream &stream, const QTextFrameFormat &fmt)
+{
+ return stream << static_cast<const QTextFormat &>(fmt);
+}
+
+Q_GUI_EXPORT QDataStream &operator>>(QDataStream &stream, QTextFrameFormat &fmt)
+{
+ return stream >> static_cast<QTextFormat &>(fmt);
+}
+
+Q_GUI_EXPORT QDataStream &operator<<(QDataStream &stream, const QTextTableCellFormat &fmt)
+{
+ return stream << static_cast<const QTextFormat &>(fmt);
+}
+
+Q_GUI_EXPORT QDataStream &operator>>(QDataStream &stream, QTextTableCellFormat &fmt)
+{
+ return stream >> static_cast<QTextFormat &>(fmt);
+}
#endif // QT_NO_DATASTREAM
/*!
@@ -539,7 +566,6 @@ Q_GUI_EXPORT QDataStream &operator>>(QDataStream &stream, QTextFormat &fmt)
\value BlockFormat The object formats a text block
\value CharFormat The object formats a single character
\value ListFormat The object formats a list
- \omitvalue TableFormat \omit Unused Value, a table's FormatType is FrameFormat. \endomit
\value FrameFormat The object formats a frame
\value UserFormat
@@ -598,7 +624,8 @@ Q_GUI_EXPORT QDataStream &operator>>(QDataStream &stream, QTextFormat &fmt)
Character properties
- \value FontFamily
+ \value FontFamily e{This property has been deprecated.} Use QTextFormat::FontFamilies instead.
+ \omitvalue OldFontFamily
\value FontFamilies
\value FontStyleName
\value FontPointSize
@@ -630,7 +657,7 @@ Q_GUI_EXPORT QDataStream &operator>>(QDataStream &stream, QTextFormat &fmt)
\omitvalue FirstFontProperty
\omitvalue LastFontProperty
- \value TextUnderlineColor
+ \value TextUnderlineColor Specifies the color to draw underlines, overlines and strikeouts.
\value TextVerticalAlignment
\value TextOutline
\value TextUnderlineStyle
@@ -645,6 +672,7 @@ Q_GUI_EXPORT QDataStream &operator>>(QDataStream &stream, QTextFormat &fmt)
\value AnchorName
\omitvalue OldFontLetterSpacingType
\omitvalue OldFontStretch
+ \omitvalue OldTextUnderlineColor
\value ObjectType
List properties
@@ -656,6 +684,8 @@ Q_GUI_EXPORT QDataStream &operator>>(QDataStream &stream, QTextFormat &fmt)
numeric lists.
\value ListNumberSuffix Defines the text which is appended to item numbers in
numeric lists.
+ \value [since 6.6] ListStart
+ Defines the first value of a list.
Table and frame properties
@@ -919,13 +949,17 @@ void QTextFormat::merge(const QTextFormat &other)
if (!other.d)
return;
- QTextFormatPrivate *d = this->d;
+ QTextFormatPrivate *p = d.data();
- const QList<QT_PREPEND_NAMESPACE(Property)> &otherProps = other.d->props;
- d->props.reserve(d->props.size() + otherProps.size());
- for (int i = 0; i < otherProps.count(); ++i) {
- const QT_PREPEND_NAMESPACE(Property) &p = otherProps.at(i);
- d->insertProperty(p.key, p.value);
+ const QList<QT_PREPEND_NAMESPACE(Property)> &otherProps = other.d.constData()->props;
+ p->props.reserve(p->props.size() + otherProps.size());
+ for (int i = 0; i < otherProps.size(); ++i) {
+ const QT_PREPEND_NAMESPACE(Property) &prop = otherProps.at(i);
+ if (prop.value.isValid()) {
+ p->insertProperty(prop.key, prop.value);
+ } else {
+ p->clearProperty(prop.key);
+ }
}
}
@@ -1036,7 +1070,7 @@ int QTextFormat::intProperty(int propertyId) const
/*!
Returns the value of the property specified by \a propertyId. If the
- property isn't of QVariant::Double or QMetaType::Float type, 0 is
+ property isn't of QMetaType::Double or QMetaType::Float type, 0 is
returned instead.
\sa setProperty(), boolProperty(), intProperty(), stringProperty(), colorProperty(),
@@ -1054,7 +1088,7 @@ qreal QTextFormat::doubleProperty(int propertyId) const
/*!
Returns the value of the property given by \a propertyId; if the
- property isn't of QVariant::String type, an empty string is
+ property isn't of QMetaType::QString type, an empty string is
returned instead.
\sa setProperty(), boolProperty(), intProperty(), doubleProperty(), colorProperty(),
@@ -1072,7 +1106,7 @@ QString QTextFormat::stringProperty(int propertyId) const
/*!
Returns the value of the property given by \a propertyId; if the
- property isn't of QVariant::Color type, an invalid color is
+ property isn't of QMetaType::QColor type, an invalid color is
returned instead.
\sa setProperty(), boolProperty(), intProperty(), doubleProperty(),
@@ -1090,7 +1124,7 @@ QColor QTextFormat::colorProperty(int propertyId) const
/*!
Returns the value of the property given by \a propertyId; if the
- property isn't of QVariant::Pen type, Qt::NoPen is
+ property isn't of QMetaType::QPen type, Qt::NoPen is
returned instead.
\sa setProperty(), boolProperty(), intProperty(), doubleProperty(), stringProperty(),
@@ -1108,7 +1142,7 @@ QPen QTextFormat::penProperty(int propertyId) const
/*!
Returns the value of the property given by \a propertyId; if the
- property isn't of QVariant::Brush type, Qt::NoBrush is
+ property isn't of QMetaType::QBrush type, Qt::NoBrush is
returned instead.
\sa setProperty(), boolProperty(), intProperty(), doubleProperty(), stringProperty(),
@@ -1182,10 +1216,8 @@ void QTextFormat::setProperty(int propertyId, const QVariant &value)
{
if (!d)
d = new QTextFormatPrivate;
- if (!value.isValid())
- clearProperty(propertyId);
- else
- d->insertProperty(propertyId, value);
+
+ d->insertProperty(propertyId, value);
}
/*!
@@ -1261,10 +1293,10 @@ int QTextFormat::objectIndex() const
void QTextFormat::setObjectIndex(int o)
{
if (o == -1) {
- if (d)
+ if (d.constData())
d->clearProperty(ObjectIndex);
} else {
- if (!d)
+ if (!d.constData())
d = new QTextFormatPrivate;
// ### type
d->insertProperty(ObjectIndex, o);
@@ -1295,7 +1327,7 @@ QMap<int, QVariant> QTextFormat::properties() const
{
QMap<int, QVariant> map;
if (d) {
- for (int i = 0; i < d->props.count(); ++i)
+ for (int i = 0; i < d->props.size(); ++i)
map.insert(d->props.at(i).key, d->props.at(i).value);
}
return map;
@@ -1307,7 +1339,7 @@ QMap<int, QVariant> QTextFormat::properties() const
*/
int QTextFormat::propertyCount() const
{
- return d ? d->props.count() : 0;
+ return d ? d->props.size() : 0;
}
/*!
@@ -1360,7 +1392,7 @@ bool QTextFormat::operator==(const QTextFormat &rhs) const
The font used can be set by supplying a font to the setFont() function, and
each aspect of its appearance can be adjusted to give the desired effect.
- setFontFamily() and setFontPointSize() define the font's family (e.g. Times)
+ setFontFamilies() and setFontPointSize() define the font's family (e.g. Times)
and printed size; setFontWeight() and setFontItalic() provide control over
the style of the font. setFontUnderline(), setFontOverline(),
setFontStrikeOut(), and setFontFixedPitch() provide additional effects for
@@ -1405,7 +1437,7 @@ bool QTextFormat::operator==(const QTextFormat &rhs) const
\value SingleUnderline A line is drawn using Qt::SolidLine.
\value DashUnderline Dashes are drawn using Qt::DashLine.
\value DotLine Dots are drawn using Qt::DotLine;
- \value DashDotLine Dashs and dots are drawn using Qt::DashDotLine.
+ \value DashDotLine Dashes and dots are drawn using Qt::DashDotLine.
\value DashDotDotLine Underlines draw drawn using Qt::DashDotDotLine.
\value WaveUnderline The text is underlined using a wave shaped line.
\value SpellCheckUnderline The underline is drawn depending on the SpellCheckUnderlineStyle
@@ -1444,6 +1476,7 @@ QTextCharFormat::QTextCharFormat(const QTextFormat &fmt)
/*!
\fn void QTextCharFormat::setFontFamily(const QString &family)
+ \deprecated [6.1] Use setFontFamilies() instead.
Sets the text format's font \a family.
@@ -1453,6 +1486,7 @@ QTextCharFormat::QTextCharFormat(const QTextFormat &fmt)
/*!
\fn QString QTextCharFormat::fontFamily() const
+ \deprecated [6.1] Use fontFamilies() instead.
Returns the text format's font family.
@@ -1468,7 +1502,21 @@ QTextCharFormat::QTextCharFormat(const QTextFormat &fmt)
\sa setFont()
*/
+#if QT_VERSION < QT_VERSION_CHECK(7, 0, 0)
/*!
+ \fn QVariant QTextCharFormat::fontFamilies() const
+ \since 5.13
+
+ Returns the text format's font families.
+
+ \note This function returns a QVariant for historical reasons. It will be
+ corrected to return QStringList in Qt 7. The variant contains a QStringList
+ object, which can be extracted by calling \c{toStringList()} on it.
+
+ \sa font()
+*/
+#else
+/* // Qt 7 documents this function
\fn QStringList QTextCharFormat::fontFamilies() const
\since 5.13
@@ -1476,6 +1524,7 @@ QTextCharFormat::QTextCharFormat(const QTextFormat &fmt)
\sa font()
*/
+#endif
/*!
\fn void QTextCharFormat::setFontStyleName(const QString &styleName)
@@ -1486,7 +1535,21 @@ QTextCharFormat::QTextCharFormat(const QTextFormat &fmt)
\sa setFont(), QFont::setStyleName()
*/
+#if QT_VERSION < QT_VERSION_CHECK(7, 0, 0)
/*!
+ \fn QVariant QTextCharFormat::fontStyleName() const
+ \since 5.13
+
+ Returns the text format's font style name.
+
+ \note This function returns a QVariant for historical reasons. It will be
+ corrected to return QStringList in Qt 7. The variant contains a QStringList
+ object, which can be extracted by calling \c{toStringList()} on it.
+
+ \sa font(), QFont::styleName()
+*/
+#else
+/* // Qt 7 documents this function
\fn QStringList QTextCharFormat::fontStyleName() const
\since 5.13
@@ -1494,6 +1557,7 @@ QTextCharFormat::QTextCharFormat(const QTextFormat &fmt)
\sa font(), QFont::styleName()
*/
+#endif
/*!
\fn void QTextCharFormat::setFontPointSize(qreal size)
@@ -1969,8 +2033,8 @@ QStringList QTextCharFormat::anchorNames() const
/*!
\fn void QTextCharFormat::setUnderlineColor(const QColor &color)
- Sets the underline color used for the characters with this format to
- the \a color specified.
+ Sets the color used to draw underlines, overlines and strikeouts on the
+ characters with this format to the \a color specified.
\sa underlineColor()
*/
@@ -1978,7 +2042,8 @@ QStringList QTextCharFormat::anchorNames() const
/*!
\fn QColor QTextCharFormat::underlineColor() const
- Returns the color used to underline the characters with this format.
+ Returns the color used to draw underlines, overlines and strikeouts
+ on the characters with this format.
\sa setUnderlineColor()
*/
@@ -2033,8 +2098,6 @@ void QTextCharFormat::setFont(const QFont &font, FontPropertiesInheritanceBehavi
const uint mask = behavior == FontPropertiesAll ? uint(QFont::AllPropertiesResolved)
: font.resolveMask();
- if (mask & QFont::FamilyResolved)
- setFontFamily(font.family());
if (mask & QFont::FamiliesResolved)
setFontFamilies(font.families());
if (mask & QFont::StyleNameResolved)
@@ -2108,7 +2171,7 @@ QFont QTextCharFormat::font() const
associated QTextBlockFormat that specifies its characteristics.
To cater for left-to-right and right-to-left languages you can set
- a block's direction with setDirection(). Paragraph alignment is
+ a block's direction with setLayoutDirection(). Paragraph alignment is
set with setAlignment(). Margins are controlled by setTopMargin(),
setBottomMargin(), setLeftMargin(), setRightMargin(). Overall
indentation is set with setIndent(), the indentation of the first
@@ -2178,14 +2241,9 @@ QTextBlockFormat::QTextBlockFormat(const QTextFormat &fmt)
void QTextBlockFormat::setTabPositions(const QList<QTextOption::Tab> &tabs)
{
QList<QVariant> list;
- list.reserve(tabs.count());
- QList<QTextOption::Tab>::ConstIterator iter = tabs.constBegin();
- while (iter != tabs.constEnd()) {
- QVariant v;
- v.setValue(*iter);
- list.append(v);
- ++iter;
- }
+ list.reserve(tabs.size());
+ for (const auto &e : tabs)
+ list.append(QVariant::fromValue(e));
setProperty(TabPositions, list);
}
@@ -2198,16 +2256,13 @@ void QTextBlockFormat::setTabPositions(const QList<QTextOption::Tab> &tabs)
QList<QTextOption::Tab> QTextBlockFormat::tabPositions() const
{
QVariant variant = property(TabPositions);
- if(variant.isNull())
+ if (variant.isNull())
return QList<QTextOption::Tab>();
QList<QTextOption::Tab> answer;
- QList<QVariant> variantsList = qvariant_cast<QList<QVariant> >(variant);
- QList<QVariant>::Iterator iter = variantsList.begin();
- answer.reserve(variantsList.count());
- while(iter != variantsList.end()) {
- answer.append( qvariant_cast<QTextOption::Tab>(*iter));
- ++iter;
- }
+ const QList<QVariant> variantsList = qvariant_cast<QList<QVariant> >(variant);
+ answer.reserve(variantsList.size());
+ for (const auto &e: variantsList)
+ answer.append(qvariant_cast<QTextOption::Tab>(e));
return answer;
}
@@ -2551,7 +2606,8 @@ QList<QTextOption::Tab> QTextBlockFormat::tabPositions() const
The style used to decorate each item is set with setStyle() and can be read
with the style() function. The style controls the type of bullet points and
numbering scheme used for items in the list. Note that lists that use the
- decimal numbering scheme begin counting at 1 rather than 0.
+ decimal numbering scheme begin counting at 1 rather than 0, unless it has
+ been overridden via setStart().
Style properties can be set to further configure the appearance of list
items; for example, the ListNumberPrefix and ListNumberSuffix properties
@@ -2588,6 +2644,7 @@ QTextListFormat::QTextListFormat()
: QTextFormat(ListFormat)
{
setIndent(1);
+ setStart(1);
}
/*!
@@ -2692,6 +2749,32 @@ QTextListFormat::QTextListFormat(const QTextFormat &fmt)
*/
/*!
+ \fn void QTextListFormat::setStart(int start)
+ \since 6.6
+
+ Sets the list format's \a start index.
+
+ This allows you to start a list with an index other than 1. This can be
+ used with all sorted list types: for example if the style() is
+ QTextListFormat::ListLowerAlpha and start() is \c 4, the first list item
+ begins with "d". It does not have any effect on unsorted list types.
+
+ The default start is \c 1.
+
+ \sa start()
+*/
+
+/*!
+ \fn int QTextListFormat::start() const
+ \since 6.6
+
+ Returns the number to be shown by the first list item, if the style() is
+ QTextListFormat::ListDecimal, or to offset other sorted list types.
+
+ \sa setStart()
+*/
+
+/*!
\class QTextFrameFormat
\reentrant
@@ -3073,7 +3156,8 @@ QTextTableFormat::QTextTableFormat()
: QTextFrameFormat()
{
setObjectType(TableObject);
- setCellSpacing(2);
+ setCellPadding(4);
+ setBorderCollapse(true);
setBorder(1);
}
@@ -3398,7 +3482,7 @@ QTextImageFormat::QTextImageFormat(const QTextFormat &fmt)
\fn void QTextCharFormat::setFontCapitalization(QFont::Capitalization capitalization)
\since 4.4
- Sets the capitalization of the text that apppears in this font to \a capitalization.
+ Sets the capitalization of the text that appears in this font to \a capitalization.
A font's capitalization makes the text appear in the selected capitalization mode.
@@ -3910,7 +3994,7 @@ bool QTextFormatCollection::hasFormatCached(const QTextFormat &format) const
int QTextFormatCollection::objectFormatIndex(int objectIndex) const
{
- if (objectIndex == -1)
+ if (objectIndex == -1 || objectIndex >= objFormats.size())
return -1;
return objFormats.at(objectIndex);
}
@@ -3929,7 +4013,7 @@ int QTextFormatCollection::createObjectIndex(const QTextFormat &f)
QTextFormat QTextFormatCollection::format(int idx) const
{
- if (idx < 0 || idx >= formats.count())
+ if (idx < 0 || idx >= formats.size())
return QTextFormat();
return formats.at(idx);
@@ -3938,7 +4022,7 @@ QTextFormat QTextFormatCollection::format(int idx) const
void QTextFormatCollection::setDefaultFont(const QFont &f)
{
defaultFnt = f;
- for (int i = 0; i < formats.count(); ++i)
+ for (int i = 0; i < formats.size(); ++i)
if (formats.at(i).d)
formats[i].d->resolveFont(defaultFnt);
}
@@ -3961,3 +4045,5 @@ QDebug operator<<(QDebug dbg, const QTextFormat &f)
#endif
QT_END_NAMESPACE
+
+#include "moc_qtextformat.cpp"
diff --git a/src/gui/text/qtextformat.h b/src/gui/text/qtextformat.h
index 9d422786f0..c009d328cb 100644
--- a/src/gui/text/qtextformat.h
+++ b/src/gui/text/qtextformat.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) 2021 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 QTEXTFORMAT_H
#define QTEXTFORMAT_H
@@ -73,11 +37,6 @@ class QTextCursor;
class QTextDocument;
class QTextLength;
-#ifndef QT_NO_DATASTREAM
-Q_GUI_EXPORT QDataStream &operator<<(QDataStream &, const QTextLength &);
-Q_GUI_EXPORT QDataStream &operator>>(QDataStream &, QTextLength &);
-#endif
-
#ifndef QT_NO_DEBUG_STREAM
Q_GUI_EXPORT QDebug operator<<(QDebug, const QTextLength &);
#endif
@@ -118,16 +77,11 @@ private:
friend Q_GUI_EXPORT QDataStream &operator<<(QDataStream &, const QTextLength &);
friend Q_GUI_EXPORT QDataStream &operator>>(QDataStream &, QTextLength &);
};
-Q_DECLARE_TYPEINFO(QTextLength, QT_VERSION >= QT_VERSION_CHECK(6,0,0) ? Q_PRIMITIVE_TYPE : Q_RELOCATABLE_TYPE);
+Q_DECLARE_TYPEINFO(QTextLength, Q_PRIMITIVE_TYPE);
inline QTextLength::QTextLength(Type atype, qreal avalue)
: lengthType(atype), fixedValueOrPercentage(avalue) {}
-#ifndef QT_NO_DATASTREAM
-Q_GUI_EXPORT QDataStream &operator<<(QDataStream &, const QTextFormat &);
-Q_GUI_EXPORT QDataStream &operator>>(QDataStream &, QTextFormat &);
-#endif
-
#ifndef QT_NO_DEBUG_STREAM
Q_GUI_EXPORT QDebug operator<<(QDebug, const QTextFormat &);
#endif
@@ -141,9 +95,6 @@ public:
BlockFormat = 1,
CharFormat = 2,
ListFormat = 3,
-#if QT_DEPRECATED_SINCE(5, 3)
- TableFormat = 4,
-#endif
FrameFormat = 5,
UserFormat = 100
@@ -195,7 +146,9 @@ public:
FontStyleName = 0x1FE8,
FontLetterSpacingType = 0x1FE9,
FontStretch = 0x1FEA,
+#if QT_DEPRECATED_SINCE(6, 0)
FontFamily = 0x2000,
+#endif
FontPointSize = 0x2001,
FontSizeAdjustment = 0x2002,
FontSizeIncrement = FontSizeAdjustment, // old name, compat
@@ -225,6 +178,8 @@ public:
// Should not be referenced in user code.
OldFontLetterSpacingType = 0x2033,
OldFontStretch = 0x2034,
+ OldTextUnderlineColor = 0x2010,
+ OldFontFamily = 0x2000, // same as FontFamily
ObjectType = 0x2f00,
@@ -233,6 +188,7 @@ public:
ListIndent = 0x3001,
ListNumberPrefix = 0x3002,
ListNumberSuffix = 0x3003,
+ ListStart = 0x3004,
// table and frame properties
FrameBorder = 0x4000,
@@ -330,7 +286,7 @@ public:
~QTextFormat();
void swap(QTextFormat &other)
- { qSwap(d, other.d); qSwap(format_type, other.format_type); }
+ { d.swap(other.d); std::swap(format_type, other.format_type); }
void merge(const QTextFormat &other);
@@ -456,20 +412,32 @@ public:
void setFont(const QFont &font, FontPropertiesInheritanceBehavior behavior = FontPropertiesAll);
QFont font() const;
- inline void setFontFamily(const QString &family)
- { setProperty(FontFamily, family); }
- inline QString fontFamily() const
- { return stringProperty(FontFamily); }
+#if QT_DEPRECATED_SINCE(6, 1)
+ QT_DEPRECATED_VERSION_X_6_1("Use setFontFamilies instead") inline void setFontFamily(const QString &family)
+ { setProperty(FontFamilies, QVariant(QStringList(family))); }
+ QT_DEPRECATED_VERSION_X_6_1("Use fontFamilies instead") inline QString fontFamily() const
+ { return property(FontFamilies).toStringList().first(); }
+#endif
inline void setFontFamilies(const QStringList &families)
{ setProperty(FontFamilies, QVariant(families)); }
+#if QT_VERSION < QT_VERSION_CHECK(7, 0, 0)
inline QVariant fontFamilies() const
{ return property(FontFamilies); }
+#else
+ inline QStringList fontFamilies() const
+ { return property(FontFamilies).toStringList(); }
+#endif
inline void setFontStyleName(const QString &styleName)
{ setProperty(FontStyleName, styleName); }
+#if QT_VERSION < QT_VERSION_CHECK(7, 0, 0)
inline QVariant fontStyleName() const
{ return property(FontStyleName); }
+#else
+ inline QStringList fontStyleName() const
+ { return property(FontStyleName).toStringList(); }
+#endif
inline void setFontPointSize(qreal size)
{ setProperty(FontPointSize, size); }
@@ -612,6 +580,8 @@ public:
protected:
explicit QTextCharFormat(const QTextFormat &fmt);
friend class QTextFormat;
+ friend Q_GUI_EXPORT QDataStream &operator<<(QDataStream &, const QTextCharFormat &);
+ friend Q_GUI_EXPORT QDataStream &operator>>(QDataStream &, QTextCharFormat &);
};
Q_DECLARE_SHARED(QTextCharFormat)
@@ -705,7 +675,7 @@ public:
{ return boolProperty(BlockNonBreakableLines); }
inline void setPageBreakPolicy(PageBreakFlags flags)
- { setProperty(PageBreakPolicy, int(flags)); }
+ { setProperty(PageBreakPolicy, int(flags.toInt())); }
inline PageBreakFlags pageBreakPolicy() const
{ return PageBreakFlags(intProperty(PageBreakPolicy)); }
@@ -720,12 +690,14 @@ public:
protected:
explicit QTextBlockFormat(const QTextFormat &fmt);
friend class QTextFormat;
+ friend Q_GUI_EXPORT QDataStream &operator<<(QDataStream &, const QTextBlockFormat &);
+ friend Q_GUI_EXPORT QDataStream &operator>>(QDataStream &, QTextBlockFormat &);
};
Q_DECLARE_SHARED(QTextBlockFormat)
inline void QTextBlockFormat::setAlignment(Qt::Alignment aalignment)
-{ setProperty(BlockAlignment, int(aalignment)); }
+{ setProperty(BlockAlignment, int(aalignment.toInt())); }
inline void QTextBlockFormat::setIndent(int aindent)
{ setProperty(BlockIndent, aindent); }
@@ -782,6 +754,9 @@ public:
inline QString numberSuffix() const
{ return stringProperty(ListNumberSuffix); }
+ inline void setStart(int indent);
+ inline int start() const { return intProperty(ListStart); }
+
protected:
explicit QTextListFormat(const QTextFormat &fmt);
friend class QTextFormat;
@@ -801,6 +776,11 @@ inline void QTextListFormat::setNumberPrefix(const QString &np)
inline void QTextListFormat::setNumberSuffix(const QString &ns)
{ setProperty(ListNumberSuffix, ns); }
+inline void QTextListFormat::setStart(int astart)
+{
+ setProperty(ListStart, astart);
+}
+
class Q_GUI_EXPORT QTextImageFormat : public QTextCharFormat
{
public:
@@ -820,13 +800,19 @@ public:
inline qreal height() const
{ return doubleProperty(ImageHeight); }
- inline void setQuality(int quality = 100);
+ inline void setQuality(int quality);
+#if QT_DEPRECATED_SINCE(6, 3)
+ QT_DEPRECATED_VERSION_X_6_3("Pass a quality value, the default is 100") inline void setQuality()
+ { setQuality(100); }
+#endif
inline int quality() const
{ return intProperty(ImageQuality); }
protected:
explicit QTextImageFormat(const QTextFormat &format);
friend class QTextFormat;
+ friend Q_GUI_EXPORT QDataStream &operator<<(QDataStream &, const QTextListFormat &);
+ friend Q_GUI_EXPORT QDataStream &operator>>(QDataStream &, QTextListFormat &);
};
Q_DECLARE_SHARED(QTextImageFormat)
@@ -923,13 +909,15 @@ public:
{ return lengthProperty(FrameHeight); }
inline void setPageBreakPolicy(PageBreakFlags flags)
- { setProperty(PageBreakPolicy, int(flags)); }
+ { setProperty(PageBreakPolicy, int(flags.toInt())); }
inline PageBreakFlags pageBreakPolicy() const
{ return PageBreakFlags(intProperty(PageBreakPolicy)); }
protected:
explicit QTextFrameFormat(const QTextFormat &fmt);
friend class QTextFormat;
+ friend Q_GUI_EXPORT QDataStream &operator<<(QDataStream &, const QTextFrameFormat &);
+ friend Q_GUI_EXPORT QDataStream &operator>>(QDataStream &, QTextFrameFormat &);
};
Q_DECLARE_SHARED(QTextFrameFormat)
@@ -1021,7 +1009,7 @@ inline void QTextTableFormat::setCellPadding(qreal apadding)
{ setProperty(TableCellPadding, apadding); }
inline void QTextTableFormat::setAlignment(Qt::Alignment aalignment)
-{ setProperty(BlockAlignment, int(aalignment)); }
+{ setProperty(BlockAlignment, int(aalignment.toInt())); }
class Q_GUI_EXPORT QTextTableCellFormat : public QTextCharFormat
{
@@ -1112,6 +1100,8 @@ public:
protected:
explicit QTextTableCellFormat(const QTextFormat &fmt);
+ friend Q_GUI_EXPORT QDataStream &operator<<(QDataStream &, const QTextTableCellFormat &);
+ friend Q_GUI_EXPORT QDataStream &operator>>(QDataStream &, QTextTableCellFormat &);
friend class QTextFormat;
};
diff --git a/src/gui/text/qtextformat_p.h b/src/gui/text/qtextformat_p.h
index 13815d06b3..1bd9120513 100644
--- a/src/gui/text/qtextformat_p.h
+++ b/src/gui/text/qtextformat_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 QTEXTFORMAT_P_H
#define QTEXTFORMAT_P_H
@@ -54,6 +18,7 @@
#include <QtGui/private/qtguiglobal_p.h>
#include "QtGui/qtextformat.h"
#include "QtCore/qlist.h"
+#include <QtCore/qhash.h> // QMultiHash
QT_BEGIN_NAMESPACE
@@ -90,7 +55,7 @@ public:
inline QTextImageFormat imageFormat(int index) const
{ return format(index).toImageFormat(); }
- inline int numFormats() const { return formats.count(); }
+ inline int numFormats() const { return formats.size(); }
typedef QList<QTextFormat> FormatVector;
diff --git a/src/gui/text/qtexthtmlparser.cpp b/src/gui/text/qtexthtmlparser.cpp
index 9bfb0d0ec8..ee92cece78 100644
--- a/src/gui/text/qtexthtmlparser.cpp
+++ b/src/gui/text/qtexthtmlparser.cpp
@@ -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 "qtexthtmlparser_p.h"
@@ -57,6 +21,8 @@
QT_BEGIN_NAMESPACE
+using namespace Qt::StringLiterals;
+
// see also tst_qtextdocumentfragment.cpp
#define MAX_ENTITY 258
static const struct QTextHtmlEntity { const char name[9]; char16_t code; } entities[]= {
@@ -321,21 +287,21 @@ static const struct QTextHtmlEntity { const char name[9]; char16_t code; } entit
};
static_assert(MAX_ENTITY == sizeof entities / sizeof *entities);
-#if defined(Q_CC_MSVC) && _MSC_VER < 1600
+#if defined(Q_CC_MSVC_ONLY) && _MSC_VER < 1600
bool operator<(const QTextHtmlEntity &entity1, const QTextHtmlEntity &entity2)
{
- return QLatin1String(entity1.name) < QLatin1String(entity2.name);
+ return QLatin1StringView(entity1.name) < QLatin1StringView(entity2.name);
}
#endif
static bool operator<(QStringView entityStr, const QTextHtmlEntity &entity)
{
- return entityStr < QLatin1String(entity.name);
+ return entityStr < QLatin1StringView(entity.name);
}
static bool operator<(const QTextHtmlEntity &entity, QStringView entityStr)
{
- return QLatin1String(entity.name) < entityStr;
+ return QLatin1StringView(entity.name) < entityStr;
}
static QChar resolveEntity(QStringView entity)
@@ -447,17 +413,17 @@ static const QTextHtmlElement elements[Html_NumElements]= {
{ "var", Html_var, QTextHtmlElement::DisplayInline },
};
-static bool operator<(const QString &str, const QTextHtmlElement &e)
+static bool operator<(QStringView str, const QTextHtmlElement &e)
{
- return str < QLatin1String(e.name);
+ return str < QLatin1StringView(e.name);
}
-static bool operator<(const QTextHtmlElement &e, const QString &str)
+static bool operator<(const QTextHtmlElement &e, QStringView str)
{
- return QLatin1String(e.name) < str;
+ return QLatin1StringView(e.name) < str;
}
-static const QTextHtmlElement *lookupElementHelper(const QString &element)
+static const QTextHtmlElement *lookupElementHelper(QStringView element)
{
const QTextHtmlElement *start = &elements[0];
const QTextHtmlElement *end = &elements[Html_NumElements];
@@ -467,7 +433,7 @@ static const QTextHtmlElement *lookupElementHelper(const QString &element)
return e;
}
-int QTextHtmlParser::lookupElement(const QString &element)
+int QTextHtmlParser::lookupElement(QStringView element)
{
const QTextHtmlElement *e = lookupElementHelper(element);
if (!e)
@@ -479,7 +445,7 @@ int QTextHtmlParser::lookupElement(const QString &element)
static QString quoteNewline(const QString &s)
{
QString n = s;
- n.replace(QLatin1Char('\n'), QLatin1String("\\n"));
+ n.replace(u'\n', "\\n"_L1);
return n;
}
@@ -509,21 +475,20 @@ QTextHtmlParserNode::QTextHtmlParserNode()
void QTextHtmlParser::dumpHtml()
{
for (int i = 0; i < count(); ++i) {
- qDebug().nospace() << qPrintable(QString(depth(i)*4, QLatin1Char(' ')))
+ qDebug().nospace() << qPrintable(QString(depth(i) * 4, u' '))
<< qPrintable(at(i).tag) << ':'
<< quoteNewline(at(i).text);
- ;
}
}
QTextHtmlParserNode *QTextHtmlParser::newNode(int parent)
{
- QTextHtmlParserNode *lastNode = &nodes.last();
+ QTextHtmlParserNode *lastNode = nodes.last();
QTextHtmlParserNode *newNode = nullptr;
bool reuseLastNode = true;
- if (nodes.count() == 1) {
+ if (nodes.size() == 1) {
reuseLastNode = false;
} else if (lastNode->tag.isEmpty()) {
@@ -531,7 +496,7 @@ QTextHtmlParserNode *QTextHtmlParser::newNode(int parent)
reuseLastNode = true;
} else { // last node is a text node (empty tag) with some text
- if (lastNode->text.length() == 1 && lastNode->text.at(0).isSpace()) {
+ if (lastNode->text.size() == 1 && lastNode->text.at(0).isSpace()) {
int lastSibling = count() - 2;
while (lastSibling
@@ -563,8 +528,8 @@ QTextHtmlParserNode *QTextHtmlParser::newNode(int parent)
newNode->text.clear();
newNode->id = Html_unknown;
} else {
- nodes.resize(nodes.size() + 1);
- newNode = &nodes.last();
+ nodes.append(new QTextHtmlParserNode);
+ newNode = nodes.last();
}
newNode->parent = parent;
@@ -573,11 +538,12 @@ QTextHtmlParserNode *QTextHtmlParser::newNode(int parent)
void QTextHtmlParser::parse(const QString &text, const QTextDocument *_resourceProvider)
{
+ qDeleteAll(nodes);
nodes.clear();
- nodes.resize(1);
+ nodes.append(new QTextHtmlParserNode);
txt = text;
pos = 0;
- len = txt.length();
+ len = txt.size();
textEditMode = false;
resourceProvider = _resourceProvider;
parse();
@@ -636,12 +602,12 @@ void QTextHtmlParser::parse()
{
while (pos < len) {
QChar c = txt.at(pos++);
- if (c == QLatin1Char('<')) {
+ if (c == u'<') {
parseTag();
- } else if (c == QLatin1Char('&')) {
- nodes.last().text += parseEntity();
+ } else if (c == u'&') {
+ nodes.last()->text += parseEntity();
} else {
- nodes.last().text += c;
+ nodes.last()->text += c;
}
}
}
@@ -652,20 +618,20 @@ void QTextHtmlParser::parseTag()
eatSpace();
// handle comments and other exclamation mark declarations
- if (hasPrefix(QLatin1Char('!'))) {
+ if (hasPrefix(u'!')) {
parseExclamationTag();
- if (nodes.last().wsm != QTextHtmlParserNode::WhiteSpacePre
- && nodes.last().wsm != QTextHtmlParserNode::WhiteSpacePreWrap
+ if (nodes.last()->wsm != QTextHtmlParserNode::WhiteSpacePre
+ && nodes.last()->wsm != QTextHtmlParserNode::WhiteSpacePreWrap
&& !textEditMode)
eatSpace();
return;
}
// if close tag just close
- if (hasPrefix(QLatin1Char('/'))) {
- if (nodes.last().id == Html_style) {
+ if (hasPrefix(u'/')) {
+ if (nodes.last()->id == Html_style) {
#ifndef QT_NO_CSSPARSER
- QCss::Parser parser(nodes.constLast().text);
+ QCss::Parser parser(nodes.constLast()->text);
QCss::StyleSheet sheet;
sheet.origin = QCss::StyleSheetOrigin_Author;
parser.parse(&sheet, Qt::CaseInsensitive);
@@ -706,15 +672,15 @@ void QTextHtmlParser::parseTag()
resolveNode();
#ifndef QT_NO_CSSPARSER
- const int nodeIndex = nodes.count() - 1; // this new node is always the last
+ const int nodeIndex = nodes.size() - 1; // this new node is always the last
node->applyCssDeclarations(declarationsForNode(nodeIndex), resourceProvider);
#endif
applyAttributes(node->attributes);
// finish tag
bool tagClosed = false;
- while (pos < len && txt.at(pos) != QLatin1Char('>')) {
- if (txt.at(pos) == QLatin1Char('/'))
+ while (pos < len && txt.at(pos) != u'>') {
+ if (txt.at(pos) == u'/')
tagClosed = true;
pos++;
@@ -727,7 +693,7 @@ void QTextHtmlParser::parseTag()
|| node->wsm == QTextHtmlParserNode::WhiteSpacePreWrap
|| node->wsm == QTextHtmlParserNode::WhiteSpacePreLine)
&& node->isBlock()) {
- if (pos < len - 1 && txt.at(pos) == QLatin1Char('\n'))
+ if (pos < len - 1 && txt.at(pos) == u'\n')
++pos;
}
@@ -744,7 +710,7 @@ void QTextHtmlParser::parseCloseTag()
QString tag = parseWord().toLower().trimmed();
while (pos < len) {
QChar c = txt.at(pos++);
- if (c == QLatin1Char('>'))
+ if (c == u'>')
break;
}
@@ -772,8 +738,8 @@ void QTextHtmlParser::parseCloseTag()
|| at(p).wsm == QTextHtmlParserNode::WhiteSpacePreWrap
|| at(p).wsm == QTextHtmlParserNode::WhiteSpacePreLine)
&& at(p).isBlock()) {
- if (at(last()).text.endsWith(QLatin1Char('\n')))
- nodes[last()].text.chop(1);
+ if (at(last()).text.endsWith(u'\n'))
+ nodes[last()]->text.chop(1);
}
newNode(at(p).parent);
@@ -784,87 +750,95 @@ void QTextHtmlParser::parseCloseTag()
void QTextHtmlParser::parseExclamationTag()
{
++pos;
- if (hasPrefix(QLatin1Char('-'),1) && hasPrefix(QLatin1Char('-'),2)) {
- pos += 3;
+ if (hasPrefix(u'-') && hasPrefix(u'-', 1)) {
+ pos += 2;
// eat comments
- int end = txt.indexOf(QLatin1String("-->"), pos);
+ int end = txt.indexOf("-->"_L1, pos);
pos = (end >= 0 ? end + 3 : len);
} else {
// eat internal tags
while (pos < len) {
QChar c = txt.at(pos++);
- if (c == QLatin1Char('>'))
+ if (c == u'>')
break;
}
}
}
+QString QTextHtmlParser::parseEntity(QStringView entity)
+{
+ QChar resolved = resolveEntity(entity);
+ if (!resolved.isNull())
+ return QString(resolved);
+
+ if (entity.size() > 1 && entity.at(0) == u'#') {
+ entity = entity.mid(1); // removing leading #
+
+ int base = 10;
+ bool ok = false;
+
+ if (entity.at(0).toLower() == u'x') { // hex entity?
+ entity = entity.mid(1);
+ base = 16;
+ }
+
+ uint uc = entity.toUInt(&ok, base);
+ if (ok) {
+ if (uc >= 0x80 && uc < 0x80 + (sizeof(windowsLatin1ExtendedCharacters)/sizeof(windowsLatin1ExtendedCharacters[0])))
+ uc = windowsLatin1ExtendedCharacters[uc - 0x80];
+ return QStringView{QChar::fromUcs4(uc)}.toString();
+ }
+ }
+ return {};
+}
+
// parses an entity after "&", and returns it
QString QTextHtmlParser::parseEntity()
{
const int recover = pos;
int entityLen = 0;
- QStringView entity;
while (pos < len) {
QChar c = txt.at(pos++);
if (c.isSpace() || pos - recover > 9) {
goto error;
}
- if (c == QLatin1Char(';'))
+ if (c == u';')
break;
++entityLen;
}
if (entityLen) {
- entity = QStringView(txt).mid(recover, entityLen);
- QChar resolved = resolveEntity(entity);
- if (!resolved.isNull())
- return QString(resolved);
-
- if (entityLen > 1 && entity.at(0) == QLatin1Char('#')) {
- entity = entity.mid(1); // removing leading #
-
- int base = 10;
- bool ok = false;
-
- if (entity.at(0).toLower() == QLatin1Char('x')) { // hex entity?
- entity = entity.mid(1);
- base = 16;
- }
-
- uint uc = entity.toUInt(&ok, base);
- if (ok) {
- if (uc >= 0x80 && uc < 0x80 + (sizeof(windowsLatin1ExtendedCharacters)/sizeof(windowsLatin1ExtendedCharacters[0])))
- uc = windowsLatin1ExtendedCharacters[uc - 0x80];
- return QStringView{QChar::fromUcs4(uc)}.toString();
- }
+ const QStringView entity = QStringView(txt).mid(recover, entityLen);
+ QString parsedEntity = parseEntity(entity);
+ if (!parsedEntity.isNull()) {
+ return parsedEntity;
}
}
error:
pos = recover;
- return QLatin1String("&");
+ return "&"_L1;
}
// parses one word, possibly quoted, and returns it
QString QTextHtmlParser::parseWord()
{
QString word;
- if (hasPrefix(QLatin1Char('\"'))) { // double quotes
+ if (hasPrefix(u'\"')) { // double quotes
++pos;
while (pos < len) {
QChar c = txt.at(pos++);
- if (c == QLatin1Char('\"'))
+ if (c == u'\"')
break;
- else if (c == QLatin1Char('&'))
+ else if (c == u'&')
word += parseEntity();
else
word += c;
}
- } else if (hasPrefix(QLatin1Char('\''))) { // single quotes
+ } else if (hasPrefix(u'\'')) { // single quotes
++pos;
while (pos < len) {
QChar c = txt.at(pos++);
// Allow for escaped single quotes as they may be part of the string
- if (c == QLatin1Char('\'') && (txt.length() > 1 && txt.at(pos - 2) != QLatin1Char('\\')))
+ if (c == u'\'' && (txt.size() > 1 && txt.at(pos - 2) != u'\\'))
break;
else
word += c;
@@ -872,15 +846,12 @@ QString QTextHtmlParser::parseWord()
} else { // normal text
while (pos < len) {
QChar c = txt.at(pos++);
- if (c == QLatin1Char('>')
- || (c == QLatin1Char('/') && hasPrefix(QLatin1Char('>'), 1))
- || c == QLatin1Char('<')
- || c == QLatin1Char('=')
- || c.isSpace()) {
+ if (c == u'>' || (c == u'/' && hasPrefix(u'>'))
+ || c == u'<' || c == u'=' || c.isSpace()) {
--pos;
break;
}
- if (c == QLatin1Char('&'))
+ if (c == u'&')
word += parseEntity();
else
word += c;
@@ -892,7 +863,7 @@ QString QTextHtmlParser::parseWord()
// gives the new node the right parent
QTextHtmlParserNode *QTextHtmlParser::resolveParent()
{
- QTextHtmlParserNode *node = &nodes.last();
+ QTextHtmlParserNode *node = nodes.last();
int p = node->parent;
@@ -905,22 +876,22 @@ QTextHtmlParserNode *QTextHtmlParser::resolveParent()
n = at(n).parent;
if (!n) {
- nodes.insert(nodes.count() - 1, QTextHtmlParserNode());
- nodes.insert(nodes.count() - 1, QTextHtmlParserNode());
+ nodes.insert(nodes.size() - 1, new QTextHtmlParserNode);
+ nodes.insert(nodes.size() - 1, new QTextHtmlParserNode);
- QTextHtmlParserNode *table = &nodes[nodes.count() - 3];
+ QTextHtmlParserNode *table = nodes[nodes.size() - 3];
table->parent = p;
table->id = Html_table;
- table->tag = QLatin1String("table");
- table->children.append(nodes.count() - 2); // add row as child
+ table->tag = "table"_L1;
+ table->children.append(nodes.size() - 2); // add row as child
- QTextHtmlParserNode *row = &nodes[nodes.count() - 2];
- row->parent = nodes.count() - 3; // table as parent
+ QTextHtmlParserNode *row = nodes[nodes.size() - 2];
+ row->parent = nodes.size() - 3; // table as parent
row->id = Html_tr;
- row->tag = QLatin1String("tr");
+ row->tag = "tr"_L1;
- p = nodes.count() - 2;
- node = &nodes.last(); // re-initialize pointer
+ p = nodes.size() - 2;
+ node = nodes.last(); // re-initialize pointer
}
}
@@ -930,13 +901,13 @@ QTextHtmlParserNode *QTextHtmlParser::resolveParent()
n = at(n).parent;
if (!n) {
- nodes.insert(nodes.count() - 1, QTextHtmlParserNode());
- QTextHtmlParserNode *table = &nodes[nodes.count() - 2];
+ nodes.insert(nodes.size() - 1, new QTextHtmlParserNode);
+ QTextHtmlParserNode *table = nodes[nodes.size() - 2];
table->parent = p;
table->id = Html_table;
- table->tag = QLatin1String("table");
- p = nodes.count() - 2;
- node = &nodes.last(); // re-initialize pointer
+ table->tag = "table"_L1;
+ p = nodes.size() - 2;
+ node = nodes.last(); // re-initialize pointer
}
}
@@ -983,15 +954,15 @@ QTextHtmlParserNode *QTextHtmlParser::resolveParent()
node->parent = p;
// makes it easier to traverse the tree, later
- nodes[p].children.append(nodes.count() - 1);
+ nodes[p]->children.append(nodes.size() - 1);
return node;
}
// sets all properties on the new node
void QTextHtmlParser::resolveNode()
{
- QTextHtmlParserNode *node = &nodes.last();
- const QTextHtmlParserNode *parent = &nodes.at(node->parent);
+ QTextHtmlParserNode *node = nodes.last();
+ const QTextHtmlParserNode *parent = nodes.at(node->parent);
node->initializeProperties(parent, this);
}
@@ -1031,8 +1002,11 @@ void QTextHtmlParserNode::initializeProperties(const QTextHtmlParserNode *parent
// we don't paint per-row background colors, yet. so as an
// exception inherit the background color here
// we also inherit the background between inline elements
+ // we also inherit from non-body block elements since we merge them together
if ((parent->id != Html_tr || !isTableCell())
- && (displayMode != QTextHtmlElement::DisplayInline || parent->displayMode != QTextHtmlElement::DisplayInline)) {
+ && (displayMode != QTextHtmlElement::DisplayInline || parent->displayMode != QTextHtmlElement::DisplayInline)
+ && (parent->id == Html_body || displayMode != QTextHtmlElement::DisplayBlock || parent->displayMode != QTextHtmlElement::DisplayBlock)
+ ) {
charFormat.clearProperty(QTextFormat::BackgroundBrush);
}
@@ -1055,9 +1029,9 @@ void QTextHtmlParserNode::initializeProperties(const QTextHtmlParserNode *parent
// set element specific attributes
switch (id) {
case Html_a:
- for (int i = 0; i < attributes.count(); i += 2) {
+ for (int i = 0; i < attributes.size(); i += 2) {
const QString key = attributes.at(i);
- if (key.compare(QLatin1String("href"), Qt::CaseInsensitive) == 0
+ if (key.compare("href"_L1, Qt::CaseInsensitive) == 0
&& !attributes.at(i + 1).isEmpty()) {
hasHref = true;
}
@@ -1143,7 +1117,7 @@ void QTextHtmlParserNode::initializeProperties(const QTextHtmlParserNode *parent
#ifndef QT_NO_CSSPARSER
void QTextHtmlParserNode::setListStyle(const QList<QCss::Value> &cssValues)
{
- for (int i = 0; i < cssValues.count(); ++i) {
+ for (int i = 0; i < cssValues.size(); ++i) {
if (cssValues.at(i).type == QCss::Value::KnownIdentifier) {
switch (static_cast<QCss::KnownValue>(cssValues.at(i).variant.toInt())) {
case QCss::Value_None: hasOwnListStyle = true; listStyle = QTextListFormat::ListStyleUndefined; break;
@@ -1164,12 +1138,50 @@ void QTextHtmlParserNode::setListStyle(const QList<QCss::Value> &cssValues)
blockFormat.setProperty(QTextFormat::ListStyle, listStyle);
}
+static QTextFrameFormat::BorderStyle toQTextFrameFormat(QCss::BorderStyle cssStyle)
+{
+ switch (cssStyle) {
+ case QCss::BorderStyle::BorderStyle_Dotted:
+ return QTextFrameFormat::BorderStyle::BorderStyle_Dotted;
+ case QCss::BorderStyle::BorderStyle_Dashed:
+ return QTextFrameFormat::BorderStyle::BorderStyle_Dashed;
+ case QCss::BorderStyle::BorderStyle_Solid:
+ return QTextFrameFormat::BorderStyle::BorderStyle_Solid;
+ case QCss::BorderStyle::BorderStyle_Double:
+ return QTextFrameFormat::BorderStyle::BorderStyle_Double;
+ case QCss::BorderStyle::BorderStyle_DotDash:
+ return QTextFrameFormat::BorderStyle::BorderStyle_DotDash;
+ case QCss::BorderStyle::BorderStyle_DotDotDash:
+ return QTextFrameFormat::BorderStyle::BorderStyle_DotDotDash;
+ case QCss::BorderStyle::BorderStyle_Groove:
+ return QTextFrameFormat::BorderStyle::BorderStyle_Groove;
+ case QCss::BorderStyle::BorderStyle_Ridge:
+ return QTextFrameFormat::BorderStyle::BorderStyle_Ridge;
+ case QCss::BorderStyle::BorderStyle_Inset:
+ return QTextFrameFormat::BorderStyle::BorderStyle_Inset;
+ case QCss::BorderStyle::BorderStyle_Outset:
+ return QTextFrameFormat::BorderStyle::BorderStyle_Outset;
+ case QCss::BorderStyle::BorderStyle_Unknown:
+ case QCss::BorderStyle::BorderStyle_None:
+ case QCss::BorderStyle::BorderStyle_Native:
+ return QTextFrameFormat::BorderStyle::BorderStyle_None;
+ case QCss::BorderStyle::NumKnownBorderStyles:
+ break;
+ // Intentionally no "default" to allow a compiler warning when extending the enum
+ // without updating this here. clang gives such a warning.
+ }
+ // Must not happen, intentionally trigger undefined behavior which sanitizers will detect.
+ // Having all cases covered in switch is not sufficient:
+ // MSVC would warn when there is no "default".
+ return static_cast<QTextFrameFormat::BorderStyle>(-1);
+}
+
void QTextHtmlParserNode::applyCssDeclarations(const QList<QCss::Declaration> &declarations, const QTextDocument *resourceProvider)
{
QCss::ValueExtractor extractor(declarations);
extractor.extractBox(margin, padding);
- if (id == Html_td || id == Html_th) {
+ auto getBorderValues = [&extractor](qreal *borderWidth, QBrush *borderBrush, QTextFrameFormat::BorderStyle *borderStyles) {
QCss::BorderStyle cssStyles[4];
int cssBorder[4];
QSize cssRadii[4]; // unused
@@ -1181,20 +1193,24 @@ void QTextHtmlParserNode::applyCssDeclarations(const QList<QCss::Declaration> &d
// QCss::BorderWidth parsing below which expects a single value
// will not work as expected - which in this case does not matter
// because tableBorder is not relevant for cells.
- extractor.extractBorder(cssBorder, tableCellBorderBrush, cssStyles, cssRadii);
+ bool hit = extractor.extractBorder(cssBorder, borderBrush, cssStyles, cssRadii);
for (int i = 0; i < 4; ++i) {
- tableCellBorderStyle[i] = static_cast<QTextFrameFormat::BorderStyle>(cssStyles[i] - 1);
- tableCellBorder[i] = static_cast<qreal>(cssBorder[i]);
+ borderStyles[i] = toQTextFrameFormat(cssStyles[i]);
+ borderWidth[i] = static_cast<qreal>(cssBorder[i]);
}
- }
+ return hit;
+ };
+
+ if (id == Html_td || id == Html_th)
+ getBorderValues(tableCellBorder, tableCellBorderBrush, tableCellBorderStyle);
- for (int i = 0; i < declarations.count(); ++i) {
+ for (int i = 0; i < declarations.size(); ++i) {
const QCss::Declaration &decl = declarations.at(i);
if (decl.d->values.isEmpty()) continue;
QCss::KnownValue identifier = QCss::UnknownValue;
- if (decl.d->values.first().type == QCss::Value::KnownIdentifier)
- identifier = static_cast<QCss::KnownValue>(decl.d->values.first().variant.toInt());
+ if (decl.d->values.constFirst().type == QCss::Value::KnownIdentifier)
+ identifier = static_cast<QCss::KnownValue>(decl.d->values.constFirst().variant.toInt());
switch (decl.d->propertyId) {
case QCss::BorderColor: borderBrush = QBrush(decl.colorValue()); break;
@@ -1208,6 +1224,19 @@ void QTextHtmlParserNode::applyCssDeclarations(const QList<QCss::Declaration> &d
tableBorder = borders[0];
}
break;
+ case QCss::Border: {
+ qreal tblBorder[4];
+ QBrush tblBorderBrush[4];
+ QTextFrameFormat::BorderStyle tblBorderStyle[4];
+ if (getBorderValues(tblBorder, tblBorderBrush, tblBorderStyle)) {
+ tableBorder = tblBorder[0];
+ if (tblBorderBrush[0].color().isValid())
+ borderBrush = tblBorderBrush[0];
+ if (tblBorderStyle[0] != static_cast<QTextFrameFormat::BorderStyle>(-1))
+ borderStyle = tblBorderStyle[0];
+ }
+ }
+ break;
case QCss::BorderCollapse:
borderCollapse = decl.borderCollapseValue();
break;
@@ -1221,18 +1250,18 @@ void QTextHtmlParserNode::applyCssDeclarations(const QList<QCss::Declaration> &d
}
break;
case QCss::QtBlockIndent:
- blockFormat.setIndent(decl.d->values.first().variant.toInt());
+ blockFormat.setIndent(decl.d->values.constFirst().variant.toInt());
break;
case QCss::QtLineHeightType: {
- QString lineHeightTypeName = decl.d->values.first().variant.toString();
+ QString lineHeightTypeName = decl.d->values.constFirst().variant.toString();
QTextBlockFormat::LineHeightTypes lineHeightType;
- if (lineHeightTypeName.compare(QLatin1String("proportional"), Qt::CaseInsensitive) == 0)
+ if (lineHeightTypeName.compare("proportional"_L1, Qt::CaseInsensitive) == 0)
lineHeightType = QTextBlockFormat::ProportionalHeight;
- else if (lineHeightTypeName.compare(QLatin1String("fixed"), Qt::CaseInsensitive) == 0)
+ else if (lineHeightTypeName.compare("fixed"_L1, Qt::CaseInsensitive) == 0)
lineHeightType = QTextBlockFormat::FixedHeight;
- else if (lineHeightTypeName.compare(QLatin1String("minimum"), Qt::CaseInsensitive) == 0)
+ else if (lineHeightTypeName.compare("minimum"_L1, Qt::CaseInsensitive) == 0)
lineHeightType = QTextBlockFormat::MinimumHeight;
- else if (lineHeightTypeName.compare(QLatin1String("line-distance"), Qt::CaseInsensitive) == 0)
+ else if (lineHeightTypeName.compare("line-distance"_L1, Qt::CaseInsensitive) == 0)
lineHeightType = QTextBlockFormat::LineDistanceHeight;
else
lineHeightType = QTextBlockFormat::SingleHeight;
@@ -1253,7 +1282,7 @@ void QTextHtmlParserNode::applyCssDeclarations(const QList<QCss::Declaration> &d
lineHeightType = QTextBlockFormat::MinimumHeight;
} else {
bool ok;
- QCss::Value cssValue = decl.d->values.first();
+ QCss::Value cssValue = decl.d->values.constFirst();
QString value = cssValue.toString();
lineHeight = value.toDouble(&ok);
if (ok) {
@@ -1285,19 +1314,19 @@ void QTextHtmlParserNode::applyCssDeclarations(const QList<QCss::Declaration> &d
hasCssListIndent = true;
break;
case QCss::QtParagraphType:
- if (decl.d->values.first().variant.toString().compare(QLatin1String("empty"), Qt::CaseInsensitive) == 0)
+ if (decl.d->values.constFirst().variant.toString().compare("empty"_L1, Qt::CaseInsensitive) == 0)
isEmptyParagraph = true;
break;
case QCss::QtTableType:
- if (decl.d->values.first().variant.toString().compare(QLatin1String("frame"), Qt::CaseInsensitive) == 0)
+ if (decl.d->values.constFirst().variant.toString().compare("frame"_L1, Qt::CaseInsensitive) == 0)
isTextFrame = true;
- else if (decl.d->values.first().variant.toString().compare(QLatin1String("root"), Qt::CaseInsensitive) == 0) {
+ else if (decl.d->values.constFirst().variant.toString().compare("root"_L1, Qt::CaseInsensitive) == 0) {
isTextFrame = true;
isRootFrame = true;
}
break;
case QCss::QtUserState:
- userState = decl.d->values.first().variant.toInt();
+ userState = decl.d->values.constFirst().variant.toInt();
break;
case QCss::Whitespace:
switch (identifier) {
@@ -1345,15 +1374,16 @@ void QTextHtmlParserNode::applyCssDeclarations(const QList<QCss::Declaration> &d
default: break;
}
break;
+ case QCss::TextDecorationColor: charFormat.setUnderlineColor(decl.colorValue()); break;
case QCss::ListStyleType:
case QCss::ListStyle:
setListStyle(decl.d->values);
break;
case QCss::QtListNumberPrefix:
- textListNumberPrefix = decl.d->values.first().variant.toString();
+ textListNumberPrefix = decl.d->values.constFirst().variant.toString();
break;
case QCss::QtListNumberSuffix:
- textListNumberSuffix = decl.d->values.first().variant.toString();
+ textListNumberSuffix = decl.d->values.constFirst().variant.toString();
break;
case QCss::TextAlignment:
switch (identifier) {
@@ -1368,12 +1398,36 @@ void QTextHtmlParserNode::applyCssDeclarations(const QList<QCss::Declaration> &d
{
if (resourceProvider != nullptr && QTextDocumentPrivate::get(resourceProvider) != nullptr) {
bool ok;
- qint64 searchKey = decl.d->values.first().variant.toLongLong(&ok);
+ qint64 searchKey = decl.d->values.constFirst().variant.toLongLong(&ok);
if (ok)
applyForegroundImage(searchKey, resourceProvider);
}
break;
}
+ case QCss::QtStrokeColor:
+ {
+ QPen pen = charFormat.textOutline();
+ pen.setStyle(Qt::SolidLine);
+ pen.setColor(decl.colorValue());
+ charFormat.setTextOutline(pen);
+ break;
+ }
+ case QCss::QtStrokeWidth:
+ {
+ qreal width;
+ if (decl.realValue(&width, "px")) {
+ QPen pen = charFormat.textOutline();
+ pen.setWidthF(width);
+ charFormat.setTextOutline(pen);
+ }
+ break;
+ }
+ case QCss::QtForeground:
+ {
+ QBrush brush = decl.brushValue();
+ charFormat.setForeground(brush);
+ break;
+ }
default: break;
}
}
@@ -1402,6 +1456,8 @@ void QTextHtmlParserNode::applyCssDeclarations(const QList<QCss::Declaration> &d
applyBackgroundImage(bgImage, resourceProvider);
} else if (bgBrush.style() != Qt::NoBrush) {
charFormat.setBackground(bgBrush);
+ if (id == Html_hr)
+ blockFormat.setProperty(QTextFormat::BackgroundBrush, bgBrush);
}
}
}
@@ -1472,7 +1528,7 @@ void QTextHtmlParserNode::applyBackgroundImage(const QString &url, const QTextDo
bool QTextHtmlParserNode::hasOnlyWhitespace() const
{
- for (int i = 0; i < text.count(); ++i)
+ for (int i = 0; i < text.size(); ++i)
if (!text.at(i).isSpace() || text.at(i) == QChar::LineSeparator)
return false;
return true;
@@ -1506,7 +1562,7 @@ static void setWidthAttribute(QTextLength *width, const QString &valueStr)
*width = QTextLength(QTextLength::FixedLength, realVal);
} else {
auto value = QStringView(valueStr).trimmed();
- if (!value.isEmpty() && value.endsWith(QLatin1Char('%'))) {
+ if (!value.isEmpty() && value.endsWith(u'%')) {
value.truncate(value.size() - 1);
realVal = value.toDouble(&ok);
if (ok)
@@ -1518,11 +1574,11 @@ static void setWidthAttribute(QTextLength *width, const QString &valueStr)
#ifndef QT_NO_CSSPARSER
void QTextHtmlParserNode::parseStyleAttribute(const QString &value, const QTextDocument *resourceProvider)
{
- const QString css = QLatin1String("* {") + value + QLatin1Char('}');
+ const QString css = "* {"_L1 + value + u'}';
QCss::Parser parser(css);
QCss::StyleSheet sheet;
parser.parse(&sheet, Qt::CaseInsensitive);
- if (sheet.styleRules.count() != 1) return;
+ if (sheet.styleRules.size() != 1) return;
applyCssDeclarations(sheet.styleRules.at(0).declarations, resourceProvider);
}
#endif
@@ -1533,14 +1589,14 @@ QStringList QTextHtmlParser::parseAttributes()
while (pos < len) {
eatSpace();
- if (hasPrefix(QLatin1Char('>')) || hasPrefix(QLatin1Char('/')))
+ if (hasPrefix(u'>') || hasPrefix(u'/'))
break;
QString key = parseWord().toLower();
- QString value = QLatin1String("1");
+ QString value = "1"_L1;
if (key.size() == 0)
break;
eatSpace();
- if (hasPrefix(QLatin1Char('='))){
+ if (hasPrefix(u'=')){
pos++;
eatSpace();
value = parseWord();
@@ -1560,36 +1616,34 @@ void QTextHtmlParser::applyAttributes(const QStringList &attributes)
QString linkHref;
QString linkType;
- if (attributes.count() % 2 == 1)
+ if (attributes.size() % 2 == 1)
return;
- QTextHtmlParserNode *node = &nodes.last();
+ QTextHtmlParserNode *node = nodes.last();
- for (int i = 0; i < attributes.count(); i += 2) {
+ for (int i = 0; i < attributes.size(); i += 2) {
QString key = attributes.at(i);
QString value = attributes.at(i + 1);
switch (node->id) {
case Html_font:
// the infamous font tag
- if (key == QLatin1String("size") && value.size()) {
+ if (key == "size"_L1 && value.size()) {
int n = value.toInt();
- if (value.at(0) != QLatin1Char('+') && value.at(0) != QLatin1Char('-'))
+ if (value.at(0) != u'+' && value.at(0) != u'-')
n -= 3;
node->charFormat.setProperty(QTextFormat::FontSizeAdjustment, n);
- } else if (key == QLatin1String("face")) {
- if (value.contains(QLatin1Char(','))) {
- const QStringList values = value.split(QLatin1Char(','));
+ } else if (key == "face"_L1) {
+ if (value.contains(u',')) {
QStringList families;
- for (const QString &family : values)
- families << family.trimmed();
+ for (auto family : value.tokenize(u','))
+ families << family.trimmed().toString();
node->charFormat.setFontFamilies(families);
- node->charFormat.setFontFamily(families.at(0));
} else {
- node->charFormat.setFontFamily(value);
+ node->charFormat.setFontFamilies(QStringList(value));
}
- } else if (key == QLatin1String("color")) {
- QColor c; c.setNamedColor(value);
+ } else if (key == "color"_L1) {
+ QColor c = QColor::fromString(value);
if (!c.isValid())
qWarning("QTextHtmlParser::applyAttributes: Unknown color name '%s'",value.toLatin1().constData());
node->charFormat.setForeground(c);
@@ -1597,153 +1651,158 @@ void QTextHtmlParser::applyAttributes(const QStringList &attributes)
break;
case Html_ol:
case Html_ul:
- if (key == QLatin1String("type")) {
+ if (key == "type"_L1) {
node->hasOwnListStyle = true;
- if (value == QLatin1String("1")) {
+ if (value == "1"_L1) {
node->listStyle = QTextListFormat::ListDecimal;
- } else if (value == QLatin1String("a")) {
+ } else if (value == "a"_L1) {
node->listStyle = QTextListFormat::ListLowerAlpha;
- } else if (value == QLatin1String("A")) {
+ } else if (value == "A"_L1) {
node->listStyle = QTextListFormat::ListUpperAlpha;
- } else if (value == QLatin1String("i")) {
+ } else if (value == "i"_L1) {
node->listStyle = QTextListFormat::ListLowerRoman;
- } else if (value == QLatin1String("I")) {
+ } else if (value == "I"_L1) {
node->listStyle = QTextListFormat::ListUpperRoman;
} else {
value = std::move(value).toLower();
- if (value == QLatin1String("square"))
+ if (value == "square"_L1)
node->listStyle = QTextListFormat::ListSquare;
- else if (value == QLatin1String("disc"))
+ else if (value == "disc"_L1)
node->listStyle = QTextListFormat::ListDisc;
- else if (value == QLatin1String("circle"))
+ else if (value == "circle"_L1)
node->listStyle = QTextListFormat::ListCircle;
- else if (value == QLatin1String("none"))
+ else if (value == "none"_L1)
node->listStyle = QTextListFormat::ListStyleUndefined;
}
+ } else if (key == "start"_L1) {
+ setIntAttribute(&node->listStart, value);
+ }
+ break;
+ case Html_li:
+ if (key == "class"_L1) {
+ if (value == "unchecked"_L1)
+ node->blockFormat.setMarker(QTextBlockFormat::MarkerType::Unchecked);
+ else if (value == "checked"_L1)
+ node->blockFormat.setMarker(QTextBlockFormat::MarkerType::Checked);
}
break;
case Html_a:
- if (key == QLatin1String("href"))
+ if (key == "href"_L1)
node->charFormat.setAnchorHref(value);
- else if (key == QLatin1String("name"))
+ else if (key == "name"_L1)
node->charFormat.setAnchorNames({value});
break;
case Html_img:
- if (key == QLatin1String("src") || key == QLatin1String("source")) {
+ if (key == "src"_L1 || key == "source"_L1) {
node->imageName = value;
- } else if (key == QLatin1String("width")) {
+ } else if (key == "width"_L1) {
node->imageWidth = -2; // register that there is a value for it.
setFloatAttribute(&node->imageWidth, value);
- } else if (key == QLatin1String("height")) {
+ } else if (key == "height"_L1) {
node->imageHeight = -2; // register that there is a value for it.
setFloatAttribute(&node->imageHeight, value);
- } else if (key == QLatin1String("alt")) {
+ } else if (key == "alt"_L1) {
node->imageAlt = value;
- } else if (key == QLatin1String("title")) {
+ } else if (key == "title"_L1) {
node->text = value;
}
break;
case Html_tr:
case Html_body:
- if (key == QLatin1String("bgcolor")) {
- QColor c; c.setNamedColor(value);
+ if (key == "bgcolor"_L1) {
+ QColor c = QColor::fromString(value);
if (!c.isValid())
qWarning("QTextHtmlParser::applyAttributes: Unknown color name '%s'",value.toLatin1().constData());
node->charFormat.setBackground(c);
- } else if (key == QLatin1String("background")) {
+ } else if (key == "background"_L1) {
node->applyBackgroundImage(value, resourceProvider);
}
break;
case Html_th:
case Html_td:
- if (key == QLatin1String("width")) {
+ if (key == "width"_L1) {
setWidthAttribute(&node->width, value);
- } else if (key == QLatin1String("bgcolor")) {
- QColor c; c.setNamedColor(value);
+ } else if (key == "bgcolor"_L1) {
+ QColor c = QColor::fromString(value);
if (!c.isValid())
qWarning("QTextHtmlParser::applyAttributes: Unknown color name '%s'",value.toLatin1().constData());
node->charFormat.setBackground(c);
- } else if (key == QLatin1String("background")) {
+ } else if (key == "background"_L1) {
node->applyBackgroundImage(value, resourceProvider);
- } else if (key == QLatin1String("rowspan")) {
+ } else if (key == "rowspan"_L1) {
if (setIntAttribute(&node->tableCellRowSpan, value))
node->tableCellRowSpan = qMax(1, node->tableCellRowSpan);
- } else if (key == QLatin1String("colspan")) {
+ } else if (key == "colspan"_L1) {
if (setIntAttribute(&node->tableCellColSpan, value))
- node->tableCellColSpan = qMax(1, node->tableCellColSpan);
+ node->tableCellColSpan = qBound(1, node->tableCellColSpan, 20480);
}
break;
case Html_table:
- if (key == QLatin1String("border")) {
+ // If table border already set through css style, prefer that one otherwise consider this value
+ if (key == "border"_L1 && !node->tableBorder) {
setFloatAttribute(&node->tableBorder, value);
- } else if (key == QLatin1String("bgcolor")) {
- QColor c; c.setNamedColor(value);
+ } else if (key == "bgcolor"_L1) {
+ QColor c = QColor::fromString(value);
if (!c.isValid())
qWarning("QTextHtmlParser::applyAttributes: Unknown color name '%s'",value.toLatin1().constData());
node->charFormat.setBackground(c);
- } else if (key == QLatin1String("bordercolor")) {
- QColor c; c.setNamedColor(value);
+ } else if (key == "bordercolor"_L1) {
+ QColor c = QColor::fromString(value);
if (!c.isValid())
qWarning("QTextHtmlParser::applyAttributes: Unknown color name '%s'",value.toLatin1().constData());
node->borderBrush = c;
- } else if (key == QLatin1String("background")) {
+ } else if (key == "background"_L1) {
node->applyBackgroundImage(value, resourceProvider);
- } else if (key == QLatin1String("cellspacing")) {
+ } else if (key == "cellspacing"_L1) {
setFloatAttribute(&node->tableCellSpacing, value);
- } else if (key == QLatin1String("cellpadding")) {
+ } else if (key == "cellpadding"_L1) {
setFloatAttribute(&node->tableCellPadding, value);
- } else if (key == QLatin1String("width")) {
+ } else if (key == "width"_L1) {
setWidthAttribute(&node->width, value);
- } else if (key == QLatin1String("height")) {
+ } else if (key == "height"_L1) {
setWidthAttribute(&node->height, value);
}
break;
case Html_meta:
- if (key == QLatin1String("name")
- && value == QLatin1String("qrichtext")) {
+ if (key == "name"_L1 && value == "qrichtext"_L1)
seenQt3Richtext = true;
- }
-
- if (key == QLatin1String("content")
- && value == QLatin1String("1")
- && seenQt3Richtext) {
+ if (key == "content"_L1 && value == "1"_L1 && seenQt3Richtext)
textEditMode = true;
- }
break;
case Html_hr:
- if (key == QLatin1String("width"))
+ if (key == "width"_L1)
setWidthAttribute(&node->width, value);
break;
case Html_link:
- if (key == QLatin1String("href"))
+ if (key == "href"_L1)
linkHref = value;
- else if (key == QLatin1String("type"))
+ else if (key == "type"_L1)
linkType = value;
break;
case Html_pre:
- if (key == QLatin1String("class") && value.startsWith(QLatin1String("language-")))
+ if (key == "class"_L1 && value.startsWith("language-"_L1))
node->blockFormat.setProperty(QTextFormat::BlockCodeLanguage, value.mid(9));
break;
default:
break;
}
- if (key == QLatin1String("style")) {
+ if (key == "style"_L1) {
#ifndef QT_NO_CSSPARSER
node->parseStyleAttribute(value, resourceProvider);
#endif
- } else if (key == QLatin1String("align")) {
+ } else if (key == "align"_L1) {
value = std::move(value).toLower();
bool alignmentSet = true;
- if (value == QLatin1String("left"))
+ if (value == "left"_L1)
node->blockFormat.setAlignment(Qt::AlignLeft|Qt::AlignAbsolute);
- else if (value == QLatin1String("right"))
+ else if (value == "right"_L1)
node->blockFormat.setAlignment(Qt::AlignRight|Qt::AlignAbsolute);
- else if (value == QLatin1String("center"))
+ else if (value == "center"_L1)
node->blockFormat.setAlignment(Qt::AlignHCenter);
- else if (value == QLatin1String("justify"))
+ else if (value == "justify"_L1)
node->blockFormat.setAlignment(Qt::AlignJustify);
else
alignmentSet = false;
@@ -1755,36 +1814,36 @@ void QTextHtmlParser::applyAttributes(const QStringList &attributes)
node->cssFloat = QTextFrameFormat::FloatLeft;
else if (node->blockFormat.alignment() & Qt::AlignRight)
node->cssFloat = QTextFrameFormat::FloatRight;
- } else if (value == QLatin1String("middle")) {
+ } else if (value == "middle"_L1) {
node->charFormat.setVerticalAlignment(QTextCharFormat::AlignMiddle);
- } else if (value == QLatin1String("top")) {
+ } else if (value == "top"_L1) {
node->charFormat.setVerticalAlignment(QTextCharFormat::AlignTop);
}
}
- } else if (key == QLatin1String("valign")) {
+ } else if (key == "valign"_L1) {
value = std::move(value).toLower();
- if (value == QLatin1String("top"))
+ if (value == "top"_L1)
node->charFormat.setVerticalAlignment(QTextCharFormat::AlignTop);
- else if (value == QLatin1String("middle"))
+ else if (value == "middle"_L1)
node->charFormat.setVerticalAlignment(QTextCharFormat::AlignMiddle);
- else if (value == QLatin1String("bottom"))
+ else if (value == "bottom"_L1)
node->charFormat.setVerticalAlignment(QTextCharFormat::AlignBottom);
- } else if (key == QLatin1String("dir")) {
+ } else if (key == "dir"_L1) {
value = std::move(value).toLower();
- if (value == QLatin1String("ltr"))
+ if (value == "ltr"_L1)
node->blockFormat.setLayoutDirection(Qt::LeftToRight);
- else if (value == QLatin1String("rtl"))
+ else if (value == "rtl"_L1)
node->blockFormat.setLayoutDirection(Qt::RightToLeft);
- } else if (key == QLatin1String("title")) {
+ } else if (key == "title"_L1) {
node->charFormat.setToolTip(value);
- } else if (key == QLatin1String("id")) {
+ } else if (key == "id"_L1) {
node->charFormat.setAnchor(true);
node->charFormat.setAnchorNames({value});
}
}
#ifndef QT_NO_CSSPARSER
- if (resourceProvider && !linkHref.isEmpty() && linkType == QLatin1String("text/css"))
+ if (resourceProvider && !linkHref.isEmpty() && linkType == "text/css"_L1)
importStyleSheet(linkHref);
#endif
}
@@ -1796,14 +1855,14 @@ public:
inline QTextHtmlStyleSelector(const QTextHtmlParser *parser)
: parser(parser) { nameCaseSensitivity = Qt::CaseInsensitive; }
- virtual QStringList nodeNames(NodePtr node) const override;
- virtual QString attribute(NodePtr node, const QString &name) const override;
- virtual bool hasAttributes(NodePtr node) const override;
- virtual bool isNullNode(NodePtr node) const override;
- virtual NodePtr parentNode(NodePtr node) const override;
- virtual NodePtr previousSiblingNode(NodePtr node) const override;
- virtual NodePtr duplicateNode(NodePtr node) const override;
- virtual void freeNode(NodePtr node) const override;
+ QStringList nodeNames(NodePtr node) const override;
+ QString attributeValue(NodePtr node, const QCss::AttributeSelector &aSelector) const override;
+ bool hasAttributes(NodePtr node) const override;
+ bool isNullNode(NodePtr node) const override;
+ NodePtr parentNode(NodePtr node) const override;
+ NodePtr previousSiblingNode(NodePtr node) const override;
+ NodePtr duplicateNode(NodePtr node) const override;
+ void freeNode(NodePtr node) const override;
private:
const QTextHtmlParser *parser;
@@ -1827,10 +1886,10 @@ static inline int findAttribute(const QStringList &attributes, const QString &na
return idx;
}
-QString QTextHtmlStyleSelector::attribute(NodePtr node, const QString &name) const
+QString QTextHtmlStyleSelector::attributeValue(NodePtr node, const QCss::AttributeSelector &aSelector) const
{
const QStringList &attributes = parser->at(node.id).attributes;
- const int idx = findAttribute(attributes, name);
+ const int idx = findAttribute(attributes, aSelector.name);
if (idx == -1)
return QString();
return attributes.at(idx + 1);
@@ -1884,10 +1943,9 @@ void QTextHtmlStyleSelector::freeNode(NodePtr) const
void QTextHtmlParser::resolveStyleSheetImports(const QCss::StyleSheet &sheet)
{
- for (int i = 0; i < sheet.importRules.count(); ++i) {
+ for (int i = 0; i < sheet.importRules.size(); ++i) {
const QCss::ImportRule &rule = sheet.importRules.at(i);
- if (rule.media.isEmpty()
- || rule.media.contains(QLatin1String("screen"), Qt::CaseInsensitive))
+ if (rule.media.isEmpty() || rule.media.contains("screen"_L1, Qt::CaseInsensitive))
importStyleSheet(rule.href);
}
}
@@ -1896,7 +1954,7 @@ void QTextHtmlParser::importStyleSheet(const QString &href)
{
if (!resourceProvider)
return;
- for (int i = 0; i < externalStyleSheets.count(); ++i)
+ for (int i = 0; i < externalStyleSheets.size(); ++i)
if (externalStyleSheets.at(i).url == href)
return;
@@ -1927,15 +1985,15 @@ QList<QCss::Declaration> standardDeclarationForNode(const QTextHtmlParserNode &n
case Html_u: {
bool needsUnderline = (node.id == Html_u) ? true : false;
if (node.id == Html_a) {
- for (int i = 0; i < node.attributes.count(); i += 2) {
+ for (int i = 0; i < node.attributes.size(); i += 2) {
const QString key = node.attributes.at(i);
- if (key.compare(QLatin1String("href"), Qt::CaseInsensitive) == 0
+ if (key.compare("href"_L1, Qt::CaseInsensitive) == 0
&& !node.attributes.at(i + 1).isEmpty()) {
needsUnderline = true;
- decl.d->property = QLatin1String("color");
+ decl.d->property = "color"_L1;
decl.d->propertyId = QCss::Color;
val.type = QCss::Value::Function;
- val.variant = QStringList() << QLatin1String("palette") << QLatin1String("link");
+ val.variant = QStringList() << "palette"_L1 << "link"_L1;
decl.d->values = QList<QCss::Value> { val };
decl.d->inheritable = true;
decls << decl;
@@ -1945,7 +2003,7 @@ QList<QCss::Declaration> standardDeclarationForNode(const QTextHtmlParserNode &n
}
if (needsUnderline) {
decl = QCss::Declaration();
- decl.d->property = QLatin1String("text-decoration");
+ decl.d->property = "text-decoration"_L1;
decl.d->propertyId = QCss::TextDecoration;
val.type = QCss::Value::KnownIdentifier;
val.variant = QVariant(QCss::Value_Underline);
@@ -1964,7 +2022,7 @@ QList<QCss::Declaration> standardDeclarationForNode(const QTextHtmlParserNode &n
case Html_h5:
case Html_th:
decl = QCss::Declaration();
- decl.d->property = QLatin1String("font-weight");
+ decl.d->property = "font-weight"_L1;
decl.d->propertyId = QCss::FontWeight;
val.type = QCss::Value::KnownIdentifier;
val.variant = QVariant(QCss::Value_Bold);
@@ -1978,7 +2036,7 @@ QList<QCss::Declaration> standardDeclarationForNode(const QTextHtmlParserNode &n
case Html_small:
if (node.id != Html_th) {
decl = QCss::Declaration();
- decl.d->property = QLatin1String("font-size");
+ decl.d->property = "font-size"_L1;
decl.d->propertyId = QCss::FontSize;
decl.d->inheritable = false;
val.type = QCss::Value::KnownIdentifier;
@@ -1998,7 +2056,7 @@ QList<QCss::Declaration> standardDeclarationForNode(const QTextHtmlParserNode &n
case Html_center:
case Html_td:
decl = QCss::Declaration();
- decl.d->property = QLatin1String("text-align");
+ decl.d->property = "text-align"_L1;
decl.d->propertyId = QCss::TextAlignment;
val.type = QCss::Value::KnownIdentifier;
val.variant = (node.id == Html_td) ? QVariant(QCss::Value_Left) : QVariant(QCss::Value_Center);
@@ -2008,7 +2066,7 @@ QList<QCss::Declaration> standardDeclarationForNode(const QTextHtmlParserNode &n
break;
case Html_s:
decl = QCss::Declaration();
- decl.d->property = QLatin1String("text-decoration");
+ decl.d->property = "text-decoration"_L1;
decl.d->propertyId = QCss::TextDecoration;
val.type = QCss::Value::KnownIdentifier;
val.variant = QVariant(QCss::Value_LineThrough);
@@ -2023,7 +2081,7 @@ QList<QCss::Declaration> standardDeclarationForNode(const QTextHtmlParserNode &n
case Html_var:
case Html_dfn:
decl = QCss::Declaration();
- decl.d->property = QLatin1String("font-style");
+ decl.d->property = "font-style"_L1;
decl.d->propertyId = QCss::FontStyle;
val.type = QCss::Value::KnownIdentifier;
val.variant = QVariant(QCss::Value_Italic);
@@ -2034,7 +2092,7 @@ QList<QCss::Declaration> standardDeclarationForNode(const QTextHtmlParserNode &n
case Html_sub:
case Html_sup:
decl = QCss::Declaration();
- decl.d->property = QLatin1String("vertical-align");
+ decl.d->property = "vertical-align"_L1;
decl.d->propertyId = QCss::VerticalAlignment;
val.type = QCss::Value::KnownIdentifier;
val.variant = (node.id == Html_sub) ? QVariant(QCss::Value_Sub) : QVariant(QCss::Value_Super);
@@ -2045,7 +2103,7 @@ QList<QCss::Declaration> standardDeclarationForNode(const QTextHtmlParserNode &n
case Html_ul:
case Html_ol:
decl = QCss::Declaration();
- decl.d->property = QLatin1String("list-style");
+ decl.d->property = "list-style"_L1;
decl.d->propertyId = QCss::ListStyle;
val.type = QCss::Value::KnownIdentifier;
val.variant = (node.id == Html_ul) ? QVariant(QCss::Value_Disc) : QVariant(QCss::Value_Decimal);
@@ -2059,11 +2117,11 @@ QList<QCss::Declaration> standardDeclarationForNode(const QTextHtmlParserNode &n
case Html_samp:
case Html_pre: {
decl = QCss::Declaration();
- decl.d->property = QLatin1String("font-family");
+ decl.d->property = "font-family"_L1;
decl.d->propertyId = QCss::FontFamily;
QList<QCss::Value> values;
val.type = QCss::Value::String;
- val.variant = QFontDatabase::systemFont(QFontDatabase::FixedFont).family();
+ val.variant = QFontDatabase::systemFont(QFontDatabase::FixedFont).families().constFirst();
values << val;
decl.d->values = values;
decl.d->inheritable = true;
@@ -2075,7 +2133,7 @@ QList<QCss::Declaration> standardDeclarationForNode(const QTextHtmlParserNode &n
case Html_br:
case Html_nobr:
decl = QCss::Declaration();
- decl.d->property = QLatin1String("whitespace");
+ decl.d->property = "whitespace"_L1;
decl.d->propertyId = QCss::Whitespace;
val.type = QCss::Value::KnownIdentifier;
switch (node.id) {
@@ -2102,27 +2160,27 @@ QList<QCss::Declaration> QTextHtmlParser::declarationsForNode(int node) const
int idx = 0;
selector.styleSheets.resize((resourceProvider ? 1 : 0)
- + externalStyleSheets.count()
- + inlineStyleSheets.count());
+ + externalStyleSheets.size()
+ + inlineStyleSheets.size());
if (resourceProvider)
selector.styleSheets[idx++] = QTextDocumentPrivate::get(resourceProvider)->parsedDefaultStyleSheet;
- for (int i = 0; i < externalStyleSheets.count(); ++i, ++idx)
+ for (int i = 0; i < externalStyleSheets.size(); ++i, ++idx)
selector.styleSheets[idx] = externalStyleSheets.at(i).sheet;
- for (int i = 0; i < inlineStyleSheets.count(); ++i, ++idx)
+ for (int i = 0; i < inlineStyleSheets.size(); ++i, ++idx)
selector.styleSheets[idx] = inlineStyleSheets.at(i);
- selector.medium = QLatin1String("screen");
+ selector.medium = resourceProvider ? resourceProvider->metaInformation(QTextDocument::CssMedia) : "screen"_L1;
QCss::StyleSelector::NodePtr n;
n.id = node;
const char *extraPseudo = nullptr;
- if (nodes.at(node).id == Html_a && nodes.at(node).hasHref)
+ if (nodes.at(node)->id == Html_a && nodes.at(node)->hasHref)
extraPseudo = "link";
// Ensure that our own style is taken into consideration
- decls = standardDeclarationForNode(nodes.at(node));
+ decls = standardDeclarationForNode(*nodes.at(node));
decls += selector.declarationsForNode(n, extraPseudo);
n = selector.parentNode(n);
while (!selector.isNullNode(n)) {
diff --git a/src/gui/text/qtexthtmlparser_p.h b/src/gui/text/qtexthtmlparser_p.h
index 4bfdab124b..dd52baa23e 100644
--- a/src/gui/text/qtexthtmlparser_p.h
+++ b/src/gui/text/qtexthtmlparser_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 QTEXTHTMLPARSER_P_H
#define QTEXTHTMLPARSER_P_H
@@ -62,7 +26,9 @@
#include "private/qtextformat_p.h"
#include "private/qtextdocument_p.h"
+#if QT_CONFIG(cssparser)
#include "private/qcssparser_p.h"
+#endif
#ifndef QT_NO_TEXTHTMLPARSER
@@ -184,6 +150,7 @@ struct QTextHtmlParserNode {
uint displayMode : 3; // QTextHtmlElement::DisplayMode
uint hasHref : 1;
QTextListFormat::Style listStyle;
+ int listStart = 1;
QString textListNumberPrefix;
QString textListNumberSuffix;
QString imageName;
@@ -269,7 +236,7 @@ struct QTextHtmlParserNode {
friend class QTextHtmlParser;
};
-Q_DECLARE_TYPEINFO(QTextHtmlParserNode, Q_MOVABLE_TYPE);
+Q_DECLARE_TYPEINFO(QTextHtmlParserNode, Q_RELOCATABLE_TYPE);
class QTextHtmlParser
@@ -281,11 +248,15 @@ public:
MarginBottom,
MarginLeft
};
+ ~QTextHtmlParser()
+ {
+ qDeleteAll(nodes);
+ }
- inline const QTextHtmlParserNode &at(int i) const { return nodes.at(i); }
- inline QTextHtmlParserNode &operator[](int i) { return nodes[i]; }
- inline int count() const { return nodes.count(); }
- inline int last() const { return nodes.count()-1; }
+ inline const QTextHtmlParserNode &at(int i) const { return *nodes.at(i); }
+ inline QTextHtmlParserNode &operator[](int i) { return *nodes[i]; }
+ inline int count() const { return nodes.size(); }
+ inline int last() const { return nodes.size()-1; }
int depth(int i) const;
int topMargin(int i) const;
int bottomMargin(int i) const;
@@ -305,10 +276,13 @@ public:
void parse(const QString &text, const QTextDocument *resourceProvider);
- static int lookupElement(const QString &element);
+ static int lookupElement(QStringView element);
+
+ Q_GUI_EXPORT static QString parseEntity(QStringView entity);
+
protected:
QTextHtmlParserNode *newNode(int parent);
- QList<QTextHtmlParserNode> nodes;
+ QList<QTextHtmlParserNode *> nodes;
QString txt;
int pos, len;
@@ -326,7 +300,9 @@ protected:
void applyAttributes(const QStringList &attributes);
void eatSpace();
inline bool hasPrefix(QChar c, int lookahead = 0) const
- {return pos + lookahead < len && txt.at(pos) == c; }
+ {
+ return pos + lookahead < len && txt.at(pos + lookahead) == c;
+ }
int margin(int i, int mar) const;
bool nodeIsChildOf(int i, QTextHTMLElements id) const;
@@ -353,7 +329,7 @@ protected:
const QTextDocument *resourceProvider;
};
#if QT_CONFIG(cssparser)
-Q_DECLARE_TYPEINFO(QTextHtmlParser::ExternalStyleSheet, Q_MOVABLE_TYPE);
+Q_DECLARE_TYPEINFO(QTextHtmlParser::ExternalStyleSheet, Q_RELOCATABLE_TYPE);
#endif
QT_END_NAMESPACE
diff --git a/src/gui/text/qtextimagehandler.cpp b/src/gui/text/qtextimagehandler.cpp
index 27112d3000..5c56c30711 100644
--- a/src/gui/text/qtextimagehandler.cpp
+++ b/src/gui/text/qtextimagehandler.cpp
@@ -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 "qtextimagehandler_p.h"
@@ -51,164 +15,96 @@
QT_BEGIN_NAMESPACE
-extern QString qt_findAtNxFile(const QString &baseFileName, qreal targetDevicePixelRatio,
- qreal *sourceDevicePixelRatio);
-static QString resolveFileName(QString fileName, QUrl *url, qreal targetDevicePixelRatio,
- qreal *sourceDevicePixelRatio)
-{
- // We might use the fileName for loading if url loading fails
- // try to make sure it is a valid file path.
- // Also, QFile{Info}::exists works only on filepaths (not urls)
-
- if (url->isValid()) {
- if (url->scheme() == QLatin1String("qrc")) {
- fileName = fileName.right(fileName.length() - 3);
- }
- else if (url->scheme() == QLatin1String("file")) {
- fileName = url->toLocalFile();
- }
- }
+using namespace Qt::StringLiterals;
- if (targetDevicePixelRatio <= 1.0)
- return fileName;
-
- // try to find a Nx version
- return qt_findAtNxFile(fileName, targetDevicePixelRatio, sourceDevicePixelRatio);
+static inline QString findAtNxFileOrResource(const QString &baseFileName,
+ qreal targetDevicePixelRatio,
+ qreal *sourceDevicePixelRatio)
+{
+ // qt_findAtNxFile expects a file name that can be tested with QFile::exists.
+ // so if the format.name() is a file:/ or qrc:/ URL, then we need to strip away the schema.
+ QString localFile;
+ const QUrl url(baseFileName);
+ if (url.isLocalFile())
+ localFile = url.toLocalFile();
+ else if (baseFileName.startsWith("qrc:/"_L1))
+ localFile = baseFileName.sliced(3);
+ else
+ localFile = baseFileName;
+ extern QString qt_findAtNxFile(const QString &baseFileName, qreal targetDevicePixelRatio,
+ qreal *sourceDevicePixelRatio);
+ return qt_findAtNxFile(localFile, targetDevicePixelRatio, sourceDevicePixelRatio);
}
-
-static QPixmap getPixmap(QTextDocument *doc, const QTextImageFormat &format, const qreal devicePixelRatio = 1.0)
+static inline QUrl fromLocalfileOrResources(QString path)
{
- QPixmap pm;
+ if (path.startsWith(":/"_L1)) // auto-detect resources and convert them to url
+ path = path.prepend("qrc"_L1);
+ return QUrl(path);
+}
- QString name = format.name();
- if (name.startsWith(QLatin1String(":/"))) // auto-detect resources and convert them to url
- name.prepend(QLatin1String("qrc"));
- QUrl url = QUrl(name);
+template<typename T>
+static T getAs(QTextDocument *doc, const QTextImageFormat &format, const qreal devicePixelRatio = 1.0)
+{
qreal sourcePixelRatio = 1.0;
- name = resolveFileName(name, &url, devicePixelRatio, &sourcePixelRatio);
- const QVariant data = doc->resource(QTextDocument::ImageResource, url);
- if (data.userType() == QMetaType::QPixmap || data.userType() == QMetaType::QImage) {
- pm = qvariant_cast<QPixmap>(data);
- } else if (data.userType() == QMetaType::QByteArray) {
- pm.loadFromData(data.toByteArray());
- }
-
- if (pm.isNull()) {
- QImage img;
- if (name.isEmpty() || !img.load(name))
- return QPixmap(QLatin1String(":/qt-project.org/styles/commonstyle/images/file-16.png"));
+ const QString name = findAtNxFileOrResource(format.name(), devicePixelRatio, &sourcePixelRatio);
+ const QUrl url = fromLocalfileOrResources(name);
- pm = QPixmap::fromImage(img);
- doc->addResource(QTextDocument::ImageResource, url, pm);
+ const QVariant data = doc->resource(QTextDocument::ImageResource, url);
+ T result;
+ if (data.userType() == QMetaType::QPixmap || data.userType() == QMetaType::QImage)
+ result = data.value<T>();
+ else if (data.metaType() == QMetaType::fromType<QByteArray>())
+ result.loadFromData(data.toByteArray());
+
+ if (result.isNull()) {
+ if (name.isEmpty() || !result.load(name))
+ return T(":/qt-project.org/styles/commonstyle/images/file-16.png"_L1);
+ doc->addResource(QTextDocument::ImageResource, url, result);
}
- if (name.contains(QLatin1String("@2x")))
- pm.setDevicePixelRatio(sourcePixelRatio);
-
- return pm;
+ if (sourcePixelRatio != 1.0)
+ result.setDevicePixelRatio(sourcePixelRatio);
+ return result;
}
-static QSize getPixmapSize(QTextDocument *doc, const QTextImageFormat &format)
+template<typename T>
+static QSize getSize(QTextDocument *doc, const QTextImageFormat &format)
{
- QPixmap pm;
-
const bool hasWidth = format.hasProperty(QTextFormat::ImageWidth);
const int width = qRound(format.width());
const bool hasHeight = format.hasProperty(QTextFormat::ImageHeight);
const int height = qRound(format.height());
+ T source;
QSize size(width, height);
if (!hasWidth || !hasHeight) {
- pm = getPixmap(doc, format);
- const int pmWidth = pm.width() / pm.devicePixelRatio();
- const int pmHeight = pm.height() / pm.devicePixelRatio();
+ source = getAs<T>(doc, format);
+ const QSizeF sourceSize = source.deviceIndependentSize();
if (!hasWidth) {
if (!hasHeight)
- size.setWidth(pmWidth);
+ size.setWidth(sourceSize.width());
else
- size.setWidth(qRound(height * (pmWidth / (qreal) pmHeight)));
+ size.setWidth(qRound(height * (sourceSize.width() / qreal(sourceSize.height()))));
}
if (!hasHeight) {
if (!hasWidth)
- size.setHeight(pmHeight);
+ size.setHeight(sourceSize.height());
else
- size.setHeight(qRound(width * (pmHeight / (qreal) pmWidth)));
+ size.setHeight(qRound(width * (sourceSize.height() / qreal(sourceSize.width()))));
}
}
qreal scale = 1.0;
QPaintDevice *pdev = doc->documentLayout()->paintDevice();
if (pdev) {
- if (pm.isNull())
- pm = getPixmap(doc, format);
- if (!pm.isNull())
- scale = qreal(pdev->logicalDpiY()) / qreal(qt_defaultDpi());
- }
- size *= scale;
-
- return size;
-}
-
-static QImage getImage(QTextDocument *doc, const QTextImageFormat &format, const qreal devicePixelRatio = 1.0)
-{
- QImage image;
-
- QString name = format.name();
- if (name.startsWith(QLatin1String(":/"))) // auto-detect resources
- name.prepend(QLatin1String("qrc"));
- QUrl url = QUrl(name);
- qreal sourcePixelRatio = 1.0;
- name = resolveFileName(name, &url, devicePixelRatio, &sourcePixelRatio);
- const QVariant data = doc->resource(QTextDocument::ImageResource, url);
- if (data.userType() == QMetaType::QImage) {
- image = qvariant_cast<QImage>(data);
- } else if (data.userType() == QMetaType::QByteArray) {
- image.loadFromData(data.toByteArray());
- }
-
- if (image.isNull()) {
- if (name.isEmpty() || !image.load(name))
- return QImage(QLatin1String(":/qt-project.org/styles/commonstyle/images/file-16.png"));
-
- doc->addResource(QTextDocument::ImageResource, url, image);
- }
-
- if (sourcePixelRatio != 1.0)
- image.setDevicePixelRatio(sourcePixelRatio);
-
- return image;
-}
-
-static QSize getImageSize(QTextDocument *doc, const QTextImageFormat &format)
-{
- QImage image;
-
- const bool hasWidth = format.hasProperty(QTextFormat::ImageWidth);
- const int width = qRound(format.width());
- const bool hasHeight = format.hasProperty(QTextFormat::ImageHeight);
- const int height = qRound(format.height());
-
- QSize size(width, height);
- if (!hasWidth || !hasHeight) {
- image = getImage(doc, format);
- if (!hasWidth)
- size.setWidth(image.width() / image.devicePixelRatio());
- if (!hasHeight)
- size.setHeight(image.height() / image.devicePixelRatio());
- }
-
- qreal scale = 1.0;
- QPaintDevice *pdev = doc->documentLayout()->paintDevice();
- if (pdev) {
- if (image.isNull())
- image = getImage(doc, format);
- if (!image.isNull())
+ if (source.isNull())
+ source = getAs<T>(doc, format);
+ if (!source.isNull())
scale = qreal(pdev->logicalDpiY()) / qreal(qt_defaultDpi());
}
size *= scale;
-
return size;
}
@@ -223,15 +119,15 @@ QSizeF QTextImageHandler::intrinsicSize(QTextDocument *doc, int posInDocument, c
const QTextImageFormat imageFormat = format.toImageFormat();
if (QCoreApplication::instance()->thread() != QThread::currentThread())
- return getImageSize(doc, imageFormat);
- return getPixmapSize(doc, imageFormat);
+ return getSize<QImage>(doc, imageFormat);
+ return getSize<QPixmap>(doc, imageFormat);
}
QImage QTextImageHandler::image(QTextDocument *doc, const QTextImageFormat &imageFormat)
{
Q_ASSERT(doc != nullptr);
- return getImage(doc, imageFormat);
+ return getAs<QImage>(doc, imageFormat);
}
void QTextImageHandler::drawObject(QPainter *p, const QRectF &rect, QTextDocument *doc, int posInDocument, const QTextFormat &format)
@@ -240,12 +136,14 @@ void QTextImageHandler::drawObject(QPainter *p, const QRectF &rect, QTextDocumen
const QTextImageFormat imageFormat = format.toImageFormat();
if (QCoreApplication::instance()->thread() != QThread::currentThread()) {
- const QImage image = getImage(doc, imageFormat, p->device()->devicePixelRatio());
+ const QImage image = getAs<QImage>(doc, imageFormat, p->device()->devicePixelRatio());
p->drawImage(rect, image, image.rect());
} else {
- const QPixmap pixmap = getPixmap(doc, imageFormat, p->device()->devicePixelRatio());
+ const QPixmap pixmap = getAs<QPixmap>(doc, imageFormat, p->device()->devicePixelRatio());
p->drawPixmap(rect, pixmap, pixmap.rect());
}
}
QT_END_NAMESPACE
+
+#include "moc_qtextimagehandler_p.cpp"
diff --git a/src/gui/text/qtextimagehandler_p.h b/src/gui/text/qtextimagehandler_p.h
index fafd394ad3..d28f474c67 100644
--- a/src/gui/text/qtextimagehandler_p.h
+++ b/src/gui/text/qtextimagehandler_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 QTEXTIMAGEHANDLER_P_H
#define QTEXTIMAGEHANDLER_P_H
diff --git a/src/gui/text/qtextlayout.cpp b/src/gui/text/qtextlayout.cpp
index bec1f4a542..f0c7dd24e5 100644
--- a/src/gui/text/qtextlayout.cpp
+++ b/src/gui/text/qtextlayout.cpp
@@ -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 "qtextlayout.h"
#include "qtextengine_p.h"
@@ -85,7 +49,7 @@ QT_BEGIN_NAMESPACE
/*!
\variable QTextLayout::FormatRange::length
- Specifies the numer of characters the format range spans.
+ Specifies the number of characters the format range spans.
*/
/*!
@@ -93,15 +57,13 @@ QT_BEGIN_NAMESPACE
Specifies the format to apply.
*/
-/*! \fn bool operator==(const QTextLayout::FormatRange &lhs, const QTextLayout::FormatRange &rhs)
- \relates QTextLayout::FormatRange
+/*! \fn bool QTextLayout::FormatRange::operator==(const QTextLayout::FormatRange &lhs, const QTextLayout::FormatRange &rhs)
Returns true if the \c {start}, \c {length}, and \c {format} fields
in \a lhs and \a rhs contain the same values respectively.
*/
-/*! \fn bool operator!=(const QTextLayout::FormatRange &lhs, const QTextLayout::FormatRange &rhs)
- \relates QTextLayout::FormatRange
+/*! \fn bool QTextLayout::FormatRange::operator!=(const QTextLayout::FormatRange &lhs, const QTextLayout::FormatRange &rhs)
Returns true if any of the \c {start}, \c {length}, or \c {format} fields
in \a lhs and \a rhs contain different values respectively.
@@ -303,6 +265,10 @@ Qt::LayoutDirection QTextInlineObject::textDirection() const
The text can then be rendered by calling the layout's draw() function:
\snippet code/src_gui_text_qtextlayout.cpp 1
+ It is also possible to draw each line individually, for instance to draw
+ the last line that fits into a widget elided:
+ \snippet code/src_gui_text_qtextlayout.cpp elided
+
For a given position in the text you can find a valid cursor position with
isValidCursorPosition(), nextCursorPosition(), and previousCursorPosition().
@@ -320,6 +286,26 @@ Qt::LayoutDirection QTextInlineObject::textDirection() const
*/
/*!
+ \enum QTextLayout::GlyphRunRetrievalFlag
+ \since 6.5
+
+ GlyphRunRetrievalFlag specifies flags passed to the glyphRuns() functions to determine
+ which properties of the layout are returned in the QGlyphRun objects. Since each property
+ will consume memory and may require additional allocations, it is a good practice to only
+ request the properties you will need to access later.
+
+ \value RetrieveGlyphIndexes Retrieves the indexes in the font which correspond to the glyphs.
+ \value RetrieveGlyphPositions Retrieves the relative positions of the glyphs in the layout.
+ \value RetrieveStringIndexes Retrieves the indexes in the original string that correspond to
+ each of the glyphs.
+ \value RetrieveString Retrieves the original source string from the layout.
+ \value RetrieveAll Retrieves all available properties of the layout.
+ \omitvalue DefaultRetrievalFlags
+
+ \sa glyphRuns(), QTextLine::glyphRuns()
+*/
+
+/*!
\fn QTextEngine *QTextLayout::engine() const
\internal
@@ -354,21 +340,7 @@ QTextLayout::QTextLayout(const QString& text)
calculations will be done in screen metrics.
*/
-#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
-/*!
-\if !defined(qt6)
- \fn QTextLayout::QTextLayout(const QString &text, const QFont &font, QPaintDevice *paintdevice)
- \obsolete
- Identical to QTextLayout::QTextLayout(const QString &text, const QFont &font, const QPaintDevice *paintdevice)
-\else
- \nothing
-\endif
-*/
-
-QTextLayout::QTextLayout(const QString &text, const QFont &font, QPaintDevice *paintdevice)
-#else
QTextLayout::QTextLayout(const QString &text, const QFont &font, const QPaintDevice *paintdevice)
-#endif
{
const QFont f(paintdevice ? QFont(font, paintdevice) : font);
d = new QTextEngine((text.isNull() ? (const QString&)QString::fromLatin1("") : text), f);
@@ -679,8 +651,8 @@ int QTextLayout::nextCursorPosition(int oldPos, CursorMode mode) const
{
const QCharAttributes *attributes = d->attributes();
int len = d->block.isValid() ? d->block.length() - 1
- : d->layoutData->string.length();
- Q_ASSERT(len <= d->layoutData->string.length());
+ : d->layoutData->string.size();
+ Q_ASSERT(len <= d->layoutData->string.size());
if (!attributes || oldPos < 0 || oldPos >= len)
return oldPos;
@@ -715,8 +687,8 @@ int QTextLayout::previousCursorPosition(int oldPos, CursorMode mode) const
{
const QCharAttributes *attributes = d->attributes();
int len = d->block.isValid() ? d->block.length() - 1
- : d->layoutData->string.length();
- Q_ASSERT(len <= d->layoutData->string.length());
+ : d->layoutData->string.size();
+ Q_ASSERT(len <= d->layoutData->string.size());
if (!attributes || oldPos <= 0 || oldPos > len)
return oldPos;
@@ -769,7 +741,7 @@ int QTextLayout::leftCursorPosition(int oldPos) const
return newPos;
}
-/*!/
+/*!
Returns \c true if position \a pos is a valid cursor position.
In a Unicode context some positions in the text are not valid
@@ -779,7 +751,7 @@ int QTextLayout::leftCursorPosition(int oldPos) const
A grapheme cluster is a sequence of two or more Unicode characters
that form one indivisible entity on the screen. For example the
latin character `\unicode{0xC4}' can be represented in Unicode by two
- characters, `A' (0x41), and the combining diaresis (0x308). A text
+ characters, `A' (0x41), and the combining diaeresis (0x308). A text
cursor can only validly be positioned before or after these two
characters, never between them since that wouldn't make sense. In
indic languages every syllable forms a grapheme cluster.
@@ -787,7 +759,7 @@ int QTextLayout::leftCursorPosition(int oldPos) const
bool QTextLayout::isValidCursorPosition(int pos) const
{
const QCharAttributes *attributes = d->attributes();
- if (!attributes || pos < 0 || pos > (int)d->layoutData->string.length())
+ if (!attributes || pos < 0 || pos > (int)d->layoutData->string.size())
return false;
return attributes[pos].graphemeBoundary;
}
@@ -822,9 +794,13 @@ QTextLine QTextLayout::createLine()
int l = d->lines.size();
if (l && d->lines.at(l-1).length < 0) {
QTextLine(l-1, d).setNumColumns(INT_MAX);
+ if (d->maxWidth > QFIXED_MAX / 2) {
+ qWarning("QTextLayout: text too long, truncated.");
+ return QTextLine();
+ }
}
int from = l > 0 ? d->lines.at(l-1).from + d->lines.at(l-1).length + d->lines.at(l-1).trailingSpaces : 0;
- int strlen = d->layoutData->string.length();
+ int strlen = d->layoutData->string.size();
if (l && from >= strlen) {
if (!d->lines.at(l-1).length || d->layoutData->string.at(strlen - 1) != QChar::LineSeparator)
return QTextLine();
@@ -1009,7 +985,9 @@ static inline QRectF clipIfValid(const QRectF &rect, const QRectF &clip)
}
+#if QT_VERSION < QT_VERSION_CHECK(7, 0, 0)
/*!
+ \overload
Returns the glyph indexes and positions for all glyphs corresponding to the \a length characters
starting at the position \a from in this QTextLayout. This is an expensive function, and should
not be called in a time sensitive context.
@@ -1017,27 +995,57 @@ static inline QRectF clipIfValid(const QRectF &rect, const QRectF &clip)
If \a from is less than zero, then the glyph run will begin at the first character in the
layout. If \a length is less than zero, it will span the entire string from the start position.
+ \note This is equivalent to calling
+ glyphRuns(from,
+ length,
+ QTextLayout::GlyphRunRetrievalFlag::GlyphIndexes |
+ QTextLayout::GlyphRunRetrievalFlag::GlyphPositions).
+
\since 4.8
\sa draw(), QPainter::drawGlyphRun()
*/
-#if !defined(QT_NO_RAWFONT)
+# if !defined(QT_NO_RAWFONT)
QList<QGlyphRun> QTextLayout::glyphRuns(int from, int length) const
{
+ return glyphRuns(from, length, QTextLayout::GlyphRunRetrievalFlag::DefaultRetrievalFlags);
+}
+# endif
+#endif
+
+/*!
+ \overload
+ Returns the glyph indexes and positions for all glyphs corresponding to the \a length characters
+ starting at the position \a from in this QTextLayout. This is an expensive function, and should
+ not be called in a time sensitive context.
+
+ If \a from is less than zero, then the glyph run will begin at the first character in the
+ layout. If \a length is less than zero, it will span the entire string from the start position.
+
+ The \a retrievalFlags specifies which properties of the QGlyphRun will be retrieved from the
+ layout. To minimize allocations and memory consumption, this should be set to include only the
+ properties that you need to access later.
+
+ \since 6.5
+ \sa draw(), QPainter::drawGlyphRun()
+*/
+#if !defined(QT_NO_RAWFONT)
+QList<QGlyphRun> QTextLayout::glyphRuns(int from,
+ int length,
+ QTextLayout::GlyphRunRetrievalFlags retrievalFlags) const
+{
if (from < 0)
from = 0;
if (length < 0)
- length = text().length();
+ length = text().size();
QHash<QPair<QFontEngine *, int>, QGlyphRun> glyphRunHash;
for (int i=0; i<d->lines.size(); ++i) {
if (d->lines.at(i).from > from + length)
break;
- else if (d->lines.at(i).from + d->lines[i].length >= from) {
- QList<QGlyphRun> glyphRuns = QTextLine(i, d).glyphRuns(from, length);
-
- for (int j = 0; j < glyphRuns.size(); j++) {
- const QGlyphRun &glyphRun = glyphRuns.at(j);
+ else if (d->lines.at(i).from + d->lines.at(i).length >= from) {
+ const QList<QGlyphRun> glyphRuns = QTextLine(i, d).glyphRuns(from, length, retrievalFlags);
+ for (const QGlyphRun &glyphRun : glyphRuns) {
QRawFont rawFont = glyphRun.rawFont();
QFontEngine *fontEngine = rawFont.d->fontEngine;
@@ -1050,14 +1058,17 @@ QList<QGlyphRun> QTextLayout::glyphRuns(int from, int length) const
} else {
QList<quint32> indexes = oldGlyphRun.glyphIndexes();
QList<QPointF> positions = oldGlyphRun.positions();
+ QList<qsizetype> stringIndexes = oldGlyphRun.stringIndexes();
QRectF boundingRect = oldGlyphRun.boundingRect();
indexes += glyphRun.glyphIndexes();
positions += glyphRun.positions();
+ stringIndexes += glyphRun.stringIndexes();
boundingRect = boundingRect.united(glyphRun.boundingRect());
oldGlyphRun.setGlyphIndexes(indexes);
oldGlyphRun.setPositions(positions);
+ oldGlyphRun.setStringIndexes(stringIndexes);
oldGlyphRun.setBoundingRect(boundingRect);
}
}
@@ -1093,7 +1104,6 @@ void QTextLayout::draw(QPainter *p, const QPointF &pos, const QList<FormatRange>
int firstLine = 0;
int lastLine = d->lines.size();
for (int i = 0; i < d->lines.size(); ++i) {
- QTextLine l(i, d);
const QScriptLine &sl = d->lines.at(i);
if (sl.y > clipe) {
@@ -1120,6 +1130,7 @@ void QTextLayout::draw(QPainter *p, const QPointF &pos, const QList<FormatRange>
QRectF lineRect(tl.naturalTextRect());
lineRect.translate(position);
lineRect.adjust(0, 0, d->leadingSpaceWidth(sl).toReal(), 0);
+ lineRect.setBottom(qCeil(lineRect.bottom()));
bool isLastLineInBlock = (line == d->lines.size()-1);
int sl_length = sl.length + (isLastLineInBlock? 1 : 0); // the infamous newline
@@ -1141,10 +1152,18 @@ void QTextLayout::draw(QPainter *p, const QPointF &pos, const QList<FormatRange>
QRectF fullLineRect(tl.rect());
fullLineRect.translate(position);
fullLineRect.setRight(QFIXED_MAX);
- if (!selectionEndInLine)
- region.addRect(clipIfValid(QRectF(lineRect.topRight(), fullLineRect.bottomRight()), clip));
- if (!selectionStartInLine)
- region.addRect(clipIfValid(QRectF(fullLineRect.topLeft(), lineRect.bottomLeft()), clip));
+ fullLineRect.setBottom(qCeil(fullLineRect.bottom()));
+
+ const bool rightToLeft = d->isRightToLeft();
+
+ if (!selectionEndInLine) {
+ region.addRect(clipIfValid(rightToLeft ? QRectF(fullLineRect.topLeft(), lineRect.bottomLeft())
+ : QRectF(lineRect.topRight(), fullLineRect.bottomRight()), clip));
+ }
+ if (!selectionStartInLine) {
+ region.addRect(clipIfValid(rightToLeft ? QRectF(lineRect.topRight(), fullLineRect.bottomRight())
+ : QRectF(fullLineRect.topLeft(), lineRect.bottomLeft()), clip));
+ }
} else if (!selectionEndInLine
&& isLastLineInBlock
&&!(d->option.flags() & QTextOption::ShowLineAndParagraphSeparators)) {
@@ -1271,7 +1290,7 @@ void QTextLayout::drawCursor(QPainter *p, const QPointF &pos, int cursorPosition
QPointF position = pos + d->position;
- cursorPosition = qBound(0, cursorPosition, d->layoutData->string.length());
+ cursorPosition = qBound(0, cursorPosition, d->layoutData->string.size());
int line = d->lineNumberForTextPosition(cursorPosition);
if (line < 0)
line = 0;
@@ -1283,25 +1302,44 @@ void QTextLayout::drawCursor(QPainter *p, const QPointF &pos, int cursorPosition
qreal x = position.x() + l.cursorToX(cursorPosition);
- int itm;
+ QFixed base = sl.base();
+ QFixed descent = sl.descent;
+ bool rightToLeft = d->isRightToLeft();
+ const int realCursorPosition = cursorPosition;
if (d->visualCursorMovement()) {
if (cursorPosition == sl.from + sl.length)
- cursorPosition--;
- itm = d->findItem(cursorPosition);
- } else
- itm = d->findItem(cursorPosition - 1);
+ --cursorPosition;
+ } else {
+ --cursorPosition;
+ }
+ int itm = d->findItem(cursorPosition);
- QFixed base = sl.base();
- QFixed descent = sl.descent;
- bool rightToLeft = d->isRightToLeft();
if (itm >= 0) {
- const QScriptItem &si = d->layoutData->items.at(itm);
- if (si.ascent > 0)
- base = si.ascent;
- if (si.descent > 0)
- descent = si.descent;
- rightToLeft = si.analysis.bidiLevel % 2;
+ const QScriptItem *si = &d->layoutData->items.at(itm);
+ // Same logic as in cursorToX to handle edges between writing directions to prioritise the script item
+ // that matches the writing direction of the paragraph.
+ if (d->layoutData->hasBidi && !d->visualCursorMovement() && si->analysis.bidiLevel % 2 != rightToLeft) {
+ int neighborItem = itm;
+ if (neighborItem > 0 && si->position == realCursorPosition)
+ --neighborItem;
+ else if (neighborItem < d->layoutData->items.size() - 1 && si->position + si->num_glyphs == realCursorPosition)
+ ++neighborItem;
+ const bool onBoundary = neighborItem != itm
+ && si->analysis.bidiLevel != d->layoutData->items[neighborItem].analysis.bidiLevel;
+ if (onBoundary && rightToLeft != si->analysis.bidiLevel % 2) {
+ itm = neighborItem;
+ si = &d->layoutData->items[itm];
+ }
+ }
+ // objects need some special treatment as they can have special alignment or be floating
+ if (si->analysis.flags != QScriptAnalysis::Object) {
+ if (si->ascent > 0)
+ base = si->ascent;
+ if (si->descent > 0)
+ descent = si->descent;
+ }
+ rightToLeft = si->analysis.bidiLevel % 2;
}
qreal y = position.y() + (sl.y + sl.base() - base).toReal();
bool toggleAntialiasing = !(p->renderHints() & QPainter::Antialiasing)
@@ -1311,7 +1349,20 @@ void QTextLayout::drawCursor(QPainter *p, const QPointF &pos, int cursorPosition
QPainter::CompositionMode origCompositionMode = p->compositionMode();
if (p->paintEngine()->hasFeature(QPaintEngine::RasterOpModes))
p->setCompositionMode(QPainter::RasterOp_NotDestination);
- p->fillRect(QRectF(x, y, qreal(width), (base + descent).toReal()), p->pen().brush());
+ const QTransform &deviceTransform = p->deviceTransform();
+ const qreal xScale = deviceTransform.m11();
+ if (deviceTransform.type() != QTransform::TxScale || std::trunc(xScale) == xScale) {
+ p->fillRect(QRectF(x, y, qreal(width), (base + descent).toReal()), p->pen().brush());
+ } else {
+ // Ensure consistently rendered cursor width under fractional scaling
+ const QPen origPen = p->pen();
+ QPen pen(origPen.brush(), qRound(width * xScale), Qt::SolidLine, Qt::FlatCap);
+ pen.setCosmetic(true);
+ const qreal center = x + qreal(width) / 2;
+ p->setPen(pen);
+ p->drawLine(QPointF(center, y), QPointF(center, qCeil(y + (base + descent).toReal())));
+ p->setPen(origPen);
+ }
p->setCompositionMode(origCompositionMode);
if (toggleAntialiasing)
p->setRenderHint(QPainter::Antialiasing, false);
@@ -1561,13 +1612,10 @@ void QTextLine::setLineWidth(qreal width)
return;
}
- if (width > QFIXED_MAX)
- width = QFIXED_MAX;
-
- line.width = QFixed::fromReal(width);
+ line.width = QFixed::fromReal(qBound(0.0, width, qreal(QFIXED_MAX)));
if (line.length
&& line.textWidth <= line.width
- && line.from + line.length == eng->layoutData->string.length())
+ && line.from + line.length == eng->layoutData->string.size())
// no need to do anything if the line is already layouted and the last one. This optimization helps
// when using things in a single line layout.
return;
@@ -1604,7 +1652,7 @@ void QTextLine::setNumColumns(int numColumns)
void QTextLine::setNumColumns(int numColumns, qreal alignmentWidth)
{
QScriptLine &line = eng->lines[index];
- line.width = QFixed::fromReal(alignmentWidth);
+ line.width = QFixed::fromReal(qBound(0.0, alignmentWidth, qreal(QFIXED_MAX)));
line.length = 0;
line.textWidth = 0;
layout_helper(numColumns);
@@ -1620,23 +1668,18 @@ namespace {
struct LineBreakHelper
{
- LineBreakHelper()
- : glyphCount(0), maxGlyphs(0), currentPosition(0), fontEngine(nullptr), logClusters(nullptr),
- manualWrap(false), whiteSpaceOrObject(true)
- {
- }
-
+ LineBreakHelper() = default;
QScriptLine tmpData;
QScriptLine spaceData;
QGlyphLayout glyphs;
- int glyphCount;
- int maxGlyphs;
- int currentPosition;
- glyph_t previousGlyph;
- QFontEngine *previousGlyphFontEngine;
+ int glyphCount = 0;
+ int maxGlyphs = 0;
+ int currentPosition = 0;
+ glyph_t previousGlyph = 0;
+ QExplicitlySharedDataPointer<QFontEngine> previousGlyphFontEngine;
QFixed minw;
QFixed currentSoftHyphenWidth;
@@ -1644,11 +1687,11 @@ namespace {
QFixed rightBearing;
QFixed minimumRightBearing;
- QFontEngine *fontEngine;
- const unsigned short *logClusters;
+ QExplicitlySharedDataPointer<QFontEngine> fontEngine;
+ const unsigned short *logClusters = nullptr;
- bool manualWrap;
- bool whiteSpaceOrObject;
+ bool manualWrap = false;
+ bool whiteSpaceOrObject = true;
bool checkFullOtherwiseExtend(QScriptLine &line);
@@ -1677,6 +1720,7 @@ namespace {
inline void calculateRightBearing(QFontEngine *engine, glyph_t glyph)
{
+ Q_ASSERT(engine);
qreal rb;
engine->getGlyphBearings(glyph, nullptr, &rb);
@@ -1691,13 +1735,13 @@ namespace {
{
if (currentPosition <= 0)
return;
- calculateRightBearing(fontEngine, currentGlyph());
+ calculateRightBearing(fontEngine.data(), currentGlyph());
}
inline void calculateRightBearingForPreviousGlyph()
{
if (previousGlyph > 0)
- calculateRightBearing(previousGlyphFontEngine, previousGlyph);
+ calculateRightBearing(previousGlyphFontEngine.data(), previousGlyph);
}
static const QFixed RightBearingNotCalculated;
@@ -1718,7 +1762,7 @@ namespace {
}
};
-const QFixed LineBreakHelper::RightBearingNotCalculated = QFixed(1);
+Q_CONSTINIT const QFixed LineBreakHelper::RightBearingNotCalculated = QFixed(1);
inline bool LineBreakHelper::checkFullOtherwiseExtend(QScriptLine &line)
{
@@ -1782,12 +1826,12 @@ void QTextLine::layout_helper(int maxGlyphs)
line.textWidth = 0;
line.hasTrailingSpaces = false;
- if (!eng->layoutData->items.size() || line.from >= eng->layoutData->string.length()) {
+ if (!eng->layoutData->items.size() || line.from >= eng->layoutData->string.size()) {
line.setDefaultHeight(eng);
return;
}
- Q_ASSERT(line.from < eng->layoutData->string.length());
+ Q_ASSERT(line.from < eng->layoutData->string.size());
LineBreakHelper lbh;
@@ -1814,6 +1858,7 @@ void QTextLine::layout_helper(int maxGlyphs)
lbh.logClusters = eng->layoutData->logClustersPtr;
lbh.previousGlyph = 0;
+ bool manuallyWrapped = false;
bool hasInlineObject = false;
QFixed maxInlineObjectHeight = 0;
@@ -1889,6 +1934,7 @@ void QTextLine::layout_helper(int maxGlyphs)
lbh.calculateRightBearingForPreviousGlyph();
}
line += lbh.tmpData;
+ manuallyWrapped = true;
goto found;
} else if (current.analysis.flags == QScriptAnalysis::Object) {
lbh.whiteSpaceOrObject = true;
@@ -1923,12 +1969,10 @@ void QTextLine::layout_helper(int maxGlyphs)
addNextCluster(lbh.currentPosition, end, lbh.spaceData, lbh.glyphCount,
current, lbh.logClusters, lbh.glyphs);
}
-
- if (!lbh.manualWrap && lbh.spaceData.textWidth > line.width) {
- lbh.spaceData.textWidth = line.width; // ignore spaces that fall out of the line.
- goto found;
- }
} else {
+ if (!lbh.manualWrap && lbh.spaceData.textWidth > line.width)
+ goto found;
+
lbh.whiteSpaceOrObject = false;
bool sb_or_ws = false;
lbh.saveCurrentGlyph();
@@ -1942,13 +1986,14 @@ void QTextLine::layout_helper(int maxGlyphs)
// spaces to behave as in previous Qt versions in the line breaking algorithm.
// The line breaks do not currently follow the Unicode specs, but fixing this would
// require refactoring the code and would cause behavioral regressions.
- bool isBreakableSpace = lbh.currentPosition < eng->layoutData->string.length()
+ const bool isBreakableSpace = lbh.currentPosition < eng->layoutData->string.size()
&& attributes[lbh.currentPosition].whiteSpace
&& eng->layoutData->string.at(lbh.currentPosition).decompositionTag() != QChar::NoBreak;
- if (lbh.currentPosition >= eng->layoutData->string.length()
+ if (lbh.currentPosition >= eng->layoutData->string.size()
|| isBreakableSpace
- || attributes[lbh.currentPosition].lineBreak) {
+ || attributes[lbh.currentPosition].lineBreak
+ || lbh.tmpData.textWidth >= QFIXED_MAX) {
sb_or_ws = true;
break;
} else if (attributes[lbh.currentPosition].graphemeBoundary) {
@@ -2002,7 +2047,7 @@ void QTextLine::layout_helper(int maxGlyphs)
// and when we then end up breaking on the next glyph we compute the right bearing
// and end up with a line width that is slightly larger width than what was requested.
// Unfortunately we can't remove this optimization as it will slow down text
- // layouting significantly, so we accept the slight correctnes issue.
+ // layouting significantly, so we accept the slight correctness issue.
if ((lbh.calculateNewWidth(line) + qAbs(lbh.minimumRightBearing)) > line.width)
lbh.calculateRightBearing();
@@ -2110,12 +2155,17 @@ found:
eng->maxWidth = qMax(eng->maxWidth, line.textWidth);
} else {
eng->minWidth = qMax(eng->minWidth, lbh.minw);
- eng->maxWidth += line.textWidth;
+ if (qAddOverflow(eng->layoutData->currentMaxWidth, line.textWidth, &eng->layoutData->currentMaxWidth))
+ eng->layoutData->currentMaxWidth = QFIXED_MAX;
+ if (!manuallyWrapped) {
+ if (qAddOverflow(eng->layoutData->currentMaxWidth, lbh.spaceData.textWidth, &eng->layoutData->currentMaxWidth))
+ eng->layoutData->currentMaxWidth = QFIXED_MAX;
+ }
+ eng->maxWidth = qMax(eng->maxWidth, eng->layoutData->currentMaxWidth);
+ if (manuallyWrapped)
+ eng->layoutData->currentMaxWidth = 0;
}
- if (line.textWidth > 0 && item < eng->layoutData->items.size())
- eng->maxWidth += lbh.spaceData.textWidth;
-
line.textWidth += trailingSpace;
if (lbh.spaceData.length) {
line.trailingSpaces = lbh.spaceData.length;
@@ -2167,35 +2217,37 @@ int QTextLine::textStart() const
int QTextLine::textLength() const
{
if (eng->option.flags() & QTextOption::ShowLineAndParagraphSeparators
- && eng->block.isValid() && index == eng->lines.count()-1) {
+ && eng->block.isValid() && index == eng->lines.size()-1) {
return eng->lines.at(index).length - 1;
}
return eng->lines.at(index).length + eng->lines.at(index).trailingSpaces;
}
-static void setPenAndDrawBackground(QPainter *p, const QPen &defaultPen, const QTextCharFormat &chf, const QRectF &r)
+static void drawBackground(QPainter *p, const QTextCharFormat &chf, const QRectF &r)
{
- QBrush c = chf.foreground();
- if (c.style() == Qt::NoBrush) {
- p->setPen(defaultPen);
- }
-
QBrush bg = chf.background();
if (bg.style() != Qt::NoBrush && !chf.property(SuppressBackground).toBool())
p->fillRect(r.toAlignedRect(), bg);
- if (c.style() != Qt::NoBrush) {
- p->setPen(QPen(c, 0));
- }
+}
+static void setPen(QPainter *p, const QPen &defaultPen, const QTextCharFormat &chf)
+{
+ QBrush c = chf.foreground();
+ if (c.style() == Qt::NoBrush)
+ p->setPen(defaultPen);
+ else
+ p->setPen(QPen(c, 0));
}
#if !defined(QT_NO_RAWFONT)
static QGlyphRun glyphRunWithInfo(QFontEngine *fontEngine,
+ const QString &text,
const QGlyphLayout &glyphLayout,
const QPointF &pos,
const QGlyphRun::GlyphRunFlags &flags,
- const QFixed &selectionX,
- const QFixed &selectionWidth,
+ QTextLayout::GlyphRunRetrievalFlags retrievalFlags,
+ QFixed selectionX,
+ QFixed selectionWidth,
int glyphsStart,
int glyphsEnd,
unsigned short *logClusters,
@@ -2209,14 +2261,15 @@ static QGlyphRun glyphRunWithInfo(QFontEngine *fontEngine,
QGlyphRunPrivate *d = QGlyphRunPrivate::get(glyphRun);
int rangeStart = textPosition;
- while (*logClusters != glyphsStart && rangeStart < textPosition + textLength) {
- ++logClusters;
+ int logClusterIndex = 0;
+ while (logClusters[logClusterIndex] != glyphsStart && rangeStart < textPosition + textLength) {
+ ++logClusterIndex;
++rangeStart;
}
int rangeEnd = rangeStart;
- while (*logClusters != glyphsEnd && rangeEnd < textPosition + textLength) {
- ++logClusters;
+ while (logClusters[logClusterIndex] != glyphsEnd && rangeEnd < textPosition + textLength) {
+ ++logClusterIndex;
++rangeEnd;
}
@@ -2249,14 +2302,43 @@ static QGlyphRun glyphRunWithInfo(QFontEngine *fontEngine,
qreal minY = 0;
qreal maxY = 0;
QList<quint32> glyphs;
- glyphs.reserve(glyphsArray.size());
+ if (retrievalFlags & QTextLayout::RetrieveGlyphIndexes)
+ glyphs.reserve(glyphsArray.size());
QList<QPointF> positions;
- positions.reserve(glyphsArray.size());
- for (int i=0; i<glyphsArray.size(); ++i) {
- glyphs.append(glyphsArray.at(i) & 0xffffff);
+ if (retrievalFlags & QTextLayout::RetrieveGlyphPositions)
+ positions.reserve(glyphsArray.size());
+ QList<qsizetype> stringIndexes;
+ if (retrievalFlags & QTextLayout::RetrieveStringIndexes)
+ stringIndexes.reserve(glyphsArray.size());
+
+ int nextClusterIndex = 0;
+ int currentClusterIndex = 0;
+ for (int i = 0; i < glyphsArray.size(); ++i) {
+ const int glyphArrayIndex = i + glyphsStart;
+ // Search for the next cluster in the string (or the end of string if there are no
+ // more clusters)
+ if (retrievalFlags & QTextLayout::RetrieveStringIndexes) {
+ if (nextClusterIndex < textLength && logClusters[nextClusterIndex] == glyphArrayIndex) {
+ currentClusterIndex = nextClusterIndex; // Store current cluster
+ while (logClusters[nextClusterIndex] == glyphArrayIndex && nextClusterIndex < textLength)
+ ++nextClusterIndex;
+ }
+
+ // We are now either at end of string (no more clusters) or we are not yet at the
+ // next cluster in glyph array. We fill in current cluster so that there is always one
+ // entry in stringIndexes for each glyph.
+ Q_ASSERT(nextClusterIndex == textLength || logClusters[nextClusterIndex] != glyphArrayIndex);
+ stringIndexes.append(textPosition + currentClusterIndex);
+ }
+
+ if (retrievalFlags & QTextLayout::RetrieveGlyphIndexes) {
+ glyph_t glyphIndex = glyphsArray.at(i) & 0xffffff;
+ glyphs.append(glyphIndex);
+ }
QPointF position = positionsArray.at(i).toPointF() + pos;
- positions.append(position);
+ if (retrievalFlags & QTextLayout::RetrieveGlyphPositions)
+ positions.append(position);
if (i == 0) {
maxY = minY = position.y();
@@ -2268,8 +2350,14 @@ static QGlyphRun glyphRunWithInfo(QFontEngine *fontEngine,
qreal height = maxY + fontHeight - minY;
- glyphRun.setGlyphIndexes(glyphs);
- glyphRun.setPositions(positions);
+ if (retrievalFlags & QTextLayout::RetrieveGlyphIndexes)
+ glyphRun.setGlyphIndexes(glyphs);
+ if (retrievalFlags & QTextLayout::RetrieveGlyphPositions)
+ glyphRun.setPositions(positions);
+ if (retrievalFlags & QTextLayout::RetrieveStringIndexes)
+ glyphRun.setStringIndexes(stringIndexes);
+ if (retrievalFlags & QTextLayout::RetrieveString)
+ glyphRun.setSourceString(text);
glyphRun.setFlags(flags);
glyphRun.setRawFont(font);
@@ -2279,7 +2367,9 @@ static QGlyphRun glyphRunWithInfo(QFontEngine *fontEngine,
return glyphRun;
}
+# if QT_VERSION < QT_VERSION_CHECK(7, 0, 0)
/*!
+ \overload
Returns the glyph indexes and positions for all glyphs in this QTextLine for characters
in the range defined by \a from and \a length. The \a from index is relative to the beginning
of the text in the containing QTextLayout, and the range must be within the range of QTextLine
@@ -2288,12 +2378,43 @@ static QGlyphRun glyphRunWithInfo(QFontEngine *fontEngine,
If \a from is negative, it will default to textStart(), and if \a length is negative it will
default to the return value of textLength().
+ \note This is equivalent to calling
+ glyphRuns(from,
+ length,
+ QTextLayout::GlyphRunRetrievalFlag::GlyphIndexes |
+ QTextLayout::GlyphRunRetrievalFlag::GlyphPositions).
+
\since 5.0
\sa QTextLayout::glyphRuns()
*/
QList<QGlyphRun> QTextLine::glyphRuns(int from, int length) const
{
+ return glyphRuns(from, length, QTextLayout::GlyphRunRetrievalFlag::DefaultRetrievalFlags);
+}
+# endif
+
+/*!
+ Returns the glyph indexes and positions for all glyphs in this QTextLine for characters
+ in the range defined by \a from and \a length. The \a from index is relative to the beginning
+ of the text in the containing QTextLayout, and the range must be within the range of QTextLine
+ as given by functions textStart() and textLength().
+
+ The \a retrievalFlags specifies which properties of the QGlyphRun will be retrieved from the
+ layout. To minimize allocations and memory consumption, this should be set to include only the
+ properties that you need to access later.
+
+ If \a from is negative, it will default to textStart(), and if \a length is negative it will
+ default to the return value of textLength().
+
+ \since 6.5
+
+ \sa QTextLayout::glyphRuns()
+*/
+QList<QGlyphRun> QTextLine::glyphRuns(int from,
+ int length,
+ QTextLayout::GlyphRunRetrievalFlags retrievalFlags) const
+{
const QScriptLine &line = eng->lines.at(index);
if (line.length == 0)
@@ -2365,14 +2486,18 @@ QList<QGlyphRun> QTextLine::glyphRuns(int from, int length) const
// when we're breaking a RTL script item, since the expected position passed into
// getGlyphPositions() is the left-most edge of the left-most glyph in an RTL run.
if (relativeFrom != (iterator.itemStart - si.position) && !rtl) {
- for (int i=itemGlyphsStart; i<glyphsStart; ++i) {
- QFixed justification = QFixed::fromFixed(glyphLayout.justifications[i].space_18d6);
- pos.rx() += (glyphLayout.advances[i] + justification).toReal();
+ for (int i = itemGlyphsStart; i < glyphsStart; ++i) {
+ if (!glyphLayout.attributes[i].dontPrint) {
+ QFixed justification = QFixed::fromFixed(glyphLayout.justifications[i].space_18d6);
+ pos.rx() += (glyphLayout.advances[i] + justification).toReal();
+ }
}
} else if (relativeTo != (iterator.itemEnd - si.position - 1) && rtl) {
- for (int i=itemGlyphsEnd; i>glyphsEnd; --i) {
- QFixed justification = QFixed::fromFixed(glyphLayout.justifications[i].space_18d6);
- pos.rx() += (glyphLayout.advances[i] + justification).toReal();
+ for (int i = itemGlyphsEnd; i > glyphsEnd; --i) {
+ if (!glyphLayout.attributes[i].dontPrint) {
+ QFixed justification = QFixed::fromFixed(glyphLayout.justifications[i].space_18d6);
+ pos.rx() += (glyphLayout.advances[i] + justification).toReal();
+ }
}
}
@@ -2409,20 +2534,28 @@ QList<QGlyphRun> QTextLine::glyphRuns(int from, int length) const
if (start == 0 && startsInsideLigature)
subFlags |= QGlyphRun::SplitLigature;
- glyphRuns.append(glyphRunWithInfo(multiFontEngine->engine(which),
- subLayout,
- pos,
- subFlags,
- x,
- width,
- glyphsStart + start,
- glyphsStart + end,
- logClusters + relativeFrom,
- relativeFrom + si.position,
- relativeTo - relativeFrom + 1));
+ {
+ QGlyphRun glyphRun = glyphRunWithInfo(multiFontEngine->engine(which),
+ eng->text,
+ subLayout,
+ pos,
+ subFlags,
+ retrievalFlags,
+ x,
+ width,
+ glyphsStart + start,
+ glyphsStart + end,
+ logClusters + relativeFrom,
+ relativeFrom + si.position,
+ relativeTo - relativeFrom + 1);
+ if (!glyphRun.isEmpty())
+ glyphRuns.append(glyphRun);
+ }
for (int i = 0; i < subLayout.numGlyphs; ++i) {
- QFixed justification = QFixed::fromFixed(subLayout.justifications[i].space_18d6);
- pos.rx() += (subLayout.advances[i] + justification).toReal();
+ if (!subLayout.attributes[i].dontPrint) {
+ QFixed justification = QFixed::fromFixed(subLayout.justifications[i].space_18d6);
+ pos.rx() += (subLayout.advances[i] + justification).toReal();
+ }
}
if (rtl)
@@ -2440,9 +2573,11 @@ QList<QGlyphRun> QTextLine::glyphRuns(int from, int length) const
subFlags |= QGlyphRun::SplitLigature;
QGlyphRun glyphRun = glyphRunWithInfo(multiFontEngine->engine(which),
+ eng->text,
subLayout,
pos,
subFlags,
+ retrievalFlags,
x,
width,
glyphsStart + start,
@@ -2456,9 +2591,11 @@ QList<QGlyphRun> QTextLine::glyphRuns(int from, int length) const
if (startsInsideLigature || endsInsideLigature)
flags |= QGlyphRun::SplitLigature;
QGlyphRun glyphRun = glyphRunWithInfo(mainFontEngine,
+ eng->text,
glyphLayout,
pos,
flags,
+ retrievalFlags,
x,
width,
glyphsStart,
@@ -2486,7 +2623,7 @@ void QTextLine::draw(QPainter *painter, const QPointF &position) const
draw_internal(painter, position, nullptr);
}
-void QTextLine::draw_internal(QPainter *p, const QPointF &pos,
+void QTextLine::draw_internal(QPainter *p, const QPointF &origPos,
const QTextLayout::FormatRange *selection) const
{
#ifndef QT_NO_RAWFONT
@@ -2494,7 +2631,6 @@ void QTextLine::draw_internal(QPainter *p, const QPointF &pos,
Q_ASSERT(!eng->useRawFont);
#endif
const QScriptLine &line = eng->lines[index];
- QPen pen = p->pen();
bool noText = (selection && selection->format.property(SuppressText).toBool());
@@ -2504,16 +2640,22 @@ void QTextLine::draw_internal(QPainter *p, const QPointF &pos,
&& selection->start + selection->length > line.from) {
const qreal lineHeight = line.height().toReal();
- QRectF r(pos.x() + line.x.toReal(), pos.y() + line.y.toReal(),
- lineHeight / 2, QFontMetrics(eng->font()).horizontalAdvance(QLatin1Char(' ')));
- setPenAndDrawBackground(p, QPen(), selection->format, r);
- p->setPen(pen);
+ QRectF r(origPos.x() + line.x.toReal(), origPos.y() + line.y.toReal(),
+ lineHeight / 2, QFontMetrics(eng->font()).horizontalAdvance(u' '));
+ drawBackground(p, selection->format, r);
}
return;
}
+ Q_CONSTINIT static QRectF maxFixedRect(-QFIXED_MAX / 2, -QFIXED_MAX / 2, QFIXED_MAX, QFIXED_MAX);
+ const bool xlateToFixedRange = !maxFixedRect.contains(origPos);
+ QPointF pos;
+ if (Q_LIKELY(!xlateToFixedRange))
+ pos = origPos;
+ else
+ p->translate(origPos);
+
- QTextLineItemIterator iterator(eng, index, pos, selection);
QFixed lineBase = line.base();
eng->clearDecorations();
eng->enableDelayDecorations();
@@ -2523,187 +2665,214 @@ void QTextLine::draw_internal(QPainter *p, const QPointF &pos,
const QTextFormatCollection *formatCollection = eng->formatCollection();
bool suppressColors = (eng->option.flags() & QTextOption::SuppressColors);
- while (!iterator.atEnd()) {
- QScriptItem &si = iterator.next();
-
- if (selection && selection->start >= 0 && iterator.isOutsideSelection())
- continue;
- if (si.analysis.flags == QScriptAnalysis::LineOrParagraphSeparator
- && !(eng->option.flags() & QTextOption::ShowLineAndParagraphSeparators))
- continue;
-
- QFixed itemBaseLine = y;
- QFont f = eng->font(si);
- QTextCharFormat format;
- if (formatCollection != nullptr)
- format = formatCollection->defaultTextFormat();
+ auto prepareFormat = [suppressColors, selection, this](QTextCharFormat &format,
+ QScriptItem *si) {
+ format.merge(eng->format(si));
- if (eng->hasFormats() || selection || formatCollection) {
- format.merge(eng->format(&si));
+ if (suppressColors) {
+ format.clearForeground();
+ format.clearBackground();
+ format.clearProperty(QTextFormat::TextUnderlineColor);
+ }
+ if (selection)
+ format.merge(selection->format);
+ };
- if (suppressColors) {
- format.clearForeground();
- format.clearBackground();
- format.clearProperty(QTextFormat::TextUnderlineColor);
- }
- if (selection)
- format.merge(selection->format);
-
- setPenAndDrawBackground(p, pen, format, QRectF(iterator.x.toReal(), (y - lineBase).toReal(),
- iterator.itemWidth.toReal(), line.height().toReal()));
-
- const qreal baseLineOffset = format.baselineOffset() / 100.0;
- QTextCharFormat::VerticalAlignment valign = format.verticalAlignment();
- if (valign == QTextCharFormat::AlignSuperScript
- || valign == QTextCharFormat::AlignSubScript
- || !qFuzzyIsNull(baseLineOffset))
- {
- QFontEngine *fe = f.d->engineForScript(si.analysis.script);
- QFixed height = fe->ascent() + fe->descent();
- itemBaseLine -= height * QFixed::fromReal(baseLineOffset);
-
- if (valign == QTextCharFormat::AlignSubScript)
- itemBaseLine += height * QFixed::fromReal(format.subScriptBaseline() / 100.0);
- else if (valign == QTextCharFormat::AlignSuperScript)
- itemBaseLine -= height * QFixed::fromReal(format.superScriptBaseline() / 100.0);
+ {
+ QTextLineItemIterator iterator(eng, index, pos, selection);
+ while (!iterator.atEnd()) {
+ QScriptItem &si = iterator.next();
+
+ if (eng->hasFormats() || selection || formatCollection) {
+ QTextCharFormat format;
+ if (formatCollection != nullptr)
+ format = formatCollection->defaultTextFormat();
+ prepareFormat(format, &si);
+ drawBackground(p, format, QRectF(iterator.x.toReal(), (y - lineBase).toReal(),
+ iterator.itemWidth.toReal(), line.height().toReal()));
}
}
+ }
- if (si.analysis.flags >= QScriptAnalysis::TabOrObject) {
+ QPen pen = p->pen();
+ {
+ QTextLineItemIterator iterator(eng, index, pos, selection);
+ while (!iterator.atEnd()) {
+ QScriptItem &si = iterator.next();
- if (eng->hasFormats()) {
- p->save();
- if (si.analysis.flags == QScriptAnalysis::Object && QTextDocumentPrivate::get(eng->block)) {
- QFixed itemY = y - si.ascent;
- switch (format.verticalAlignment()) {
- case QTextCharFormat::AlignTop:
- itemY = y - lineBase;
- break;
- case QTextCharFormat::AlignMiddle:
- itemY = y - lineBase + (line.height() - si.height()) / 2;
- break;
- case QTextCharFormat::AlignBottom:
- itemY = y - lineBase + line.height() - si.height();
- break;
- default:
- break;
- }
+ if (selection && selection->start >= 0 && iterator.isOutsideSelection())
+ continue;
- QRectF itemRect(iterator.x.toReal(), itemY.toReal(), iterator.itemWidth.toReal(), si.height().toReal());
-
- eng->docLayout()->drawInlineObject(p, itemRect,
- QTextInlineObject(iterator.item, eng),
- si.position + eng->block.position(),
- format);
- if (selection) {
- QBrush bg = format.brushProperty(ObjectSelectionBrush);
- if (bg.style() != Qt::NoBrush) {
- QColor c = bg.color();
- c.setAlpha(128);
- p->fillRect(itemRect, c);
- }
- }
- } else { // si.isTab
- QFont f = eng->font(si);
- QTextItemInt gf(si, &f, format);
- gf.chars = nullptr;
- gf.num_chars = 0;
- gf.width = iterator.itemWidth;
- QPainterPrivate::get(p)->drawTextItem(QPointF(iterator.x.toReal(), y.toReal()), gf, eng);
- if (eng->option.flags() & QTextOption::ShowTabsAndSpaces) {
- const QChar visualTab = u'\x2192';
- int w = QFontMetrics(f).horizontalAdvance(visualTab);
- qreal x = iterator.itemWidth.toReal() - w; // Right-aligned
- if (x < 0)
- p->setClipRect(QRectF(iterator.x.toReal(), line.y.toReal(),
- iterator.itemWidth.toReal(), line.height().toReal()),
- Qt::IntersectClip);
- else
- x /= 2; // Centered
- p->setFont(f);
- p->drawText(QPointF(iterator.x.toReal() + x,
- y.toReal()), visualTab);
- }
+ if (si.analysis.flags == QScriptAnalysis::LineOrParagraphSeparator
+ && !(eng->option.flags() & QTextOption::ShowLineAndParagraphSeparators))
+ continue;
+ QFixed itemBaseLine = y;
+ QFont f = eng->font(si);
+ QTextCharFormat format;
+ if (formatCollection != nullptr)
+ format = formatCollection->defaultTextFormat();
+
+ if (eng->hasFormats() || selection || formatCollection) {
+ prepareFormat(format, &si);
+ setPen(p, pen, format);
+
+ const qreal baseLineOffset = format.baselineOffset() / 100.0;
+ QTextCharFormat::VerticalAlignment valign = format.verticalAlignment();
+ if (valign == QTextCharFormat::AlignSuperScript
+ || valign == QTextCharFormat::AlignSubScript
+ || !qFuzzyIsNull(baseLineOffset))
+ {
+ QFontEngine *fe = f.d->engineForScript(si.analysis.script);
+ QFixed height = fe->ascent() + fe->descent();
+ itemBaseLine -= height * QFixed::fromReal(baseLineOffset);
+
+ if (valign == QTextCharFormat::AlignSubScript)
+ itemBaseLine += height * QFixed::fromReal(format.subScriptBaseline() / 100.0);
+ else if (valign == QTextCharFormat::AlignSuperScript)
+ itemBaseLine -= height * QFixed::fromReal(format.superScriptBaseline() / 100.0);
}
- p->restore();
}
- continue;
- }
+ if (si.analysis.flags >= QScriptAnalysis::TabOrObject) {
- unsigned short *logClusters = eng->logClusters(&si);
- QGlyphLayout glyphs = eng->shapedGlyphs(&si);
+ if (eng->hasFormats()) {
+ p->save();
+ if (si.analysis.flags == QScriptAnalysis::Object && QTextDocumentPrivate::get(eng->block)) {
+ QFixed itemY = y - si.ascent;
+ switch (format.verticalAlignment()) {
+ case QTextCharFormat::AlignTop:
+ itemY = y - lineBase;
+ break;
+ case QTextCharFormat::AlignMiddle:
+ itemY = y - lineBase + (line.height() - si.height()) / 2;
+ break;
+ case QTextCharFormat::AlignBottom:
+ itemY = y - lineBase + line.height() - si.height();
+ break;
+ default:
+ break;
+ }
- QTextItemInt gf(glyphs.mid(iterator.glyphsStart, iterator.glyphsEnd - iterator.glyphsStart),
- &f, eng->layoutData->string.unicode() + iterator.itemStart,
- iterator.itemEnd - iterator.itemStart, eng->fontEngine(si), format);
- gf.logClusters = logClusters + iterator.itemStart - si.position;
- gf.width = iterator.itemWidth;
- gf.justified = line.justified;
- gf.initWithScriptItem(si);
-
- Q_ASSERT(gf.fontEngine);
-
- QPointF pos(iterator.x.toReal(), itemBaseLine.toReal());
- if (format.penProperty(QTextFormat::TextOutline).style() != Qt::NoPen) {
- QPainterPath path;
- path.setFillRule(Qt::WindingFill);
-
- if (gf.glyphs.numGlyphs)
- gf.fontEngine->addOutlineToPath(pos.x(), pos.y(), gf.glyphs, &path, gf.flags);
- if (gf.flags) {
- const QFontEngine *fe = gf.fontEngine;
- const qreal lw = fe->lineThickness().toReal();
- if (gf.flags & QTextItem::Underline) {
- qreal offs = fe->underlinePosition().toReal();
- path.addRect(pos.x(), pos.y() + offs, gf.width.toReal(), lw);
- }
- if (gf.flags & QTextItem::Overline) {
- qreal offs = fe->ascent().toReal() + 1;
- path.addRect(pos.x(), pos.y() - offs, gf.width.toReal(), lw);
- }
- if (gf.flags & QTextItem::StrikeOut) {
- qreal offs = fe->ascent().toReal() / 3;
- path.addRect(pos.x(), pos.y() - offs, gf.width.toReal(), lw);
+ QRectF itemRect(iterator.x.toReal(), itemY.toReal(), iterator.itemWidth.toReal(), si.height().toReal());
+
+ eng->docLayout()->drawInlineObject(p, itemRect,
+ QTextInlineObject(iterator.item, eng),
+ si.position + eng->block.position(),
+ format);
+ if (selection) {
+ QBrush bg = format.brushProperty(ObjectSelectionBrush);
+ if (bg.style() != Qt::NoBrush) {
+ QColor c = bg.color();
+ c.setAlpha(128);
+ p->fillRect(itemRect, c);
+ }
+ }
+ } else { // si.isTab
+ QFont f = eng->font(si);
+ QTextItemInt gf(si, &f, format);
+ gf.chars = nullptr;
+ gf.num_chars = 0;
+ gf.width = iterator.itemWidth;
+ QPainterPrivate::get(p)->drawTextItem(QPointF(iterator.x.toReal(), y.toReal()), gf, eng);
+ if (eng->option.flags() & QTextOption::ShowTabsAndSpaces) {
+ const QChar visualTab = QChar(QChar::VisualTabCharacter);
+ int w = QFontMetrics(f).horizontalAdvance(visualTab);
+ qreal x = iterator.itemWidth.toReal() - w; // Right-aligned
+ if (x < 0)
+ p->setClipRect(QRectF(iterator.x.toReal(), line.y.toReal(),
+ iterator.itemWidth.toReal(), line.height().toReal()),
+ Qt::IntersectClip);
+ else
+ x /= 2; // Centered
+ p->setFont(f);
+ p->drawText(QPointF(iterator.x.toReal() + x,
+ y.toReal()), visualTab);
+ }
+
+ }
+ p->restore();
}
+
+ continue;
}
- p->save();
- p->setRenderHint(QPainter::Antialiasing);
- //Currently QPen with a Qt::NoPen style still returns a default
- //QBrush which != Qt::NoBrush so we need this specialcase to reset it
- if (p->pen().style() == Qt::NoPen)
- p->setBrush(Qt::NoBrush);
- else
- p->setBrush(p->pen().brush());
+ unsigned short *logClusters = eng->logClusters(&si);
+ QGlyphLayout glyphs = eng->shapedGlyphs(&si);
- p->setPen(format.textOutline());
- p->drawPath(path);
- p->restore();
- } else {
- if (noText)
- gf.glyphs.numGlyphs = 0; // slightly less elegant than it should be
- QPainterPrivate::get(p)->drawTextItem(pos, gf, eng);
- }
+ QTextItemInt gf(glyphs.mid(iterator.glyphsStart, iterator.glyphsEnd - iterator.glyphsStart),
+ &f, eng->layoutData->string.unicode() + iterator.itemStart,
+ iterator.itemEnd - iterator.itemStart, eng->fontEngine(si), format);
+ gf.logClusters = logClusters + iterator.itemStart - si.position;
+ gf.width = iterator.itemWidth;
+ gf.justified = line.justified;
+ gf.initWithScriptItem(si);
+
+ Q_ASSERT(gf.fontEngine);
+
+ QPointF pos(iterator.x.toReal(), itemBaseLine.toReal());
+ if (format.penProperty(QTextFormat::TextOutline).style() != Qt::NoPen) {
+ QPainterPath path;
+ path.setFillRule(Qt::WindingFill);
+
+ if (gf.glyphs.numGlyphs)
+ gf.fontEngine->addOutlineToPath(pos.x(), pos.y(), gf.glyphs, &path, gf.flags);
+ if (gf.flags) {
+ const QFontEngine *fe = gf.fontEngine;
+ const qreal lw = fe->lineThickness().toReal();
+ if (gf.flags & QTextItem::Underline) {
+ qreal offs = fe->underlinePosition().toReal();
+ path.addRect(pos.x(), pos.y() + offs, gf.width.toReal(), lw);
+ }
+ if (gf.flags & QTextItem::Overline) {
+ qreal offs = fe->ascent().toReal() + 1;
+ path.addRect(pos.x(), pos.y() - offs, gf.width.toReal(), lw);
+ }
+ if (gf.flags & QTextItem::StrikeOut) {
+ qreal offs = fe->ascent().toReal() / 3;
+ path.addRect(pos.x(), pos.y() - offs, gf.width.toReal(), lw);
+ }
+ }
- if ((si.analysis.flags == QScriptAnalysis::Space
- || si.analysis.flags == QScriptAnalysis::Nbsp)
- && (eng->option.flags() & QTextOption::ShowTabsAndSpaces)) {
- QBrush c = format.foreground();
- if (c.style() != Qt::NoBrush)
- p->setPen(c.color());
- const QChar visualSpace = si.analysis.flags == QScriptAnalysis::Space ? u'\xb7' : u'\xb0';
- QFont oldFont = p->font();
- p->setFont(eng->font(si));
- p->drawText(QPointF(iterator.x.toReal(), itemBaseLine.toReal()), visualSpace);
- p->setPen(pen);
- p->setFont(oldFont);
+ p->save();
+ p->setRenderHint(QPainter::Antialiasing);
+ //Currently QPen with a Qt::NoPen style still returns a default
+ //QBrush which != Qt::NoBrush so we need this specialcase to reset it
+ if (p->pen().style() == Qt::NoPen)
+ p->setBrush(Qt::NoBrush);
+ else
+ p->setBrush(p->pen().brush());
+
+ p->setPen(format.textOutline());
+ p->drawPath(path);
+ p->restore();
+ } else {
+ if (noText)
+ gf.glyphs.numGlyphs = 0; // slightly less elegant than it should be
+ QPainterPrivate::get(p)->drawTextItem(pos, gf, eng);
+ }
+
+ if ((si.analysis.flags == QScriptAnalysis::Space
+ || si.analysis.flags == QScriptAnalysis::Nbsp)
+ && (eng->option.flags() & QTextOption::ShowTabsAndSpaces)) {
+ QBrush c = format.foreground();
+ if (c.style() != Qt::NoBrush)
+ p->setPen(c.color());
+ const QChar visualSpace = si.analysis.flags == QScriptAnalysis::Space ? u'\xb7' : u'\xb0';
+ QFont oldFont = p->font();
+ p->setFont(eng->font(si));
+ p->drawText(QPointF(iterator.x.toReal(), itemBaseLine.toReal()), visualSpace);
+ p->setPen(pen);
+ p->setFont(oldFont);
+ }
}
}
eng->drawDecorations(p);
+ if (xlateToFixedRange)
+ p->translate(-origPos);
+
if (eng->hasFormats())
p->setPen(pen);
}
@@ -2740,7 +2909,6 @@ qreal QTextLine::cursorToX(int *cursorPos, Edge edge) const
int lineEnd = line.from + line.length + line.trailingSpaces;
int pos = qBound(line.from, *cursorPos, lineEnd);
- int itm;
const QCharAttributes *attributes = eng->attributes();
if (!attributes) {
*cursorPos = line.from;
@@ -2748,38 +2916,54 @@ qreal QTextLine::cursorToX(int *cursorPos, Edge edge) const
}
while (pos < lineEnd && !attributes[pos].graphemeBoundary)
pos++;
- if (pos == lineEnd) {
- // end of line ensure we have the last item on the line
- itm = eng->findItem(pos-1);
- }
- else
- itm = eng->findItem(pos);
+ // end of line ensure we have the last item on the line
+ int itm = pos == lineEnd ? eng->findItem(pos-1) : eng->findItem(pos);
if (itm < 0) {
*cursorPos = line.from;
return x.toReal();
}
eng->shapeLine(line);
- const QScriptItem *si = &eng->layoutData->items[itm];
- if (!si->num_glyphs)
+ const QScriptItem *scriptItem = &eng->layoutData->items[itm];
+ if (!scriptItem->num_glyphs)
eng->shape(itm);
+ if ((scriptItem->analysis.bidiLevel % 2 != eng->isRightToLeft()) && !eng->visualCursorMovement()) {
+ // If the item we found has a different writing direction than the engine,
+ // check if the cursor is between two items with different writing direction
+ int neighborItem = itm;
+ if (neighborItem > 0 && scriptItem->position == pos)
+ --neighborItem;
+ else if (neighborItem < eng->layoutData->items.size() - 1 && scriptItem->position + scriptItem->num_glyphs == pos)
+ ++neighborItem;
+ const bool onBoundary = neighborItem != itm && scriptItem->analysis.bidiLevel != eng->layoutData->items[neighborItem].analysis.bidiLevel;
+ // If we are, prioritise the neighbor item that has the same direction as the engine
+ if (onBoundary) {
+ if (eng->isRightToLeft() != scriptItem->analysis.bidiLevel % 2) {
+ itm = neighborItem;
+ scriptItem = &eng->layoutData->items[itm];
+ if (!scriptItem->num_glyphs)
+ eng->shape(itm);
+ }
+ }
+ }
+
const int l = eng->length(itm);
- pos = qBound(0, pos - si->position, l);
+ pos = qBound(0, pos - scriptItem->position, l);
- QGlyphLayout glyphs = eng->shapedGlyphs(si);
- unsigned short *logClusters = eng->logClusters(si);
+ QGlyphLayout glyphs = eng->shapedGlyphs(scriptItem);
+ unsigned short *logClusters = eng->logClusters(scriptItem);
Q_ASSERT(logClusters);
- int glyph_pos = pos == l ? si->num_glyphs : logClusters[pos];
- if (edge == Trailing && glyph_pos < si->num_glyphs) {
+ int glyph_pos = pos == l ? scriptItem->num_glyphs : logClusters[pos];
+ if (edge == Trailing && glyph_pos < scriptItem->num_glyphs) {
// trailing edge is leading edge of next cluster
glyph_pos++;
- while (glyph_pos < si->num_glyphs && !glyphs.attributes[glyph_pos].clusterStart)
+ while (glyph_pos < scriptItem->num_glyphs && !glyphs.attributes[glyph_pos].clusterStart)
glyph_pos++;
}
- bool reverse = si->analysis.bidiLevel % 2;
+ bool reverse = scriptItem->analysis.bidiLevel % 2;
// add the items left of the cursor
@@ -2824,32 +3008,32 @@ qreal QTextLine::cursorToX(int *cursorPos, Edge edge) const
}
}
- logClusters = eng->logClusters(si);
- glyphs = eng->shapedGlyphs(si);
- if (si->analysis.flags >= QScriptAnalysis::TabOrObject) {
+ logClusters = eng->logClusters(scriptItem);
+ glyphs = eng->shapedGlyphs(scriptItem);
+ if (scriptItem->analysis.flags >= QScriptAnalysis::TabOrObject) {
if (pos == (reverse ? 0 : l))
- x += si->width;
+ x += scriptItem->width;
} else {
bool rtl = eng->isRightToLeft();
bool visual = eng->visualCursorMovement();
- int end = qMin(lineEnd, si->position + l) - si->position;
+ int end = qMin(lineEnd, scriptItem->position + l) - scriptItem->position;
if (reverse) {
- int glyph_end = end == l ? si->num_glyphs : logClusters[end];
+ int glyph_end = end == l ? scriptItem->num_glyphs : logClusters[end];
int glyph_start = glyph_pos;
if (visual && !rtl && !(lastLine && itm == (visualOrder[nItems - 1] + firstItem)))
glyph_start++;
for (int i = glyph_end - 1; i >= glyph_start; i--)
x += glyphs.effectiveAdvance(i);
- x -= eng->offsetInLigature(si, pos, end, glyph_pos);
+ x -= eng->offsetInLigature(scriptItem, pos, end, glyph_pos);
} else {
- int start = qMax(line.from - si->position, 0);
+ int start = qMax(line.from - scriptItem->position, 0);
int glyph_start = logClusters[start];
int glyph_end = glyph_pos;
if (!visual || !rtl || (lastLine && itm == visualOrder[0] + firstItem))
glyph_end--;
for (int i = glyph_start; i <= glyph_end; i++)
x += glyphs.effectiveAdvance(i);
- x += eng->offsetInLigature(si, pos, end, glyph_pos);
+ x += eng->offsetInLigature(scriptItem, pos, end, glyph_pos);
}
}
@@ -2858,7 +3042,7 @@ qreal QTextLine::cursorToX(int *cursorPos, Edge edge) const
if (eng->option.wrapMode() != QTextOption::NoWrap && x < 0)
x = 0;
- *cursorPos = pos + si->position;
+ *cursorPos = pos + scriptItem->position;
return x.toReal();
}
@@ -2906,18 +3090,10 @@ int QTextLine::xToCursor(qreal _x, CursorPosition cpos) const
bool visual = eng->visualCursorMovement();
if (x <= 0) {
// left of first item
- int item = visualOrder[0]+firstItem;
- QScriptItem &si = eng->layoutData->items[item];
- if (!si.num_glyphs)
- eng->shape(item);
- int pos = si.position;
- if (si.analysis.bidiLevel % 2)
- pos += eng->length(item);
- pos = qMax(line.from, pos);
- pos = qMin(line.from + line_length, pos);
- return pos;
- } else if (x < line.textWidth
- || (line.justified && x < line.width)) {
+ if (eng->isRightToLeft())
+ return line.from + line_length;
+ return line.from;
+ } else if (x < line.textWidth || (line.justified && x < line.width)) {
// has to be in one of the runs
QFixed pos;
bool rtl = eng->isRightToLeft();
@@ -3067,26 +3243,17 @@ int QTextLine::xToCursor(qreal _x, CursorPosition cpos) const
}
}
// right of last item
-// qDebug("right of last");
- int item = visualOrder[nItems-1]+firstItem;
- QScriptItem &si = eng->layoutData->items[item];
- if (!si.num_glyphs)
- eng->shape(item);
- int pos = si.position;
- if (!(si.analysis.bidiLevel % 2))
- pos += eng->length(item);
- pos = qMax(line.from, pos);
-
- int maxPos = line.from + line_length;
+ int pos = line.from;
+ if (!eng->isRightToLeft())
+ pos += line_length;
// except for the last line we assume that the
// character between lines is a space and we want
// to position the cursor to the left of that
// character.
- if (this->index < eng->lines.count() - 1)
- maxPos = eng->previousLogicalPosition(maxPos);
+ if (index < eng->lines.size() - 1)
+ pos = qMin(eng->previousLogicalPosition(pos), pos);
- pos = qMin(pos, maxPos);
return pos;
}
diff --git a/src/gui/text/qtextlayout.h b/src/gui/text/qtextlayout.h
index 16dde50f05..5b1b64d7ee 100644
--- a/src/gui/text/qtextlayout.h
+++ b/src/gui/text/qtextlayout.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 QTEXTLAYOUT_H
#define QTEXTLAYOUT_H
@@ -105,22 +69,21 @@ class QTextOption;
class Q_GUI_EXPORT QTextLayout
{
public:
+ enum GlyphRunRetrievalFlag : quint16 {
+ RetrieveGlyphIndexes = 0x1,
+ RetrieveGlyphPositions = 0x2,
+ RetrieveStringIndexes = 0x4,
+ RetrieveString = 0x8,
+
+ DefaultRetrievalFlags = RetrieveGlyphIndexes | RetrieveGlyphPositions,
+ RetrieveAll = 0xffff
+ };
+ Q_DECLARE_FLAGS(GlyphRunRetrievalFlags, GlyphRunRetrievalFlag)
+
// does itemization
QTextLayout();
QTextLayout(const QString& text);
-#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
- QTextLayout(const QString &text, const QFont &font, QPaintDevice *paintdevice = nullptr);
-#ifndef Q_QDOC
- // the template is necessary to make QTextLayout(font,text,nullptr) and QTextLayout(font,text,NULL)
- // not ambiguous. Implementation detail that should not be documented.
- template<char = 0>
-#endif
- QTextLayout(const QString &textData, const QFont &textFont, const QPaintDevice *paintdevice)
- : QTextLayout(textData, textFont, const_cast<QPaintDevice*>(paintdevice))
- {}
-#else
QTextLayout(const QString &text, const QFont &font, const QPaintDevice *paintdevice = nullptr);
-#endif
QTextLayout(const QTextBlock &b);
~QTextLayout();
@@ -196,7 +159,15 @@ public:
qreal maximumWidth() const;
#if !defined(QT_NO_RAWFONT)
+
+# if QT_VERSION < QT_VERSION_CHECK(7, 0, 0)
+ QList<QGlyphRun> glyphRuns(int from, int length, GlyphRunRetrievalFlags flags) const;
QList<QGlyphRun> glyphRuns(int from = -1, int length = -1) const;
+# else
+ QList<QGlyphRun> glyphRuns(int from = -1,
+ int length = -1,
+ GlyphRunRetrievalFlags flags = DefaultRetrievalFlags) const;
+# endif
#endif
QTextEngine *engine() const { return d; }
@@ -214,7 +185,7 @@ private:
QTextEngine *d;
};
Q_DECLARE_TYPEINFO(QTextLayout::FormatRange, Q_RELOCATABLE_TYPE);
-
+Q_DECLARE_OPERATORS_FOR_FLAGS(QTextLayout::GlyphRunRetrievalFlags)
class Q_GUI_EXPORT QTextLine
{
@@ -267,7 +238,14 @@ public:
void draw(QPainter *painter, const QPointF &position) const;
#if !defined(QT_NO_RAWFONT)
+# if QT_VERSION < QT_VERSION_CHECK(7, 0, 0)
+ QList<QGlyphRun> glyphRuns(int from, int length, QTextLayout::GlyphRunRetrievalFlags flags) const;
QList<QGlyphRun> glyphRuns(int from = -1, int length = -1) const;
+# else
+ QList<QGlyphRun> glyphRuns(int from = -1,
+ int length = -1,
+ QTextLayout::GlyphRunRetrievalFlags flags = QTextLayout::DefaultRetrievalFlags) const;
+# endif
#endif
private:
diff --git a/src/gui/text/qtextlist.cpp b/src/gui/text/qtextlist.cpp
index c9bbe687b2..7ec8b6215e 100644
--- a/src/gui/text/qtextlist.cpp
+++ b/src/gui/text/qtextlist.cpp
@@ -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 "qtextlist.h"
@@ -46,6 +10,8 @@
QT_BEGIN_NAMESPACE
+using namespace Qt::StringLiterals;
+
class QTextListPrivate : public QTextBlockGroupPrivate
{
public:
@@ -120,7 +86,7 @@ QTextList::~QTextList()
int QTextList::count() const
{
Q_D(const QTextList);
- return d->blocks.count();
+ return d->blocks.size();
}
/*!
@@ -179,7 +145,10 @@ QString QTextList::itemText(const QTextBlock &blockIt) const
const int style = format().style();
QString numberPrefix;
- QString numberSuffix = QLatin1String(".");
+ QString numberSuffix = u"."_s;
+
+ // the number of the item might be offset by start, which defaults to 1
+ const int itemNumber = item + format().start() - 1;
if (format().hasProperty(QTextFormat::ListNumberPrefix))
numberPrefix = format().numberPrefix();
@@ -188,15 +157,21 @@ QString QTextList::itemText(const QTextBlock &blockIt) const
switch (style) {
case QTextListFormat::ListDecimal:
- result = QString::number(item);
+ result = QString::number(itemNumber);
break;
// from the old richtext
case QTextListFormat::ListLowerAlpha:
case QTextListFormat::ListUpperAlpha:
{
+ // match the html default behavior of falling back to decimal numbers
+ if (itemNumber < 1) {
+ result = QString::number(itemNumber);
+ break;
+ }
+
const char baseChar = style == QTextListFormat::ListUpperAlpha ? 'A' : 'a';
- int c = item;
+ int c = itemNumber;
while (c > 0) {
c--;
result.prepend(QChar::fromUcs2(baseChar + (c % 26)));
@@ -207,20 +182,21 @@ QString QTextList::itemText(const QTextBlock &blockIt) const
case QTextListFormat::ListLowerRoman:
case QTextListFormat::ListUpperRoman:
{
- if (item < 5000) {
- QByteArray romanNumeral;
+ // match the html default behavior of falling back to decimal numbers
+ if (itemNumber < 1) {
+ result = QString::number(itemNumber);
+ } else if (itemNumber < 5000) {
+ QString romanNumeral;
// works for up to 4999 items
- static const char romanSymbolsLower[] = "iiivixxxlxcccdcmmmm";
- static const char romanSymbolsUpper[] = "IIIVIXXXLXCCCDCMMMM";
- QByteArray romanSymbols; // wrap to have "mid"
+ QLatin1StringView romanSymbols;
if (style == QTextListFormat::ListLowerRoman)
- romanSymbols = QByteArray::fromRawData(romanSymbolsLower, sizeof(romanSymbolsLower));
+ romanSymbols = "iiivixxxlxcccdcmmmm"_L1;
else
- romanSymbols = QByteArray::fromRawData(romanSymbolsUpper, sizeof(romanSymbolsUpper));
+ romanSymbols = "IIIVIXXXLXCCCDCMMMM"_L1;
int c[] = { 1, 4, 5, 9, 10, 40, 50, 90, 100, 400, 500, 900, 1000 };
- int n = item;
+ int n = itemNumber;
for (int i = 12; i >= 0; n %= c[i], i--) {
int q = n / c[i];
if (q > 0) {
@@ -242,13 +218,12 @@ QString QTextList::itemText(const QTextBlock &blockIt) const
numDigits = q;
}
- romanNumeral.append(romanSymbols.mid(startDigit, numDigits));
+ romanNumeral.append(romanSymbols.sliced(startDigit, numDigits));
}
}
- result = QString::fromLatin1(romanNumeral);
- }
- else {
- result = QLatin1String("?");
+ result = std::move(romanNumeral);
+ } else {
+ result = u"?"_s;
}
}
@@ -306,3 +281,5 @@ void QTextList::add(const QTextBlock &block)
}
QT_END_NAMESPACE
+
+#include "moc_qtextlist.cpp"
diff --git a/src/gui/text/qtextlist.h b/src/gui/text/qtextlist.h
index fa945c31fd..025213c2f9 100644
--- a/src/gui/text/qtextlist.h
+++ b/src/gui/text/qtextlist.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 QTEXTLIST_H
#define QTEXTLIST_H
diff --git a/src/gui/text/qtextmarkdownimporter.cpp b/src/gui/text/qtextmarkdownimporter.cpp
index 97bdef50d7..e7fcad67b5 100644
--- a/src/gui/text/qtextmarkdownimporter.cpp
+++ b/src/gui/text/qtextmarkdownimporter.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2019 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) 2019 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 "qtextmarkdownimporter_p.h"
#include "qtextdocumentfragment_p.h"
@@ -48,17 +12,26 @@
#include <QTextDocumentFragment>
#include <QTextList>
#include <QTextTable>
+#if QT_CONFIG(system_textmarkdownreader)
+#include <md4c.h>
+#else
#include "../../3rdparty/md4c/md4c.h"
+#endif
QT_BEGIN_NAMESPACE
+using namespace Qt::StringLiterals;
+
Q_LOGGING_CATEGORY(lcMD, "qt.text.markdown")
-static const QChar Newline = QLatin1Char('\n');
-static const QChar Space = QLatin1Char(' ');
+static const QChar qtmi_Newline = u'\n';
+static const QChar qtmi_Space = u' ';
+
+static constexpr auto markerString() noexcept { return "---"_L1; }
// TODO maybe eliminate the margins after all views recognize BlockQuoteLevel, CSS can format it, etc.
-static const int BlockQuoteIndent = 40; // pixels, same as in QTextHtmlParserNode::initializeProperties
+static const int qtmi_BlockQuoteIndent =
+ 40; // pixels, same as in QTextHtmlParserNode::initializeProperties
static_assert(int(QTextMarkdownImporter::FeatureCollapseWhitespace) == MD_FLAG_COLLAPSEWHITESPACE);
static_assert(int(QTextMarkdownImporter::FeaturePermissiveATXHeaders) == MD_FLAG_PERMISSIVEATXHEADERS);
@@ -69,12 +42,14 @@ static_assert(int(QTextMarkdownImporter::FeatureNoHTMLBlocks) == MD_FLAG_NOHTMLB
static_assert(int(QTextMarkdownImporter::FeatureNoHTMLSpans) == MD_FLAG_NOHTMLSPANS);
static_assert(int(QTextMarkdownImporter::FeatureTables) == MD_FLAG_TABLES);
static_assert(int(QTextMarkdownImporter::FeatureStrikeThrough) == MD_FLAG_STRIKETHROUGH);
+static_assert(int(QTextMarkdownImporter::FeatureUnderline) == MD_FLAG_UNDERLINE);
static_assert(int(QTextMarkdownImporter::FeaturePermissiveWWWAutoLinks) == MD_FLAG_PERMISSIVEWWWAUTOLINKS);
static_assert(int(QTextMarkdownImporter::FeaturePermissiveAutoLinks) == MD_FLAG_PERMISSIVEAUTOLINKS);
static_assert(int(QTextMarkdownImporter::FeatureTasklists) == MD_FLAG_TASKLISTS);
static_assert(int(QTextMarkdownImporter::FeatureNoHTML) == MD_FLAG_NOHTML);
static_assert(int(QTextMarkdownImporter::DialectCommonMark) == MD_DIALECT_COMMONMARK);
-static_assert(int(QTextMarkdownImporter::DialectGitHub) == MD_DIALECT_GITHUB);
+static_assert(int(QTextMarkdownImporter::DialectGitHub) ==
+ (MD_DIALECT_GITHUB | MD_FLAG_UNDERLINE | QTextMarkdownImporter::FeatureFrontMatter));
// --------------------------------------------------------
// MD4C callback function wrappers
@@ -132,18 +107,19 @@ static Qt::Alignment MdAlignment(MD_ALIGN a, Qt::Alignment defaultAlignment = Qt
}
}
-QTextMarkdownImporter::QTextMarkdownImporter(QTextMarkdownImporter::Features features)
- : m_monoFont(QFontDatabase::systemFont(QFontDatabase::FixedFont))
+QTextMarkdownImporter::QTextMarkdownImporter(QTextDocument *doc, QTextMarkdownImporter::Features features)
+ : m_cursor(doc)
+ , m_monoFont(QFontDatabase::systemFont(QFontDatabase::FixedFont))
, m_features(features)
{
}
-QTextMarkdownImporter::QTextMarkdownImporter(QTextDocument::MarkdownFeatures features)
- : QTextMarkdownImporter(static_cast<QTextMarkdownImporter::Features>(int(features)))
+QTextMarkdownImporter::QTextMarkdownImporter(QTextDocument *doc, QTextDocument::MarkdownFeatures features)
+ : QTextMarkdownImporter(doc, static_cast<QTextMarkdownImporter::Features>(int(features)))
{
}
-void QTextMarkdownImporter::import(QTextDocument *doc, const QString &markdown)
+void QTextMarkdownImporter::import(const QString &markdown)
{
MD_PARSER callbacks = {
0, // abi_version
@@ -156,19 +132,36 @@ void QTextMarkdownImporter::import(QTextDocument *doc, const QString &markdown)
&CbDebugLog,
nullptr // syntax
};
- m_doc = doc;
- m_paragraphMargin = m_doc->defaultFont().pointSize() * 2 / 3;
- m_cursor = new QTextCursor(doc);
+ QTextDocument *doc = m_cursor.document();
+ const auto defaultFont = doc->defaultFont();
+ m_paragraphMargin = defaultFont.pointSize() * 2 / 3;
doc->clear();
- if (doc->defaultFont().pointSize() != -1)
- m_monoFont.setPointSize(doc->defaultFont().pointSize());
+ if (defaultFont.pointSize() != -1)
+ m_monoFont.setPointSize(defaultFont.pointSize());
else
- m_monoFont.setPixelSize(doc->defaultFont().pixelSize());
- qCDebug(lcMD) << "default font" << doc->defaultFont() << "mono font" << m_monoFont;
- QByteArray md = markdown.toUtf8();
- md_parse(md.constData(), MD_SIZE(md.size()), &callbacks, this);
- delete m_cursor;
- m_cursor = nullptr;
+ m_monoFont.setPixelSize(defaultFont.pixelSize());
+ qCDebug(lcMD) << "default font" << defaultFont << "mono font" << m_monoFont;
+ QStringView md = markdown;
+
+ if (m_features.testFlag(QTextMarkdownImporter::FeatureFrontMatter) && md.startsWith(markerString())) {
+ qsizetype endMarkerPos = md.indexOf(markerString(), markerString().size() + 1);
+ if (endMarkerPos > 4) {
+ qsizetype firstLinePos = 4; // first line of yaml
+ while (md.at(firstLinePos) == '\n'_L1 || md.at(firstLinePos) == '\r'_L1)
+ ++firstLinePos;
+ auto frontMatter = md.sliced(firstLinePos, endMarkerPos - firstLinePos);
+ firstLinePos = endMarkerPos + 4; // first line of markdown after yaml
+ while (md.size() > firstLinePos && (md.at(firstLinePos) == '\n'_L1 || md.at(firstLinePos) == '\r'_L1))
+ ++firstLinePos;
+ md = md.sliced(firstLinePos);
+ doc->setMetaInformation(QTextDocument::FrontMatter, frontMatter.toString());
+ qCDebug(lcMD) << "extracted FrontMatter: size" << frontMatter.size();
+ }
+ }
+ const auto mdUtf8 = md.toUtf8();
+ m_cursor.beginEditBlock();
+ md_parse(mdUtf8.constData(), MD_SIZE(mdUtf8.size()), &callbacks, this);
+ m_cursor.endEditBlock();
}
int QTextMarkdownImporter::cbEnterBlock(int blockType, void *det)
@@ -177,7 +170,7 @@ int QTextMarkdownImporter::cbEnterBlock(int blockType, void *det)
switch (blockType) {
case MD_BLOCK_P:
if (!m_listStack.isEmpty())
- qCDebug(lcMD, m_listItem ? "P of LI at level %d" : "P continuation inside LI at level %d", int(m_listStack.count()));
+ qCDebug(lcMD, m_listItem ? "P of LI at level %d" : "P continuation inside LI at level %d", int(m_listStack.size()));
else
qCDebug(lcMD, "P");
m_needsInsertBlock = true;
@@ -189,9 +182,9 @@ int QTextMarkdownImporter::cbEnterBlock(int blockType, void *det)
case MD_BLOCK_CODE: {
MD_BLOCK_CODE_DETAIL *detail = static_cast<MD_BLOCK_CODE_DETAIL *>(det);
m_codeBlock = true;
- m_blockCodeLanguage = QLatin1String(detail->lang.text, int(detail->lang.size));
+ m_blockCodeLanguage = QLatin1StringView(detail->lang.text, int(detail->lang.size));
m_blockCodeFence = detail->fence_char;
- QString info = QLatin1String(detail->info.text, int(detail->info.size));
+ QString info = QLatin1StringView(detail->info.text, int(detail->info.size));
m_needsInsertBlock = true;
if (m_blockQuoteDepth)
qCDebug(lcMD, "CODE lang '%s' info '%s' fenced with '%c' inside QUOTE %d", qPrintable(m_blockCodeLanguage), qPrintable(info), m_blockCodeFence, m_blockQuoteDepth);
@@ -207,11 +200,11 @@ int QTextMarkdownImporter::cbEnterBlock(int blockType, void *det)
charFmt.setFontWeight(QFont::Bold);
blockFmt.setHeadingLevel(int(detail->level));
m_needsInsertBlock = false;
- if (m_doc->isEmpty()) {
- m_cursor->setBlockFormat(blockFmt);
- m_cursor->setCharFormat(charFmt);
+ if (m_cursor.document()->isEmpty()) {
+ m_cursor.setBlockFormat(blockFmt);
+ m_cursor.setCharFormat(charFmt);
} else {
- m_cursor->insertBlock(blockFmt, charFmt);
+ m_cursor.insertBlock(blockFmt, charFmt);
}
qCDebug(lcMD, "H%d", detail->level);
} break;
@@ -226,12 +219,12 @@ int QTextMarkdownImporter::cbEnterBlock(int blockType, void *det)
} break;
case MD_BLOCK_UL: {
if (m_needsInsertList) // list nested in an empty list
- m_listStack.push(m_cursor->insertList(m_listFormat));
+ m_listStack.push(m_cursor.insertList(m_listFormat));
else
m_needsInsertList = true;
MD_BLOCK_UL_DETAIL *detail = static_cast<MD_BLOCK_UL_DETAIL *>(det);
m_listFormat = QTextListFormat();
- m_listFormat.setIndent(m_listStack.count() + 1);
+ m_listFormat.setIndent(m_listStack.size() + 1);
switch (detail->mark) {
case '*':
m_listFormat.setStyle(QTextListFormat::ListCircle);
@@ -243,19 +236,20 @@ int QTextMarkdownImporter::cbEnterBlock(int blockType, void *det)
m_listFormat.setStyle(QTextListFormat::ListDisc);
break;
}
- qCDebug(lcMD, "UL %c level %d", detail->mark, int(m_listStack.count()) + 1);
+ qCDebug(lcMD, "UL %c level %d", detail->mark, int(m_listStack.size()) + 1);
} break;
case MD_BLOCK_OL: {
if (m_needsInsertList) // list nested in an empty list
- m_listStack.push(m_cursor->insertList(m_listFormat));
+ m_listStack.push(m_cursor.insertList(m_listFormat));
else
m_needsInsertList = true;
MD_BLOCK_OL_DETAIL *detail = static_cast<MD_BLOCK_OL_DETAIL *>(det);
m_listFormat = QTextListFormat();
- m_listFormat.setIndent(m_listStack.count() + 1);
+ m_listFormat.setIndent(m_listStack.size() + 1);
m_listFormat.setNumberSuffix(QChar::fromLatin1(detail->mark_delimiter));
m_listFormat.setStyle(QTextListFormat::ListDecimal);
- qCDebug(lcMD, "OL xx%d level %d", detail->mark_delimiter, int(m_listStack.count()) + 1);
+ m_listFormat.setStart(detail->start);
+ qCDebug(lcMD, "OL xx%d level %d start %d", detail->mark_delimiter, int(m_listStack.size()) + 1, detail->start);
} break;
case MD_BLOCK_TD: {
MD_BLOCK_TD_DETAIL *detail = static_cast<MD_BLOCK_TD_DETAIL *>(det);
@@ -267,10 +261,10 @@ int QTextMarkdownImporter::cbEnterBlock(int blockType, void *det)
qWarning("malformed table in Markdown input");
return 1;
}
- *m_cursor = cell.firstCursorPosition();
- QTextBlockFormat blockFmt = m_cursor->blockFormat();
+ m_cursor = cell.firstCursorPosition();
+ QTextBlockFormat blockFmt = m_cursor.blockFormat();
blockFmt.setAlignment(MdAlignment(detail->align));
- m_cursor->setBlockFormat(blockFmt);
+ m_cursor.setBlockFormat(blockFmt);
qCDebug(lcMD) << "TD; align" << detail->align << MdAlignment(detail->align) << "col" << m_tableCol;
} break;
case MD_BLOCK_TH: {
@@ -298,13 +292,13 @@ int QTextMarkdownImporter::cbEnterBlock(int blockType, void *det)
case MD_BLOCK_TABLE:
m_tableColumnCount = 0;
m_tableRowCount = 0;
- m_currentTable = m_cursor->insertTable(1, 1); // we don't know the dimensions yet
+ m_currentTable = m_cursor.insertTable(1, 1); // we don't know the dimensions yet
break;
case MD_BLOCK_HR: {
qCDebug(lcMD, "HR");
QTextBlockFormat blockFmt;
blockFmt.setProperty(QTextFormat::BlockTrailingHorizontalRulerWidth, 1);
- m_cursor->insertBlock(blockFmt, QTextCharFormat());
+ m_cursor.insertBlock(blockFmt, QTextCharFormat());
} break;
default:
break; // nothing to do for now
@@ -322,11 +316,11 @@ int QTextMarkdownImporter::cbLeaveBlock(int blockType, void *detail)
case MD_BLOCK_UL:
case MD_BLOCK_OL:
if (Q_UNLIKELY(m_needsInsertList))
- m_listStack.push(m_cursor->createList(m_listFormat));
+ m_listStack.push(m_cursor.createList(m_listFormat));
if (Q_UNLIKELY(m_listStack.isEmpty())) {
qCWarning(lcMD, "list ended unexpectedly");
} else {
- qCDebug(lcMD, "list at level %d ended", int(m_listStack.count()));
+ qCDebug(lcMD, "list at level %d ended", int(m_listStack.size()));
m_listStack.pop();
}
break;
@@ -360,10 +354,10 @@ int QTextMarkdownImporter::cbLeaveBlock(int blockType, void *detail)
case MD_BLOCK_TABLE:
qCDebug(lcMD) << "table ended with" << m_currentTable->columns() << "cols and" << m_currentTable->rows() << "rows";
m_currentTable = nullptr;
- m_cursor->movePosition(QTextCursor::End);
+ m_cursor.movePosition(QTextCursor::End);
break;
case MD_BLOCK_LI:
- qCDebug(lcMD, "LI at level %d ended", int(m_listStack.count()));
+ qCDebug(lcMD, "LI at level %d ended", int(m_listStack.size()));
m_listItem = false;
break;
case MD_BLOCK_CODE: {
@@ -377,7 +371,7 @@ int QTextMarkdownImporter::cbLeaveBlock(int blockType, void *detail)
m_needsInsertBlock = true;
} break;
case MD_BLOCK_H:
- m_cursor->setCharFormat(QTextCharFormat());
+ m_cursor.setCharFormat(QTextCharFormat());
break;
default:
break;
@@ -397,6 +391,9 @@ int QTextMarkdownImporter::cbEnterSpan(int spanType, void *det)
case MD_SPAN_STRONG:
charFmt.setFontWeight(QFont::Bold);
break;
+ case MD_SPAN_U:
+ charFmt.setFontUnderline(true);
+ break;
case MD_SPAN_A: {
MD_SPAN_A_DETAIL *detail = static_cast<MD_SPAN_A_DETAIL *>(det);
QString url = QString::fromUtf8(detail->href.text, int(detail->href.size));
@@ -418,15 +415,17 @@ int QTextMarkdownImporter::cbEnterSpan(int spanType, void *det)
}
case MD_SPAN_CODE:
charFmt.setFont(m_monoFont);
+ charFmt.setFontFixedPitch(true);
break;
case MD_SPAN_DEL:
charFmt.setFontStrikeOut(true);
break;
}
m_spanFormatStack.push(charFmt);
- qCDebug(lcMD) << spanType << "setCharFormat" << charFmt.font().family() << charFmt.fontWeight()
- << (charFmt.fontItalic() ? "italic" : "") << charFmt.foreground().color().name();
- m_cursor->setCharFormat(charFmt);
+ qCDebug(lcMD) << spanType << "setCharFormat" << charFmt.font().families().constFirst()
+ << charFmt.fontWeight() << (charFmt.fontItalic() ? "italic" : "")
+ << charFmt.foreground().color().name();
+ m_cursor.setCharFormat(charFmt);
return 0; // no error
}
@@ -439,9 +438,10 @@ int QTextMarkdownImporter::cbLeaveSpan(int spanType, void *detail)
if (!m_spanFormatStack.isEmpty())
charFmt = m_spanFormatStack.top();
}
- m_cursor->setCharFormat(charFmt);
- qCDebug(lcMD) << spanType << "setCharFormat" << charFmt.font().family() << charFmt.fontWeight()
- << (charFmt.fontItalic() ? "italic" : "") << charFmt.foreground().color().name();
+ m_cursor.setCharFormat(charFmt);
+ qCDebug(lcMD) << spanType << "setCharFormat" << charFmt.font().families().constFirst()
+ << charFmt.fontWeight() << (charFmt.fontItalic() ? "italic" : "")
+ << charFmt.foreground().color().name();
if (spanType == int(MD_SPAN_IMG))
m_imageSpan = false;
return 0; // no error
@@ -470,17 +470,20 @@ int QTextMarkdownImporter::cbText(int textType, const char *text, unsigned size)
s = QString(QChar(u'\xFFFD')); // CommonMark-required replacement for null
break;
case MD_TEXT_BR:
- s = QString(Newline);
+ s = QString(qtmi_Newline);
break;
case MD_TEXT_SOFTBR:
- s = QString(Space);
+ s = QString(qtmi_Space);
break;
case MD_TEXT_CODE:
// We'll see MD_SPAN_CODE too, which will set the char format, and that's enough.
break;
#if QT_CONFIG(texthtmlparser)
case MD_TEXT_ENTITY:
- m_cursor->insertHtml(s);
+ if (m_htmlTagDepth)
+ m_htmlAccumulator += s;
+ else
+ m_cursor.insertHtml(s);
s = QString();
break;
#endif
@@ -502,11 +505,11 @@ int QTextMarkdownImporter::cbText(int textType, const char *text, unsigned size)
m_htmlAccumulator += s;
if (!m_htmlTagDepth) { // all open tags are now closed
qCDebug(lcMD) << "HTML" << m_htmlAccumulator;
- m_cursor->insertHtml(m_htmlAccumulator);
+ m_cursor.insertHtml(m_htmlAccumulator);
if (m_spanFormatStack.isEmpty())
- m_cursor->setCharFormat(QTextCharFormat());
+ m_cursor.setCharFormat(QTextCharFormat());
else
- m_cursor->setCharFormat(m_spanFormatStack.top());
+ m_cursor.setCharFormat(m_spanFormatStack.top());
m_htmlAccumulator = QString();
}
#endif
@@ -518,6 +521,14 @@ int QTextMarkdownImporter::cbText(int textType, const char *text, unsigned size)
case MD_BLOCK_TD:
m_nonEmptyTableCells.append(m_tableCol);
break;
+ case MD_BLOCK_CODE:
+ if (s == qtmi_Newline) {
+ // defer a blank line until we see something else in the code block,
+ // to avoid ending every code block with a gratuitous blank line
+ m_needsInsertBlock = true;
+ s = QString();
+ }
+ break;
default:
break;
}
@@ -528,35 +539,33 @@ int QTextMarkdownImporter::cbText(int textType, const char *text, unsigned size)
m_imageFormat.setProperty(QTextFormat::ImageAltText, s);
qCDebug(lcMD) << "image" << m_imageFormat.name()
<< "title" << m_imageFormat.stringProperty(QTextFormat::ImageTitle)
- << "alt" << s << "relative to" << m_doc->baseUrl();
- m_cursor->insertImage(m_imageFormat);
+ << "alt" << s << "relative to" << m_cursor.document()->baseUrl();
+ m_cursor.insertImage(m_imageFormat);
return 0; // no error
}
if (!s.isEmpty())
- m_cursor->insertText(s);
- if (m_cursor->currentList()) {
+ m_cursor.insertText(s);
+ if (m_cursor.currentList()) {
// The list item will indent the list item's text, so we don't need indentation on the block.
- QTextBlockFormat bfmt = m_cursor->blockFormat();
+ QTextBlockFormat bfmt = m_cursor.blockFormat();
bfmt.setIndent(0);
- m_cursor->setBlockFormat(bfmt);
+ m_cursor.setBlockFormat(bfmt);
}
if (lcMD().isEnabled(QtDebugMsg)) {
- QTextBlockFormat bfmt = m_cursor->blockFormat();
+ QTextBlockFormat bfmt = m_cursor.blockFormat();
QString debugInfo;
- if (m_cursor->currentList())
- debugInfo = QLatin1String("in list at depth ") + QString::number(m_cursor->currentList()->format().indent());
+ if (m_cursor.currentList())
+ debugInfo = "in list at depth "_L1 + QString::number(m_cursor.currentList()->format().indent());
if (bfmt.hasProperty(QTextFormat::BlockQuoteLevel))
- debugInfo += QLatin1String("in blockquote at depth ") +
+ debugInfo += "in blockquote at depth "_L1 +
QString::number(bfmt.intProperty(QTextFormat::BlockQuoteLevel));
if (bfmt.hasProperty(QTextFormat::BlockCodeLanguage))
- debugInfo += QLatin1String("in a code block");
+ debugInfo += "in a code block"_L1;
qCDebug(lcMD) << textType << "in block" << m_blockType << s << qPrintable(debugInfo)
<< "bindent" << bfmt.indent() << "tindent" << bfmt.textIndent()
<< "margins" << bfmt.leftMargin() << bfmt.topMargin() << bfmt.bottomMargin() << bfmt.rightMargin();
}
- qCDebug(lcMD) << textType << "in block" << m_blockType << s << "in list?" << m_cursor->currentList()
- << "indent" << m_cursor->blockFormat().indent();
return 0; // no error
}
@@ -564,7 +573,7 @@ int QTextMarkdownImporter::cbText(int textType, const char *text, unsigned size)
Insert a new block based on stored state.
m_cursor cannot store the state for the _next_ block ahead of time, because
- m_cursor->setBlockFormat() controls the format of the block that the cursor
+ m_cursor.setBlockFormat() controls the format of the block that the cursor
is already in; so cbLeaveBlock() cannot call setBlockFormat() without
altering the block that was just added. Therefore cbLeaveBlock() and the
following cbEnterBlock() set variables to remember what formatting should
@@ -586,13 +595,15 @@ void QTextMarkdownImporter::insertBlock()
}
if (m_blockQuoteDepth) {
blockFormat.setProperty(QTextFormat::BlockQuoteLevel, m_blockQuoteDepth);
- blockFormat.setLeftMargin(BlockQuoteIndent * m_blockQuoteDepth);
- blockFormat.setRightMargin(BlockQuoteIndent);
+ blockFormat.setLeftMargin(qtmi_BlockQuoteIndent * m_blockQuoteDepth);
+ blockFormat.setRightMargin(qtmi_BlockQuoteIndent);
}
if (m_codeBlock) {
blockFormat.setProperty(QTextFormat::BlockCodeLanguage, m_blockCodeLanguage);
- if (m_blockCodeFence)
+ if (m_blockCodeFence) {
+ blockFormat.setNonBreakableLines(true);
blockFormat.setProperty(QTextFormat::BlockCodeFence, QString(QLatin1Char(m_blockCodeFence)));
+ }
charFormat.setFont(m_monoFont);
} else {
blockFormat.setTopMargin(m_paragraphMargin);
@@ -603,17 +614,20 @@ void QTextMarkdownImporter::insertBlock()
else
blockFormat.setMarker(m_markerType);
if (!m_listStack.isEmpty())
- blockFormat.setIndent(m_listStack.count());
- if (m_doc->isEmpty()) {
- m_cursor->setBlockFormat(blockFormat);
- m_cursor->setCharFormat(charFormat);
+ blockFormat.setIndent(m_listStack.size());
+ if (m_cursor.document()->isEmpty()) {
+ m_cursor.setBlockFormat(blockFormat);
+ m_cursor.setCharFormat(charFormat);
+ } else if (m_listItem) {
+ m_cursor.insertBlock(blockFormat, QTextCharFormat());
+ m_cursor.setCharFormat(charFormat);
} else {
- m_cursor->insertBlock(blockFormat, charFormat);
+ m_cursor.insertBlock(blockFormat, charFormat);
}
if (m_needsInsertList) {
- m_listStack.push(m_cursor->createList(m_listFormat));
+ m_listStack.push(m_cursor.createList(m_listFormat));
} else if (!m_listStack.isEmpty() && m_listItem && m_listStack.top()) {
- m_listStack.top()->add(m_cursor->block());
+ m_listStack.top()->add(m_cursor.block());
}
m_needsInsertList = false;
m_needsInsertBlock = false;
diff --git a/src/gui/text/qtextmarkdownimporter_p.h b/src/gui/text/qtextmarkdownimporter_p.h
index c05ac68820..8b8f4ec9bb 100644
--- a/src/gui/text/qtextmarkdownimporter_p.h
+++ b/src/gui/text/qtextmarkdownimporter_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2019 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) 2019 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 QTEXTMARKDOWNIMPORTER_H
#define QTEXTMARKDOWNIMPORTER_H
@@ -58,6 +22,7 @@
#include <QtGui/qtextlist.h>
#include <QtCore/qpointer.h>
#include <QtCore/qstack.h>
+#include <QtCore/private/qglobal_p.h>
QT_BEGIN_NAMESPACE
@@ -80,6 +45,8 @@ public:
FeatureStrikeThrough = 0x0200,
FeaturePermissiveWWWAutoLinks = 0x0400,
FeatureTasklists = 0x0800,
+ FeatureUnderline = 0x4000,
+ FeatureFrontMatter = 0x100000, // Qt feature, not yet in MD4C
// composite flags
FeaturePermissiveAutoLinks = FeaturePermissiveMailAutoLinks
| FeaturePermissiveURLAutoLinks | FeaturePermissiveWWWAutoLinks,
@@ -89,10 +56,10 @@ public:
};
Q_DECLARE_FLAGS(Features, Feature)
- QTextMarkdownImporter(Features features);
- QTextMarkdownImporter(QTextDocument::MarkdownFeatures features);
+ QTextMarkdownImporter(QTextDocument *doc, Features features);
+ QTextMarkdownImporter(QTextDocument *doc, QTextDocument::MarkdownFeatures features);
- void import(QTextDocument *doc, const QString &markdown);
+ void import(const QString &markdown);
public:
// MD4C callbacks
@@ -106,8 +73,7 @@ private:
void insertBlock();
private:
- QTextDocument *m_doc = nullptr;
- QTextCursor *m_cursor = nullptr;
+ QTextCursor m_cursor;
QTextTable *m_currentTable = nullptr; // because m_cursor->currentTable() doesn't work
#if QT_CONFIG(regularexpression)
QString m_htmlAccumulator;
diff --git a/src/gui/text/qtextmarkdownwriter.cpp b/src/gui/text/qtextmarkdownwriter.cpp
index 26e41149b2..361158e722 100644
--- a/src/gui/text/qtextmarkdownwriter.cpp
+++ b/src/gui/text/qtextmarkdownwriter.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2019 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) 2019 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 "qtextmarkdownwriter_p.h"
#include "qtextdocumentlayout_p.h"
@@ -46,24 +10,28 @@
#include "qtexttable.h"
#include "qtextcursor.h"
#include "qtextimagehandler_p.h"
+#include "qtextmarkdownimporter_p.h"
#include "qloggingcategory.h"
+#include <QtCore/QRegularExpression>
#if QT_CONFIG(itemmodel)
#include "qabstractitemmodel.h"
#endif
QT_BEGIN_NAMESPACE
+using namespace Qt::StringLiterals;
+
Q_LOGGING_CATEGORY(lcMDW, "qt.text.markdown.writer")
-static const QChar Space = QLatin1Char(' ');
-static const QChar Tab = QLatin1Char('\t');
-static const QChar Newline = QLatin1Char('\n');
-static const QChar CarriageReturn = QLatin1Char('\r');
-static const QChar LineBreak = u'\x2028';
-static const QChar DoubleQuote = QLatin1Char('"');
-static const QChar Backtick = QLatin1Char('`');
-static const QChar Backslash = QLatin1Char('\\');
-static const QChar Period = QLatin1Char('.');
+static const QChar qtmw_Space = u' ';
+static const QChar qtmw_Tab = u'\t';
+static const QChar qtmw_Newline = u'\n';
+static const QChar qtmw_CarriageReturn = u'\r';
+static const QChar qtmw_LineBreak = u'\x2028';
+static const QChar qtmw_DoubleQuote = u'"';
+static const QChar qtmw_Backtick = u'`';
+static const QChar qtmw_Backslash = u'\\';
+static const QChar qtmw_Period = u'.';
QTextMarkdownWriter::QTextMarkdownWriter(QTextStream &stream, QTextDocument::MarkdownFeatures features)
: m_stream(stream), m_features(features)
@@ -72,6 +40,7 @@ QTextMarkdownWriter::QTextMarkdownWriter(QTextStream &stream, QTextDocument::Mar
bool QTextMarkdownWriter::writeAll(const QTextDocument *document)
{
+ writeFrontMatter(document->metaInformation(QTextDocument::FrontMatter));
writeFrame(document->rootFrame());
return true;
}
@@ -81,28 +50,28 @@ void QTextMarkdownWriter::writeTable(const QAbstractItemModel *table)
{
QList<int> tableColumnWidths(table->columnCount());
for (int col = 0; col < table->columnCount(); ++col) {
- tableColumnWidths[col] = table->headerData(col, Qt::Horizontal).toString().length();
+ tableColumnWidths[col] = table->headerData(col, Qt::Horizontal).toString().size();
for (int row = 0; row < table->rowCount(); ++row) {
tableColumnWidths[col] = qMax(tableColumnWidths[col],
- table->data(table->index(row, col)).toString().length());
+ table->data(table->index(row, col)).toString().size());
}
}
// write the header and separator
for (int col = 0; col < table->columnCount(); ++col) {
QString s = table->headerData(col, Qt::Horizontal).toString();
- m_stream << "|" << s << QString(tableColumnWidths[col] - s.length(), Space);
+ m_stream << '|' << s << QString(tableColumnWidths[col] - s.size(), qtmw_Space);
}
m_stream << "|" << Qt::endl;
- for (int col = 0; col < tableColumnWidths.length(); ++col)
- m_stream << '|' << QString(tableColumnWidths[col], QLatin1Char('-'));
+ for (int col = 0; col < tableColumnWidths.size(); ++col)
+ m_stream << '|' << QString(tableColumnWidths[col], u'-');
m_stream << '|'<< Qt::endl;
// write the body
for (int row = 0; row < table->rowCount(); ++row) {
for (int col = 0; col < table->columnCount(); ++col) {
QString s = table->data(table->index(row, col)).toString();
- m_stream << "|" << s << QString(tableColumnWidths[col] - s.length(), Space);
+ m_stream << '|' << s << QString(tableColumnWidths[col] - s.size(), qtmw_Space);
}
m_stream << '|'<< Qt::endl;
}
@@ -110,6 +79,19 @@ void QTextMarkdownWriter::writeTable(const QAbstractItemModel *table)
}
#endif
+void QTextMarkdownWriter::writeFrontMatter(const QString &fm)
+{
+ const bool featureEnabled = m_features.testFlag(
+ static_cast<QTextDocument::MarkdownFeature>(QTextMarkdownImporter::FeatureFrontMatter));
+ qCDebug(lcMDW) << "writing FrontMatter?" << featureEnabled << "size" << fm.size();
+ if (fm.isEmpty() || !featureEnabled)
+ return;
+ m_stream << "---\n"_L1 << fm;
+ if (!fm.endsWith(qtmw_Newline))
+ m_stream << qtmw_Newline;
+ m_stream << "---\n"_L1;
+}
+
void QTextMarkdownWriter::writeFrame(const QTextFrame *frame)
{
Q_ASSERT(frame);
@@ -129,7 +111,7 @@ void QTextMarkdownWriter::writeFrame(const QTextFrame *frame)
while (it != cell.end()) {
QTextBlock block = it.currentBlock();
if (block.isValid())
- cellTextLen += block.text().length();
+ cellTextLen += block.text().size();
++it;
}
if (cell.columnSpan() == 1 && tableColumnWidths[col] < cellTextLen)
@@ -146,17 +128,22 @@ void QTextMarkdownWriter::writeFrame(const QTextFrame *frame)
// suppress needless blank lines, when there will be a big change in block format
bool nextIsDifferent = false;
bool ending = false;
+ int blockQuoteIndent = 0;
+ int nextBlockQuoteIndent = 0;
{
QTextFrame::iterator next = iterator;
++next;
+ QTextBlockFormat format = iterator.currentBlock().blockFormat();
+ QTextBlockFormat nextFormat = next.currentBlock().blockFormat();
+ blockQuoteIndent = format.intProperty(QTextFormat::BlockQuoteLevel);
+ nextBlockQuoteIndent = nextFormat.intProperty(QTextFormat::BlockQuoteLevel);
if (next.atEnd()) {
nextIsDifferent = true;
ending = true;
} else {
- QTextBlockFormat format = iterator.currentBlock().blockFormat();
- QTextBlockFormat nextFormat = next.currentBlock().blockFormat();
if (nextFormat.indent() != format.indent() ||
- nextFormat.property(QTextFormat::BlockCodeLanguage) != format.property(QTextFormat::BlockCodeLanguage))
+ nextFormat.property(QTextFormat::BlockCodeLanguage) !=
+ format.property(QTextFormat::BlockCodeLanguage))
nextIsDifferent = true;
}
}
@@ -164,17 +151,19 @@ void QTextMarkdownWriter::writeFrame(const QTextFrame *frame)
QTextTableCell cell = table->cellAt(block.position());
if (tableRow < cell.row()) {
if (tableRow == 0) {
- m_stream << Newline;
- for (int col = 0; col < tableColumnWidths.length(); ++col)
- m_stream << '|' << QString(tableColumnWidths[col], QLatin1Char('-'));
+ m_stream << qtmw_Newline;
+ for (int col = 0; col < tableColumnWidths.size(); ++col)
+ m_stream << '|' << QString(tableColumnWidths[col], u'-');
m_stream << '|';
}
- m_stream << Newline << "|";
+ m_stream << qtmw_Newline << '|';
tableRow = cell.row();
}
} else if (!block.textList()) {
- if (lastWasList)
- m_stream << Newline;
+ if (lastWasList) {
+ m_stream << qtmw_Newline;
+ m_linePrefixWritten = false;
+ }
}
int endingCol = writeBlock(block, !table, table && tableRow == 0,
nextIsDifferent && !block.textList());
@@ -186,20 +175,28 @@ void QTextMarkdownWriter::writeFrame(const QTextFrame *frame)
for (int col = cell.column(); col < spanEndCol; ++col)
paddingLen += tableColumnWidths[col];
if (paddingLen > 0)
- m_stream << QString(paddingLen, Space);
+ m_stream << QString(paddingLen, qtmw_Space);
for (int col = cell.column(); col < spanEndCol; ++col)
m_stream << "|";
} else if (m_fencedCodeBlock && ending) {
- m_stream << m_linePrefix << QString(m_wrappedLineIndent, Space)
- << m_codeBlockFence << Newline << Newline;
+ m_stream << qtmw_Newline << m_linePrefix << QString(m_wrappedLineIndent, qtmw_Space)
+ << m_codeBlockFence << qtmw_Newline << qtmw_Newline;
m_codeBlockFence.clear();
} else if (m_indentedCodeBlock && nextIsDifferent) {
- m_stream << Newline;
+ m_stream << qtmw_Newline << qtmw_Newline;
} else if (endingCol > 0) {
if (block.textList() || block.blockFormat().hasProperty(QTextFormat::BlockCodeLanguage)) {
- m_stream << Newline;
+ m_stream << qtmw_Newline;
+ if (block.textList()) {
+ m_stream << m_linePrefix;
+ m_linePrefixWritten = true;
+ }
} else {
- m_stream << Newline << Newline;
+ m_stream << qtmw_Newline;
+ if (nextBlockQuoteIndent < blockQuoteIndent)
+ setLinePrefixForBlockQuote(nextBlockQuoteIndent);
+ m_stream << m_linePrefix;
+ m_stream << qtmw_Newline;
m_doubleNewlineWritten = true;
}
}
@@ -209,7 +206,7 @@ void QTextMarkdownWriter::writeFrame(const QTextFrame *frame)
++iterator;
}
if (table) {
- m_stream << Newline << Newline;
+ m_stream << qtmw_Newline << qtmw_Newline;
m_doubleNewlineWritten = true;
}
m_listInfo.clear();
@@ -245,18 +242,28 @@ QTextMarkdownWriter::ListInfo QTextMarkdownWriter::listInfo(QTextList *list)
return m_listInfo.value(list);
}
+void QTextMarkdownWriter::setLinePrefixForBlockQuote(int level)
+{
+ m_linePrefix.clear();
+ if (level > 0) {
+ m_linePrefix.reserve(level * 2);
+ for (int i = 0; i < level; ++i)
+ m_linePrefix += u"> ";
+ }
+}
+
static int nearestWordWrapIndex(const QString &s, int before)
{
- before = qMin(before, s.length());
+ before = qMin(before, s.size());
int fragBegin = qMax(before - 15, 0);
if (lcMDW().isDebugEnabled()) {
QString frag = s.mid(fragBegin, 30);
qCDebug(lcMDW) << frag << before;
- qCDebug(lcMDW) << QString(before - fragBegin, Period) + QLatin1Char('<');
+ qCDebug(lcMDW) << QString(before - fragBegin, qtmw_Period) + u'<';
}
for (int i = before - 1; i >= 0; --i) {
if (s.at(i).isSpace()) {
- qCDebug(lcMDW) << QString(i - fragBegin, Period) + QLatin1Char('^') << i;
+ qCDebug(lcMDW) << QString(i - fragBegin, qtmw_Period) + u'^' << i;
return i;
}
}
@@ -266,10 +273,10 @@ static int nearestWordWrapIndex(const QString &s, int before)
static int adjacentBackticksCount(const QString &s)
{
- int start = -1, len = s.length();
+ int start = -1, len = s.size();
int ret = 0;
for (int i = 0; i < len; ++i) {
- if (s.at(i) == Backtick) {
+ if (s.at(i) == qtmw_Backtick) {
if (start < 0)
start = i;
} else if (start >= 0) {
@@ -277,20 +284,58 @@ static int adjacentBackticksCount(const QString &s)
start = -1;
}
}
- if (s.at(len - 1) == Backtick)
+ if (s.at(len - 1) == qtmw_Backtick)
ret = qMax(ret, len - start);
return ret;
}
+/*! \internal
+ Escape anything at the beginning of a line of markdown that would be
+ misinterpreted by a markdown parser, including any period that follows a
+ number (to avoid misinterpretation as a numbered list item).
+ https://spec.commonmark.org/0.31.2/#backslash-escapes
+*/
static void maybeEscapeFirstChar(QString &s)
{
+ static const QRegularExpression numericListRe(uR"(\d+([\.)])\s)"_s);
+ static const QLatin1StringView specialFirstCharacters("#*+-");
+
QString sTrimmed = s.trimmed();
if (sTrimmed.isEmpty())
return;
- char firstChar = sTrimmed.at(0).toLatin1();
- if (firstChar == '*' || firstChar == '+' || firstChar == '-') {
- int i = s.indexOf(QLatin1Char(firstChar));
- s.insert(i, QLatin1Char('\\'));
+ QChar firstChar = sTrimmed.at(0);
+ if (specialFirstCharacters.contains(firstChar)) {
+ int i = s.indexOf(firstChar); // == 0 unless s got trimmed
+ s.insert(i, u'\\');
+ } else {
+ auto match = numericListRe.match(s, 0, QRegularExpression::NormalMatch,
+ QRegularExpression::AnchorAtOffsetMatchOption);
+ if (match.hasMatch())
+ s.insert(match.capturedStart(1), qtmw_Backslash);
+ }
+}
+
+/*! \internal
+ Escape all backslashes. Then escape any special character that stands
+ alone or prefixes a "word", including the \c < that starts an HTML tag.
+ https://spec.commonmark.org/0.31.2/#backslash-escapes
+*/
+static void escapeSpecialCharacters(QString &s)
+{
+ static const QRegularExpression spaceRe(uR"(\s+)"_s);
+ static const QRegularExpression specialRe(uR"([<!*[`&]+[/\w])"_s);
+
+ s.replace("\\"_L1, "\\\\"_L1);
+
+ int i = 0;
+ while (i >= 0) {
+ if (int j = s.indexOf(specialRe, i); j >= 0) {
+ s.insert(j, qtmw_Backslash);
+ i = j + 3;
+ }
+ i = s.indexOf(spaceRe, i);
+ if (i >= 0)
+ ++i; // past the whitespace, if found
}
}
@@ -304,14 +349,14 @@ static LineEndPositions findLineEnd(const QChar *begin, const QChar *end)
LineEndPositions result{ end, end };
while (begin < end) {
- if (*begin == Newline) {
+ if (*begin == qtmw_Newline) {
result.lineEnd = begin;
result.nextLineBegin = begin + 1;
break;
- } else if (*begin == CarriageReturn) {
+ } else if (*begin == qtmw_CarriageReturn) {
result.lineEnd = begin;
result.nextLineBegin = begin + 1;
- if (((begin + 1) < end) && begin[1] == Newline)
+ if (((begin + 1) < end) && begin[1] == qtmw_Newline)
++result.nextLineBegin;
break;
}
@@ -325,7 +370,7 @@ static LineEndPositions findLineEnd(const QChar *begin, const QChar *end)
static bool isBlankLine(const QChar *begin, const QChar *end)
{
while (begin < end) {
- if (*begin != Space && *begin != Tab)
+ if (*begin != qtmw_Space && *begin != qtmw_Tab)
return false;
++begin;
}
@@ -336,7 +381,7 @@ static QString createLinkTitle(const QString &title)
{
QString result;
result.reserve(title.size() + 2);
- result += DoubleQuote;
+ result += qtmw_DoubleQuote;
const QChar *data = title.data();
const QChar *end = data + title.size();
@@ -346,8 +391,8 @@ static QString createLinkTitle(const QString &title)
if (!isBlankLine(data, lineEndPositions.lineEnd)) {
while (data < lineEndPositions.nextLineBegin) {
- if (*data == DoubleQuote)
- result += Backslash;
+ if (*data == qtmw_DoubleQuote)
+ result += qtmw_Backslash;
result += *data;
++data;
}
@@ -356,7 +401,7 @@ static QString createLinkTitle(const QString &title)
data = lineEndPositions.nextLineBegin;
}
- result += DoubleQuote;
+ result += qtmw_DoubleQuote;
return result;
}
@@ -368,17 +413,29 @@ int QTextMarkdownWriter::writeBlock(const QTextBlock &block, bool wrap, bool ign
QTextBlockFormat blockFmt = block.blockFormat();
bool missedBlankCodeBlockLine = false;
const bool codeBlock = blockFmt.hasProperty(QTextFormat::BlockCodeFence) ||
- blockFmt.stringProperty(QTextFormat::BlockCodeLanguage).length() > 0;
+ blockFmt.stringProperty(QTextFormat::BlockCodeLanguage).size() > 0 ||
+ blockFmt.nonBreakableLines();
+ const int blockQuoteLevel = blockFmt.intProperty(QTextFormat::BlockQuoteLevel);
if (m_fencedCodeBlock && !codeBlock) {
- m_stream << m_linePrefix << QString(m_wrappedLineIndent, Space)
- << m_codeBlockFence << Newline;
+ m_stream << m_linePrefix << m_codeBlockFence << qtmw_Newline;
m_fencedCodeBlock = false;
m_codeBlockFence.clear();
+ m_linePrefixWritten = m_linePrefix.size() > 0;
+ }
+ m_linePrefix.clear();
+ if (!blockFmt.headingLevel() && blockQuoteLevel > 0) {
+ setLinePrefixForBlockQuote(blockQuoteLevel);
+ if (!m_linePrefixWritten) {
+ m_stream << m_linePrefix;
+ m_linePrefixWritten = true;
+ }
}
if (block.textList()) { // it's a list-item
auto fmt = block.textList()->format();
const int listLevel = fmt.indent();
- const int number = block.textList()->itemNumber(block) + 1;
+ // Negative numbers don't start a list in Markdown, so ignore them.
+ const int start = fmt.start() >= 0 ? fmt.start() : 1;
+ const int number = block.textList()->itemNumber(block) + start;
QByteArray bullet = " ";
bool numeric = false;
switch (fmt.style()) {
@@ -417,19 +474,19 @@ int QTextMarkdownWriter::writeBlock(const QTextBlock &block, bool wrap, bool ign
int indentFirstLine = (listLevel - 1) * (numeric ? 4 : 2);
m_wrappedLineIndent += indentFirstLine;
if (m_lastListIndent != listLevel && !m_doubleNewlineWritten && listInfo(block.textList()).loose)
- m_stream << Newline;
+ m_stream << qtmw_Newline;
m_lastListIndent = listLevel;
- QString prefix(indentFirstLine, Space);
+ QString prefix(indentFirstLine, qtmw_Space);
if (numeric) {
QString suffix = fmt.numberSuffix();
if (suffix.isEmpty())
- suffix = QString(Period);
- QString numberStr = QString::number(number) + suffix + Space;
- if (numberStr.length() == 3)
- numberStr += Space;
+ suffix = QString(qtmw_Period);
+ QString numberStr = QString::number(number) + suffix + qtmw_Space;
+ if (numberStr.size() == 3)
+ numberStr += qtmw_Space;
prefix += numberStr;
} else {
- prefix += QLatin1String(bullet) + Space;
+ prefix += QLatin1StringView(bullet) + qtmw_Space;
}
m_stream << prefix;
} else if (blockFmt.hasProperty(QTextFormat::BlockTrailingHorizontalRulerWidth)) {
@@ -443,136 +500,159 @@ int QTextMarkdownWriter::writeBlock(const QTextBlock &block, bool wrap, bool ign
if (!m_fencedCodeBlock) {
QString fenceChar = blockFmt.stringProperty(QTextFormat::BlockCodeFence);
if (fenceChar.isEmpty())
- fenceChar = QLatin1String("`");
+ fenceChar = "`"_L1;
m_codeBlockFence = QString(3, fenceChar.at(0));
+ if (blockFmt.hasProperty(QTextFormat::BlockIndent))
+ m_codeBlockFence = QString(m_wrappedLineIndent, qtmw_Space) + m_codeBlockFence;
// A block quote can contain an indented code block, but not vice-versa.
- m_stream << m_linePrefix << QString(m_wrappedLineIndent, Space) << m_codeBlockFence
- << blockFmt.stringProperty(QTextFormat::BlockCodeLanguage) << Newline;
+ m_stream << m_codeBlockFence << blockFmt.stringProperty(QTextFormat::BlockCodeLanguage)
+ << qtmw_Newline << m_linePrefix;
m_fencedCodeBlock = true;
}
wrap = false;
} else if (!blockFmt.indent()) {
m_wrappedLineIndent = 0;
- m_linePrefix.clear();
- if (blockFmt.hasProperty(QTextFormat::BlockQuoteLevel)) {
- int level = blockFmt.intProperty(QTextFormat::BlockQuoteLevel);
- QString quoteMarker = QStringLiteral("> ");
- m_linePrefix.reserve(level * 2);
- for (int i = 0; i < level; ++i)
- m_linePrefix += quoteMarker;
- }
if (blockFmt.hasProperty(QTextFormat::BlockCodeLanguage)) {
// A block quote can contain an indented code block, but not vice-versa.
- m_linePrefix += QString(4, Space);
+ m_linePrefix += QString(4, qtmw_Space);
m_indentedCodeBlock = true;
}
+ if (!m_linePrefixWritten) {
+ m_stream << m_linePrefix;
+ m_linePrefixWritten = true;
+ }
}
- if (blockFmt.headingLevel())
+ if (blockFmt.headingLevel()) {
m_stream << QByteArray(blockFmt.headingLevel(), '#') << ' ';
- else
- m_stream << m_linePrefix;
+ wrap = false;
+ }
- QString wrapIndentString = m_linePrefix + QString(m_wrappedLineIndent, Space);
+ QString wrapIndentString = m_linePrefix + QString(m_wrappedLineIndent, qtmw_Space);
// It would be convenient if QTextStream had a lineCharPos() accessor,
// to keep track of how many characters (not bytes) have been written on the current line,
// but it doesn't. So we have to keep track with this col variable.
- int col = wrapIndentString.length();
+ int col = wrapIndentString.size();
bool mono = false;
bool startsOrEndsWithBacktick = false;
bool bold = false;
bool italic = false;
bool underline = false;
bool strikeOut = false;
- QString backticks(Backtick);
+ bool endingMarkers = false;
+ QString backticks(qtmw_Backtick);
for (QTextBlock::Iterator frag = block.begin(); !frag.atEnd(); ++frag) {
missedBlankCodeBlockLine = false;
QString fragmentText = frag.fragment().text();
- while (fragmentText.endsWith(Newline))
+ while (fragmentText.endsWith(qtmw_Newline))
fragmentText.chop(1);
+ if (!(m_fencedCodeBlock || m_indentedCodeBlock)) {
+ escapeSpecialCharacters(fragmentText);
+ maybeEscapeFirstChar(fragmentText);
+ }
if (block.textList()) { // <li>first line</br>continuation</li>
- QString newlineIndent = QString(Newline) + QString(m_wrappedLineIndent, Space);
- fragmentText.replace(QString(LineBreak), newlineIndent);
+ QString newlineIndent =
+ QString(qtmw_Newline) + QString(m_wrappedLineIndent, qtmw_Space);
+ fragmentText.replace(QString(qtmw_LineBreak), newlineIndent);
} else if (blockFmt.indent() > 0) { // <li>first line<p>continuation</p></li>
- m_stream << QString(m_wrappedLineIndent, Space);
+ m_stream << QString(m_wrappedLineIndent, qtmw_Space);
} else {
- fragmentText.replace(LineBreak, Newline);
+ fragmentText.replace(qtmw_LineBreak, qtmw_Newline);
}
- startsOrEndsWithBacktick |= fragmentText.startsWith(Backtick) || fragmentText.endsWith(Backtick);
+ startsOrEndsWithBacktick |=
+ fragmentText.startsWith(qtmw_Backtick) || fragmentText.endsWith(qtmw_Backtick);
QTextCharFormat fmt = frag.fragment().charFormat();
if (fmt.isImageFormat()) {
QTextImageFormat ifmt = fmt.toImageFormat();
QString desc = ifmt.stringProperty(QTextFormat::ImageAltText);
if (desc.isEmpty())
- desc = QLatin1String("image");
- QString s = QLatin1String("![") + desc + QLatin1String("](") + ifmt.name();
+ desc = "image"_L1;
+ QString s = "!["_L1 + desc + "]("_L1 + ifmt.name();
QString title = ifmt.stringProperty(QTextFormat::ImageTitle);
if (!title.isEmpty())
- s += Space + DoubleQuote + title + DoubleQuote;
- s += QLatin1Char(')');
- if (wrap && col + s.length() > ColumnLimit) {
- m_stream << Newline << wrapIndentString;
+ s += qtmw_Space + qtmw_DoubleQuote + title + qtmw_DoubleQuote;
+ s += u')';
+ if (wrap && col + s.size() > ColumnLimit) {
+ m_stream << qtmw_Newline << wrapIndentString;
col = m_wrappedLineIndent;
}
m_stream << s;
- col += s.length();
+ col += s.size();
} else if (fmt.hasProperty(QTextFormat::AnchorHref)) {
- QString s = QLatin1Char('[') + fragmentText + QLatin1String("](") +
- fmt.property(QTextFormat::AnchorHref).toString();
- if (fmt.hasProperty(QTextFormat::TextToolTip)) {
- s += Space;
- s += createLinkTitle(fmt.property(QTextFormat::TextToolTip).toString());
+ const auto href = fmt.property(QTextFormat::AnchorHref).toString();
+ const bool hasToolTip = fmt.hasProperty(QTextFormat::TextToolTip);
+ QString s;
+ if (!hasToolTip && href == fragmentText && !QUrl(href, QUrl::StrictMode).scheme().isEmpty()) {
+ s = u'<' + href + u'>';
+ } else {
+ s = u'[' + fragmentText + "]("_L1 + href;
+ if (hasToolTip) {
+ s += qtmw_Space;
+ s += createLinkTitle(fmt.property(QTextFormat::TextToolTip).toString());
+ }
+ s += u')';
}
- s += QLatin1Char(')');
- if (wrap && col + s.length() > ColumnLimit) {
- m_stream << Newline << wrapIndentString;
+ if (wrap && col + s.size() > ColumnLimit) {
+ m_stream << qtmw_Newline << wrapIndentString;
col = m_wrappedLineIndent;
}
m_stream << s;
- col += s.length();
+ col += s.size();
} else {
QFontInfo fontInfo(fmt.font());
- bool monoFrag = fontInfo.fixedPitch();
+ bool monoFrag = fontInfo.fixedPitch() || fmt.fontFixedPitch();
QString markers;
if (!ignoreFormat) {
if (monoFrag != mono && !m_indentedCodeBlock && !m_fencedCodeBlock) {
if (monoFrag)
- backticks = QString(adjacentBackticksCount(fragmentText) + 1, Backtick);
+ backticks =
+ QString(adjacentBackticksCount(fragmentText) + 1, qtmw_Backtick);
markers += backticks;
if (startsOrEndsWithBacktick)
- markers += Space;
+ markers += qtmw_Space;
mono = monoFrag;
+ if (!mono)
+ endingMarkers = true;
}
if (!blockFmt.headingLevel() && !mono) {
if (fontInfo.bold() != bold) {
- markers += QLatin1String("**");
+ markers += "**"_L1;
bold = fontInfo.bold();
+ if (!bold)
+ endingMarkers = true;
}
if (fontInfo.italic() != italic) {
- markers += QLatin1Char('*');
+ markers += u'*';
italic = fontInfo.italic();
+ if (!italic)
+ endingMarkers = true;
}
if (fontInfo.strikeOut() != strikeOut) {
- markers += QLatin1String("~~");
+ markers += "~~"_L1;
strikeOut = fontInfo.strikeOut();
+ if (!strikeOut)
+ endingMarkers = true;
}
if (fontInfo.underline() != underline) {
- // Markdown doesn't support underline, but the parser will treat a single underline
- // the same as a single asterisk, and the marked fragment will be rendered in italics.
- // That will have to do.
- markers += QLatin1Char('_');
+ // CommonMark specifies underline as another way to get emphasis (italics):
+ // https://spec.commonmark.org/0.31.2/#example-148
+ // but md4c allows us to distinguish them; so we support underlining (in GitHub dialect).
+ markers += u'_';
underline = fontInfo.underline();
+ if (!underline)
+ endingMarkers = true;
}
}
}
- if (wrap && col + markers.length() * 2 + fragmentText.length() > ColumnLimit) {
+ if (wrap && col + markers.size() * 2 + fragmentText.size() > ColumnLimit) {
int i = 0;
- int fragLen = fragmentText.length();
+ const int fragLen = fragmentText.size();
bool breakingLine = false;
while (i < fragLen) {
if (col >= ColumnLimit) {
- m_stream << Newline << wrapIndentString;
+ m_stream << markers << qtmw_Newline << wrapIndentString;
+ markers.clear();
col = m_wrappedLineIndent;
- while (fragmentText[i].isSpace())
+ while (i < fragLen && fragmentText[i].isSpace())
++i;
}
int j = i + ColumnLimit - col;
@@ -580,6 +660,13 @@ int QTextMarkdownWriter::writeBlock(const QTextBlock &block, bool wrap, bool ign
int wi = nearestWordWrapIndex(fragmentText, j);
if (wi < 0) {
j = fragLen;
+ // can't break within the fragment: we need to break already _before_ it
+ if (endingMarkers) {
+ m_stream << markers;
+ markers.clear();
+ }
+ m_stream << qtmw_Newline << wrapIndentString;
+ col = m_wrappedLineIndent;
} else if (wi >= i) {
j = wi;
breakingLine = true;
@@ -591,28 +678,32 @@ int QTextMarkdownWriter::writeBlock(const QTextBlock &block, bool wrap, bool ign
QString subfrag = fragmentText.mid(i, j - i);
if (!i) {
m_stream << markers;
- col += markers.length();
+ col += markers.size();
}
if (col == m_wrappedLineIndent)
maybeEscapeFirstChar(subfrag);
m_stream << subfrag;
if (breakingLine) {
- m_stream << Newline << wrapIndentString;
+ m_stream << qtmw_Newline << wrapIndentString;
col = m_wrappedLineIndent;
} else {
- col += subfrag.length();
+ col += subfrag.size();
}
i = j + 1;
- }
+ } // loop over fragment characters (we know we need to break somewhere)
} else {
+ if (!m_linePrefixWritten && col == wrapIndentString.size()) {
+ m_stream << m_linePrefix;
+ col += m_linePrefix.size();
+ }
m_stream << markers << fragmentText;
- col += markers.length() + fragmentText.length();
+ col += markers.size() + fragmentText.size();
}
}
}
if (mono) {
if (startsOrEndsWithBacktick) {
- m_stream << Space;
+ m_stream << qtmw_Space;
col += 1;
}
m_stream << backticks;
@@ -635,7 +726,8 @@ int QTextMarkdownWriter::writeBlock(const QTextBlock &block, bool wrap, bool ign
col += 2;
}
if (missedBlankCodeBlockLine)
- m_stream << Newline;
+ m_stream << qtmw_Newline;
+ m_linePrefixWritten = false;
return col;
}
diff --git a/src/gui/text/qtextmarkdownwriter_p.h b/src/gui/text/qtextmarkdownwriter_p.h
index c3076155d0..c0989b8f72 100644
--- a/src/gui/text/qtextmarkdownwriter_p.h
+++ b/src/gui/text/qtextmarkdownwriter_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2019 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) 2019 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 QTEXTMARKDOWNWRITER_P_H
#define QTEXTMARKDOWNWRITER_P_H
@@ -72,6 +36,7 @@ public:
int writeBlock(const QTextBlock &block, bool table, bool ignoreFormat, bool ignoreEmpty);
void writeFrame(const QTextFrame *frame);
+ void writeFrontMatter(const QString &fm);
private:
struct ListInfo {
@@ -79,6 +44,7 @@ private:
};
ListInfo listInfo(QTextList *list);
+ void setLinePrefixForBlockQuote(int level);
private:
QTextStream &m_stream;
@@ -89,6 +55,7 @@ private:
int m_wrappedLineIndent = 0;
int m_lastListIndent = 1;
bool m_doubleNewlineWritten = false;
+ bool m_linePrefixWritten = false;
bool m_indentedCodeBlock = false;
bool m_fencedCodeBlock = false;
};
diff --git a/src/gui/text/qtextobject.cpp b/src/gui/text/qtextobject.cpp
index 91e607ca0e..6aafdc1a25 100644
--- a/src/gui/text/qtextobject.cpp
+++ b/src/gui/text/qtextobject.cpp
@@ -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 "qtextobject.h"
#include "qtextobject_p.h"
@@ -78,7 +42,7 @@ QT_BEGIN_NAMESPACE
objects, you will also need to reimplement QTextDocument::createObject()
which acts as a factory method for creating text objects.
- \sa QTextDocument, {Text Object Example}
+ \sa QTextDocument
*/
/*!
@@ -201,7 +165,7 @@ QTextDocument *QTextObject::document() const
void QTextBlockGroupPrivate::markBlocksDirty()
{
- for (int i = 0; i < blocks.count(); ++i) {
+ for (int i = 0; i < blocks.size(); ++i) {
const QTextBlock &block = blocks.at(i);
pieceTable->documentChange(block.position(), block.length());
}
@@ -210,7 +174,7 @@ void QTextBlockGroupPrivate::markBlocksDirty()
/*!
\fn QTextBlockGroup::QTextBlockGroup(QTextDocument *document)
- Creates a new new block group for the given \a document.
+ Creates a new block group for the given \a document.
\warning This function should only be called from
QTextDocument::createObject().
@@ -359,28 +323,28 @@ QTextFrameLayoutData::~QTextFrameLayoutData()
/*!
\fn bool QTextFrame::iterator::operator==(const iterator &other) const
- Retuns true if the iterator is the same as the \a other iterator;
+ Returns true if the iterator is the same as the \a other iterator;
otherwise returns \c false.
*/
/*!
\fn bool QTextFrame::iterator::operator!=(const iterator &other) const
- Retuns true if the iterator is different from the \a other iterator;
+ Returns true if the iterator is different from the \a other iterator;
otherwise returns \c false.
*/
/*!
\fn QTextFrame::iterator QTextFrame::iterator::operator++(int)
- The postfix ++ operator (\c{i++}) advances the iterator to the
+ The postfix \c{++} operator (\c{i++}) advances the iterator to the
next item in the text frame, and returns an iterator to the old item.
*/
/*!
\fn QTextFrame::iterator QTextFrame::iterator::operator--(int)
- The postfix -- operator (\c{i--}) makes the preceding item in the
+ The postfix \c{--} operator (\c{i--}) makes the preceding item in the
current frame, and returns an iterator to the old item.
*/
@@ -648,27 +612,12 @@ QTextFrame::iterator QTextFrame::end() const
*/
/*!
- \internal
-*/
-QTextFrame::iterator::iterator(QTextFrame *frame, int block, int begin, int end)
-{
- f = frame;
- b = begin;
- e = end;
- cf = nullptr;
- cb = block;
-}
-
-/*!
+ \fn QTextFrame *QTextFrame::iterator::currentFrame() const
Returns the current frame pointed to by the iterator, or \nullptr
if the iterator currently points to a block.
\sa currentBlock()
*/
-QTextFrame *QTextFrame::iterator::currentFrame() const
-{
- return cf;
-}
/*!
Returns the current block the iterator points to. If the iterator
@@ -937,14 +886,14 @@ bool QTextBlock::isValid() const
/*!
\fn bool QTextBlock::iterator::operator==(const iterator &other) const
- Retuns true if this iterator is the same as the \a other iterator;
+ Returns true if this iterator is the same as the \a other iterator;
otherwise returns \c false.
*/
/*!
\fn bool QTextBlock::iterator::operator!=(const iterator &other) const
- Retuns true if this iterator is different from the \a other iterator;
+ Returns true if this iterator is different from the \a other iterator;
otherwise returns \c false.
*/
@@ -1807,3 +1756,5 @@ QString QTextFragment::text() const
}
QT_END_NAMESPACE
+
+#include "moc_qtextobject.cpp"
diff --git a/src/gui/text/qtextobject.h b/src/gui/text/qtextobject.h
index 1d4448029d..875886f825 100644
--- a/src/gui/text/qtextobject.h
+++ b/src/gui/text/qtextobject.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 QTEXTOBJECT_H
#define QTEXTOBJECT_H
@@ -135,7 +99,7 @@ public:
QList<QTextFrame *> childFrames() const;
QTextFrame *parentFrame() const;
- class Q_GUI_EXPORT iterator {
+ class iterator {
QTextFrame *f = nullptr;
int b = 0;
int e = 0;
@@ -145,21 +109,23 @@ public:
friend class QTextFrame;
friend class QTextTableCell;
friend class QTextDocumentLayoutPrivate;
- iterator(QTextFrame *frame, int block, int begin, int end);
+ inline iterator(QTextFrame *frame, int block, int begin, int end)
+ : f(frame), b(begin), e(end), cb(block)
+ {}
public:
constexpr iterator() noexcept = default;
QTextFrame *parentFrame() const { return f; }
- QTextFrame *currentFrame() const;
- QTextBlock currentBlock() const;
+ QTextFrame *currentFrame() const { return cf; }
+ Q_GUI_EXPORT QTextBlock currentBlock() const;
bool atEnd() const { return !cf && cb == e; }
inline bool operator==(const iterator &o) const { return f == o.f && cf == o.cf && cb == o.cb; }
inline bool operator!=(const iterator &o) const { return f != o.f || cf != o.cf || cb != o.cb; }
- iterator &operator++();
+ Q_GUI_EXPORT iterator &operator++();
inline iterator operator++(int) { iterator tmp = *this; operator++(); return tmp; }
- iterator &operator--();
+ Q_GUI_EXPORT iterator &operator--();
inline iterator operator--(int) { iterator tmp = *this; operator--(); return tmp; }
};
@@ -177,7 +143,7 @@ private:
Q_DECLARE_PRIVATE(QTextFrame)
Q_DISABLE_COPY(QTextFrame)
};
-Q_DECLARE_TYPEINFO(QTextFrame::iterator, Q_MOVABLE_TYPE);
+Q_DECLARE_TYPEINFO(QTextFrame::iterator, Q_RELOCATABLE_TYPE);
inline void QTextFrame::setFrameFormat(const QTextFrameFormat &aformat)
{ QTextObject::setFormat(aformat); }
@@ -241,29 +207,26 @@ public:
void setLineCount(int count);
int lineCount() const;
- class Q_GUI_EXPORT iterator {
- const QTextDocumentPrivate *p;
- int b;
- int e;
- int n;
+ class iterator {
+ const QTextDocumentPrivate *p = nullptr;
+ int b = 0;
+ int e = 0;
+ int n = 0;
friend class QTextBlock;
- iterator(const QTextDocumentPrivate *priv, int begin, int end, int f) : p(priv), b(begin), e(end), n(f) {}
+ iterator(const QTextDocumentPrivate *priv, int begin, int end, int f)
+ : p(priv), b(begin), e(end), n(f) {}
public:
- iterator() : p(nullptr), b(0), e(0), n(0) {}
-#if QT_VERSION < QT_VERSION_CHECK(6,0,0)
- iterator(const iterator &o) : p(o.p), b(o.b), e(o.e), n(o.n) {}
- iterator &operator=(const iterator &o) = default;
-#endif
+ constexpr iterator() = default;
- QTextFragment fragment() const;
+ Q_GUI_EXPORT QTextFragment fragment() const;
bool atEnd() const { return n == e; }
inline bool operator==(const iterator &o) const { return p == o.p && n == o.n; }
inline bool operator!=(const iterator &o) const { return p != o.p || n != o.n; }
- iterator &operator++();
+ Q_GUI_EXPORT iterator &operator++();
inline iterator operator++(int) { iterator tmp = *this; operator++(); return tmp; }
- iterator &operator--();
+ Q_GUI_EXPORT iterator &operator--();
inline iterator operator--(int) { iterator tmp = *this; operator--(); return tmp; }
};
@@ -285,8 +248,8 @@ private:
friend class QTextLayout;
};
-Q_DECLARE_TYPEINFO(QTextBlock, Q_MOVABLE_TYPE);
-Q_DECLARE_TYPEINFO(QTextBlock::iterator, Q_MOVABLE_TYPE);
+Q_DECLARE_TYPEINFO(QTextBlock, Q_RELOCATABLE_TYPE);
+Q_DECLARE_TYPEINFO(QTextBlock::iterator, Q_RELOCATABLE_TYPE);
class Q_GUI_EXPORT QTextFragment
@@ -321,7 +284,7 @@ private:
int ne;
};
-Q_DECLARE_TYPEINFO(QTextFragment, Q_MOVABLE_TYPE);
+Q_DECLARE_TYPEINFO(QTextFragment, Q_RELOCATABLE_TYPE);
QT_END_NAMESPACE
diff --git a/src/gui/text/qtextobject_p.h b/src/gui/text/qtextobject_p.h
index 87c83868da..8dad4d7ef4 100644
--- a/src/gui/text/qtextobject_p.h
+++ b/src/gui/text/qtextobject_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 QTEXTOBJECT_P_H
#define QTEXTOBJECT_P_H
diff --git a/src/gui/text/qtextodfwriter.cpp b/src/gui/text/qtextodfwriter.cpp
index 953c05c74c..b50771c12f 100644
--- a/src/gui/text/qtextodfwriter.cpp
+++ b/src/gui/text/qtextodfwriter.cpp
@@ -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 <qglobal.h>
@@ -54,17 +18,20 @@
#include "qtexttable.h"
#include "qtextcursor.h"
#include "qtextimagehandler_p.h"
-#include "qzipwriter_p.h"
#include <QDebug>
+#include <QtCore/private/qzipwriter_p.h>
+
QT_BEGIN_NAMESPACE
+using namespace Qt::StringLiterals;
+
/// Convert pixels to postscript point units
static QString pixelToPoint(qreal pixels)
{
// we hardcode 96 DPI, we do the same in the ODF importer to have a perfect roundtrip.
- return QString::number(pixels * 72 / 96) + QLatin1String("pt");
+ return QString::number(pixels * 72 / 96) + "pt"_L1;
}
// strategies
@@ -296,17 +263,17 @@ void QTextOdfWriter::writeBlock(QXmlStreamWriter &writer, const QTextBlock &bloc
const int listLevel = block.textList()->format().indent();
if (m_listStack.isEmpty() || m_listStack.top() != block.textList()) {
// not the same list we were in.
- while (m_listStack.count() >= listLevel && !m_listStack.isEmpty() && m_listStack.top() != block.textList() ) { // we need to close tags
+ while (m_listStack.size() >= listLevel && !m_listStack.isEmpty() && m_listStack.top() != block.textList() ) { // we need to close tags
m_listStack.pop();
writer.writeEndElement(); // list
- if (m_listStack.count())
+ if (m_listStack.size())
writer.writeEndElement(); // list-item
}
- while (m_listStack.count() < listLevel) {
- if (m_listStack.count())
+ while (m_listStack.size() < listLevel) {
+ if (m_listStack.size())
writer.writeStartElement(textNS, QString::fromLatin1("list-item"));
writer.writeStartElement(textNS, QString::fromLatin1("list"));
- if (m_listStack.count() == listLevel - 1) {
+ if (m_listStack.size() == listLevel - 1) {
m_listStack.push(block.textList());
writer.writeAttribute(textNS, QString::fromLatin1("style-name"), QString::fromLatin1("L%1")
.arg(block.textList()->formatIndex()));
@@ -322,7 +289,7 @@ void QTextOdfWriter::writeBlock(QXmlStreamWriter &writer, const QTextBlock &bloc
while (! m_listStack.isEmpty()) {
m_listStack.pop();
writer.writeEndElement(); // list
- if (m_listStack.count())
+ if (m_listStack.size())
writer.writeEndElement(); // list-item
}
}
@@ -349,7 +316,7 @@ void QTextOdfWriter::writeBlock(QXmlStreamWriter &writer, const QTextBlock &bloc
writer.writeStartElement(textNS, QString::fromLatin1("span"));
QString fragmentText = frag.fragment().text();
- if (fragmentText.length() == 1 && fragmentText[0] == u'\xFFFC') { // its an inline character.
+ if (fragmentText.size() == 1 && fragmentText[0] == u'\xFFFC') { // its an inline character.
writeInlineCharacter(writer, frag.fragment());
writer.writeEndElement(); // span
continue;
@@ -360,8 +327,8 @@ void QTextOdfWriter::writeBlock(QXmlStreamWriter &writer, const QTextBlock &bloc
bool escapeNextSpace = true;
int precedingSpaces = 0;
int exportedIndex = 0;
- for (int i=0; i <= fragmentText.count(); ++i) {
- QChar character = (i == fragmentText.count() ? QChar() : fragmentText.at(i));
+ for (int i=0; i <= fragmentText.size(); ++i) {
+ QChar character = (i == fragmentText.size() ? QChar() : fragmentText.at(i));
bool isSpace = character.unicode() == ' ';
// find more than one space. -> <text:s text:c="2" />
@@ -377,7 +344,7 @@ void QTextOdfWriter::writeBlock(QXmlStreamWriter &writer, const QTextBlock &bloc
exportedIndex = i;
}
- if (i < fragmentText.count()) {
+ if (i < fragmentText.size()) {
if (character.unicode() == 0x2028) { // soft-return
//if (exportedIndex < i)
writer.writeCharacters(fragmentText.mid(exportedIndex, i - exportedIndex));
@@ -454,11 +421,11 @@ void QTextOdfWriter::writeInlineCharacter(QXmlStreamWriter &writer, const QTextF
QImage image;
QString name = imageFormat.name();
- if (name.startsWith(QLatin1String(":/"))) // auto-detect resources
- name.prepend(QLatin1String("qrc"));
+ if (name.startsWith(":/"_L1)) // auto-detect resources
+ name.prepend("qrc"_L1);
QUrl url = QUrl(name);
const QVariant variant = m_document->resource(QTextDocument::ImageResource, url);
- if (variant.userType() == QMetaType::QImage) {
+ if (variant.userType() == QMetaType::QPixmap || variant.userType() == QMetaType::QImage) {
image = qvariant_cast<QImage>(variant);
} else if (variant.userType() == QMetaType::QByteArray) {
data = variant.toByteArray();
@@ -479,7 +446,7 @@ void QTextOdfWriter::writeInlineCharacter(QXmlStreamWriter &writer, const QTextF
QBuffer imageBytes;
int imgQuality = imageFormat.quality();
- if (imgQuality >= 100 || imgQuality < 0 || image.hasAlphaChannel()) {
+ if (imgQuality >= 100 || imgQuality <= 0 || image.hasAlphaChannel()) {
QImageWriter imageWriter(&imageBytes, "png");
imageWriter.write(image);
@@ -547,12 +514,6 @@ void QTextOdfWriter::writeFormats(QXmlStreamWriter &writer, const QSet<int> &for
else
writeFrameFormat(writer, textFormat.toFrameFormat(), formatIndex);
break;
-#if QT_DEPRECATED_SINCE(5, 3)
- case QTextFormat::TableFormat:
- // this case never happens, because TableFormat is a FrameFormat
- Q_UNREACHABLE();
- break;
-#endif
}
}
@@ -684,8 +645,8 @@ void QTextOdfWriter::writeCharacterFormat(QXmlStreamWriter &writer, QTextCharFor
value = QString::number(format.fontWeight());
writer.writeAttribute(foNS, QString::fromLatin1("font-weight"), value);
}
- if (format.hasProperty(QTextFormat::FontFamily))
- writer.writeAttribute(foNS, QString::fromLatin1("font-family"), format.fontFamily());
+ if (format.hasProperty(QTextFormat::OldFontFamily))
+ writer.writeAttribute(foNS, QString::fromLatin1("font-family"), format.fontFamilies().toStringList().value(0, QString()));
else
writer.writeAttribute(foNS, QString::fromLatin1("font-family"), QString::fromLatin1("Sans")); // Qt default
if (format.hasProperty(QTextFormat::FontPointSize))
@@ -873,7 +834,7 @@ void QTextOdfWriter::writeTableFormat(QXmlStreamWriter &writer, QTextTableFormat
writer.writeAttribute(tableNS, QString::fromLatin1("align"), QString::fromLatin1(align));
if (format.width().rawValue()) {
writer.writeAttribute(styleNS, QString::fromLatin1("width"),
- QString::number(format.width().rawValue()) + QLatin1String("pt"));
+ QString::number(format.width().rawValue()) + "pt"_L1);
}
writer.writeEndElement();
// start writing table-column style element
@@ -889,14 +850,14 @@ void QTextOdfWriter::writeTableFormat(QXmlStreamWriter &writer, QTextTableFormat
QString columnWidth;
if (format.columnWidthConstraints().at(colit).type() == QTextLength::PercentageLength) {
columnWidth = QString::number(format.columnWidthConstraints().at(colit).rawValue())
- + QLatin1String("%");
+ + "%"_L1;
} else if (format.columnWidthConstraints().at(colit).type() == QTextLength::FixedLength) {
columnWidth = QString::number(format.columnWidthConstraints().at(colit).rawValue())
- + QLatin1String("pt");
+ + "pt"_L1;
} else {
//!! HARD-CODING variableWidth Constraints to 100% / nr constraints
columnWidth = QString::number(100 / format.columnWidthConstraints().size())
- + QLatin1String("%");
+ + "%"_L1;
}
writer.writeAttribute(styleNS, QString::fromLatin1("column-width"), columnWidth);
writer.writeEndElement();
@@ -938,8 +899,8 @@ void QTextOdfWriter::tableCellStyleElement(QXmlStreamWriter &writer, const int &
writer.writeEmptyElement(styleNS, QString::fromLatin1("table-cell-properties"));
if (hasBorder) {
writer.writeAttribute(foNS, QString::fromLatin1("border"),
- pixelToPoint(tableFormatTmp.border()) + QLatin1String(" ")
- + borderStyleName(tableFormatTmp.borderStyle()) + QLatin1String(" ")
+ pixelToPoint(tableFormatTmp.border()) + " "_L1
+ + borderStyleName(tableFormatTmp.borderStyle()) + " "_L1
+ tableFormatTmp.borderBrush().color().name(QColor::HexRgb));
}
qreal topPadding = format.topPadding();
@@ -990,14 +951,14 @@ void QTextOdfWriter::tableCellStyleElement(QXmlStreamWriter &writer, const int &
///////////////////////
QTextOdfWriter::QTextOdfWriter(const QTextDocument &document, QIODevice *device)
- : officeNS (QLatin1String("urn:oasis:names:tc:opendocument:xmlns:office:1.0")),
- textNS (QLatin1String("urn:oasis:names:tc:opendocument:xmlns:text:1.0")),
- styleNS (QLatin1String("urn:oasis:names:tc:opendocument:xmlns:style:1.0")),
- foNS (QLatin1String("urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0")),
- tableNS (QLatin1String("urn:oasis:names:tc:opendocument:xmlns:table:1.0")),
- drawNS (QLatin1String("urn:oasis:names:tc:opendocument:xmlns:drawing:1.0")),
- xlinkNS (QLatin1String("http://www.w3.org/1999/xlink")),
- svgNS (QLatin1String("urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0")),
+ : officeNS ("urn:oasis:names:tc:opendocument:xmlns:office:1.0"_L1),
+ textNS ("urn:oasis:names:tc:opendocument:xmlns:text:1.0"_L1),
+ styleNS ("urn:oasis:names:tc:opendocument:xmlns:style:1.0"_L1),
+ foNS ("urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0"_L1),
+ tableNS ("urn:oasis:names:tc:opendocument:xmlns:table:1.0"_L1),
+ drawNS ("urn:oasis:names:tc:opendocument:xmlns:drawing:1.0"_L1),
+ xlinkNS ("http://www.w3.org/1999/xlink"_L1),
+ svgNS ("urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0"_L1),
m_document(&document),
m_device(device),
m_strategy(nullptr),
diff --git a/src/gui/text/qtextodfwriter_p.h b/src/gui/text/qtextodfwriter_p.h
index 2c41dfba67..e02297da98 100644
--- a/src/gui/text/qtextodfwriter_p.h
+++ b/src/gui/text/qtextodfwriter_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 QTEXTODFWRITER_H
#define QTEXTODFWRITER_H
diff --git a/src/gui/text/qtextoption.cpp b/src/gui/text/qtextoption.cpp
index 908e0f914a..b6beadbe91 100644
--- a/src/gui/text/qtextoption.cpp
+++ b/src/gui/text/qtextoption.cpp
@@ -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 "qtextoption.h"
#include "qguiapplication.h"
@@ -43,6 +7,8 @@
QT_BEGIN_NAMESPACE
+QT_IMPL_METATYPE_EXTERN_TAGGED(QTextOption::Tab, QTextOption_Tab)
+
struct QTextOptionPrivate
{
QList<QTextOption::Tab> tabStops;
@@ -143,7 +109,7 @@ void QTextOption::setTabArray(const QList<qreal> &tabStops)
d = new QTextOptionPrivate;
QList<QTextOption::Tab> tabs;
QTextOption::Tab tab;
- tabs.reserve(tabStops.count());
+ tabs.reserve(tabStops.size());
for (qreal pos : tabStops) {
tab.position = pos;
tabs.append(tab);
@@ -176,7 +142,7 @@ QList<qreal> QTextOption::tabArray() const
if (!d)
return answer;
- answer.reserve(d->tabStops.count());
+ answer.reserve(d->tabStops.size());
QList<QTextOption::Tab>::ConstIterator iter = d->tabStops.constBegin();
while(iter != d->tabStops.constEnd()) {
answer.append( (*iter).position);
@@ -363,11 +329,11 @@ QList<QTextOption::Tab> QTextOption::tabs() const
*/
/*!
- \variable Tab::position
+ \variable QTextOption::Tab::position
Distance from the start of the paragraph.
The position of a tab is from the start of the paragraph which implies that when
the alignment of the paragraph is set to centered, the tab is interpreted to be
- moved the same distance as the left ege of the paragraph does.
+ moved the same distance as the left edge of the paragraph does.
In case the paragraph is set to have a layoutDirection() RightToLeft the position
is interpreted to be from the right side of the paragraph with higher numbers moving
the tab to the left.
diff --git a/src/gui/text/qtextoption.h b/src/gui/text/qtextoption.h
index 51e255e6f2..dcff41584f 100644
--- a/src/gui/text/qtextoption.h
+++ b/src/gui/text/qtextoption.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 QTEXTOPTION_H
#define QTEXTOPTION_H
@@ -81,7 +45,7 @@ public:
};
QTextOption();
- /*implicit*/ QTextOption(Qt::Alignment alignment);
+ Q_IMPLICIT QTextOption(Qt::Alignment alignment);
~QTextOption();
QTextOption(const QTextOption &o);
@@ -141,16 +105,16 @@ private:
Q_DECLARE_OPERATORS_FOR_FLAGS(QTextOption::Flags)
inline void QTextOption::setAlignment(Qt::Alignment aalignment)
-{ align = aalignment; }
+{ align = uint(aalignment.toInt()); }
inline void QTextOption::setFlags(Flags aflags)
-{ f = aflags; }
+{ f = uint(aflags.toInt()); }
inline void QTextOption::setTabStopDistance(qreal atabStop)
{ tab = atabStop; }
QT_END_NAMESPACE
-Q_DECLARE_METATYPE( QTextOption::Tab )
+QT_DECL_METATYPE_EXTERN_TAGGED(QTextOption::Tab, QTextOption_Tab, Q_GUI_EXPORT)
#endif // QTEXTOPTION_H
diff --git a/src/gui/text/qtexttable.cpp b/src/gui/text/qtexttable.cpp
index 6594b4e879..64b7aa6765 100644
--- a/src/gui/text/qtexttable.cpp
+++ b/src/gui/text/qtexttable.cpp
@@ -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 "qtexttable.h"
#include "qtextcursor.h"
@@ -50,6 +14,8 @@
QT_BEGIN_NAMESPACE
+using namespace Qt::StringLiterals;
+
/*!
\class QTextTableCell
\reentrant
@@ -318,13 +284,6 @@ QTextFrame::iterator QTextTableCell::end() const
Destroys the table cell.
*/
-QTextTablePrivate::~QTextTablePrivate()
-{
- if (grid)
- free(grid);
-}
-
-
QTextTable *QTextTablePrivate::createTable(QTextDocumentPrivate *pieceTable, int pos, int rows, int cols, const QTextTableFormat &tableFormat)
{
QTextTableFormat fmt = tableFormat;
@@ -403,7 +362,7 @@ void QTextTablePrivate::fragmentAdded(QChar type, uint fragment)
if (blockFragmentUpdates)
return;
if (type == QTextBeginningOfFrame) {
- Q_ASSERT(cells.indexOf(fragment) == -1);
+ Q_ASSERT(cells.indexOf(int(fragment)) == -1);
const uint pos = pieceTable->fragmentMap().position(fragment);
QFragmentFindHelper helper(pos, pieceTable->fragmentMap());
auto it = std::lower_bound(cells.begin(), cells.end(), helper);
@@ -421,8 +380,8 @@ void QTextTablePrivate::fragmentRemoved(QChar type, uint fragment)
if (blockFragmentUpdates)
return;
if (type == QTextBeginningOfFrame) {
- Q_ASSERT(cells.indexOf(fragment) != -1);
- cells.removeAll(fragment);
+ Q_ASSERT(cells.indexOf(int(fragment)) != -1);
+ cells.removeAll(int(fragment));
if (fragment_start == fragment && cells.size()) {
fragment_start = cells.at(0);
}
@@ -446,8 +405,7 @@ void QTextTablePrivate::update() const
nRows = (cells.size() + nCols-1)/nCols;
// qDebug(">>>> QTextTablePrivate::update, nRows=%d, nCols=%d", nRows, nCols);
- grid = q_check_ptr((int *)realloc(grid, nRows*nCols*sizeof(int)));
- memset(grid, 0, nRows*nCols*sizeof(int));
+ grid.assign(nRows * nCols, 0);
QTextDocumentPrivate *p = pieceTable;
QTextFormatCollection *c = p->formatCollection();
@@ -470,8 +428,7 @@ void QTextTablePrivate::update() const
cellIndices[i] = cell;
if (r + rowspan > nRows) {
- grid = q_check_ptr((int *)realloc(grid, sizeof(int)*(r + rowspan)*nCols));
- memset(grid + (nRows*nCols), 0, sizeof(int)*(r+rowspan-nRows)*nCols);
+ grid.resize((r + rowspan) * nCols, 0);
nRows = r + rowspan;
}
@@ -982,7 +939,7 @@ void QTextTable::removeColumns(int pos, int num)
QTextTableFormat tfmt = format();
tfmt.setColumns(tfmt.columns()-num);
QList<QTextLength> columnWidths = tfmt.columnWidthConstraints();
- if (columnWidths.count() > pos) {
+ if (columnWidths.size() > pos) {
columnWidths.remove(pos, num);
tfmt.setColumnWidthConstraints (columnWidths);
}
@@ -1119,7 +1076,7 @@ void QTextTable::mergeCells(int row, int column, int numRows, int numCols)
QTextCursorPrivate::fromPosition(p, insertPos++).insertBlock();
p->move(pos + 1, insertPos, nextPos - pos);
} else if (rowHasText) {
- QTextCursorPrivate::fromPosition(p, insertPos++).insertText(QLatin1String(" "));
+ QTextCursorPrivate::fromPosition(p, insertPos++).insertText(" "_L1);
p->move(pos + 1, insertPos, nextPos - pos);
} else {
p->move(pos, insertPos, nextPos - pos);
@@ -1344,3 +1301,5 @@ void QTextTable::setFormat(const QTextTableFormat &format)
*/
QT_END_NAMESPACE
+
+#include "moc_qtexttable.cpp"
diff --git a/src/gui/text/qtexttable.h b/src/gui/text/qtexttable.h
index 156b091b05..49f606d429 100644
--- a/src/gui/text/qtexttable.h
+++ b/src/gui/text/qtexttable.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 QTEXTTABLE_H
#define QTEXTTABLE_H
diff --git a/src/gui/text/qtexttable_p.h b/src/gui/text/qtexttable_p.h
index 7dcab6c147..3a652af046 100644
--- a/src/gui/text/qtexttable_p.h
+++ b/src/gui/text/qtexttable_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 QTEXTTABLE_P_H
#define QTEXTTABLE_P_H
@@ -55,14 +19,15 @@
#include "private/qtextobject_p.h"
#include "private/qtextdocument_p.h"
+#include <vector>
+
QT_BEGIN_NAMESPACE
class QTextTablePrivate : public QTextFramePrivate
{
Q_DECLARE_PUBLIC(QTextTable)
public:
- QTextTablePrivate(QTextDocument *document) : QTextFramePrivate(document), grid(nullptr), nRows(0), nCols(0), dirty(true), blockFragmentUpdates(false) {}
- ~QTextTablePrivate();
+ QTextTablePrivate(QTextDocument *document) : QTextFramePrivate(document), nRows(0), nCols(0), dirty(true), blockFragmentUpdates(false) {}
static QTextTable *createTable(QTextDocumentPrivate *, int pos, int rows, int cols, const QTextTableFormat &tableFormat);
void fragmentAdded(QChar type, uint fragment) override;
@@ -76,7 +41,7 @@ public:
// symmetric to cells array and maps to indecs in grid,
// used for fast-lookup for row/column by fragment
mutable QList<int> cellIndices;
- mutable int *grid;
+ mutable std::vector<int> grid;
mutable int nRows;
mutable int nCols;
mutable bool dirty;
diff --git a/src/gui/text/qzip.cpp b/src/gui/text/qzip.cpp
deleted file mode 100644
index 867e67698b..0000000000
--- a/src/gui/text/qzip.cpp
+++ /dev/null
@@ -1,1366 +0,0 @@
-/****************************************************************************
-**
-** 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$
-**
-****************************************************************************/
-
-#include <qglobal.h>
-
-#ifndef QT_NO_TEXTODFWRITER
-
-#include "qzipreader_p.h"
-#include "qzipwriter_p.h"
-#include <qdatetime.h>
-#include <qendian.h>
-#include <qdebug.h>
-#include <qdir.h>
-
-#include <zlib.h>
-
-// Zip standard version for archives handled by this API
-// (actually, the only basic support of this version is implemented but it is enough for now)
-#define ZIP_VERSION 20
-
-#if 0
-#define ZDEBUG qDebug
-#else
-#define ZDEBUG if (0) qDebug
-#endif
-
-QT_BEGIN_NAMESPACE
-
-static inline uint readUInt(const uchar *data)
-{
- return (data[0]) + (data[1]<<8) + (data[2]<<16) + (data[3]<<24);
-}
-
-static inline ushort readUShort(const uchar *data)
-{
- return (data[0]) + (data[1]<<8);
-}
-
-static inline void writeUInt(uchar *data, uint i)
-{
- data[0] = i & 0xff;
- data[1] = (i>>8) & 0xff;
- data[2] = (i>>16) & 0xff;
- data[3] = (i>>24) & 0xff;
-}
-
-static inline void writeUShort(uchar *data, ushort i)
-{
- data[0] = i & 0xff;
- data[1] = (i>>8) & 0xff;
-}
-
-static inline void copyUInt(uchar *dest, const uchar *src)
-{
- dest[0] = src[0];
- dest[1] = src[1];
- dest[2] = src[2];
- dest[3] = src[3];
-}
-
-static inline void copyUShort(uchar *dest, const uchar *src)
-{
- dest[0] = src[0];
- dest[1] = src[1];
-}
-
-static void writeMSDosDate(uchar *dest, const QDateTime& dt)
-{
- if (dt.isValid()) {
- quint16 time =
- (dt.time().hour() << 11) // 5 bit hour
- | (dt.time().minute() << 5) // 6 bit minute
- | (dt.time().second() >> 1); // 5 bit double seconds
-
- dest[0] = time & 0xff;
- dest[1] = time >> 8;
-
- quint16 date =
- ((dt.date().year() - 1980) << 9) // 7 bit year 1980-based
- | (dt.date().month() << 5) // 4 bit month
- | (dt.date().day()); // 5 bit day
-
- dest[2] = char(date);
- dest[3] = char(date >> 8);
- } else {
- dest[0] = 0;
- dest[1] = 0;
- dest[2] = 0;
- dest[3] = 0;
- }
-}
-
-static int inflate(Bytef *dest, ulong *destLen, const Bytef *source, ulong sourceLen)
-{
- z_stream stream;
- int err;
-
- stream.next_in = const_cast<Bytef*>(source);
- stream.avail_in = (uInt)sourceLen;
- if ((uLong)stream.avail_in != sourceLen)
- return Z_BUF_ERROR;
-
- stream.next_out = dest;
- stream.avail_out = (uInt)*destLen;
- if ((uLong)stream.avail_out != *destLen)
- return Z_BUF_ERROR;
-
- stream.zalloc = (alloc_func)nullptr;
- stream.zfree = (free_func)nullptr;
-
- err = inflateInit2(&stream, -MAX_WBITS);
- if (err != Z_OK)
- return err;
-
- err = inflate(&stream, Z_FINISH);
- if (err != Z_STREAM_END) {
- inflateEnd(&stream);
- if (err == Z_NEED_DICT || (err == Z_BUF_ERROR && stream.avail_in == 0))
- return Z_DATA_ERROR;
- return err;
- }
- *destLen = stream.total_out;
-
- err = inflateEnd(&stream);
- return err;
-}
-
-static int deflate (Bytef *dest, ulong *destLen, const Bytef *source, ulong sourceLen)
-{
- z_stream stream;
- int err;
-
- stream.next_in = const_cast<Bytef*>(source);
- stream.avail_in = (uInt)sourceLen;
- stream.next_out = dest;
- stream.avail_out = (uInt)*destLen;
- if ((uLong)stream.avail_out != *destLen) return Z_BUF_ERROR;
-
- stream.zalloc = (alloc_func)nullptr;
- stream.zfree = (free_func)nullptr;
- stream.opaque = (voidpf)nullptr;
-
- err = deflateInit2(&stream, Z_DEFAULT_COMPRESSION, Z_DEFLATED, -MAX_WBITS, 8, Z_DEFAULT_STRATEGY);
- if (err != Z_OK) return err;
-
- err = deflate(&stream, Z_FINISH);
- if (err != Z_STREAM_END) {
- deflateEnd(&stream);
- return err == Z_OK ? Z_BUF_ERROR : err;
- }
- *destLen = stream.total_out;
-
- err = deflateEnd(&stream);
- return err;
-}
-
-
-namespace WindowsFileAttributes {
-enum {
- Dir = 0x10, // FILE_ATTRIBUTE_DIRECTORY
- File = 0x80, // FILE_ATTRIBUTE_NORMAL
- TypeMask = 0x90,
-
- ReadOnly = 0x01, // FILE_ATTRIBUTE_READONLY
- PermMask = 0x01
-};
-}
-
-namespace UnixFileAttributes {
-enum {
- Dir = 0040000, // __S_IFDIR
- File = 0100000, // __S_IFREG
- SymLink = 0120000, // __S_IFLNK
- TypeMask = 0170000, // __S_IFMT
-
- ReadUser = 0400, // __S_IRUSR
- WriteUser = 0200, // __S_IWUSR
- ExeUser = 0100, // __S_IXUSR
- ReadGroup = 0040, // __S_IRGRP
- WriteGroup = 0020, // __S_IWGRP
- ExeGroup = 0010, // __S_IXGRP
- ReadOther = 0004, // __S_IROTH
- WriteOther = 0002, // __S_IWOTH
- ExeOther = 0001, // __S_IXOTH
- PermMask = 0777
-};
-}
-
-static QFile::Permissions modeToPermissions(quint32 mode)
-{
- QFile::Permissions ret;
- if (mode & UnixFileAttributes::ReadUser)
- ret |= QFile::ReadOwner | QFile::ReadUser;
- if (mode & UnixFileAttributes::WriteUser)
- ret |= QFile::WriteOwner | QFile::WriteUser;
- if (mode & UnixFileAttributes::ExeUser)
- ret |= QFile::ExeOwner | QFile::ExeUser;
- if (mode & UnixFileAttributes::ReadGroup)
- ret |= QFile::ReadGroup;
- if (mode & UnixFileAttributes::WriteGroup)
- ret |= QFile::WriteGroup;
- if (mode & UnixFileAttributes::ExeGroup)
- ret |= QFile::ExeGroup;
- if (mode & UnixFileAttributes::ReadOther)
- ret |= QFile::ReadOther;
- if (mode & UnixFileAttributes::WriteOther)
- ret |= QFile::WriteOther;
- if (mode & UnixFileAttributes::ExeOther)
- ret |= QFile::ExeOther;
- return ret;
-}
-
-static quint32 permissionsToMode(QFile::Permissions perms)
-{
- quint32 mode = 0;
- if (perms & (QFile::ReadOwner | QFile::ReadUser))
- mode |= UnixFileAttributes::ReadUser;
- if (perms & (QFile::WriteOwner | QFile::WriteUser))
- mode |= UnixFileAttributes::WriteUser;
- if (perms & (QFile::ExeOwner | QFile::ExeUser))
- mode |= UnixFileAttributes::WriteUser;
- if (perms & QFile::ReadGroup)
- mode |= UnixFileAttributes::ReadGroup;
- if (perms & QFile::WriteGroup)
- mode |= UnixFileAttributes::WriteGroup;
- if (perms & QFile::ExeGroup)
- mode |= UnixFileAttributes::ExeGroup;
- if (perms & QFile::ReadOther)
- mode |= UnixFileAttributes::ReadOther;
- if (perms & QFile::WriteOther)
- mode |= UnixFileAttributes::WriteOther;
- if (perms & QFile::ExeOther)
- mode |= UnixFileAttributes::ExeOther;
- return mode;
-}
-
-static QDateTime readMSDosDate(const uchar *src)
-{
- uint dosDate = readUInt(src);
- quint64 uDate;
- uDate = (quint64)(dosDate >> 16);
- uint tm_mday = (uDate & 0x1f);
- uint tm_mon = ((uDate & 0x1E0) >> 5);
- uint tm_year = (((uDate & 0x0FE00) >> 9) + 1980);
- uint tm_hour = ((dosDate & 0xF800) >> 11);
- uint tm_min = ((dosDate & 0x7E0) >> 5);
- uint tm_sec = ((dosDate & 0x1f) << 1);
-
- return QDateTime(QDate(tm_year, tm_mon, tm_mday), QTime(tm_hour, tm_min, tm_sec));
-}
-
-// for details, see http://www.pkware.com/documents/casestudies/APPNOTE.TXT
-
-enum HostOS {
- HostFAT = 0,
- HostAMIGA = 1,
- HostVMS = 2, // VAX/VMS
- HostUnix = 3,
- HostVM_CMS = 4,
- HostAtari = 5, // what if it's a minix filesystem? [cjh]
- HostHPFS = 6, // filesystem used by OS/2 (and NT 3.x)
- HostMac = 7,
- HostZ_System = 8,
- HostCPM = 9,
- HostTOPS20 = 10, // pkzip 2.50 NTFS
- HostNTFS = 11, // filesystem used by Windows NT
- HostQDOS = 12, // SMS/QDOS
- HostAcorn = 13, // Archimedes Acorn RISC OS
- HostVFAT = 14, // filesystem used by Windows 95, NT
- HostMVS = 15,
- HostBeOS = 16, // hybrid POSIX/database filesystem
- HostTandem = 17,
- HostOS400 = 18,
- HostOSX = 19
-};
-Q_DECLARE_TYPEINFO(HostOS, Q_PRIMITIVE_TYPE);
-
-enum GeneralPurposeFlag {
- Encrypted = 0x01,
- AlgTune1 = 0x02,
- AlgTune2 = 0x04,
- HasDataDescriptor = 0x08,
- PatchedData = 0x20,
- StrongEncrypted = 0x40,
- Utf8Names = 0x0800,
- CentralDirectoryEncrypted = 0x2000
-};
-Q_DECLARE_TYPEINFO(GeneralPurposeFlag, Q_PRIMITIVE_TYPE);
-
-enum CompressionMethod {
- CompressionMethodStored = 0,
- CompressionMethodShrunk = 1,
- CompressionMethodReduced1 = 2,
- CompressionMethodReduced2 = 3,
- CompressionMethodReduced3 = 4,
- CompressionMethodReduced4 = 5,
- CompressionMethodImploded = 6,
- CompressionMethodReservedTokenizing = 7, // reserved for tokenizing
- CompressionMethodDeflated = 8,
- CompressionMethodDeflated64 = 9,
- CompressionMethodPKImploding = 10,
-
- CompressionMethodBZip2 = 12,
-
- CompressionMethodLZMA = 14,
-
- CompressionMethodTerse = 18,
- CompressionMethodLz77 = 19,
-
- CompressionMethodJpeg = 96,
- CompressionMethodWavPack = 97,
- CompressionMethodPPMd = 98,
- CompressionMethodWzAES = 99
-};
-Q_DECLARE_TYPEINFO(CompressionMethod, Q_PRIMITIVE_TYPE);
-
-struct LocalFileHeader
-{
- uchar signature[4]; // 0x04034b50
- uchar version_needed[2];
- uchar general_purpose_bits[2];
- uchar compression_method[2];
- uchar last_mod_file[4];
- uchar crc_32[4];
- uchar compressed_size[4];
- uchar uncompressed_size[4];
- uchar file_name_length[2];
- uchar extra_field_length[2];
-};
-Q_DECLARE_TYPEINFO(LocalFileHeader, Q_PRIMITIVE_TYPE);
-
-struct DataDescriptor
-{
- uchar crc_32[4];
- uchar compressed_size[4];
- uchar uncompressed_size[4];
-};
-Q_DECLARE_TYPEINFO(DataDescriptor, Q_PRIMITIVE_TYPE);
-
-struct CentralFileHeader
-{
- uchar signature[4]; // 0x02014b50
- uchar version_made[2];
- uchar version_needed[2];
- uchar general_purpose_bits[2];
- uchar compression_method[2];
- uchar last_mod_file[4];
- uchar crc_32[4];
- uchar compressed_size[4];
- uchar uncompressed_size[4];
- uchar file_name_length[2];
- uchar extra_field_length[2];
- uchar file_comment_length[2];
- uchar disk_start[2];
- uchar internal_file_attributes[2];
- uchar external_file_attributes[4];
- uchar offset_local_header[4];
-};
-Q_DECLARE_TYPEINFO(CentralFileHeader, Q_PRIMITIVE_TYPE);
-
-struct EndOfDirectory
-{
- uchar signature[4]; // 0x06054b50
- uchar this_disk[2];
- uchar start_of_directory_disk[2];
- uchar num_dir_entries_this_disk[2];
- uchar num_dir_entries[2];
- uchar directory_size[4];
- uchar dir_start_offset[4];
- uchar comment_length[2];
-};
-Q_DECLARE_TYPEINFO(EndOfDirectory, Q_PRIMITIVE_TYPE);
-
-struct FileHeader
-{
- CentralFileHeader h;
- QByteArray file_name;
- QByteArray extra_field;
- QByteArray file_comment;
-};
-Q_DECLARE_TYPEINFO(FileHeader, Q_MOVABLE_TYPE);
-
-class QZipPrivate
-{
-public:
- QZipPrivate(QIODevice *device, bool ownDev)
- : device(device), ownDevice(ownDev), dirtyFileTree(true), start_of_directory(0)
- {
- }
-
- ~QZipPrivate()
- {
- if (ownDevice)
- delete device;
- }
-
- QZipReader::FileInfo fillFileInfo(int index) const;
-
- QIODevice *device;
- bool ownDevice;
- bool dirtyFileTree;
- QList<FileHeader> fileHeaders;
- QByteArray comment;
- uint start_of_directory;
-};
-
-QZipReader::FileInfo QZipPrivate::fillFileInfo(int index) const
-{
- QZipReader::FileInfo fileInfo;
- FileHeader header = fileHeaders.at(index);
- quint32 mode = readUInt(header.h.external_file_attributes);
- const HostOS hostOS = HostOS(readUShort(header.h.version_made) >> 8);
- switch (hostOS) {
- case HostUnix:
- mode = (mode >> 16) & 0xffff;
- switch (mode & UnixFileAttributes::TypeMask) {
- case UnixFileAttributes::SymLink:
- fileInfo.isSymLink = true;
- break;
- case UnixFileAttributes::Dir:
- fileInfo.isDir = true;
- break;
- case UnixFileAttributes::File:
- default: // ### just for the case; should we warn?
- fileInfo.isFile = true;
- break;
- }
- fileInfo.permissions = modeToPermissions(mode);
- break;
- case HostFAT:
- case HostNTFS:
- case HostHPFS:
- case HostVFAT:
- switch (mode & WindowsFileAttributes::TypeMask) {
- case WindowsFileAttributes::Dir:
- fileInfo.isDir = true;
- break;
- case WindowsFileAttributes::File:
- default:
- fileInfo.isFile = true;
- break;
- }
- fileInfo.permissions |= QFile::ReadOwner | QFile::ReadUser | QFile::ReadGroup | QFile::ReadOther;
- if ((mode & WindowsFileAttributes::ReadOnly) == 0)
- fileInfo.permissions |= QFile::WriteOwner | QFile::WriteUser | QFile::WriteGroup | QFile::WriteOther;
- if (fileInfo.isDir)
- fileInfo.permissions |= QFile::ExeOwner | QFile::ExeUser | QFile::ExeGroup | QFile::ExeOther;
- break;
- default:
- qWarning("QZip: Zip entry format at %d is not supported.", index);
- return fileInfo; // we don't support anything else
- }
-
- ushort general_purpose_bits = readUShort(header.h.general_purpose_bits);
- // if bit 11 is set, the filename and comment fields must be encoded using UTF-8
- const bool inUtf8 = (general_purpose_bits & Utf8Names) != 0;
- fileInfo.filePath = inUtf8 ? QString::fromUtf8(header.file_name) : QString::fromLocal8Bit(header.file_name);
- fileInfo.crc = readUInt(header.h.crc_32);
- fileInfo.size = readUInt(header.h.uncompressed_size);
- fileInfo.lastModified = readMSDosDate(header.h.last_mod_file);
-
- // fix the file path, if broken (convert separators, eat leading and trailing ones)
- fileInfo.filePath = QDir::fromNativeSeparators(fileInfo.filePath);
- QStringView filePathRef(fileInfo.filePath);
- while (filePathRef.startsWith(QLatin1Char('.')) || filePathRef.startsWith(QLatin1Char('/')))
- filePathRef = filePathRef.mid(1);
- while (filePathRef.endsWith(QLatin1Char('/')))
- filePathRef.chop(1);
-
- fileInfo.filePath = filePathRef.toString();
- return fileInfo;
-}
-
-class QZipReaderPrivate : public QZipPrivate
-{
-public:
- QZipReaderPrivate(QIODevice *device, bool ownDev)
- : QZipPrivate(device, ownDev), status(QZipReader::NoError)
- {
- }
-
- void scanFiles();
-
- QZipReader::Status status;
-};
-
-class QZipWriterPrivate : public QZipPrivate
-{
-public:
- QZipWriterPrivate(QIODevice *device, bool ownDev)
- : QZipPrivate(device, ownDev),
- status(QZipWriter::NoError),
- permissions(QFile::ReadOwner | QFile::WriteOwner),
- compressionPolicy(QZipWriter::AlwaysCompress)
- {
- }
-
- QZipWriter::Status status;
- QFile::Permissions permissions;
- QZipWriter::CompressionPolicy compressionPolicy;
-
- enum EntryType { Directory, File, Symlink };
-
- void addEntry(EntryType type, const QString &fileName, const QByteArray &contents);
-};
-
-static LocalFileHeader toLocalHeader(const CentralFileHeader &ch)
-{
- LocalFileHeader h;
- writeUInt(h.signature, 0x04034b50);
- copyUShort(h.version_needed, ch.version_needed);
- copyUShort(h.general_purpose_bits, ch.general_purpose_bits);
- copyUShort(h.compression_method, ch.compression_method);
- copyUInt(h.last_mod_file, ch.last_mod_file);
- copyUInt(h.crc_32, ch.crc_32);
- copyUInt(h.compressed_size, ch.compressed_size);
- copyUInt(h.uncompressed_size, ch.uncompressed_size);
- copyUShort(h.file_name_length, ch.file_name_length);
- copyUShort(h.extra_field_length, ch.extra_field_length);
- return h;
-}
-
-void QZipReaderPrivate::scanFiles()
-{
- if (!dirtyFileTree)
- return;
-
- if (! (device->isOpen() || device->open(QIODevice::ReadOnly))) {
- status = QZipReader::FileOpenError;
- return;
- }
-
- if ((device->openMode() & QIODevice::ReadOnly) == 0) { // only read the index from readable files.
- status = QZipReader::FileReadError;
- return;
- }
-
- dirtyFileTree = false;
- uchar tmp[4];
- device->read((char *)tmp, 4);
- if (readUInt(tmp) != 0x04034b50) {
- qWarning("QZip: not a zip file!");
- return;
- }
-
- // find EndOfDirectory header
- int i = 0;
- int start_of_directory = -1;
- int num_dir_entries = 0;
- EndOfDirectory eod;
- while (start_of_directory == -1) {
- const int pos = device->size() - int(sizeof(EndOfDirectory)) - i;
- if (pos < 0 || i > 65535) {
- qWarning("QZip: EndOfDirectory not found");
- return;
- }
-
- device->seek(pos);
- device->read((char *)&eod, sizeof(EndOfDirectory));
- if (readUInt(eod.signature) == 0x06054b50)
- break;
- ++i;
- }
-
- // have the eod
- start_of_directory = readUInt(eod.dir_start_offset);
- num_dir_entries = readUShort(eod.num_dir_entries);
- ZDEBUG("start_of_directory at %d, num_dir_entries=%d", start_of_directory, num_dir_entries);
- int comment_length = readUShort(eod.comment_length);
- if (comment_length != i)
- qWarning("QZip: failed to parse zip file.");
- comment = device->read(qMin(comment_length, i));
-
-
- device->seek(start_of_directory);
- for (i = 0; i < num_dir_entries; ++i) {
- FileHeader header;
- int read = device->read((char *) &header.h, sizeof(CentralFileHeader));
- if (read < (int)sizeof(CentralFileHeader)) {
- qWarning("QZip: Failed to read complete header, index may be incomplete");
- break;
- }
- if (readUInt(header.h.signature) != 0x02014b50) {
- qWarning("QZip: invalid header signature, index may be incomplete");
- break;
- }
-
- int l = readUShort(header.h.file_name_length);
- header.file_name = device->read(l);
- if (header.file_name.length() != l) {
- qWarning("QZip: Failed to read filename from zip index, index may be incomplete");
- break;
- }
- l = readUShort(header.h.extra_field_length);
- header.extra_field = device->read(l);
- if (header.extra_field.length() != l) {
- qWarning("QZip: Failed to read extra field in zip file, skipping file, index may be incomplete");
- break;
- }
- l = readUShort(header.h.file_comment_length);
- header.file_comment = device->read(l);
- if (header.file_comment.length() != l) {
- qWarning("QZip: Failed to read read file comment, index may be incomplete");
- break;
- }
-
- ZDEBUG("found file '%s'", header.file_name.data());
- fileHeaders.append(header);
- }
-}
-
-void QZipWriterPrivate::addEntry(EntryType type, const QString &fileName, const QByteArray &contents/*, QFile::Permissions permissions, QZip::Method m*/)
-{
-#ifndef NDEBUG
- static const char *const entryTypes[] = {
- "directory",
- "file ",
- "symlink " };
- ZDEBUG() << "adding" << entryTypes[type] <<":" << fileName.toUtf8().data() << (type == 2 ? QByteArray(" -> " + contents).constData() : "");
-#endif
-
- if (! (device->isOpen() || device->open(QIODevice::WriteOnly))) {
- status = QZipWriter::FileOpenError;
- return;
- }
- device->seek(start_of_directory);
-
- // don't compress small files
- QZipWriter::CompressionPolicy compression = compressionPolicy;
- if (compressionPolicy == QZipWriter::AutoCompress) {
- if (contents.length() < 64)
- compression = QZipWriter::NeverCompress;
- else
- compression = QZipWriter::AlwaysCompress;
- }
-
- FileHeader header;
- memset(&header.h, 0, sizeof(CentralFileHeader));
- writeUInt(header.h.signature, 0x02014b50);
-
- writeUShort(header.h.version_needed, ZIP_VERSION);
- writeUInt(header.h.uncompressed_size, contents.length());
- writeMSDosDate(header.h.last_mod_file, QDateTime::currentDateTime());
- QByteArray data = contents;
- if (compression == QZipWriter::AlwaysCompress) {
- writeUShort(header.h.compression_method, CompressionMethodDeflated);
-
- ulong len = contents.length();
- // shamelessly copied form zlib
- len += (len >> 12) + (len >> 14) + 11;
- int res;
- do {
- data.resize(len);
- res = deflate((uchar*)data.data(), &len, (const uchar*)contents.constData(), contents.length());
-
- switch (res) {
- case Z_OK:
- data.resize(len);
- break;
- case Z_MEM_ERROR:
- qWarning("QZip: Z_MEM_ERROR: Not enough memory to compress file, skipping");
- data.resize(0);
- break;
- case Z_BUF_ERROR:
- len *= 2;
- break;
- }
- } while (res == Z_BUF_ERROR);
- }
-// TODO add a check if data.length() > contents.length(). Then try to store the original and revert the compression method to be uncompressed
- writeUInt(header.h.compressed_size, data.length());
- uint crc_32 = ::crc32(0, nullptr, 0);
- crc_32 = ::crc32(crc_32, (const uchar *)contents.constData(), contents.length());
- writeUInt(header.h.crc_32, crc_32);
-
- // if bit 11 is set, the filename and comment fields must be encoded using UTF-8
- ushort general_purpose_bits = Utf8Names; // always use utf-8
- writeUShort(header.h.general_purpose_bits, general_purpose_bits);
-
- const bool inUtf8 = (general_purpose_bits & Utf8Names) != 0;
- header.file_name = inUtf8 ? fileName.toUtf8() : fileName.toLocal8Bit();
- if (header.file_name.size() > 0xffff) {
- qWarning("QZip: Filename is too long, chopping it to 65535 bytes");
- header.file_name = header.file_name.left(0xffff); // ### don't break the utf-8 sequence, if any
- }
- if (header.file_comment.size() + header.file_name.size() > 0xffff) {
- qWarning("QZip: File comment is too long, chopping it to 65535 bytes");
- header.file_comment.truncate(0xffff - header.file_name.size()); // ### don't break the utf-8 sequence, if any
- }
- writeUShort(header.h.file_name_length, header.file_name.length());
- //h.extra_field_length[2];
-
- writeUShort(header.h.version_made, HostUnix << 8);
- //uchar internal_file_attributes[2];
- //uchar external_file_attributes[4];
- quint32 mode = permissionsToMode(permissions);
- switch (type) {
- case Symlink:
- mode |= UnixFileAttributes::SymLink;
- break;
- case Directory:
- mode |= UnixFileAttributes::Dir;
- break;
- case File:
- mode |= UnixFileAttributes::File;
- break;
- default:
- Q_UNREACHABLE();
- break;
- }
- writeUInt(header.h.external_file_attributes, mode << 16);
- writeUInt(header.h.offset_local_header, start_of_directory);
-
-
- fileHeaders.append(header);
-
- LocalFileHeader h = toLocalHeader(header.h);
- device->write((const char *)&h, sizeof(LocalFileHeader));
- device->write(header.file_name);
- device->write(data);
- start_of_directory = device->pos();
- dirtyFileTree = true;
-}
-
-////////////////////////////// Reader
-
-/*!
- \class QZipReader::FileInfo
- \internal
- Represents one entry in the zip table of contents.
-*/
-
-/*!
- \variable FileInfo::filePath
- The full filepath inside the archive.
-*/
-
-/*!
- \variable FileInfo::isDir
- A boolean type indicating if the entry is a directory.
-*/
-
-/*!
- \variable FileInfo::isFile
- A boolean type, if it is one this entry is a file.
-*/
-
-/*!
- \variable FileInfo::isSymLink
- A boolean type, if it is one this entry is symbolic link.
-*/
-
-/*!
- \variable FileInfo::permissions
- A list of flags for the permissions of this entry.
-*/
-
-/*!
- \variable FileInfo::crc
- The calculated checksum as a crc type.
-*/
-
-/*!
- \variable FileInfo::size
- The total size of the unpacked content.
-*/
-
-/*!
- \class QZipReader
- \internal
- \since 4.5
-
- \brief the QZipReader class provides a way to inspect the contents of a zip
- archive and extract individual files from it.
-
- QZipReader can be used to read a zip archive either from a file or from any
- device. An in-memory QBuffer for instance. The reader can be used to read
- which files are in the archive using fileInfoList() and entryInfoAt() but
- also to extract individual files using fileData() or even to extract all
- files in the archive using extractAll()
-*/
-
-/*!
- Create a new zip archive that operates on the \a fileName. The file will be
- opened with the \a mode.
-*/
-QZipReader::QZipReader(const QString &archive, QIODevice::OpenMode mode)
-{
- QScopedPointer<QFile> f(new QFile(archive));
- const bool result = f->open(mode);
- QZipReader::Status status;
- const QFileDevice::FileError error = f->error();
- if (result && error == QFile::NoError) {
- status = NoError;
- } else {
- if (error == QFile::ReadError)
- status = FileReadError;
- else if (error == QFile::OpenError)
- status = FileOpenError;
- else if (error == QFile::PermissionsError)
- status = FilePermissionsError;
- else
- status = FileError;
- }
-
- d = new QZipReaderPrivate(f.data(), /*ownDevice=*/true);
- f.take();
- d->status = status;
-}
-
-/*!
- Create a new zip archive that operates on the archive found in \a device.
- You have to open the device previous to calling the constructor and only a
- device that is readable will be scanned for zip filecontent.
- */
-QZipReader::QZipReader(QIODevice *device)
- : d(new QZipReaderPrivate(device, /*ownDevice=*/false))
-{
- Q_ASSERT(device);
-}
-
-/*!
- Desctructor
-*/
-QZipReader::~QZipReader()
-{
- close();
- delete d;
-}
-
-/*!
- Returns device used for reading zip archive.
-*/
-QIODevice* QZipReader::device() const
-{
- return d->device;
-}
-
-/*!
- Returns \c true if the user can read the file; otherwise returns \c false.
-*/
-bool QZipReader::isReadable() const
-{
- return d->device->isReadable();
-}
-
-/*!
- Returns \c true if the file exists; otherwise returns \c false.
-*/
-bool QZipReader::exists() const
-{
- QFile *f = qobject_cast<QFile*> (d->device);
- if (f == nullptr)
- return true;
- return f->exists();
-}
-
-/*!
- Returns the list of files the archive contains.
-*/
-QList<QZipReader::FileInfo> QZipReader::fileInfoList() const
-{
- d->scanFiles();
- QList<FileInfo> files;
- const int numFileHeaders = d->fileHeaders.size();
- files.reserve(numFileHeaders);
- for (int i = 0; i < numFileHeaders; ++i)
- files.append(d->fillFileInfo(i));
- return files;
-
-}
-
-/*!
- Return the number of items in the zip archive.
-*/
-int QZipReader::count() const
-{
- d->scanFiles();
- return d->fileHeaders.count();
-}
-
-/*!
- Returns a FileInfo of an entry in the zipfile.
- The \a index is the index into the directory listing of the zipfile.
- Returns an invalid FileInfo if \a index is out of boundaries.
-
- \sa fileInfoList()
-*/
-QZipReader::FileInfo QZipReader::entryInfoAt(int index) const
-{
- d->scanFiles();
- if (index >= 0 && index < d->fileHeaders.count())
- return d->fillFileInfo(index);
- return QZipReader::FileInfo();
-}
-
-/*!
- Fetch the file contents from the zip archive and return the uncompressed bytes.
-*/
-QByteArray QZipReader::fileData(const QString &fileName) const
-{
- d->scanFiles();
- int i;
- for (i = 0; i < d->fileHeaders.size(); ++i) {
- if (QString::fromLocal8Bit(d->fileHeaders.at(i).file_name) == fileName)
- break;
- }
- if (i == d->fileHeaders.size())
- return QByteArray();
-
- FileHeader header = d->fileHeaders.at(i);
-
- ushort version_needed = readUShort(header.h.version_needed);
- if (version_needed > ZIP_VERSION) {
- qWarning("QZip: .ZIP specification version %d implementationis needed to extract the data.", version_needed);
- return QByteArray();
- }
-
- ushort general_purpose_bits = readUShort(header.h.general_purpose_bits);
- int compressed_size = readUInt(header.h.compressed_size);
- int uncompressed_size = readUInt(header.h.uncompressed_size);
- int start = readUInt(header.h.offset_local_header);
- //qDebug("uncompressing file %d: local header at %d", i, start);
-
- d->device->seek(start);
- LocalFileHeader lh;
- d->device->read((char *)&lh, sizeof(LocalFileHeader));
- uint skip = readUShort(lh.file_name_length) + readUShort(lh.extra_field_length);
- d->device->seek(d->device->pos() + skip);
-
- int compression_method = readUShort(lh.compression_method);
- //qDebug("file=%s: compressed_size=%d, uncompressed_size=%d", fileName.toLocal8Bit().data(), compressed_size, uncompressed_size);
-
- if ((general_purpose_bits & Encrypted) != 0) {
- qWarning("QZip: Unsupported encryption method is needed to extract the data.");
- return QByteArray();
- }
-
- //qDebug("file at %lld", d->device->pos());
- QByteArray compressed = d->device->read(compressed_size);
- if (compression_method == CompressionMethodStored) {
- // no compression
- compressed.truncate(uncompressed_size);
- return compressed;
- } else if (compression_method == CompressionMethodDeflated) {
- // Deflate
- //qDebug("compressed=%d", compressed.size());
- compressed.truncate(compressed_size);
- QByteArray baunzip;
- ulong len = qMax(uncompressed_size, 1);
- int res;
- do {
- baunzip.resize(len);
- res = inflate((uchar*)baunzip.data(), &len,
- (const uchar*)compressed.constData(), compressed_size);
-
- switch (res) {
- case Z_OK:
- if ((int)len != baunzip.size())
- baunzip.resize(len);
- break;
- case Z_MEM_ERROR:
- qWarning("QZip: Z_MEM_ERROR: Not enough memory");
- break;
- case Z_BUF_ERROR:
- len *= 2;
- break;
- case Z_DATA_ERROR:
- qWarning("QZip: Z_DATA_ERROR: Input data is corrupted");
- break;
- }
- } while (res == Z_BUF_ERROR);
- return baunzip;
- }
-
- qWarning("QZip: Unsupported compression method %d is needed to extract the data.", compression_method);
- return QByteArray();
-}
-
-/*!
- Extracts the full contents of the zip file into \a destinationDir on
- the local filesystem.
- In case writing or linking a file fails, the extraction will be aborted.
-*/
-bool QZipReader::extractAll(const QString &destinationDir) const
-{
- QDir baseDir(destinationDir);
-
- // create directories first
- const QList<FileInfo> allFiles = fileInfoList();
- for (const FileInfo &fi : allFiles) {
- const QString absPath = destinationDir + QDir::separator() + fi.filePath;
- if (fi.isDir) {
- if (!baseDir.mkpath(fi.filePath))
- return false;
- if (!QFile::setPermissions(absPath, fi.permissions))
- return false;
- }
- }
-
- // set up symlinks
- for (const FileInfo &fi : allFiles) {
- const QString absPath = destinationDir + QDir::separator() + fi.filePath;
- if (fi.isSymLink) {
- QString destination = QFile::decodeName(fileData(fi.filePath));
- if (destination.isEmpty())
- return false;
- QFileInfo linkFi(absPath);
- if (!QFile::exists(linkFi.absolutePath()))
- QDir::root().mkpath(linkFi.absolutePath());
- if (!QFile::link(destination, absPath))
- return false;
- /* cannot change permission of links
- if (!QFile::setPermissions(absPath, fi.permissions))
- return false;
- */
- }
- }
-
- for (const FileInfo &fi : allFiles) {
- const QString absPath = destinationDir + QDir::separator() + fi.filePath;
- if (fi.isFile) {
- QFile f(absPath);
- if (!f.open(QIODevice::WriteOnly))
- return false;
- f.write(fileData(fi.filePath));
- f.setPermissions(fi.permissions);
- f.close();
- }
- }
-
- return true;
-}
-
-/*!
- \enum QZipReader::Status
-
- The following status values are possible:
-
- \value NoError No error occurred.
- \value FileReadError An error occurred when reading from the file.
- \value FileOpenError The file could not be opened.
- \value FilePermissionsError The file could not be accessed.
- \value FileError Another file error occurred.
-*/
-
-/*!
- Returns a status code indicating the first error that was met by QZipReader,
- or QZipReader::NoError if no error occurred.
-*/
-QZipReader::Status QZipReader::status() const
-{
- return d->status;
-}
-
-/*!
- Close the zip file.
-*/
-void QZipReader::close()
-{
- d->device->close();
-}
-
-////////////////////////////// Writer
-
-/*!
- \class QZipWriter
- \internal
- \since 4.5
-
- \brief the QZipWriter class provides a way to create a new zip archive.
-
- QZipWriter can be used to create a zip archive containing any number of files
- and directories. The files in the archive will be compressed in a way that is
- compatible with common zip reader applications.
-*/
-
-
-/*!
- Create a new zip archive that operates on the \a archive filename. The file will
- be opened with the \a mode.
- \sa isValid()
-*/
-QZipWriter::QZipWriter(const QString &fileName, QIODevice::OpenMode mode)
-{
- QScopedPointer<QFile> f(new QFile(fileName));
- QZipWriter::Status status;
- if (f->open(mode) && f->error() == QFile::NoError)
- status = QZipWriter::NoError;
- else {
- if (f->error() == QFile::WriteError)
- status = QZipWriter::FileWriteError;
- else if (f->error() == QFile::OpenError)
- status = QZipWriter::FileOpenError;
- else if (f->error() == QFile::PermissionsError)
- status = QZipWriter::FilePermissionsError;
- else
- status = QZipWriter::FileError;
- }
-
- d = new QZipWriterPrivate(f.data(), /*ownDevice=*/true);
- f.take();
- d->status = status;
-}
-
-/*!
- Create a new zip archive that operates on the archive found in \a device.
- You have to open the device previous to calling the constructor and
- only a device that is readable will be scanned for zip filecontent.
- */
-QZipWriter::QZipWriter(QIODevice *device)
- : d(new QZipWriterPrivate(device, /*ownDevice=*/false))
-{
- Q_ASSERT(device);
-}
-
-QZipWriter::~QZipWriter()
-{
- close();
- delete d;
-}
-
-/*!
- Returns device used for writing zip archive.
-*/
-QIODevice* QZipWriter::device() const
-{
- return d->device;
-}
-
-/*!
- Returns \c true if the user can write to the archive; otherwise returns \c false.
-*/
-bool QZipWriter::isWritable() const
-{
- return d->device->isWritable();
-}
-
-/*!
- Returns \c true if the file exists; otherwise returns \c false.
-*/
-bool QZipWriter::exists() const
-{
- QFile *f = qobject_cast<QFile*> (d->device);
- if (f == nullptr)
- return true;
- return f->exists();
-}
-
-/*!
- \enum QZipWriter::Status
-
- The following status values are possible:
-
- \value NoError No error occurred.
- \value FileWriteError An error occurred when writing to the device.
- \value FileOpenError The file could not be opened.
- \value FilePermissionsError The file could not be accessed.
- \value FileError Another file error occurred.
-*/
-
-/*!
- Returns a status code indicating the first error that was met by QZipWriter,
- or QZipWriter::NoError if no error occurred.
-*/
-QZipWriter::Status QZipWriter::status() const
-{
- return d->status;
-}
-
-/*!
- \enum QZipWriter::CompressionPolicy
-
- \value AlwaysCompress A file that is added is compressed.
- \value NeverCompress A file that is added will be stored without changes.
- \value AutoCompress A file that is added will be compressed only if that will give a smaller file.
-*/
-
-/*!
- Sets the policy for compressing newly added files to the new \a policy.
-
- \note the default policy is AlwaysCompress
-
- \sa compressionPolicy()
- \sa addFile()
-*/
-void QZipWriter::setCompressionPolicy(CompressionPolicy policy)
-{
- d->compressionPolicy = policy;
-}
-
-/*!
- Returns the currently set compression policy.
- \sa setCompressionPolicy()
- \sa addFile()
-*/
-QZipWriter::CompressionPolicy QZipWriter::compressionPolicy() const
-{
- return d->compressionPolicy;
-}
-
-/*!
- Sets the permissions that will be used for newly added files.
-
- \note the default permissions are QFile::ReadOwner | QFile::WriteOwner.
-
- \sa creationPermissions()
- \sa addFile()
-*/
-void QZipWriter::setCreationPermissions(QFile::Permissions permissions)
-{
- d->permissions = permissions;
-}
-
-/*!
- Returns the currently set creation permissions.
-
- \sa setCreationPermissions()
- \sa addFile()
-*/
-QFile::Permissions QZipWriter::creationPermissions() const
-{
- return d->permissions;
-}
-
-/*!
- Add a file to the archive with \a data as the file contents.
- The file will be stored in the archive using the \a fileName which
- includes the full path in the archive.
-
- The new file will get the file permissions based on the current
- creationPermissions and it will be compressed using the zip compression
- based on the current compression policy.
-
- \sa setCreationPermissions()
- \sa setCompressionPolicy()
-*/
-void QZipWriter::addFile(const QString &fileName, const QByteArray &data)
-{
- d->addEntry(QZipWriterPrivate::File, QDir::fromNativeSeparators(fileName), data);
-}
-
-/*!
- Add a file to the archive with \a device as the source of the contents.
- The contents returned from QIODevice::readAll() will be used as the
- filedata.
- The file will be stored in the archive using the \a fileName which
- includes the full path in the archive.
-*/
-void QZipWriter::addFile(const QString &fileName, QIODevice *device)
-{
- Q_ASSERT(device);
- QIODevice::OpenMode mode = device->openMode();
- bool opened = false;
- if ((mode & QIODevice::ReadOnly) == 0) {
- opened = true;
- if (! device->open(QIODevice::ReadOnly)) {
- d->status = FileOpenError;
- return;
- }
- }
- d->addEntry(QZipWriterPrivate::File, QDir::fromNativeSeparators(fileName), device->readAll());
- if (opened)
- device->close();
-}
-
-/*!
- Create a new directory in the archive with the specified \a dirName and
- the \a permissions;
-*/
-void QZipWriter::addDirectory(const QString &dirName)
-{
- QString name(QDir::fromNativeSeparators(dirName));
- // separator is mandatory
- if (!name.endsWith(QLatin1Char('/')))
- name.append(QLatin1Char('/'));
- d->addEntry(QZipWriterPrivate::Directory, name, QByteArray());
-}
-
-/*!
- Create a new symbolic link in the archive with the specified \a dirName
- and the \a permissions;
- A symbolic link contains the destination (relative) path and name.
-*/
-void QZipWriter::addSymLink(const QString &fileName, const QString &destination)
-{
- d->addEntry(QZipWriterPrivate::Symlink, QDir::fromNativeSeparators(fileName), QFile::encodeName(destination));
-}
-
-/*!
- Closes the zip file.
-*/
-void QZipWriter::close()
-{
- if (!(d->device->openMode() & QIODevice::WriteOnly)) {
- d->device->close();
- return;
- }
-
- //qDebug("QZip::close writing directory, %d entries", d->fileHeaders.size());
- d->device->seek(d->start_of_directory);
- // write new directory
- for (int i = 0; i < d->fileHeaders.size(); ++i) {
- const FileHeader &header = d->fileHeaders.at(i);
- d->device->write((const char *)&header.h, sizeof(CentralFileHeader));
- d->device->write(header.file_name);
- d->device->write(header.extra_field);
- d->device->write(header.file_comment);
- }
- int dir_size = d->device->pos() - d->start_of_directory;
- // write end of directory
- EndOfDirectory eod;
- memset(&eod, 0, sizeof(EndOfDirectory));
- writeUInt(eod.signature, 0x06054b50);
- //uchar this_disk[2];
- //uchar start_of_directory_disk[2];
- writeUShort(eod.num_dir_entries_this_disk, d->fileHeaders.size());
- writeUShort(eod.num_dir_entries, d->fileHeaders.size());
- writeUInt(eod.directory_size, dir_size);
- writeUInt(eod.dir_start_offset, d->start_of_directory);
- writeUShort(eod.comment_length, d->comment.length());
-
- d->device->write((const char *)&eod, sizeof(EndOfDirectory));
- d->device->write(d->comment);
- d->device->close();
-}
-
-QT_END_NAMESPACE
-
-#endif // QT_NO_TEXTODFWRITER
diff --git a/src/gui/text/qzipreader_p.h b/src/gui/text/qzipreader_p.h
deleted file mode 100644
index 0f695c74d5..0000000000
--- a/src/gui/text/qzipreader_p.h
+++ /dev/null
@@ -1,127 +0,0 @@
-/****************************************************************************
-**
-** 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$
-**
-****************************************************************************/
-
-#ifndef QZIPREADER_H
-#define QZIPREADER_H
-
-#include <QtGui/private/qtguiglobal_p.h>
-#include <QtCore/qglobal.h>
-
-#ifndef QT_NO_TEXTODFWRITER
-
-//
-// W A R N I N G
-// -------------
-//
-// This file is not part of the Qt API. It exists for the convenience
-// of the QZipReader class. This header file may change from
-// version to version without notice, or even be removed.
-//
-// We mean it.
-//
-
-#include <QtCore/qdatetime.h>
-#include <QtCore/qfile.h>
-#include <QtCore/qstring.h>
-
-QT_BEGIN_NAMESPACE
-
-class QZipReaderPrivate;
-
-class Q_GUI_EXPORT QZipReader
-{
-public:
- explicit QZipReader(const QString &fileName, QIODevice::OpenMode mode = QIODevice::ReadOnly );
-
- explicit QZipReader(QIODevice *device);
- ~QZipReader();
-
- QIODevice* device() const;
-
- bool isReadable() const;
- bool exists() const;
-
- struct FileInfo
- {
- FileInfo() noexcept
- : isDir(false), isFile(false), isSymLink(false), crc(0), size(0)
- {}
-
- bool isValid() const noexcept { return isDir || isFile || isSymLink; }
-
- QString filePath;
- uint isDir : 1;
- uint isFile : 1;
- uint isSymLink : 1;
- QFile::Permissions permissions;
- uint crc;
- qint64 size;
- QDateTime lastModified;
- };
-
- QList<FileInfo> fileInfoList() const;
- int count() const;
-
- FileInfo entryInfoAt(int index) const;
- QByteArray fileData(const QString &fileName) const;
- bool extractAll(const QString &destinationDir) const;
-
- enum Status {
- NoError,
- FileReadError,
- FileOpenError,
- FilePermissionsError,
- FileError
- };
-
- Status status() const;
-
- void close();
-
-private:
- QZipReaderPrivate *d;
- Q_DISABLE_COPY_MOVE(QZipReader)
-};
-Q_DECLARE_TYPEINFO(QZipReader::FileInfo, Q_MOVABLE_TYPE);
-Q_DECLARE_TYPEINFO(QZipReader::Status, Q_PRIMITIVE_TYPE);
-
-QT_END_NAMESPACE
-
-#endif // QT_NO_TEXTODFWRITER
-#endif // QZIPREADER_H
diff --git a/src/gui/text/qzipwriter_p.h b/src/gui/text/qzipwriter_p.h
deleted file mode 100644
index b3bb5929cf..0000000000
--- a/src/gui/text/qzipwriter_p.h
+++ /dev/null
@@ -1,117 +0,0 @@
-/****************************************************************************
-**
-** 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$
-**
-****************************************************************************/
-#ifndef QZIPWRITER_H
-#define QZIPWRITER_H
-
-#include <QtGui/private/qtguiglobal_p.h>
-
-#ifndef QT_NO_TEXTODFWRITER
-
-//
-// W A R N I N G
-// -------------
-//
-// This file is not part of the Qt API. It exists for the convenience
-// of the QZipWriter class. This header file may change from
-// version to version without notice, or even be removed.
-//
-// We mean it.
-//
-
-#include <QtCore/qstring.h>
-#include <QtCore/qfile.h>
-
-QT_BEGIN_NAMESPACE
-
-class QZipWriterPrivate;
-
-
-class Q_GUI_EXPORT QZipWriter
-{
-public:
- explicit QZipWriter(const QString &fileName, QIODevice::OpenMode mode = (QIODevice::WriteOnly | QIODevice::Truncate) );
-
- explicit QZipWriter(QIODevice *device);
- ~QZipWriter();
-
- QIODevice* device() const;
-
- bool isWritable() const;
- bool exists() const;
-
- enum Status {
- NoError,
- FileWriteError,
- FileOpenError,
- FilePermissionsError,
- FileError
- };
-
- Status status() const;
-
- enum CompressionPolicy {
- AlwaysCompress,
- NeverCompress,
- AutoCompress
- };
-
- void setCompressionPolicy(CompressionPolicy policy);
- CompressionPolicy compressionPolicy() const;
-
- void setCreationPermissions(QFile::Permissions permissions);
- QFile::Permissions creationPermissions() const;
-
- void addFile(const QString &fileName, const QByteArray &data);
-
- void addFile(const QString &fileName, QIODevice *device);
-
- void addDirectory(const QString &dirName);
-
- void addSymLink(const QString &fileName, const QString &destination);
-
- void close();
-private:
- QZipWriterPrivate *d;
- Q_DISABLE_COPY_MOVE(QZipWriter)
-};
-
-QT_END_NAMESPACE
-
-#endif // QT_NO_TEXTODFWRITER
-#endif // QZIPWRITER_H
diff --git a/src/gui/text/text.pri b/src/gui/text/text.pri
deleted file mode 100644
index 4c927a876b..0000000000
--- a/src/gui/text/text.pri
+++ /dev/null
@@ -1,139 +0,0 @@
-# Qt kernel module
-
-HEADERS += \
- text/qfont.h \
- text/qfontdatabase.h \
- text/qfontdatabase_p.h \
- text/qfontengine_p.h \
- text/qfontengineglyphcache_p.h \
- text/qfontinfo.h \
- text/qfontmetrics.h \
- text/qfont_p.h \
- text/qfontsubset_p.h \
- text/qtextengine_p.h \
- text/qtextlayout.h \
- text/qtextformat.h \
- text/qtextformat_p.h \
- text/qtextobject.h \
- text/qtextobject_p.h \
- text/qtextoption.h \
- text/qfragmentmap_p.h \
- text/qtextdocument.h \
- text/qtextdocument_p.h \
- text/qtexthtmlparser_p.h \
- text/qabstracttextdocumentlayout.h \
- text/qabstracttextdocumentlayout_p.h \
- text/qtextdocumentlayout_p.h \
- text/qtextcursor.h \
- text/qtextcursor_p.h \
- text/qtextdocumentfragment.h \
- text/qtextdocumentfragment_p.h \
- text/qtextimagehandler_p.h \
- text/qtexttable.h \
- text/qtextlist.h \
- text/qsyntaxhighlighter.h \
- text/qtextdocumentwriter.h \
- text/qtexttable_p.h \
- text/qstatictext_p.h \
- text/qstatictext.h \
- text/qrawfont.h \
- text/qrawfont_p.h \
- text/qglyphrun.h \
- text/qglyphrun_p.h \
- text/qdistancefield_p.h \
- text/qinputcontrol_p.h
-
-SOURCES += \
- text/qfont.cpp \
- text/qfontengine.cpp \
- text/qfontengineglyphcache.cpp \
- text/qfontsubset.cpp \
- text/qfontmetrics.cpp \
- text/qfontdatabase.cpp \
- text/qtextengine.cpp \
- text/qtextlayout.cpp \
- text/qtextformat.cpp \
- text/qtextobject.cpp \
- text/qtextoption.cpp \
- text/qfragmentmap.cpp \
- text/qtextdocument.cpp \
- text/qtextdocument_p.cpp \
- text/qtexthtmlparser.cpp \
- text/qabstracttextdocumentlayout.cpp \
- text/qtextdocumentlayout.cpp \
- text/qtextcursor.cpp \
- text/qtextdocumentfragment.cpp \
- text/qtextimagehandler.cpp \
- text/qtexttable.cpp \
- text/qtextlist.cpp \
- text/qtextdocumentwriter.cpp \
- text/qsyntaxhighlighter.cpp \
- text/qstatictext.cpp \
- text/qrawfont.cpp \
- text/qglyphrun.cpp \
- text/qdistancefield.cpp \
- text/qinputcontrol.cpp
-
-SOURCES += \
- text/qplatformfontdatabase.cpp
-
-HEADERS += \
- text/qplatformfontdatabase.h
-
-qtConfig(harfbuzz) {
- QMAKE_USE_PRIVATE += harfbuzz
-
- SOURCES += text/qharfbuzzng.cpp
- HEADERS += text/qharfbuzzng_p.h
-}
-
-qtConfig(textodfwriter) {
- HEADERS += \
- text/qtextodfwriter_p.h \
- text/qzipreader_p.h \
- text/qzipwriter_p.h
- SOURCES += \
- text/qtextodfwriter.cpp \
- text/qzip.cpp
-}
-
-qtConfig(textmarkdownreader) {
- qtConfig(system-textmarkdownreader) {
- QMAKE_USE_PRIVATE += libmd4c
- } else {
- include($$PWD/../../3rdparty/md4c.pri)
- }
- HEADERS += \
- text/qtextmarkdownimporter_p.h
- SOURCES += \
- text/qtextmarkdownimporter.cpp
-}
-
-qtConfig(textmarkdownwriter) {
- HEADERS += \
- text/qtextmarkdownwriter_p.h
- SOURCES += \
- text/qtextmarkdownwriter.cpp
-}
-
-qtConfig(cssparser) {
- HEADERS += \
- text/qcssparser_p.h
- SOURCES += \
- text/qcssparser.cpp
-}
-
-qtConfig(freetype) {
- include($$PWD/freetype/freetype.pri)
- unix:!darwin {
- include($$PWD/unix/unix.pri)
- }
-}
-
-darwin {
- include($$PWD/coretext/coretext.pri)
-}
-
-win32 {
- include($$PWD/windows/windows.pri)
-}
diff --git a/src/gui/text/unix/qfontconfigdatabase.cpp b/src/gui/text/unix/qfontconfigdatabase.cpp
index a8e28618f4..d607d38235 100644
--- a/src/gui/text/unix/qfontconfigdatabase.cpp
+++ b/src/gui/text/unix/qfontconfigdatabase.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2019 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) 2019 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 "qfontconfigdatabase_p.h"
#include "qfontenginemultifontconfig_p.h"
@@ -52,7 +16,6 @@
#include <qpa/qplatformservices.h>
#include <QtGui/private/qguiapplication_p.h>
-#include <QtGui/private/qhighdpiscaling_p.h>
#include <QtGui/qguiapplication.h>
@@ -65,6 +28,8 @@
QT_BEGIN_NAMESPACE
+Q_DECLARE_LOGGING_CATEGORY(lcFontDb)
+
static inline int mapToQtWeightForRange(int fcweight, int fcLower, int fcUpper, int qtLower, int qtUpper)
{
return qtLower + ((fcweight - fcLower) * (qtUpper - qtLower)) / (fcUpper - fcLower);
@@ -276,7 +241,14 @@ static const char specialLanguages[][6] = {
"", // Chorasmian
"", // DivesAkuru
"", // KhitanSmallScript
- "" // Yezidi
+ "", // Yezidi
+ "", // CyproMinoan
+ "", // OldUyghur
+ "", // Tangsa
+ "", // Toto
+ "", // Vithkuqi
+ "", // Kawi
+ "", // NagMundari
};
static_assert(sizeof specialLanguages / sizeof *specialLanguages == QChar::ScriptCount);
@@ -395,7 +367,10 @@ static inline bool requiresOpenType(int writingSystem)
|| writingSystem == QFontDatabase::Khmer || writingSystem == QFontDatabase::Nko);
}
-static void populateFromPattern(FcPattern *pattern, QFontDatabasePrivate::ApplicationFont *applicationFont = nullptr)
+static void populateFromPattern(FcPattern *pattern,
+ QFontDatabasePrivate::ApplicationFont *applicationFont = nullptr,
+ FT_Face face = nullptr,
+ QFontconfigDatabase *db = nullptr)
{
QString familyName;
QString familyNameLang;
@@ -517,7 +492,21 @@ static void populateFromPattern(FcPattern *pattern, QFontDatabasePrivate::Applic
applicationFont->properties.append(properties);
}
- QPlatformFontDatabase::registerFont(familyName,styleName,QLatin1String((const char *)foundry_value),weight,style,stretch,antialias,scalable,pixel_size,fixedPitch,writingSystems,fontFile);
+ QPlatformFontDatabase::registerFont(familyName,styleName,QLatin1StringView((const char *)foundry_value),weight,style,stretch,antialias,scalable,pixel_size,fixedPitch,writingSystems,fontFile);
+ if (applicationFont != nullptr && face != nullptr && db != nullptr) {
+ db->addNamedInstancesForFace(face,
+ indexValue,
+ familyName,
+ styleName,
+ weight,
+ stretch,
+ style,
+ fixedPitch,
+ writingSystems,
+ QByteArray((const char*)file_value),
+ applicationFont->data);
+ }
+
// qDebug() << familyName << (const char *)foundry_value << weight << style << &writingSystems << scalable << true << pixel_size;
for (int k = 1; FcPatternGetString(pattern, FC_FAMILY, k, &value) == FcResultMatch; ++k) {
@@ -549,7 +538,7 @@ static void populateFromPattern(FcPattern *pattern, QFontDatabasePrivate::Applic
applicationFont->properties.append(properties);
}
FontFile *altFontFile = new FontFile(*fontFile);
- QPlatformFontDatabase::registerFont(altFamilyName, altStyleName, QLatin1String((const char *)foundry_value),weight,style,stretch,antialias,scalable,pixel_size,fixedPitch,writingSystems,altFontFile);
+ QPlatformFontDatabase::registerFont(altFamilyName, altStyleName, QLatin1StringView((const char *)foundry_value),weight,style,stretch,antialias,scalable,pixel_size,fixedPitch,writingSystems,altFontFile);
} else {
QPlatformFontDatabase::registerAliasToFontFamily(familyName, altFamilyName);
}
@@ -557,6 +546,16 @@ static void populateFromPattern(FcPattern *pattern, QFontDatabasePrivate::Applic
}
+static bool isDprScaling()
+{
+ return !qFuzzyCompare(qApp->devicePixelRatio(), 1.0);
+}
+
+QFontconfigDatabase::~QFontconfigDatabase()
+{
+ FcConfigDestroy(FcConfigGetCurrent());
+}
+
void QFontconfigDatabase::populateFontDatabase()
{
FcInit();
@@ -580,9 +579,17 @@ void QFontconfigDatabase::populateFontDatabase()
FcObjectSetAdd(os, *p);
++p;
}
+
+#ifdef FC_VARIABLE
+ /* Support the named instance of Variable Fonts. */
+ FcPatternAddBool(pattern, FC_VARIABLE, FcFalse);
+#endif
+
fonts = FcFontList(nullptr, pattern, os);
FcObjectSetDestroy(os);
FcPatternDestroy(pattern);
+ if (!fonts)
+ return;
}
for (int i = 0; i < fonts->nfont; i++)
@@ -635,7 +642,7 @@ QFontEngineMulti *QFontconfigDatabase::fontEngineMulti(QFontEngine *fontEngine,
}
namespace {
-QFontEngine::HintStyle defaultHintStyleFromMatch(QFont::HintingPreference hintingPreference, FcPattern *match, bool useXftConf)
+QFontEngine::HintStyle defaultHintStyleFromMatch(QFont::HintingPreference hintingPreference, FcPattern *match, bool preferXftConf)
{
switch (hintingPreference) {
case QFont::PreferNoHinting:
@@ -648,9 +655,16 @@ QFontEngine::HintStyle defaultHintStyleFromMatch(QFont::HintingPreference hintin
break;
}
- if (QHighDpiScaling::isActive())
+ if (isDprScaling())
return QFontEngine::HintNone;
+ void *hintStyleResource =
+ QGuiApplication::platformNativeInterface()->nativeResourceForScreen("hintstyle",
+ QGuiApplication::primaryScreen());
+ int xftHintStyle = int(reinterpret_cast<qintptr>(hintStyleResource));
+ if (preferXftConf && xftHintStyle > 0)
+ return QFontEngine::HintStyle(xftHintStyle - 1);
+
int hint_style = 0;
if (FcPatternGetInteger (match, FC_HINT_STYLE, 0, &hint_style) == FcResultMatch) {
switch (hint_style) {
@@ -667,21 +681,21 @@ QFontEngine::HintStyle defaultHintStyleFromMatch(QFont::HintingPreference hintin
break;
}
}
-
- if (useXftConf) {
- void *hintStyleResource =
- QGuiApplication::platformNativeInterface()->nativeResourceForScreen("hintstyle",
- QGuiApplication::primaryScreen());
- int hintStyle = int(reinterpret_cast<qintptr>(hintStyleResource));
- if (hintStyle > 0)
- return QFontEngine::HintStyle(hintStyle - 1);
- }
+ if (xftHintStyle > 0)
+ return QFontEngine::HintStyle(xftHintStyle - 1);
return QFontEngine::HintFull;
}
-QFontEngine::SubpixelAntialiasingType subpixelTypeFromMatch(FcPattern *match, bool useXftConf)
+QFontEngine::SubpixelAntialiasingType subpixelTypeFromMatch(FcPattern *match, bool preferXftConf)
{
+ void *subpixelTypeResource =
+ QGuiApplication::platformNativeInterface()->nativeResourceForScreen("subpixeltype",
+ QGuiApplication::primaryScreen());
+ int xftSubpixelType = int(reinterpret_cast<qintptr>(subpixelTypeResource));
+ if (preferXftConf && xftSubpixelType > 0)
+ return QFontEngine::SubpixelAntialiasingType(xftSubpixelType - 1);
+
int subpixel = FC_RGBA_UNKNOWN;
if (FcPatternGetInteger(match, FC_RGBA, 0, &subpixel) == FcResultMatch) {
switch (subpixel) {
@@ -702,14 +716,8 @@ QFontEngine::SubpixelAntialiasingType subpixelTypeFromMatch(FcPattern *match, bo
}
}
- if (useXftConf) {
- void *subpixelTypeResource =
- QGuiApplication::platformNativeInterface()->nativeResourceForScreen("subpixeltype",
- QGuiApplication::primaryScreen());
- int subpixelType = int(reinterpret_cast<qintptr>(subpixelTypeResource));
- if (subpixelType > 0)
- return QFontEngine::SubpixelAntialiasingType(subpixelType - 1);
- }
+ if (xftSubpixelType > 0)
+ return QFontEngine::SubpixelAntialiasingType(xftSubpixelType - 1);
return QFontEngine::Subpixel_None;
}
@@ -724,6 +732,8 @@ QFontEngine *QFontconfigDatabase::fontEngine(const QFontDef &f, void *usrPtr)
QFontEngine::FaceId fid;
fid.filename = QFile::encodeName(fontfile->fileName);
fid.index = fontfile->indexValue;
+ fid.instanceIndex = fontfile->instanceIndex;
+ fid.variableAxes = f.variableAxisValues;
// FIXME: Unify with logic in QFontEngineFT::create()
QFontEngineFT *engine = new QFontEngineFT(f);
@@ -805,8 +815,7 @@ QStringList QFontconfigDatabase::fallbacksForFamily(const QString &family, QFont
FcPatternDestroy(pattern);
if (fontSet) {
- QDuplicateTracker<QString> duplicates;
- duplicates.reserve(fontSet->nfont + 1);
+ QDuplicateTracker<QString> duplicates(fontSet->nfont + 1);
(void)duplicates.hasSeen(family.toCaseFolded());
for (int i = 0; i < fontSet->nfont; i++) {
FcChar8 *value = nullptr;
@@ -826,26 +835,28 @@ QStringList QFontconfigDatabase::fallbacksForFamily(const QString &family, QFont
return fallbackFamilies;
}
-static FcPattern *queryFont(const FcChar8 *file, const QByteArray &data, int id, FcBlanks *blanks, int *count)
+static FcPattern *queryFont(const FcChar8 *file, const QByteArray &data, int id, FcBlanks *blanks, int *count, FT_Face *face)
{
#if FC_VERSION < 20402
Q_UNUSED(data);
+ *face = nullptr;
return FcFreeTypeQuery(file, id, blanks, count);
#else
- if (data.isEmpty())
+ if (data.isEmpty()) {
+ *face = nullptr;
return FcFreeTypeQuery(file, id, blanks, count);
+ }
FT_Library lib = qt_getFreetype();
FcPattern *pattern = nullptr;
- FT_Face face;
- if (!FT_New_Memory_Face(lib, (const FT_Byte *)data.constData(), data.size(), id, &face)) {
- *count = face->num_faces;
+ if (!FT_New_Memory_Face(lib, (const FT_Byte *)data.constData(), data.size(), id, face)) {
+ *count = (*face)->num_faces;
- pattern = FcFreeTypeQueryFace(face, file, id, blanks);
-
- FT_Done_Face(face);
+ pattern = FcFreeTypeQueryFace(*face, file, id, blanks);
+ } else {
+ *face = nullptr;
}
return pattern;
@@ -873,8 +884,9 @@ QStringList QFontconfigDatabase::addApplicationFont(const QByteArray &fontData,
FcPattern *pattern;
do {
+ FT_Face face;
pattern = queryFont((const FcChar8 *)QFile::encodeName(fileName).constData(),
- fontData, id, blanks, &count);
+ fontData, id, blanks, &count, &face);
if (!pattern)
return families;
@@ -883,7 +895,10 @@ QStringList QFontconfigDatabase::addApplicationFont(const QByteArray &fontData,
QString family = QString::fromUtf8(reinterpret_cast<const char *>(fam));
families << family;
}
- populateFromPattern(pattern, applicationFont);
+ populateFromPattern(pattern, applicationFont, face, this);
+
+ if (face)
+ FT_Done_Face(face);
FcFontSetAdd(set, pattern);
@@ -948,32 +963,24 @@ QFont QFontconfigDatabase::defaultFont() const
void QFontconfigDatabase::setupFontEngine(QFontEngineFT *engine, const QFontDef &fontDef) const
{
bool antialias = !(fontDef.styleStrategy & QFont::NoAntialias);
- bool forcedAntialiasSetting = !antialias || QHighDpiScaling::isActive();
+ bool forcedAntialiasSetting = !antialias || isDprScaling();
const QPlatformServices *services = QGuiApplicationPrivate::platformIntegration()->services();
- bool useXftConf = false;
+ bool preferXftConf = false;
if (services) {
const QList<QByteArray> desktopEnv = services->desktopEnvironment().split(':');
- useXftConf = desktopEnv.contains("GNOME") || desktopEnv.contains("UNITY") || desktopEnv.contains("XFCE");
- }
-
- if (useXftConf && !forcedAntialiasSetting) {
- void *antialiasResource =
- QGuiApplication::platformNativeInterface()->nativeResourceForScreen("antialiasingEnabled",
- QGuiApplication::primaryScreen());
- int antialiasingEnabled = int(reinterpret_cast<qintptr>(antialiasResource));
- if (antialiasingEnabled > 0)
- antialias = antialiasingEnabled - 1;
+ preferXftConf = !(desktopEnv.contains("KDE") || desktopEnv.contains("LXQT") || desktopEnv.contains("UKUI"));
}
QFontEngine::GlyphFormat format;
// try and get the pattern
FcPattern *pattern = FcPatternCreate();
+ FcPattern *match = nullptr;
FcValue value;
value.type = FcTypeString;
- QByteArray cs = fontDef.family.toUtf8();
+ QByteArray cs = fontDef.families.first().toUtf8();
value.u.s = (const FcChar8 *)cs.data();
FcPatternAdd(pattern,FC_FAMILY,value,true);
@@ -988,7 +995,7 @@ void QFontconfigDatabase::setupFontEngine(QFontEngineFT *engine, const QFontDef
FcPatternAdd(pattern,FC_INDEX,value,true);
}
- if (fontDef.pixelSize > 0.1)
+ if (!qFuzzyIsNull(fontDef.pixelSize))
FcPatternAddDouble(pattern, FC_PIXEL_SIZE, fontDef.pixelSize);
FcResult result;
@@ -996,9 +1003,68 @@ void QFontconfigDatabase::setupFontEngine(QFontEngineFT *engine, const QFontDef
FcConfigSubstitute(nullptr, pattern, FcMatchPattern);
FcDefaultSubstitute(pattern);
- FcPattern *match = FcFontMatch(nullptr, pattern, &result);
+#ifdef FC_VARIABLE
+ if (!fid.filename.isEmpty()) {
+ // FC_INDEX is ignored during processing in FcFontMatch.
+ // So iterate FcPatterns directly and find it out.
+ FcFontSet *fcsets[2], *fcfs;
+
+ fcsets[0] = FcConfigGetFonts(nullptr, FcSetSystem);
+ fcsets[1] = FcConfigGetFonts(nullptr, FcSetApplication);
+ for (int nset = 0; nset < 2; nset++) {
+ fcfs = fcsets[nset];
+ if (fcfs == nullptr)
+ continue;
+ for (int fnum = 0; fnum < fcfs->nfont; fnum++) {
+ FcPattern *fcpat = fcfs->fonts[fnum];
+ FcChar8 *fcfile;
+ FcBool variable;
+ double fcpixelsize;
+ int fcindex;
+
+ // Skip the variable font itself, only to use the named instances and normal fonts here
+ if (FcPatternGetBool(fcpat, FC_VARIABLE, 0, &variable) == FcResultMatch &&
+ variable == FcTrue)
+ continue;
+
+ if (!qFuzzyIsNull(fontDef.pixelSize)) {
+ if (FcPatternGetDouble(fcpat, FC_PIXEL_SIZE, 0, &fcpixelsize) == FcResultMatch &&
+ fontDef.pixelSize != fcpixelsize)
+ continue;
+ }
+
+ if (FcPatternGetString(fcpat, FC_FILE, 0, &fcfile) == FcResultMatch &&
+ FcPatternGetInteger(fcpat, FC_INDEX, 0, &fcindex) == FcResultMatch) {
+ QByteArray f = QByteArray::fromRawData((const char *)fcfile,
+ qstrlen((const char *)fcfile));
+ if (f == fid.filename && fcindex == fid.index) {
+ // We found it.
+ match = FcFontRenderPrepare(nullptr, pattern, fcpat);
+ goto bail;
+ }
+ }
+ }
+ }
+ }
+bail:
+#endif
+
+ if (!match)
+ match = FcFontMatch(nullptr, pattern, &result);
+
+ int xftAntialias = 0;
+ if (!forcedAntialiasSetting) {
+ void *antialiasResource =
+ QGuiApplication::platformNativeInterface()->nativeResourceForScreen("antialiasingEnabled",
+ QGuiApplication::primaryScreen());
+ xftAntialias = int(reinterpret_cast<qintptr>(antialiasResource));
+ if ((preferXftConf || !match) && xftAntialias > 0) {
+ antialias = xftAntialias - 1;
+ forcedAntialiasSetting = true;
+ }
+ }
if (match) {
- engine->setDefaultHintStyle(defaultHintStyleFromMatch((QFont::HintingPreference)fontDef.hintingPreference, match, useXftConf));
+ engine->setDefaultHintStyle(defaultHintStyleFromMatch((QFont::HintingPreference)fontDef.hintingPreference, match, preferXftConf));
FcBool fc_autohint;
if (FcPatternGetBool(match, FC_AUTOHINT,0, &fc_autohint) == FcResultMatch)
@@ -1019,18 +1085,37 @@ void QFontconfigDatabase::setupFontEngine(QFontEngineFT *engine, const QFontDef
if (antialias) {
QFontEngine::SubpixelAntialiasingType subpixelType = QFontEngine::Subpixel_None;
if (!(fontDef.styleStrategy & QFont::NoSubpixelAntialias))
- subpixelType = subpixelTypeFromMatch(match, useXftConf);
+ subpixelType = subpixelTypeFromMatch(match, preferXftConf);
engine->subpixelType = subpixelType;
-
- format = (subpixelType == QFontEngine::Subpixel_None)
- ? QFontEngine::Format_A8
- : QFontEngine::Format_A32;
- } else
- format = QFontEngine::Format_Mono;
+ }
FcPatternDestroy(match);
- } else
- format = antialias ? QFontEngine::Format_A8 : QFontEngine::Format_Mono;
+ } else {
+ void *hintStyleResource =
+ QGuiApplication::platformNativeInterface()->nativeResourceForScreen("hintstyle",
+ QGuiApplication::primaryScreen());
+ int xftHintStyle = int(reinterpret_cast<qintptr>(hintStyleResource));
+ if (xftHintStyle > 0)
+ engine->setDefaultHintStyle(QFontEngine::HintStyle(xftHintStyle - 1));
+ if (antialias) {
+ engine->subpixelType = QFontEngine::Subpixel_None;
+ if (!(fontDef.styleStrategy & QFont::NoSubpixelAntialias)) {
+ void *subpixelTypeResource =
+ QGuiApplication::platformNativeInterface()->nativeResourceForScreen("subpixeltype",
+ QGuiApplication::primaryScreen());
+ int xftSubpixelType = int(reinterpret_cast<qintptr>(subpixelTypeResource));
+ if (xftSubpixelType > 1)
+ engine->subpixelType = QFontEngine::SubpixelAntialiasingType(xftSubpixelType - 1);
+ }
+ }
+ }
+ if (antialias) {
+ format = (engine->subpixelType == QFontEngine::Subpixel_None)
+ ? QFontEngine::Format_A8
+ : QFontEngine::Format_A32;
+ } else {
+ format = QFontEngine::Format_Mono;
+ }
FcPatternDestroy(pattern);
@@ -1039,4 +1124,13 @@ void QFontconfigDatabase::setupFontEngine(QFontEngineFT *engine, const QFontDef
engine->glyphFormat = format;
}
+bool QFontconfigDatabase::supportsVariableApplicationFonts() const
+{
+#if (FREETYPE_MAJOR*10000 + FREETYPE_MINOR*100 + FREETYPE_PATCH) >= 20900
+ return true;
+#else
+ return false;
+#endif
+}
+
QT_END_NAMESPACE
diff --git a/src/gui/text/unix/qfontconfigdatabase_p.h b/src/gui/text/unix/qfontconfigdatabase_p.h
index cce2bac1d3..dd7a70a375 100644
--- a/src/gui/text/unix/qfontconfigdatabase_p.h
+++ b/src/gui/text/unix/qfontconfigdatabase_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 QFONTCONFIGDATABASE_H
#define QFONTCONFIGDATABASE_H
@@ -61,8 +25,10 @@ class QFontEngineFT;
class Q_GUI_EXPORT QFontconfigDatabase : public QFreeTypeFontDatabase
{
public:
+ ~QFontconfigDatabase() override;
void populateFontDatabase() override;
void invalidate() override;
+ bool supportsVariableApplicationFonts() const override;
QFontEngineMulti *fontEngineMulti(QFontEngine *fontEngine, QChar::Script script) override;
QFontEngine *fontEngine(const QFontDef &fontDef, void *handle) override;
QFontEngine *fontEngine(const QByteArray &fontData, qreal pixelSize, QFont::HintingPreference hintingPreference) override;
diff --git a/src/gui/text/unix/qfontenginemultifontconfig.cpp b/src/gui/text/unix/qfontenginemultifontconfig.cpp
index 01d58022f2..6419a764f7 100644
--- a/src/gui/text/unix/qfontenginemultifontconfig.cpp
+++ b/src/gui/text/unix/qfontenginemultifontconfig.cpp
@@ -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 "qfontenginemultifontconfig_p.h"
@@ -50,7 +14,7 @@ QFontEngineMultiFontConfig::QFontEngineMultiFontConfig(QFontEngine *fe, int scri
QFontEngineMultiFontConfig::~QFontEngineMultiFontConfig()
{
- for (FcPattern *pattern : qAsConst(cachedMatchPatterns)) {
+ for (FcPattern *pattern : std::as_const(cachedMatchPatterns)) {
if (pattern)
FcPatternDestroy(pattern);
}
diff --git a/src/gui/text/unix/qfontenginemultifontconfig_p.h b/src/gui/text/unix/qfontenginemultifontconfig_p.h
index daaa8baecf..9ab96123ae 100644
--- a/src/gui/text/unix/qfontenginemultifontconfig_p.h
+++ b/src/gui/text/unix/qfontenginemultifontconfig_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 QFONTENGINEMULTIFONTCONFIG_H
#define QFONTENGINEMULTIFONTCONFIG_H
diff --git a/src/gui/text/unix/qgenericunixfontdatabase_p.h b/src/gui/text/unix/qgenericunixfontdatabase_p.h
index 21c57c303e..96124d025b 100644
--- a/src/gui/text/unix/qgenericunixfontdatabase_p.h
+++ b/src/gui/text/unix/qgenericunixfontdatabase_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 QGENERICUNIXFONTDATABASE_H
#define QGENERICUNIXFONTDATABASE_H
@@ -56,9 +20,12 @@
#if QT_CONFIG(fontconfig)
#include <QtGui/private/qfontconfigdatabase_p.h>
using QGenericUnixFontDatabase = QFontconfigDatabase;
-#else
+#elif QT_CONFIG(freetype)
#include <QtGui/private/qfreetypefontdatabase_p.h>
using QGenericUnixFontDatabase = QFreeTypeFontDatabase;
-#endif //Q_FONTCONFIGDATABASE
+#else
+#include <qpa/qplatformfontdatabase.h>
+using QGenericUnixFontDatabase = QPlatformFontDatabase;
+#endif
#endif // QGENERICUNIXFONTDATABASE_H
diff --git a/src/gui/text/unix/unix.pri b/src/gui/text/unix/unix.pri
deleted file mode 100644
index 538c16ecfe..0000000000
--- a/src/gui/text/unix/unix.pri
+++ /dev/null
@@ -1,13 +0,0 @@
-HEADERS += text/unix/qgenericunixfontdatabase_p.h
-
-qtConfig(fontconfig) {
- HEADERS += \
- text/unix/qfontconfigdatabase_p.h \
- text/unix/qfontenginemultifontconfig_p.h
-
- SOURCES += \
- text/unix/qfontconfigdatabase.cpp \
- text/unix/qfontenginemultifontconfig.cpp
-
- QMAKE_USE_PRIVATE += fontconfig
-}
diff --git a/src/gui/text/windows/qwindowsdirectwritefontdatabase.cpp b/src/gui/text/windows/qwindowsdirectwritefontdatabase.cpp
index c0d9e820c6..2e15fbb1ac 100644
--- a/src/gui/text/windows/qwindowsdirectwritefontdatabase.cpp
+++ b/src/gui/text/windows/qwindowsdirectwritefontdatabase.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2020 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) 2020 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 "qwindowsdirectwritefontdatabase_p.h"
#include "qwindowsfontenginedirectwrite_p.h"
@@ -54,6 +18,32 @@ QT_BEGIN_NAMESPACE
// Defined in gui/text/qfontdatabase.cpp
Q_GUI_EXPORT QFontDatabase::WritingSystem qt_writing_system_for_script(int script);
+template<typename T>
+struct DirectWriteScope {
+ DirectWriteScope(T *res = nullptr) : m_res(res) {}
+ ~DirectWriteScope() {
+ if (m_res != nullptr)
+ m_res->Release();
+ }
+
+ T **operator&()
+ {
+ return &m_res;
+ }
+
+ T *operator->()
+ {
+ return m_res;
+ }
+
+ T *operator*() {
+ return m_res;
+ }
+
+private:
+ T *m_res;
+};
+
QWindowsDirectWriteFontDatabase::QWindowsDirectWriteFontDatabase()
{
qCDebug(lcQpaFonts) << "Creating DirectWrite database";
@@ -103,16 +93,6 @@ static QFont::Weight fromDirectWriteWeight(DWRITE_FONT_WEIGHT weight)
return static_cast<QFont::Weight>(weight);
}
-static DWRITE_FONT_STYLE toDirectWriteStyle(QFont::Style style)
-{
- switch (style) {
- case QFont::StyleNormal: return DWRITE_FONT_STYLE_NORMAL;
- case QFont::StyleOblique: return DWRITE_FONT_STYLE_OBLIQUE;
- case QFont::StyleItalic: return DWRITE_FONT_STYLE_ITALIC;
- default: return DWRITE_FONT_STYLE_NORMAL;
- }
-}
-
static QFont::Style fromDirectWriteStyle(DWRITE_FONT_STYLE style)
{
switch (style) {
@@ -126,6 +106,12 @@ static QFont::Style fromDirectWriteStyle(DWRITE_FONT_STYLE style)
void QWindowsDirectWriteFontDatabase::populateFamily(const QString &familyName)
{
auto it = m_populatedFonts.find(familyName);
+ if (it == m_populatedFonts.end() && m_populatedBitmapFonts.contains(familyName)) {
+ qCDebug(lcQpaFonts) << "Populating bitmap font" << familyName;
+ QWindowsFontDatabase::populateFamily(familyName);
+ return;
+ }
+
IDWriteFontFamily *fontFamily = it != m_populatedFonts.end() ? it.value() : nullptr;
if (fontFamily == nullptr) {
qCWarning(lcQpaFonts) << "Cannot find" << familyName << "in list of fonts";
@@ -144,7 +130,7 @@ void QWindowsDirectWriteFontDatabase::populateFamily(const QString &familyName)
const bool antialias = false;
const int size = SMOOTH_SCALABLE;
- IDWriteFontList *matchingFonts;
+ DirectWriteScope<IDWriteFontList> matchingFonts;
if (SUCCEEDED(fontFamily->GetMatchingFonts(DWRITE_FONT_WEIGHT_REGULAR,
DWRITE_FONT_STRETCH_NORMAL,
DWRITE_FONT_STYLE_NORMAL,
@@ -152,7 +138,7 @@ void QWindowsDirectWriteFontDatabase::populateFamily(const QString &familyName)
for (uint j = 0; j < matchingFonts->GetFontCount(); ++j) {
IDWriteFont *font;
if (SUCCEEDED(matchingFonts->GetFont(j, &font))) {
- IDWriteFont1 *font1 = nullptr;
+ DirectWriteScope<IDWriteFont1> font1;
if (!SUCCEEDED(font->QueryInterface(__uuidof(IDWriteFont1),
reinterpret_cast<void **>(&font1)))) {
qCWarning(lcQpaFonts) << "COM object does not support IDWriteFont1";
@@ -162,27 +148,23 @@ void QWindowsDirectWriteFontDatabase::populateFamily(const QString &familyName)
QString defaultLocaleFamilyName;
QString englishLocaleFamilyName;
- IDWriteFontFamily *fontFamily2;
+ DirectWriteScope<IDWriteFontFamily> fontFamily2;
if (SUCCEEDED(font1->GetFontFamily(&fontFamily2))) {
- IDWriteLocalizedStrings *names;
+ DirectWriteScope<IDWriteLocalizedStrings> names;
if (SUCCEEDED(fontFamily2->GetFamilyNames(&names))) {
- defaultLocaleFamilyName = hasDefaultLocale ? localeString(names, defaultLocale) : QString();
- englishLocaleFamilyName = localeString(names, englishLocale);
-
- names->Release();
+ defaultLocaleFamilyName = hasDefaultLocale ? localeString(*names, defaultLocale) : QString();
+ englishLocaleFamilyName = localeString(*names, englishLocale);
}
-
- fontFamily2->Release();
}
if (defaultLocaleFamilyName.isEmpty() && englishLocaleFamilyName.isEmpty())
englishLocaleFamilyName = familyName;
{
- IDWriteLocalizedStrings *names;
+ DirectWriteScope<IDWriteLocalizedStrings> names;
if (SUCCEEDED(font1->GetFaceNames(&names))) {
- QString defaultLocaleStyleName = hasDefaultLocale ? localeString(names, defaultLocale) : QString();
- QString englishLocaleStyleName = localeString(names, englishLocale);
+ QString defaultLocaleStyleName = hasDefaultLocale ? localeString(*names, defaultLocale) : QString();
+ QString englishLocaleStyleName = localeString(*names, englishLocale);
QFont::Stretch stretch = fromDirectWriteStretch(font1->GetStretch());
QFont::Style style = fromDirectWriteStyle(font1->GetStyle());
@@ -191,77 +173,233 @@ void QWindowsDirectWriteFontDatabase::populateFamily(const QString &familyName)
qCDebug(lcQpaFonts) << "Family" << familyName << "has english variant" << englishLocaleStyleName << ", in default locale:" << defaultLocaleStyleName << stretch << style << weight << fixed;
- IDWriteFontFace *face = nullptr;
+ DirectWriteScope<IDWriteFontFace> face;
if (SUCCEEDED(font->CreateFontFace(&face))) {
- QSupportedWritingSystems writingSystems;
-
- const void *tableData = nullptr;
- UINT32 tableSize;
- void *tableContext = nullptr;
- BOOL exists;
- HRESULT hr = face->TryGetFontTable(qbswap<quint32>(MAKE_TAG('O','S','/','2')),
- &tableData,
- &tableSize,
- &tableContext,
- &exists);
- if (SUCCEEDED(hr) && exists) {
- writingSystems = QPlatformFontDatabase::writingSystemsFromOS2Table(reinterpret_cast<const char *>(tableData), tableSize);
- } else { // Fall back to checking first character of each Unicode range in font (may include too many writing systems)
- quint32 rangeCount;
- hr = font1->GetUnicodeRanges(0, nullptr, &rangeCount);
-
- if (rangeCount > 0) {
- QVarLengthArray<DWRITE_UNICODE_RANGE, QChar::ScriptCount> ranges(rangeCount);
-
- hr = font1->GetUnicodeRanges(rangeCount, ranges.data(), &rangeCount);
- if (SUCCEEDED(hr)) {
- for (uint i = 0; i < rangeCount; ++i) {
- QChar::Script script = QChar::script(ranges.at(i).first);
-
- QFontDatabase::WritingSystem writingSystem = qt_writing_system_for_script(script);
-
- if (writingSystem > QFontDatabase::Any && writingSystem < QFontDatabase::WritingSystemsCount)
- writingSystems.setSupported(writingSystem);
- }
- } else {
- const QString errorString = qt_error_string(int(hr));
- qCWarning(lcQpaFonts) << "Failed to get unicode ranges for font" << englishLocaleFamilyName << englishLocaleStyleName << ":" << errorString;
- }
- }
- }
+ QSupportedWritingSystems writingSystems = supportedWritingSystems(*face);
if (!englishLocaleStyleName.isEmpty() || defaultLocaleStyleName.isEmpty()) {
qCDebug(lcQpaFonts) << "Font" << englishLocaleFamilyName << englishLocaleStyleName << "supports writing systems:" << writingSystems;
- QPlatformFontDatabase::registerFont(englishLocaleFamilyName, englishLocaleStyleName, QString(), weight, style, stretch, antialias, scalable, size, fixed, writingSystems, face);
- face->AddRef();
+ QPlatformFontDatabase::registerFont(englishLocaleFamilyName,
+ englishLocaleStyleName,
+ QString(),
+ weight,
+ style,
+ stretch,
+ antialias,
+ scalable,
+ size,
+ fixed,
+ writingSystems,
+ new FontHandle(*face, englishLocaleFamilyName));
}
if (!defaultLocaleFamilyName.isEmpty() && defaultLocaleFamilyName != englishLocaleFamilyName) {
- QPlatformFontDatabase::registerFont(defaultLocaleFamilyName, defaultLocaleStyleName, QString(), weight, style, stretch, antialias, scalable, size, fixed, writingSystems, face);
- face->AddRef();
+ QPlatformFontDatabase::registerFont(defaultLocaleFamilyName,
+ defaultLocaleStyleName,
+ QString(),
+ weight,
+ style,
+ stretch,
+ antialias,
+ scalable,
+ size,
+ fixed,
+ writingSystems,
+ new FontHandle(*face, defaultLocaleFamilyName));
}
-
- face->Release();
}
-
- names->Release();
}
}
+ }
+ }
+ }
+}
+
+QSupportedWritingSystems QWindowsDirectWriteFontDatabase::supportedWritingSystems(IDWriteFontFace *face) const
+{
+ QSupportedWritingSystems writingSystems;
+ writingSystems.setSupported(QFontDatabase::Any);
- font1->Release();
- font->Release();
+ DirectWriteScope<IDWriteFontFace1> face1;
+ if (SUCCEEDED(face->QueryInterface(__uuidof(IDWriteFontFace1),
+ reinterpret_cast<void **>(&face1)))) {
+ const void *tableData = nullptr;
+ UINT32 tableSize;
+ void *tableContext = nullptr;
+ BOOL exists;
+ HRESULT hr = face->TryGetFontTable(qFromBigEndian(QFont::Tag("OS/2").value()),
+ &tableData,
+ &tableSize,
+ &tableContext,
+ &exists);
+ if (SUCCEEDED(hr) && exists) {
+ writingSystems = QPlatformFontDatabase::writingSystemsFromOS2Table(reinterpret_cast<const char *>(tableData), tableSize);
+ } else { // Fall back to checking first character of each Unicode range in font (may include too many writing systems)
+ quint32 rangeCount;
+ hr = face1->GetUnicodeRanges(0, nullptr, &rangeCount);
+
+ if (rangeCount > 0) {
+ QVarLengthArray<DWRITE_UNICODE_RANGE, QChar::ScriptCount> ranges(rangeCount);
+
+ hr = face1->GetUnicodeRanges(rangeCount, ranges.data(), &rangeCount);
+ if (SUCCEEDED(hr)) {
+ for (uint i = 0; i < rangeCount; ++i) {
+ QChar::Script script = QChar::script(ranges.at(i).first);
+
+ QFontDatabase::WritingSystem writingSystem = qt_writing_system_for_script(script);
+
+ if (writingSystem > QFontDatabase::Any && writingSystem < QFontDatabase::WritingSystemsCount)
+ writingSystems.setSupported(writingSystem);
+ }
+ } else {
+ const QString errorString = qt_error_string(int(hr));
+ qCWarning(lcQpaFonts) << "Failed to get unicode ranges for font:" << errorString;
+ }
}
}
+ }
+
+ return writingSystems;
+}
+
+bool QWindowsDirectWriteFontDatabase::populateFamilyAliases(const QString &missingFamily)
+{
+ // If the font has not been populated, it is possible this is a legacy font family supported
+ // by GDI. We make an attempt at loading it via GDI and then add this face directly to the
+ // database.
+ if (!missingFamily.isEmpty()
+ && missingFamily.size() < LF_FACESIZE
+ && !m_populatedFonts.contains(missingFamily)
+ && !m_populatedBitmapFonts.contains(missingFamily)) {
+ qCDebug(lcQpaFonts) << "Loading unpopulated" << missingFamily << ". Trying GDI.";
+
+ LOGFONT lf;
+ memset(&lf, 0, sizeof(LOGFONT));
+ memcpy(lf.lfFaceName, missingFamily.utf16(), missingFamily.size() * sizeof(wchar_t));
+
+ HFONT hfont = CreateFontIndirect(&lf);
+ if (hfont) {
+ HDC dummy = GetDC(0);
+ HGDIOBJ oldFont = SelectObject(dummy, hfont);
+
+ DirectWriteScope<IDWriteFontFace> directWriteFontFace;
+ if (SUCCEEDED(data()->directWriteGdiInterop->CreateFontFaceFromHdc(dummy, &directWriteFontFace))) {
+ DirectWriteScope<IDWriteFontCollection> fontCollection;
+ if (SUCCEEDED(data()->directWriteFactory->GetSystemFontCollection(&fontCollection))) {
+ DirectWriteScope<IDWriteFont> font;
+ if (SUCCEEDED(fontCollection->GetFontFromFontFace(*directWriteFontFace, &font))) {
+
+ DirectWriteScope<IDWriteFont1> font1;
+ if (SUCCEEDED(font->QueryInterface(__uuidof(IDWriteFont1),
+ reinterpret_cast<void **>(&font1)))) {
+ DirectWriteScope<IDWriteLocalizedStrings> names;
+ if (SUCCEEDED(font1->GetFaceNames(&names))) {
+ wchar_t englishLocale[] = L"en-us";
+ QString englishLocaleStyleName = localeString(*names, englishLocale);
+
+ QFont::Stretch stretch = fromDirectWriteStretch(font1->GetStretch());
+ QFont::Style style = fromDirectWriteStyle(font1->GetStyle());
+ QFont::Weight weight = fromDirectWriteWeight(font1->GetWeight());
+ bool fixed = font1->IsMonospacedFont();
+
+ QSupportedWritingSystems writingSystems = supportedWritingSystems(*directWriteFontFace);
+
+ qCDebug(lcQpaFonts) << "Registering legacy font family" << missingFamily;
+ QPlatformFontDatabase::registerFont(missingFamily,
+ englishLocaleStyleName,
+ QString(),
+ weight,
+ style,
+ stretch,
+ false,
+ true,
+ 0xffff,
+ fixed,
+ writingSystems,
+ new FontHandle(*directWriteFontFace, missingFamily));
+
+ SelectObject(dummy, oldFont);
+ DeleteObject(hfont);
+
+ return true;
+ }
+ }
+ }
+ }
+ }
- matchingFonts->Release();
+ SelectObject(dummy, oldFont);
+ DeleteObject(hfont);
+ }
}
+
+ // Skip over implementation in QWindowsFontDatabase
+ return QWindowsFontDatabaseBase::populateFamilyAliases(missingFamily);
+}
+
+QFontEngine *QWindowsDirectWriteFontDatabase::fontEngine(const QByteArray &fontData,
+ qreal pixelSize,
+ QFont::HintingPreference hintingPreference)
+{
+ // Skip over implementation in QWindowsFontDatabase
+ return QWindowsFontDatabaseBase::fontEngine(fontData, pixelSize, hintingPreference);
}
QFontEngine *QWindowsDirectWriteFontDatabase::fontEngine(const QFontDef &fontDef, void *handle)
{
- IDWriteFontFace *face = reinterpret_cast<IDWriteFontFace *>(handle);
- Q_ASSERT(face != nullptr);
+ const FontHandle *fontHandle = static_cast<const FontHandle *>(handle);
+ IDWriteFontFace *face = fontHandle->fontFace;
+ if (face == nullptr) {
+ qCDebug(lcQpaFonts) << "Falling back to GDI";
+ return QWindowsFontDatabase::fontEngine(fontDef, handle);
+ }
+
+ DWRITE_FONT_SIMULATIONS simulations = DWRITE_FONT_SIMULATIONS_NONE;
+ if (fontDef.weight >= QFont::DemiBold || fontDef.style != QFont::StyleNormal) {
+ DirectWriteScope<IDWriteFontFace3> face3;
+ if (SUCCEEDED(face->QueryInterface(__uuidof(IDWriteFontFace3),
+ reinterpret_cast<void **>(&face3)))) {
+ if (fontDef.weight >= QFont::DemiBold && face3->GetWeight() < DWRITE_FONT_WEIGHT_DEMI_BOLD)
+ simulations |= DWRITE_FONT_SIMULATIONS_BOLD;
+
+ if (fontDef.style != QFont::StyleNormal && face3->GetStyle() == DWRITE_FONT_STYLE_NORMAL)
+ simulations |= DWRITE_FONT_SIMULATIONS_OBLIQUE;
+ }
+ }
+
+ DirectWriteScope<IDWriteFontFace5> newFace;
+ if (!fontDef.variableAxisValues.isEmpty() || simulations != DWRITE_FONT_SIMULATIONS_NONE) {
+ DirectWriteScope<IDWriteFontFace5> face5;
+ if (SUCCEEDED(face->QueryInterface(__uuidof(IDWriteFontFace5),
+ reinterpret_cast<void **>(&face5)))) {
+ DirectWriteScope<IDWriteFontResource> font;
+ if (SUCCEEDED(face5->GetFontResource(&font))) {
+ UINT32 fontAxisCount = font->GetFontAxisCount();
+ QVarLengthArray<DWRITE_FONT_AXIS_VALUE, 8> fontAxisValues(fontAxisCount);
+
+ if (!fontDef.variableAxisValues.isEmpty()) {
+ if (SUCCEEDED(face5->GetFontAxisValues(fontAxisValues.data(), fontAxisCount))) {
+ for (UINT32 i = 0; i < fontAxisCount; ++i) {
+ if (auto maybeTag = QFont::Tag::fromValue(qToBigEndian<UINT32>(fontAxisValues[i].axisTag))) {
+ if (fontDef.variableAxisValues.contains(*maybeTag))
+ fontAxisValues[i].value = fontDef.variableAxisValues.value(*maybeTag);
+ }
+ }
+ }
+ }
+
+ if (SUCCEEDED(font->CreateFontFace(simulations,
+ !fontDef.variableAxisValues.isEmpty() ? fontAxisValues.data() : nullptr,
+ !fontDef.variableAxisValues.isEmpty() ? fontAxisCount : 0,
+ &newFace))) {
+ face = *newFace;
+ } else {
+ qCWarning(lcQpaFonts) << "DirectWrite: Can't create font face for variable axis values";
+ }
+ }
+ }
+ }
QWindowsFontEngineDirectWrite *fontEngine = new QWindowsFontEngineDirectWrite(face, fontDef.pixelSize, data());
fontEngine->initFontInfo(fontDef, defaultVerticalDPI());
@@ -295,111 +433,255 @@ QStringList QWindowsDirectWriteFontDatabase::addApplicationFont(const QByteArray
loadedData = file.readAll();
}
- IDWriteFontFace *face = createDirectWriteFace(loadedData);
- if (face == nullptr) {
+ QList<IDWriteFontFace *> faces = createDirectWriteFaces(loadedData);
+ if (faces.isEmpty()) {
qCWarning(lcQpaFonts) << "Failed to create DirectWrite face from font data. Font may be unsupported.";
return QStringList();
}
- wchar_t defaultLocale[LOCALE_NAME_MAX_LENGTH];
- bool hasDefaultLocale = GetUserDefaultLocaleName(defaultLocale, LOCALE_NAME_MAX_LENGTH) != 0;
- wchar_t englishLocale[] = L"en-us";
+ QSet<QString> ret;
+ for (int i = 0; i < faces.size(); ++i) {
+ IDWriteFontFace *face = faces.at(i);
+ wchar_t defaultLocale[LOCALE_NAME_MAX_LENGTH];
+ bool hasDefaultLocale = GetUserDefaultLocaleName(defaultLocale, LOCALE_NAME_MAX_LENGTH) != 0;
+ wchar_t englishLocale[] = L"en-us";
+
+ static const int SMOOTH_SCALABLE = 0xffff;
+ const bool scalable = true;
+ const bool antialias = false;
+ const int size = SMOOTH_SCALABLE;
+
+ QSupportedWritingSystems writingSystems = supportedWritingSystems(face);
+ DirectWriteScope<IDWriteFontFace3> face3;
+ if (SUCCEEDED(face->QueryInterface(__uuidof(IDWriteFontFace3),
+ reinterpret_cast<void **>(&face3)))) {
+ QString defaultLocaleFamilyName;
+ QString englishLocaleFamilyName;
+
+ IDWriteLocalizedStrings *names = nullptr;
+ if (SUCCEEDED(face3->GetFamilyNames(&names))) {
+ defaultLocaleFamilyName = hasDefaultLocale ? localeString(names, defaultLocale) : QString();
+ englishLocaleFamilyName = localeString(names, englishLocale);
+
+ names->Release();
+ }
- static const int SMOOTH_SCALABLE = 0xffff;
- const QString foundryName; // No such concept.
- const bool scalable = true;
- const bool antialias = false;
- const int size = SMOOTH_SCALABLE;
+ QString defaultLocaleStyleName;
+ QString englishLocaleStyleName;
+ if (SUCCEEDED(face3->GetFaceNames(&names))) {
+ defaultLocaleStyleName = hasDefaultLocale ? localeString(names, defaultLocale) : QString();
+ englishLocaleStyleName = localeString(names, englishLocale);
- QSupportedWritingSystems writingSystems;
- writingSystems.setSupported(QFontDatabase::Any);
- writingSystems.setSupported(QFontDatabase::Latin);
+ names->Release();
+ }
- QStringList ret;
- IDWriteFontFace3 *face3 = nullptr;
- if (SUCCEEDED(face->QueryInterface(__uuidof(IDWriteFontFace3),
- reinterpret_cast<void **>(&face3)))) {
- QString defaultLocaleFamilyName;
- QString englishLocaleFamilyName;
+ BOOL ok;
+ QString defaultLocaleGdiCompatibleFamilyName;
+ QString englishLocaleGdiCompatibleFamilyName;
+ if (SUCCEEDED(face3->GetInformationalStrings(DWRITE_INFORMATIONAL_STRING_WIN32_FAMILY_NAMES, &names, &ok)) && ok) {
+ defaultLocaleGdiCompatibleFamilyName = hasDefaultLocale ? localeString(names, defaultLocale) : QString();
+ englishLocaleGdiCompatibleFamilyName = localeString(names, englishLocale);
- IDWriteLocalizedStrings *names;
- if (SUCCEEDED(face3->GetFamilyNames(&names))) {
- defaultLocaleFamilyName = hasDefaultLocale ? localeString(names, defaultLocale) : QString();
- englishLocaleFamilyName = localeString(names, englishLocale);
+ names->Release();
+ }
- names->Release();
- }
+ QString defaultLocaleGdiCompatibleStyleName;
+ QString englishLocaleGdiCompatibleStyleName;
+ if (SUCCEEDED(face3->GetInformationalStrings(DWRITE_INFORMATIONAL_STRING_WIN32_SUBFAMILY_NAMES, &names, &ok)) && ok) {
+ defaultLocaleGdiCompatibleStyleName = hasDefaultLocale ? localeString(names, defaultLocale) : QString();
+ englishLocaleGdiCompatibleStyleName = localeString(names, englishLocale);
- QString defaultLocaleStyleName;
- QString englishLocaleStyleName;
- if (SUCCEEDED(face3->GetFaceNames(&names))) {
- defaultLocaleStyleName = hasDefaultLocale ? localeString(names, defaultLocale) : QString();
- englishLocaleStyleName = localeString(names, englishLocale);
+ names->Release();
+ }
- names->Release();
- }
+ QString defaultLocaleTypographicFamilyName;
+ QString englishLocaleTypographicFamilyName;
+ if (SUCCEEDED(face3->GetInformationalStrings(DWRITE_INFORMATIONAL_STRING_TYPOGRAPHIC_FAMILY_NAMES, &names, &ok)) && ok) {
+ defaultLocaleTypographicFamilyName = hasDefaultLocale ? localeString(names, defaultLocale) : QString();
+ englishLocaleTypographicFamilyName = localeString(names, englishLocale);
- QFont::Stretch stretch = fromDirectWriteStretch(face3->GetStretch());
- QFont::Style style = fromDirectWriteStyle(face3->GetStyle());
- QFont::Weight weight = fromDirectWriteWeight(face3->GetWeight());
- bool fixed = face3->IsMonospacedFont();
-
- qCDebug(lcQpaFonts) << "\tFont names:" << englishLocaleFamilyName << ", " << defaultLocaleFamilyName
- << ", style names:" << englishLocaleStyleName << ", " << defaultLocaleStyleName
- << ", stretch:" << stretch
- << ", style:" << style
- << ", weight:" << weight
- << ", fixed:" << fixed;
-
- if (!englishLocaleFamilyName.isEmpty()) {
- if (applicationFont != nullptr) {
- QFontDatabasePrivate::ApplicationFont::Properties properties;
- properties.style = style;
- properties.weight = weight;
- properties.familyName = englishLocaleFamilyName;
- properties.styleName = englishLocaleStyleName;
- applicationFont->properties.append(properties);
+ names->Release();
}
- ret.append(englishLocaleFamilyName);
- QPlatformFontDatabase::registerFont(englishLocaleFamilyName, englishLocaleStyleName, QString(), weight, style, stretch, antialias, scalable, size, fixed, writingSystems, face);
- face->AddRef();
- }
+ QString defaultLocaleTypographicStyleName;
+ QString englishLocaleTypographicStyleName;
+ if (SUCCEEDED(face3->GetInformationalStrings(DWRITE_INFORMATIONAL_STRING_TYPOGRAPHIC_SUBFAMILY_NAMES, &names, &ok)) && ok) {
+ defaultLocaleTypographicStyleName = hasDefaultLocale ? localeString(names, defaultLocale) : QString();
+ englishLocaleTypographicStyleName = localeString(names, englishLocale);
- if (!defaultLocaleFamilyName.isEmpty() && defaultLocaleFamilyName != englishLocaleFamilyName) {
- if (applicationFont != nullptr) {
- QFontDatabasePrivate::ApplicationFont::Properties properties;
- properties.style = style;
- properties.weight = weight;
- properties.familyName = englishLocaleFamilyName;
- properties.styleName = englishLocaleStyleName;
- applicationFont->properties.append(properties);
+ names->Release();
}
- ret.append(defaultLocaleFamilyName);
- QPlatformFontDatabase::registerFont(defaultLocaleFamilyName, defaultLocaleStyleName, QString(), weight, style, stretch, antialias, scalable, size, fixed, writingSystems, face);
- face->AddRef();
- }
+ QFont::Stretch stretch = fromDirectWriteStretch(face3->GetStretch());
+ QFont::Style style = fromDirectWriteStyle(face3->GetStyle());
+ QFont::Weight weight = fromDirectWriteWeight(face3->GetWeight());
+ bool fixed = face3->IsMonospacedFont();
+
+ qCDebug(lcQpaFonts) << "\tFont names:" << englishLocaleFamilyName << ", " << defaultLocaleFamilyName
+ << ", style names:" << englishLocaleStyleName << ", " << defaultLocaleStyleName
+ << ", stretch:" << stretch
+ << ", style:" << style
+ << ", weight:" << weight
+ << ", fixed:" << fixed;
+
+ if (!englishLocaleFamilyName.isEmpty()) {
+ if (applicationFont != nullptr) {
+ QFontDatabasePrivate::ApplicationFont::Properties properties;
+ properties.style = style;
+ properties.weight = weight;
+ properties.familyName = englishLocaleFamilyName;
+ properties.styleName = englishLocaleStyleName;
+ applicationFont->properties.append(properties);
+ }
- face3->Release();
- } else {
- qCWarning(lcQpaFonts) << "Unable to query IDWriteFontFace3 interface from font face.";
- }
+ ret.insert(englishLocaleFamilyName);
+ QPlatformFontDatabase::registerFont(englishLocaleFamilyName,
+ englishLocaleStyleName,
+ QString(),
+ weight,
+ style,
+ stretch,
+ antialias,
+ scalable,
+ size,
+ fixed,
+ writingSystems,
+ new FontHandle(face, englishLocaleFamilyName));
+ }
- face->Release();
+ if (!defaultLocaleFamilyName.isEmpty() && !ret.contains(defaultLocaleFamilyName)) {
+ if (applicationFont != nullptr) {
+ QFontDatabasePrivate::ApplicationFont::Properties properties;
+ properties.style = style;
+ properties.weight = weight;
+ properties.familyName = englishLocaleFamilyName;
+ properties.styleName = englishLocaleStyleName;
+ applicationFont->properties.append(properties);
+ }
- return ret;
-}
+ ret.insert(defaultLocaleFamilyName);
+ QPlatformFontDatabase::registerFont(defaultLocaleFamilyName,
+ defaultLocaleStyleName,
+ QString(),
+ weight,
+ style,
+ stretch,
+ antialias,
+ scalable,
+ size,
+ fixed,
+ writingSystems,
+ new FontHandle(face, defaultLocaleFamilyName));
+ }
-void QWindowsDirectWriteFontDatabase::releaseHandle(void *handle)
-{
- IDWriteFontFace *face = reinterpret_cast<IDWriteFontFace *>(handle);
- face->Release();
-}
+ if (!englishLocaleGdiCompatibleFamilyName.isEmpty() &&
+ !ret.contains(englishLocaleGdiCompatibleFamilyName)) {
+ if (applicationFont != nullptr) {
+ QFontDatabasePrivate::ApplicationFont::Properties properties;
+ properties.style = style;
+ properties.weight = weight;
+ properties.familyName = englishLocaleGdiCompatibleFamilyName;
+ applicationFont->properties.append(properties);
+ }
-bool QWindowsDirectWriteFontDatabase::fontsAlwaysScalable() const
-{
- return true;
+ ret.insert(englishLocaleGdiCompatibleFamilyName);
+ QPlatformFontDatabase::registerFont(englishLocaleGdiCompatibleFamilyName,
+ englishLocaleGdiCompatibleStyleName,
+ QString(),
+ weight,
+ style,
+ stretch,
+ antialias,
+ scalable,
+ size,
+ fixed,
+ writingSystems,
+ new FontHandle(face, englishLocaleGdiCompatibleFamilyName));
+ }
+
+ if (!defaultLocaleGdiCompatibleFamilyName.isEmpty()
+ && !ret.contains(defaultLocaleGdiCompatibleFamilyName)) {
+ if (applicationFont != nullptr) {
+ QFontDatabasePrivate::ApplicationFont::Properties properties;
+ properties.style = style;
+ properties.weight = weight;
+ properties.familyName = defaultLocaleGdiCompatibleFamilyName;
+ applicationFont->properties.append(properties);
+ }
+
+ ret.insert(defaultLocaleGdiCompatibleFamilyName);
+ QPlatformFontDatabase::registerFont(defaultLocaleGdiCompatibleFamilyName,
+ defaultLocaleGdiCompatibleStyleName,
+ QString(),
+ weight,
+ style,
+ stretch,
+ antialias,
+ scalable,
+ size,
+ fixed,
+ writingSystems,
+ new FontHandle(face, defaultLocaleGdiCompatibleFamilyName));
+ }
+
+ if (!englishLocaleTypographicFamilyName.isEmpty()
+ && !ret.contains(englishLocaleTypographicFamilyName)) {
+ if (applicationFont != nullptr) {
+ QFontDatabasePrivate::ApplicationFont::Properties properties;
+ properties.style = style;
+ properties.weight = weight;
+ properties.familyName = englishLocaleTypographicFamilyName;
+ applicationFont->properties.append(properties);
+ }
+
+ ret.insert(englishLocaleTypographicFamilyName);
+ QPlatformFontDatabase::registerFont(englishLocaleTypographicFamilyName,
+ englishLocaleTypographicStyleName,
+ QString(),
+ weight,
+ style,
+ stretch,
+ antialias,
+ scalable,
+ size,
+ fixed,
+ writingSystems,
+ new FontHandle(face, englishLocaleTypographicFamilyName));
+ }
+
+ if (!defaultLocaleTypographicFamilyName.isEmpty()
+ && !ret.contains(defaultLocaleTypographicFamilyName)) {
+ if (applicationFont != nullptr) {
+ QFontDatabasePrivate::ApplicationFont::Properties properties;
+ properties.style = style;
+ properties.weight = weight;
+ properties.familyName = defaultLocaleTypographicFamilyName;
+ applicationFont->properties.append(properties);
+ }
+
+ ret.insert(defaultLocaleTypographicFamilyName);
+ QPlatformFontDatabase::registerFont(defaultLocaleTypographicFamilyName,
+ defaultLocaleTypographicStyleName,
+ QString(),
+ weight,
+ style,
+ stretch,
+ antialias,
+ scalable,
+ size,
+ fixed,
+ writingSystems,
+ new FontHandle(face, defaultLocaleTypographicFamilyName));
+ }
+
+ } else {
+ qCWarning(lcQpaFonts) << "Unable to query IDWriteFontFace3 interface from font face.";
+ }
+
+ face->Release();
+ }
+
+ return ret.values();
}
bool QWindowsDirectWriteFontDatabase::isPrivateFontFamily(const QString &family) const
@@ -408,62 +690,103 @@ bool QWindowsDirectWriteFontDatabase::isPrivateFontFamily(const QString &family)
return false;
}
+static int QT_WIN_CALLBACK populateBitmapFonts(const LOGFONT *logFont,
+ const TEXTMETRIC *textmetric,
+ DWORD type,
+ LPARAM lparam)
+{
+ Q_UNUSED(textmetric);
+
+ // the "@family" fonts are just the same as "family". Ignore them.
+ const ENUMLOGFONTEX *f = reinterpret_cast<const ENUMLOGFONTEX *>(logFont);
+ const wchar_t *faceNameW = f->elfLogFont.lfFaceName;
+ if (faceNameW[0] && faceNameW[0] != L'@' && wcsncmp(faceNameW, L"WST_", 4)) {
+ const QString faceName = QString::fromWCharArray(faceNameW);
+ if (type & RASTER_FONTTYPE || type == 0) {
+ QWindowsDirectWriteFontDatabase *db = reinterpret_cast<QWindowsDirectWriteFontDatabase *>(lparam);
+ if (!db->hasPopulatedFont(faceName)) {
+ db->registerFontFamily(faceName);
+ db->registerBitmapFont(faceName);
+ }
+ }
+ }
+ return 1; // continue
+}
+
void QWindowsDirectWriteFontDatabase::populateFontDatabase()
{
wchar_t defaultLocale[LOCALE_NAME_MAX_LENGTH];
bool hasDefaultLocale = GetUserDefaultLocaleName(defaultLocale, LOCALE_NAME_MAX_LENGTH) != 0;
wchar_t englishLocale[] = L"en-us";
- QString defaultFontName = defaultFont().family();
- QString systemDefaultFontName = systemDefaultFont().family();
+ const QString defaultFontName = defaultFont().families().constFirst();
+ const QString systemDefaultFontName = systemDefaultFont().families().constFirst();
- IDWriteFontCollection *fontCollection;
- if (SUCCEEDED(data()->directWriteFactory->GetSystemFontCollection(&fontCollection))) {
+ DirectWriteScope<IDWriteFontCollection2> fontCollection;
+ DirectWriteScope<IDWriteFactory6> factory6;
+ if (FAILED(data()->directWriteFactory->QueryInterface(__uuidof(IDWriteFactory6),
+ reinterpret_cast<void **>(&factory6)))) {
+ qCWarning(lcQpaFonts) << "Can't initialize IDWriteFactory6. Use GDI font engine instead.";
+ return;
+ }
+
+ if (SUCCEEDED(factory6->GetSystemFontCollection(false,
+ DWRITE_FONT_FAMILY_MODEL_TYPOGRAPHIC,
+ &fontCollection))) {
for (uint i = 0; i < fontCollection->GetFontFamilyCount(); ++i) {
- IDWriteFontFamily *fontFamily;
+ DirectWriteScope<IDWriteFontFamily2> fontFamily;
if (SUCCEEDED(fontCollection->GetFontFamily(i, &fontFamily))) {
QString defaultLocaleName;
QString englishLocaleName;
- IDWriteLocalizedStrings *names;
+ DirectWriteScope<IDWriteLocalizedStrings> names;
if (SUCCEEDED(fontFamily->GetFamilyNames(&names))) {
if (hasDefaultLocale)
- defaultLocaleName = localeString(names, defaultLocale);
+ defaultLocaleName = localeString(*names, defaultLocale);
- englishLocaleName = localeString(names, englishLocale);
+ englishLocaleName = localeString(*names, englishLocale);
}
qCDebug(lcQpaFonts) << "Registering font, english name = " << englishLocaleName << ", name in current locale = " << defaultLocaleName;
if (!defaultLocaleName.isEmpty()) {
registerFontFamily(defaultLocaleName);
- m_populatedFonts.insert(defaultLocaleName, fontFamily);
+ m_populatedFonts.insert(defaultLocaleName, *fontFamily);
fontFamily->AddRef();
if (defaultLocaleName == defaultFontName && defaultFontName != systemDefaultFontName) {
qDebug(lcQpaFonts) << "Adding default font" << systemDefaultFontName << "as alternative to" << defaultLocaleName;
- m_populatedFonts.insert(systemDefaultFontName, fontFamily);
+ m_populatedFonts.insert(systemDefaultFontName, *fontFamily);
fontFamily->AddRef();
}
}
if (!englishLocaleName.isEmpty() && englishLocaleName != defaultLocaleName) {
registerFontFamily(englishLocaleName);
- m_populatedFonts.insert(englishLocaleName, fontFamily);
+ m_populatedFonts.insert(englishLocaleName, *fontFamily);
fontFamily->AddRef();
if (englishLocaleName == defaultFontName && defaultFontName != systemDefaultFontName) {
qDebug(lcQpaFonts) << "Adding default font" << systemDefaultFontName << "as alternative to" << englishLocaleName;
- m_populatedFonts.insert(systemDefaultFontName, fontFamily);
+ m_populatedFonts.insert(systemDefaultFontName, *fontFamily);
fontFamily->AddRef();
}
}
-
- fontFamily->Release();
}
}
}
+
+ // Since bitmap fonts are not supported by DirectWrite, we need to populate these as well
+ {
+ HDC dummy = GetDC(0);
+ LOGFONT lf;
+ lf.lfCharSet = DEFAULT_CHARSET;
+ lf.lfFaceName[0] = 0;
+ lf.lfPitchAndFamily = 0;
+ EnumFontFamiliesEx(dummy, &lf, populateBitmapFonts, reinterpret_cast<intptr_t>(this), 0);
+ ReleaseDC(0, dummy);
+ }
}
QFont QWindowsDirectWriteFontDatabase::defaultFont() const
@@ -471,4 +794,16 @@ QFont QWindowsDirectWriteFontDatabase::defaultFont() const
return QFont(QStringLiteral("Segoe UI"));
}
+bool QWindowsDirectWriteFontDatabase::supportsVariableApplicationFonts() const
+{
+ QSharedPointer<QWindowsFontEngineData> fontEngineData = data();
+ DirectWriteScope<IDWriteFactory5> factory5;
+ if (SUCCEEDED(fontEngineData->directWriteFactory->QueryInterface(__uuidof(IDWriteFactory5),
+ reinterpret_cast<void **>(&factory5)))) {
+ return true;
+ }
+
+ return false;
+}
+
QT_END_NAMESPACE
diff --git a/src/gui/text/windows/qwindowsdirectwritefontdatabase_p.h b/src/gui/text/windows/qwindowsdirectwritefontdatabase_p.h
index 91b432f74b..093c629a16 100644
--- a/src/gui/text/windows/qwindowsdirectwritefontdatabase_p.h
+++ b/src/gui/text/windows/qwindowsdirectwritefontdatabase_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2020 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) 2020 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 QWINDOWSDIRECTWRITEFONTDATABASE_P_H
#define QWINDOWSDIRECTWRITEFONTDATABASE_P_H
@@ -56,17 +20,18 @@
QT_REQUIRE_CONFIG(directwrite3);
-#include "qwindowsfontdatabasebase_p.h"
+#include "qwindowsfontdatabase_p.h"
#include <QtCore/qloggingcategory.h>
struct IDWriteFactory;
struct IDWriteFont;
+struct IDWriteFont1;
struct IDWriteFontFamily;
struct IDWriteLocalizedStrings;
QT_BEGIN_NAMESPACE
-class Q_GUI_EXPORT QWindowsDirectWriteFontDatabase : public QWindowsFontDatabaseBase
+class Q_GUI_EXPORT QWindowsDirectWriteFontDatabase : public QWindowsFontDatabase
{
Q_DISABLE_COPY_MOVE(QWindowsDirectWriteFontDatabase)
public:
@@ -75,19 +40,34 @@ public:
void populateFontDatabase() override;
void populateFamily(const QString &familyName) override;
+ bool populateFamilyAliases(const QString &missingFamily) override;
QFontEngine *fontEngine(const QFontDef &fontDef, void *handle) override;
+ QFontEngine *fontEngine(const QByteArray &fontData, qreal pixelSize, QFont::HintingPreference hintingPreference) override;
QStringList fallbacksForFamily(const QString &family, QFont::Style style, QFont::StyleHint styleHint, QChar::Script script) const override;
QStringList addApplicationFont(const QByteArray &fontData, const QString &fileName, QFontDatabasePrivate::ApplicationFont *font = nullptr) override;
- void releaseHandle(void *handle) override;
QFont defaultFont() const override;
- bool fontsAlwaysScalable() const override;
bool isPrivateFontFamily(const QString &family) const override;
+ bool supportsVariableApplicationFonts() const override;
+
+ void registerBitmapFont(const QString &bitmapFont)
+ {
+ m_populatedBitmapFonts.insert(bitmapFont);
+ }
+
+ bool hasPopulatedFont(const QString &fontFamily) const
+ {
+ return m_populatedFonts.contains(fontFamily);
+ }
private:
+ friend class QWindowsFontEngineDirectWrite;
static QString localeString(IDWriteLocalizedStrings *names, wchar_t localeName[]);
+ QSupportedWritingSystems supportedWritingSystems(IDWriteFontFace *face) const;
+
QHash<QString, IDWriteFontFamily *> m_populatedFonts;
+ QSet<QString> m_populatedBitmapFonts;
};
QT_END_NAMESPACE
diff --git a/src/gui/text/windows/qwindowsfontdatabase.cpp b/src/gui/text/windows/qwindowsfontdatabase.cpp
index 36d8da0ce8..adc06a6c2a 100644
--- a/src/gui/text/windows/qwindowsfontdatabase.cpp
+++ b/src/gui/text/windows/qwindowsfontdatabase.cpp
@@ -1,57 +1,23 @@
-/****************************************************************************
-**
-** 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 "qwindowsfontdatabase_p.h"
-#include "qwindowsfontdatabase_ft_p.h" // for default font
+#ifndef QT_NO_FREETYPE
+# include "qwindowsfontdatabase_ft_p.h" // for default font
+#endif
#include "qwindowsfontengine_p.h"
#include <QtCore/qt_windows.h>
#include <QtGui/QFont>
#include <QtGui/QGuiApplication>
-#include <QtGui/private/qhighdpiscaling_p.h>
#include <QtGui/private/qtgui-config_p.h>
#include <QtCore/qmath.h>
#include <QtCore/QDebug>
#include <QtCore/QFile>
#include <QtCore/QtEndian>
-#include <QtCore/private/qsystemlibrary_p.h>
+#include <QtCore/QStandardPaths>
+#include <QtCore/private/qduplicatetracker_p.h>
#include <QtCore/private/qwinregistry_p.h>
#include <wchar.h>
@@ -65,13 +31,13 @@
# include "qwindowsfontenginedirectwrite_p.h"
#endif
-QT_BEGIN_NAMESPACE
+#include <mutex>
-#if QT_CONFIG(directwrite)
-// ### fixme: Consider direct linking of dwrite.dll once Windows Vista pre SP2 is dropped (QTBUG-49711)
+QT_BEGIN_NAMESPACE
-typedef HRESULT (WINAPI *DWriteCreateFactoryType)(DWRITE_FACTORY_TYPE, const IID &, IUnknown **);
+using namespace Qt::StringLiterals;
+#if QT_CONFIG(directwrite)
static inline bool useDirectWrite(QFont::HintingPreference hintingPreference,
const QString &familyName = QString(),
bool isColorFont = false)
@@ -82,7 +48,7 @@ static inline bool useDirectWrite(QFont::HintingPreference hintingPreference,
// At some scales, GDI will misrender the MingLiU font, so we force use of
// DirectWrite to work around the issue.
- if (Q_UNLIKELY(familyName.startsWith(QLatin1String("MingLiU"))))
+ if (Q_UNLIKELY(familyName.startsWith("MingLiU"_L1)))
return true;
if (isColorFont)
@@ -90,7 +56,7 @@ static inline bool useDirectWrite(QFont::HintingPreference hintingPreference,
return hintingPreference == QFont::PreferNoHinting
|| hintingPreference == QFont::PreferVerticalHinting
- || (QHighDpiScaling::isActive() && hintingPreference == QFont::PreferDefaultHinting);
+ || (!qFuzzyCompare(qApp->devicePixelRatio(), 1.0) && hintingPreference == QFont::PreferDefaultHinting);
}
#endif // !QT_NO_DIRECTWRITE
@@ -162,7 +128,7 @@ QDebug operator<<(QDebug d, const QFontDef &def)
QDebugStateSaver saver(d);
d.nospace();
d.noquote();
- d << "QFontDef(Family=\"" << def.family << '"';
+ d << "QFontDef(Family=\"" << def.families.first() << '"';
if (!def.styleName.isEmpty())
d << ", stylename=" << def.styleName;
d << ", pointsize=" << def.pointSize << ", pixelsize=" << def.pixelSize
@@ -225,17 +191,6 @@ static inline QFontDatabase::WritingSystem writingSystemFromCharSet(uchar charSe
return QFontDatabase::Any;
}
-#ifdef MAKE_TAG
-#undef MAKE_TAG
-#endif
-// GetFontData expects the tags in little endian ;(
-#define MAKE_TAG(ch1, ch2, ch3, ch4) (\
- (((quint32)(ch4)) << 24) | \
- (((quint32)(ch3)) << 16) | \
- (((quint32)(ch2)) << 8) | \
- ((quint32)(ch1)) \
- )
-
bool qt_localizedName(const QString &name)
{
const QChar *c = name.unicode();
@@ -413,7 +368,7 @@ QString qt_getEnglishName(const QString &familyName, bool includeStyle)
HGDIOBJ oldobj = SelectObject( hdc, hfont );
- const DWORD name_tag = MAKE_TAG( 'n', 'a', 'm', 'e' );
+ const DWORD name_tag = qFromBigEndian(QFont::Tag("name").value());
// get the name table
unsigned char *table = 0;
@@ -434,7 +389,7 @@ QString qt_getEnglishName(const QString &familyName, bool includeStyle)
const QFontNames names = qt_getCanonicalFontNames(table, bytes);
i18n_name = names.name;
if (includeStyle)
- i18n_name += QLatin1Char(' ') + names.style;
+ i18n_name += u' ' + names.style;
}
error:
delete [] table;
@@ -462,7 +417,7 @@ QFontNames qt_getCanonicalFontNames(const LOGFONT &lf)
// get the name table
QByteArray table;
- const DWORD name_tag = MAKE_TAG('n', 'a', 'm', 'e');
+ const DWORD name_tag = qFromBigEndian(QFont::Tag("name").value());
DWORD bytes = GetFontData(hdc, name_tag, 0, 0, 0);
if (bytes != GDI_ERROR) {
table.resize(bytes);
@@ -478,18 +433,6 @@ QFontNames qt_getCanonicalFontNames(const LOGFONT &lf)
return fontNames;
}
-static QChar *createFontFile(const QString &faceName)
-{
- QChar *faceNamePtr = nullptr;
- if (!faceName.isEmpty()) {
- const int nameLength = qMin(faceName.length(), LF_FACESIZE - 1);
- faceNamePtr = new QChar[nameLength + 1];
- memcpy(static_cast<void *>(faceNamePtr), faceName.utf16(), sizeof(wchar_t) * nameLength);
- faceNamePtr[nameLength] = 0;
- }
- return faceNamePtr;
-}
-
namespace {
struct StoreFontPayload {
StoreFontPayload(const QString &family,
@@ -499,7 +442,7 @@ namespace {
{}
QString populatedFontFamily;
- QSet<FontAndStyle> foundFontAndStyles;
+ QDuplicateTracker<FontAndStyle> foundFontAndStyles;
QWindowsFontDatabase *windowsFontDatabase;
};
}
@@ -513,7 +456,7 @@ static bool addFontToDatabase(QString familyName,
StoreFontPayload *sfp)
{
// the "@family" fonts are just the same as "family". Ignore them.
- if (familyName.isEmpty() || familyName.at(0) == QLatin1Char('@') || familyName.startsWith(QLatin1String("WST_")))
+ if (familyName.isEmpty() || familyName.at(0) == u'@' || familyName.startsWith("WST_"_L1))
return false;
uchar charSet = logFont.lfCharSet;
@@ -522,7 +465,9 @@ static bool addFontToDatabase(QString familyName,
const QString foundryName; // No such concept.
const bool fixed = !(textmetric->tmPitchAndFamily & TMPF_FIXED_PITCH);
const bool ttf = (textmetric->tmPitchAndFamily & TMPF_TRUETYPE);
- const bool scalable = textmetric->tmPitchAndFamily & (TMPF_VECTOR|TMPF_TRUETYPE);
+ const bool unreliableTextMetrics = type == 0;
+ const bool scalable = (textmetric->tmPitchAndFamily & (TMPF_VECTOR|TMPF_TRUETYPE))
+ && !unreliableTextMetrics;
const int size = scalable ? SMOOTH_SCALABLE : textmetric->tmHeight;
const QFont::Style style = textmetric->tmItalic ? QFont::StyleItalic : QFont::StyleNormal;
const bool antialias = false;
@@ -542,7 +487,7 @@ static bool addFontToDatabase(QString familyName,
str << " TRUETYPE";
str << " scalable=" << scalable << " Size=" << size
<< " Style=" << style << " Weight=" << weight
- << " stretch=" << stretch;
+ << " stretch=" << stretch << " styleName=" << styleName;
qCDebug(lcQpaFonts) << message;
}
#endif
@@ -560,7 +505,13 @@ static bool addFontToDatabase(QString familyName,
subFamilyStyle = styleName;
faceName = familyName; // Remember the original name for later lookups
familyName = canonicalNames.preferredName;
- styleName = canonicalNames.preferredStyle;
+ // Preferred style / typographic subfamily name:
+ // "If it is absent, then name ID 2 is considered to be the typographic subfamily name."
+ // From: https://docs.microsoft.com/en-us/windows/win32/directwrite/opentype-variable-fonts
+ // Name ID 2 is already stored in the styleName variable. Furthermore, for variable fonts,
+ // styleName holds the variation instance name, which should be used over name ID 2.
+ if (!canonicalNames.preferredStyle.isEmpty())
+ styleName = canonicalNames.preferredStyle;
}
QSupportedWritingSystems writingSystems;
@@ -580,7 +531,7 @@ static bool addFontToDatabase(QString familyName,
// display Thai text by default. As a temporary work around, we special case Segoe UI
// and remove the Thai script from its list of supported writing systems.
if (writingSystems.supported(QFontDatabase::Thai) &&
- familyName == QLatin1String("Segoe UI"))
+ familyName == "Segoe UI"_L1)
writingSystems.setSupported(QFontDatabase::Thai, false);
} else {
const QFontDatabase::WritingSystem ws = writingSystemFromCharSet(charSet);
@@ -588,33 +539,35 @@ static bool addFontToDatabase(QString familyName,
writingSystems.setSupported(ws);
}
- // We came here from populating a different font family, so we have
- // to ensure the entire typographic family is populated before we
- // mark it as such inside registerFont()
- if (!subFamilyName.isEmpty()
- && familyName != subFamilyName
- && sfp->populatedFontFamily != familyName
- && !QPlatformFontDatabase::isFamilyPopulated(familyName)) {
- sfp->windowsFontDatabase->populateFamily(familyName);
- }
-
+ const bool wasPopulated = QPlatformFontDatabase::isFamilyPopulated(familyName);
QPlatformFontDatabase::registerFont(familyName, styleName, foundryName, weight,
- style, stretch, antialias, scalable, size, fixed, writingSystems, createFontFile(faceName));
+ style, stretch, antialias, scalable, size, fixed, writingSystems, new QWindowsFontDatabase::FontHandle(faceName));
+
// add fonts windows can generate for us:
if (weight <= QFont::DemiBold && styleName.isEmpty())
QPlatformFontDatabase::registerFont(familyName, QString(), foundryName, QFont::Bold,
- style, stretch, antialias, scalable, size, fixed, writingSystems, createFontFile(faceName));
+ style, stretch, antialias, scalable, size, fixed, writingSystems, new QWindowsFontDatabase::FontHandle(faceName));
if (style != QFont::StyleItalic && styleName.isEmpty())
QPlatformFontDatabase::registerFont(familyName, QString(), foundryName, weight,
- QFont::StyleItalic, stretch, antialias, scalable, size, fixed, writingSystems, createFontFile(faceName));
+ QFont::StyleItalic, stretch, antialias, scalable, size, fixed, writingSystems, new QWindowsFontDatabase::FontHandle(faceName));
if (weight <= QFont::DemiBold && style != QFont::StyleItalic && styleName.isEmpty())
QPlatformFontDatabase::registerFont(familyName, QString(), foundryName, QFont::Bold,
- QFont::StyleItalic, stretch, antialias, scalable, size, fixed, writingSystems, createFontFile(faceName));
+ QFont::StyleItalic, stretch, antialias, scalable, size, fixed, writingSystems, new QWindowsFontDatabase::FontHandle(faceName));
+
+ // We came here from populating a different font family, so we have
+ // to ensure the entire typographic family is populated before we
+ // mark it as such inside registerFont()
+ if (!subFamilyName.isEmpty()
+ && familyName != subFamilyName
+ && sfp->populatedFontFamily != familyName
+ && !wasPopulated) {
+ sfp->windowsFontDatabase->populateFamily(familyName);
+ }
if (!subFamilyName.isEmpty() && familyName != subFamilyName) {
QPlatformFontDatabase::registerFont(subFamilyName, subFamilyStyle, foundryName, weight,
- style, stretch, antialias, scalable, size, fixed, writingSystems, createFontFile(faceName));
+ style, stretch, antialias, scalable, size, fixed, writingSystems, new QWindowsFontDatabase::FontHandle(faceName));
}
if (!englishName.isEmpty() && englishName != familyName)
@@ -640,10 +593,8 @@ static int QT_WIN_CALLBACK storeFont(const LOGFONT *logFont, const TEXTMETRIC *t
signature = &reinterpret_cast<const NEWTEXTMETRICEX *>(textmetric)->ntmFontSig;
// We get a callback for each script-type supported, but we register them all
// at once using the signature, so we only need one call to addFontToDatabase().
- FontAndStyle fontAndStyle = {familyName, styleName};
- if (sfp->foundFontAndStyles.contains(fontAndStyle))
+ if (sfp->foundFontAndStyles.hasSeen({familyName, styleName}))
return 1;
- sfp->foundFontAndStyles.insert(fontAndStyle);
}
addFontToDatabase(familyName, styleName, *logFont, textmetric, signature, type, sfp);
@@ -669,8 +620,8 @@ bool QWindowsFontDatabase::populateFamilyAliases(const QString &missingFamily)
void QWindowsFontDatabase::populateFamily(const QString &familyName)
{
qCDebug(lcQpaFonts) << familyName;
- if (familyName.size() >= LF_FACESIZE) {
- qCWarning(lcQpaFonts) << "Unable to enumerate family '" << familyName << '\'';
+ if (familyName.size() >= LF_FACESIZE) { // Field length of LOGFONT::lfFaceName
+ qCDebug(lcQpaFonts) << "Unable to enumerate family '" << familyName << '\'';
return;
}
HDC dummy = GetDC(0);
@@ -704,24 +655,48 @@ static int QT_WIN_CALLBACK populateFontFamilies(const LOGFONT *logFont, const TE
return 1; // continue
}
+namespace {
+
+QString resolveFontPath(const QString &fontPath)
+{
+ if (fontPath.isEmpty())
+ return QString();
+
+ if (QFile::exists(fontPath))
+ return fontPath;
+
+ // resolve the path relatively to Windows Fonts directory
+ return QStandardPaths::locate(QStandardPaths::FontsLocation, fontPath);
+}
+
+}
+
void QWindowsFontDatabase::addDefaultEUDCFont()
{
const QString path = QWinRegistryKey(HKEY_CURRENT_USER, LR"(EUDC\1252)")
.stringValue(L"SystemDefaultEUDCFont");
- if (!path.isEmpty()) {
- QFile file(path);
- if (!file.open(QIODevice::ReadOnly)) {
- qCWarning(lcQpaFonts) << "Unable to open default EUDC font:" << path;
- return;
- }
+ if (path.isEmpty()) {
+ qCDebug(lcQpaFonts) << "There's no default EUDC font specified";
+ return;
+ }
+
+ const QString absolutePath = resolveFontPath(path);
+ if (absolutePath.isEmpty()) {
+ qCDebug(lcQpaFonts) << "Unable to locate default EUDC font:" << path;
+ return;
+ }
- m_eudcFonts = addApplicationFont(file.readAll(), path);
+ QFile file(absolutePath);
+ if (!file.open(QIODevice::ReadOnly)) {
+ qCWarning(lcQpaFonts) << "Unable to open default EUDC font:" << absolutePath;
+ return;
}
+
+ m_eudcFonts = addApplicationFont(file.readAll(), absolutePath);
}
void QWindowsFontDatabase::populateFontDatabase()
{
- removeApplicationFonts();
HDC dummy = GetDC(0);
LOGFONT lf;
lf.lfCharSet = DEFAULT_CHARSET;
@@ -730,12 +705,18 @@ void QWindowsFontDatabase::populateFontDatabase()
EnumFontFamiliesEx(dummy, &lf, populateFontFamilies, 0, 0);
ReleaseDC(0, dummy);
// Work around EnumFontFamiliesEx() not listing the system font.
- QString systemDefaultFamily = QWindowsFontDatabase::systemDefaultFont().family();
+ const QString systemDefaultFamily = QWindowsFontDatabase::systemDefaultFont().families().constFirst();
if (QPlatformFontDatabase::resolveFontFamilyAlias(systemDefaultFamily) == systemDefaultFamily)
QPlatformFontDatabase::registerFontFamily(systemDefaultFamily);
addDefaultEUDCFont();
}
+void QWindowsFontDatabase::invalidate()
+{
+ QWindowsFontDatabaseBase::invalidate();
+ removeApplicationFonts();
+}
+
QWindowsFontDatabase::QWindowsFontDatabase()
{
// Properties accessed by QWin32PrintEngine (Qt Print Support)
@@ -758,7 +739,8 @@ QWindowsFontDatabase::~QWindowsFontDatabase()
QFontEngine * QWindowsFontDatabase::fontEngine(const QFontDef &fontDef, void *handle)
{
- const QString faceName(static_cast<const QChar*>(handle));
+ FontHandle *fontHandle = static_cast<FontHandle *>(handle);
+ const QString faceName = fontHandle->faceName.left(LF_FACESIZE - 1);
QFontEngine *fe = QWindowsFontDatabase::createEngine(fontDef, faceName,
defaultVerticalDPI(),
data());
@@ -780,10 +762,10 @@ QFontEngine *QWindowsFontDatabase::fontEngine(const QByteArray &fontData, qreal
QT_WARNING_PUSH
QT_WARNING_DISABLE_GCC("-Wstrict-aliasing")
- QString uniqueFamilyName = QLatin1Char('f')
- + QString::number(guid.Data1, 36) + QLatin1Char('-')
- + QString::number(guid.Data2, 36) + QLatin1Char('-')
- + QString::number(guid.Data3, 36) + QLatin1Char('-')
+ QString uniqueFamilyName = u'f'
+ + QString::number(guid.Data1, 36) + u'-'
+ + QString::number(guid.Data2, 36) + u'-'
+ + QString::number(guid.Data3, 36) + u'-'
+ QString::number(*reinterpret_cast<quint64 *>(guid.Data4), 36);
QT_WARNING_POP
@@ -807,7 +789,7 @@ QT_WARNING_POP
qWarning("%s: AddFontMemResourceEx failed", __FUNCTION__);
} else {
QFontDef request;
- request.family = uniqueFamilyName;
+ request.families = QStringList(uniqueFamilyName);
request.pixelSize = pixelSize;
request.styleStrategy = QFont::PreferMatch;
request.hintingPreference = hintingPreference;
@@ -818,9 +800,9 @@ QT_WARNING_POP
data());
if (fontEngine) {
- if (request.family != fontEngine->fontDef.family) {
- qWarning("%s: Failed to load font. Got fallback instead: %s",
- __FUNCTION__, qPrintable(fontEngine->fontDef.family));
+ if (request.families != fontEngine->fontDef.families) {
+ qWarning("%s: Failed to load font. Got fallback instead: %s", __FUNCTION__,
+ qPrintable(fontEngine->fontDef.families.constFirst()));
if (fontEngine->ref.loadRelaxed() == 0)
delete fontEngine;
fontEngine = 0;
@@ -831,12 +813,12 @@ QT_WARNING_POP
switch (fontEngine->type()) {
case QFontEngine::Win:
static_cast<QWindowsFontEngine *>(fontEngine)->setUniqueFamilyName(uniqueFamilyName);
- fontEngine->fontDef.family = actualFontName;
+ fontEngine->fontDef.families = QStringList(actualFontName);
break;
#if QT_CONFIG(directwrite) && QT_CONFIG(direct2d)
case QFontEngine::DirectWrite:
static_cast<QWindowsFontEngineDirectWrite *>(fontEngine)->setUniqueFamilyName(uniqueFamilyName);
- fontEngine->fontDef.family = actualFontName;
+ fontEngine->fontDef.families = QStringList(actualFontName);
break;
#endif // directwrite && direct2d
@@ -844,10 +826,13 @@ QT_WARNING_POP
Q_ASSERT_X(false, Q_FUNC_INFO, "Unhandled font engine.");
}
- UniqueFontData uniqueData;
+ UniqueFontData uniqueData{};
uniqueData.handle = fontHandle;
- uniqueData.refCount.ref();
- m_uniqueFontData[uniqueFamilyName] = uniqueData;
+ ++uniqueData.refCount;
+ {
+ const std::scoped_lock lock(m_uniqueFontDataMutex);
+ m_uniqueFontData[uniqueFamilyName] = uniqueData;
+ }
}
} else {
RemoveFontMemResourceEx(fontHandle);
@@ -868,36 +853,70 @@ QT_WARNING_POP
return fontEngine;
}
-static QList<quint32> getTrueTypeFontOffsets(const uchar *fontData)
+static QList<quint32> getTrueTypeFontOffsets(const uchar *fontData, const uchar *fileEndSentinel)
{
QList<quint32> offsets;
- const quint32 headerTag = *reinterpret_cast<const quint32 *>(fontData);
- if (headerTag != MAKE_TAG('t', 't', 'c', 'f')) {
- if (headerTag != MAKE_TAG(0, 1, 0, 0)
- && headerTag != MAKE_TAG('O', 'T', 'T', 'O')
- && headerTag != MAKE_TAG('t', 'r', 'u', 'e')
- && headerTag != MAKE_TAG('t', 'y', 'p', '1'))
+ if (fileEndSentinel - fontData < 12) {
+ qCWarning(lcQpaFonts) << "Corrupted font data detected";
+ return offsets;
+ }
+
+ const quint32 headerTag = qFromUnaligned<quint32>(fontData);
+ if (headerTag != qFromBigEndian(QFont::Tag("ttcf").value())) {
+ if (headerTag != qFromBigEndian(QFont::Tag("\0\1\0\0").value())
+ && headerTag != qFromBigEndian(QFont::Tag("OTTO").value())
+ && headerTag != qFromBigEndian(QFont::Tag("true").value())
+ && headerTag != qFromBigEndian(QFont::Tag("typ1").value())) {
return offsets;
+ }
offsets << 0;
return offsets;
}
+
+ const quint32 maximumNumFonts = 0xffff;
const quint32 numFonts = qFromBigEndian<quint32>(fontData + 8);
- for (uint i = 0; i < numFonts; ++i) {
- offsets << qFromBigEndian<quint32>(fontData + 12 + i * 4);
+ if (numFonts > maximumNumFonts) {
+ qCWarning(lcQpaFonts) << "Font collection of" << numFonts << "fonts is too large. Aborting.";
+ return offsets;
}
+
+ if (quintptr(fileEndSentinel - fontData) > 12 + (numFonts - 1) * 4) {
+ for (quint32 i = 0; i < numFonts; ++i)
+ offsets << qFromBigEndian<quint32>(fontData + 12 + i * 4);
+ } else {
+ qCWarning(lcQpaFonts) << "Corrupted font data detected";
+ }
+
return offsets;
}
-static void getFontTable(const uchar *fileBegin, const uchar *data, quint32 tag, const uchar **table, quint32 *length)
+static void getFontTable(const uchar *fileBegin, const uchar *fileEndSentinel, const uchar *data, quint32 tag, const uchar **table, quint32 *length)
{
- const quint16 numTables = qFromBigEndian<quint16>(data + 4);
- for (uint i = 0; i < numTables; ++i) {
- const quint32 offset = 12 + 16 * i;
- if (*reinterpret_cast<const quint32 *>(data + offset) == tag) {
- *table = fileBegin + qFromBigEndian<quint32>(data + offset + 8);
- *length = qFromBigEndian<quint32>(data + offset + 12);
- return;
+ if (fileEndSentinel - data >= 6) {
+ const quint16 numTables = qFromBigEndian<quint16>(data + 4);
+ if (fileEndSentinel - data >= 28 + 16 * (numTables - 1)) {
+ for (quint32 i = 0; i < numTables; ++i) {
+ const quint32 offset = 12 + 16 * i;
+ if (qFromUnaligned<quint32>(data + offset) == tag) {
+ const quint32 tableOffset = qFromBigEndian<quint32>(data + offset + 8);
+ if (quintptr(fileEndSentinel - fileBegin) <= tableOffset) {
+ qCWarning(lcQpaFonts) << "Corrupted font data detected";
+ break;
+ }
+ *table = fileBegin + tableOffset;
+ *length = qFromBigEndian<quint32>(data + offset + 12);
+ if (quintptr(fileEndSentinel - *table) < *length) {
+ qCWarning(lcQpaFonts) << "Corrupted font data detected";
+ break;
+ }
+ return;
+ }
+ }
+ } else {
+ qCWarning(lcQpaFonts) << "Corrupted font data detected";
}
+ } else {
+ qCWarning(lcQpaFonts) << "Corrupted font data detected";
}
*table = 0;
*length = 0;
@@ -910,8 +929,9 @@ static void getFamiliesAndSignatures(const QByteArray &fontData,
QList<QFontValues> *values)
{
const uchar *data = reinterpret_cast<const uchar *>(fontData.constData());
+ const uchar *dataEndSentinel = data + fontData.size();
- QList<quint32> offsets = getTrueTypeFontOffsets(data);
+ QList<quint32> offsets = getTrueTypeFontOffsets(data, dataEndSentinel);
if (offsets.isEmpty())
return;
@@ -919,7 +939,9 @@ static void getFamiliesAndSignatures(const QByteArray &fontData,
const uchar *font = data + offsets.at(i);
const uchar *table;
quint32 length;
- getFontTable(data, font, MAKE_TAG('n', 'a', 'm', 'e'), &table, &length);
+ getFontTable(data, dataEndSentinel, font,
+ qFromBigEndian(QFont::Tag("name").value()),
+ &table, &length);
if (!table)
continue;
QFontNames names = qt_getCanonicalFontNames(table, length);
@@ -928,8 +950,11 @@ static void getFamiliesAndSignatures(const QByteArray &fontData,
families->append(std::move(names));
- if (values || signatures)
- getFontTable(data, font, MAKE_TAG('O', 'S', '/', '2'), &table, &length);
+ if (values || signatures) {
+ getFontTable(data, dataEndSentinel, font,
+ qFromBigEndian(QFont::Tag("OS/2").value()),
+ &table, &length);
+ }
if (values) {
QFontValues fontValues;
@@ -994,7 +1019,7 @@ QStringList QWindowsFontDatabase::addApplicationFont(const QByteArray &fontData,
HDC hdc = GetDC(0);
LOGFONT lf;
memset(&lf, 0, sizeof(LOGFONT));
- memcpy(lf.lfFaceName, familyName.utf16(), sizeof(wchar_t) * qMin(LF_FACESIZE - 1, familyName.size()));
+ memcpy(lf.lfFaceName, familyName.data(), sizeof(wchar_t) * qMin(LF_FACESIZE - 1, familyName.size()));
lf.lfCharSet = DEFAULT_CHARSET;
const QFontValues &values = fontValues.at(j);
lf.lfWeight = values.weight;
@@ -1075,7 +1100,7 @@ QStringList QWindowsFontDatabase::addApplicationFont(const QByteArray &fontData,
void QWindowsFontDatabase::removeApplicationFonts()
{
- for (const WinApplicationFont &font : qAsConst(m_applicationFonts)) {
+ for (const WinApplicationFont &font : std::as_const(m_applicationFonts)) {
if (font.handle) {
RemoveFontMemResourceEx(font.handle);
} else {
@@ -1087,10 +1112,22 @@ void QWindowsFontDatabase::removeApplicationFonts()
m_eudcFonts.clear();
}
+QWindowsFontDatabase::FontHandle::FontHandle(IDWriteFontFace *face, const QString &name)
+ : fontFace(face), faceName(name)
+{
+ fontFace->AddRef();
+}
+
+
+QWindowsFontDatabase::FontHandle::~FontHandle()
+{
+ if (fontFace != nullptr)
+ fontFace->Release();
+}
+
void QWindowsFontDatabase::releaseHandle(void *handle)
{
- const QChar *faceName = reinterpret_cast<const QChar *>(handle);
- delete[] faceName;
+ delete static_cast<FontHandle *>(handle);
}
QString QWindowsFontDatabase::fontDir() const
@@ -1107,18 +1144,22 @@ bool QWindowsFontDatabase::fontsAlwaysScalable() const
void QWindowsFontDatabase::derefUniqueFont(const QString &uniqueFont)
{
- if (m_uniqueFontData.contains(uniqueFont)) {
- if (!m_uniqueFontData[uniqueFont].refCount.deref()) {
- RemoveFontMemResourceEx(m_uniqueFontData[uniqueFont].handle);
- m_uniqueFontData.remove(uniqueFont);
+ const std::scoped_lock lock(m_uniqueFontDataMutex);
+ const auto it = m_uniqueFontData.find(uniqueFont);
+ if (it != m_uniqueFontData.end()) {
+ if (--it->refCount == 0) {
+ RemoveFontMemResourceEx(it->handle);
+ m_uniqueFontData.erase(it);
}
}
}
void QWindowsFontDatabase::refUniqueFont(const QString &uniqueFont)
{
- if (m_uniqueFontData.contains(uniqueFont))
- m_uniqueFontData[uniqueFont].refCount.ref();
+ const std::scoped_lock lock(m_uniqueFontDataMutex);
+ const auto it = m_uniqueFontData.find(uniqueFont);
+ if (it != m_uniqueFontData.end())
+ ++it->refCount;
}
QStringList QWindowsFontDatabase::fallbacksForFamily(const QString &family, QFont::Style style, QFont::StyleHint styleHint, QChar::Script script) const
@@ -1139,7 +1180,7 @@ QFontEngine *QWindowsFontDatabase::createEngine(const QFontDef &request, const Q
int dpi,
const QSharedPointer<QWindowsFontEngineData> &data)
{
- QFontEngine *fe = 0;
+ QFontEngine *fe = nullptr;
LOGFONT lf = fontDefToLOGFONT(request, faceName);
const bool preferClearTypeAA = lf.lfQuality == CLEARTYPE_QUALITY;
@@ -1168,7 +1209,7 @@ QFontEngine *QWindowsFontDatabase::createEngine(const QFontDef &request, const Q
const QString nameSubstitute = QWindowsFontEngineDirectWrite::fontNameSubstitute(fam);
if (nameSubstitute != fam) {
const int nameSubstituteLength = qMin(nameSubstitute.length(), LF_FACESIZE - 1);
- memcpy(lf.lfFaceName, nameSubstitute.utf16(), nameSubstituteLength * sizeof(wchar_t));
+ memcpy(lf.lfFaceName, nameSubstitute.data(), nameSubstituteLength * sizeof(wchar_t));
lf.lfFaceName[nameSubstituteLength] = 0;
}
@@ -1186,6 +1227,7 @@ QFontEngine *QWindowsFontDatabase::createEngine(const QFontDef &request, const Q
HRESULT hr = data->directWriteGdiInterop->CreateFontFaceFromHdc(data->hdc, &directWriteFontFace);
if (SUCCEEDED(hr)) {
bool isColorFont = false;
+ bool needsSimulation = false;
#if QT_CONFIG(direct2d)
IDWriteFontFace2 *directWriteFontFace2 = nullptr;
if (SUCCEEDED(directWriteFontFace->QueryInterface(__uuidof(IDWriteFontFace2),
@@ -1193,13 +1235,17 @@ QFontEngine *QWindowsFontDatabase::createEngine(const QFontDef &request, const Q
if (directWriteFontFace2->IsColorFont())
isColorFont = directWriteFontFace2->GetPaletteEntryCount() > 0;
+ needsSimulation = directWriteFontFace2->GetSimulations() != DWRITE_FONT_SIMULATIONS_NONE;
+
directWriteFontFace2->Release();
}
#endif // direct2d
- useDw = useDw || useDirectWrite(hintingPreference, fam, isColorFont);
- qCDebug(lcQpaFonts) << __FUNCTION__ << request.family << request.pointSize
- << "pt" << "hintingPreference=" << hintingPreference << "color=" << isColorFont
- << dpi << "dpi" << "useDirectWrite=" << useDw;
+ useDw = useDw || useDirectWrite(hintingPreference, fam, isColorFont) || needsSimulation;
+ qCDebug(lcQpaFonts)
+ << __FUNCTION__ << request.families.first() << request.pointSize << "pt"
+ << "hintingPreference=" << hintingPreference << "color=" << isColorFont
+ << dpi << "dpi"
+ << "useDirectWrite=" << useDw;
if (useDw) {
QWindowsFontEngineDirectWrite *fedw = new QWindowsFontEngineDirectWrite(directWriteFontFace,
request.pixelSize,
@@ -1209,10 +1255,7 @@ QFontEngine *QWindowsFontDatabase::createEngine(const QFontDef &request, const Q
GetTextFace(data->hdc, 64, n);
QFontDef fontDef = request;
- fontDef.family = QString::fromWCharArray(n);
-
- if (isColorFont)
- fedw->glyphFormat = QFontEngine::Format_ARGB;
+ fontDef.families = QStringList(QString::fromWCharArray(n));
fedw->initFontInfo(fontDef, dpi);
fe = fedw;
}
@@ -1230,7 +1273,7 @@ QFontEngine *QWindowsFontDatabase::createEngine(const QFontDef &request, const Q
#endif // directwrite direct2d
if (!fe) {
- QWindowsFontEngine *few = new QWindowsFontEngine(request.family, lf, data);
+ QWindowsFontEngine *few = new QWindowsFontEngine(request.families.first(), lf, data);
if (preferClearTypeAA)
few->glyphFormat = QFontEngine::Format_A32;
few->initFontInfo(request, dpi);
diff --git a/src/gui/text/windows/qwindowsfontdatabase_ft.cpp b/src/gui/text/windows/qwindowsfontdatabase_ft.cpp
index d047532c3c..0604a85e35 100644
--- a/src/gui/text/windows/qwindowsfontdatabase_ft.cpp
+++ b/src/gui/text/windows/qwindowsfontdatabase_ft.cpp
@@ -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 "qwindowsfontdatabase_ft_p.h"
#include "qwindowsfontdatabase_p.h"
@@ -51,6 +15,8 @@
#if QT_CONFIG(regularexpression)
#include <QtCore/QRegularExpression>
#endif
+#include <QtCore/private/qduplicatetracker_p.h>
+
#include <QtGui/QGuiApplication>
#include <QtGui/QFontDatabase>
@@ -58,6 +24,8 @@
QT_BEGIN_NAMESPACE
+using namespace Qt::StringLiterals;
+
static inline QFontDatabase::WritingSystem writingSystemFromCharSet(uchar charSet)
{
switch (charSet) {
@@ -138,7 +106,7 @@ static FontKeys &fontKeys()
#if QT_CONFIG(regularexpression)
realKey.remove(sizeListMatch);
#endif
- const auto fontNames = QStringView(realKey).trimmed().split(QLatin1Char('&'));
+ const auto fontNames = QStringView(realKey).trimmed().split(u'&');
fontKey.fontNames.reserve(fontNames.size());
for (const auto &fontName : fontNames)
fontKey.fontNames.append(fontName.trimmed().toString());
@@ -174,7 +142,7 @@ static bool addFontToDatabase(QString familyName,
int type)
{
// the "@family" fonts are just the same as "family". Ignore them.
- if (familyName.isEmpty() || familyName.at(0) == QLatin1Char('@') || familyName.startsWith(QLatin1String("WST_")))
+ if (familyName.isEmpty() || familyName.at(0) == u'@' || familyName.startsWith("WST_"_L1))
return false;
uchar charSet = logFont.lfCharSet;
@@ -240,8 +208,7 @@ static bool addFontToDatabase(QString familyName,
// Since it's the default UI font on this platform, most widgets will be unable to
// display Thai text by default. As a temporary work around, we special case Segoe UI
// and remove the Thai script from its list of supported writing systems.
- if (writingSystems.supported(QFontDatabase::Thai) &&
- faceName == QLatin1String("Segoe UI"))
+ if (writingSystems.supported(QFontDatabase::Thai) && faceName == "Segoe UI"_L1)
writingSystems.setSupported(QFontDatabase::Thai, false);
} else {
const QFontDatabase::WritingSystem ws = writingSystemFromCharSet(charSet);
@@ -257,8 +224,8 @@ static bool addFontToDatabase(QString familyName,
QLocale systemLocale = QLocale::system();
if (systemLocale.language() != QLocale::C
&& systemLocale.language() != QLocale::English
- && styleName != QLatin1String("Italic")
- && styleName != QLatin1String("Bold")) {
+ && styleName != "Italic"_L1
+ && styleName != "Bold"_L1) {
key = findFontKey(qt_getEnglishName(fullName, true), &index);
}
if (!key)
@@ -318,11 +285,9 @@ static int QT_WIN_CALLBACK storeFont(const LOGFONT *logFont, const TEXTMETRIC *t
signature = &reinterpret_cast<const NEWTEXTMETRICEX *>(textmetric)->ntmFontSig;
// We get a callback for each script-type supported, but we register them all
// at once using the signature, so we only need one call to addFontToDatabase().
- QSet<FontAndStyle> *foundFontAndStyles = reinterpret_cast<QSet<FontAndStyle> *>(lparam);
- FontAndStyle fontAndStyle = {faceName, styleName};
- if (foundFontAndStyles->contains(fontAndStyle))
+ auto foundFontAndStyles = reinterpret_cast<QDuplicateTracker<FontAndStyle> *>(lparam);
+ if (foundFontAndStyles->hasSeen({faceName, styleName}))
return 1;
- foundFontAndStyles->insert(fontAndStyle);
}
addFontToDatabase(faceName, styleName, fullName, *logFont, textmetric, signature, type);
@@ -330,6 +295,21 @@ static int QT_WIN_CALLBACK storeFont(const LOGFONT *logFont, const TEXTMETRIC *t
return 1;
}
+bool QWindowsFontDatabaseFT::populateFamilyAliases(const QString &missingFamily)
+{
+ Q_UNUSED(missingFamily);
+
+ if (m_hasPopulatedAliases)
+ return false;
+
+ QStringList families = QFontDatabase::families();
+ for (const QString &family : families)
+ populateFamily(family);
+ m_hasPopulatedAliases = true;
+
+ return true;
+}
+
/*
\brief Populates the font database using EnumFontFamiliesEx().
@@ -352,7 +332,7 @@ void QWindowsFontDatabaseFT::populateFamily(const QString &familyName)
lf.lfFaceName[familyName.size()] = 0;
lf.lfCharSet = DEFAULT_CHARSET;
lf.lfPitchAndFamily = 0;
- QSet<FontAndStyle> foundFontAndStyles;
+ QDuplicateTracker<FontAndStyle> foundFontAndStyles;
EnumFontFamiliesEx(dummy, &lf, storeFont, reinterpret_cast<intptr_t>(&foundFontAndStyles), 0);
ReleaseDC(0, dummy);
}
@@ -398,7 +378,7 @@ void QWindowsFontDatabaseFT::populateFontDatabase()
EnumFontFamiliesEx(dummy, &lf, populateFontFamilies, 0, 0);
ReleaseDC(0, dummy);
// Work around EnumFontFamiliesEx() not listing the system font
- QString systemDefaultFamily = QWindowsFontDatabase::systemDefaultFont().family();
+ const QString systemDefaultFamily = QWindowsFontDatabase::systemDefaultFont().families().constFirst();
if (QPlatformFontDatabase::resolveFontFamilyAlias(systemDefaultFamily) == systemDefaultFamily)
QPlatformFontDatabase::registerFontFamily(systemDefaultFamily);
}
@@ -406,7 +386,7 @@ void QWindowsFontDatabaseFT::populateFontDatabase()
QFontEngine * QWindowsFontDatabaseFT::fontEngine(const QFontDef &fontDef, void *handle)
{
QFontEngine *fe = QFreeTypeFontDatabase::fontEngine(fontDef, handle);
- qCDebug(lcQpaFonts) << __FUNCTION__ << "FONTDEF" << fontDef.family << fe << handle;
+ qCDebug(lcQpaFonts) << __FUNCTION__ << "FONTDEF" << fontDef.families.constFirst() << fe << handle;
return fe;
}
@@ -431,7 +411,7 @@ QStringList QWindowsFontDatabaseFT::fallbacksForFamily(const QString &family, QF
}
QString QWindowsFontDatabaseFT::fontDir() const
{
- const QString result = QLatin1String(qgetenv("windir")) + QLatin1String("/Fonts");//QPlatformFontDatabase::fontDir();
+ const QString result = QLatin1StringView(qgetenv("windir")) + "/Fonts"_L1;//QPlatformFontDatabase::fontDir();
qCDebug(lcQpaFonts) << __FUNCTION__ << result;
return result;
}
diff --git a/src/gui/text/windows/qwindowsfontdatabase_ft_p.h b/src/gui/text/windows/qwindowsfontdatabase_ft_p.h
index af0b1d7077..381a7be4e7 100644
--- a/src/gui/text/windows/qwindowsfontdatabase_ft_p.h
+++ b/src/gui/text/windows/qwindowsfontdatabase_ft_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 QWINDOWSFONTDATABASEFT_H
#define QWINDOWSFONTDATABASEFT_H
@@ -61,6 +25,7 @@ class Q_GUI_EXPORT QWindowsFontDatabaseFT : public QFreeTypeFontDatabase
{
public:
void populateFontDatabase() override;
+ bool populateFamilyAliases(const QString &familyName) override;
void populateFamily(const QString &familyName) override;
QFontEngine *fontEngine(const QFontDef &fontDef, void *handle) override;
QFontEngine *fontEngine(const QByteArray &fontData, qreal pixelSize,
@@ -72,6 +37,8 @@ public:
QString fontDir() const override;
QFont defaultFont() const override;
+
+ bool m_hasPopulatedAliases = false;
};
QT_END_NAMESPACE
diff --git a/src/gui/text/windows/qwindowsfontdatabase_p.h b/src/gui/text/windows/qwindowsfontdatabase_p.h
index b4b367b1e7..0c99c91fde 100644
--- a/src/gui/text/windows/qwindowsfontdatabase_p.h
+++ b/src/gui/text/windows/qwindowsfontdatabase_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 QWINDOWSFONTDATABASE_H
#define QWINDOWSFONTDATABASE_H
@@ -57,6 +21,7 @@
#include <QtCore/QSharedPointer>
#include <QtCore/QLoggingCategory>
#include <QtCore/qhashfunctions.h>
+#include <QtCore/qmutex.h>
#include <QtCore/qt_windows.h>
QT_BEGIN_NAMESPACE
@@ -79,6 +44,9 @@ public:
void ensureFamilyPopulated(const QString &familyName);
void populateFontDatabase() override;
+ void invalidate() override;
+ void removeApplicationFonts();
+
void populateFamily(const QString &familyName) override;
bool populateFamilyAliases(const QString &missingFamily) override;
QFontEngine *fontEngine(const QFontDef &fontDef, void *handle) override;
@@ -107,8 +75,16 @@ public:
static void debugFormat(QDebug &d, const LOGFONT &lf);
#endif // !QT_NO_DEBUG_STREAM
+ struct FontHandle {
+ FontHandle(const QString &name) : faceName(name) {}
+ FontHandle(IDWriteFontFace *face, const QString &name);
+ ~FontHandle();
+
+ IDWriteFontFace *fontFace = nullptr;
+ QString faceName;
+ };
+
private:
- void removeApplicationFonts();
void addDefaultEUDCFont();
struct WinApplicationFont {
@@ -120,9 +96,10 @@ private:
struct UniqueFontData {
HANDLE handle;
- QAtomicInt refCount;
+ int refCount;
};
+ QMutex m_uniqueFontDataMutex; // protects m_uniqueFontData
QMap<QString, UniqueFontData> m_uniqueFontData;
static unsigned m_fontOptions;
diff --git a/src/gui/text/windows/qwindowsfontdatabasebase.cpp b/src/gui/text/windows/qwindowsfontdatabasebase.cpp
index 9a7c1c3578..84e619b0d9 100644
--- a/src/gui/text/windows/qwindowsfontdatabasebase.cpp
+++ b/src/gui/text/windows/qwindowsfontdatabasebase.cpp
@@ -1,46 +1,9 @@
-/****************************************************************************
-**
-** Copyright (C) 2020 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) 2020 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 "qwindowsfontdatabasebase_p.h"
#include "qwindowsfontdatabase_p.h"
-#include <QtCore/private/qsystemlibrary_p.h>
#include <QtCore/QThreadStorage>
#include <QtCore/QtEndian>
@@ -56,6 +19,8 @@
QT_BEGIN_NAMESPACE
+using namespace Qt::StringLiterals;
+
// Helper classes for creating font engines directly from font data
namespace {
@@ -275,14 +240,11 @@ QString QWindowsFontDatabaseBase::EmbeddedFont::changeFamilyName(const QString &
// nameRecord now points to string data
quint16 *stringStorage = reinterpret_cast<quint16 *>(nameRecord);
- const quint16 *sourceString = newFamilyName.utf16();
- for (int i = 0; i < newFamilyName.size(); ++i)
- stringStorage[i] = qbswap<quint16>(sourceString[i]);
- stringStorage += newFamilyName.size();
+ for (QChar ch : newFamilyName)
+ *stringStorage++ = qbswap<quint16>(quint16(ch.unicode()));
- sourceString = regularString.utf16();
- for (int i = 0; i < regularString.size(); ++i)
- stringStorage[i] = qbswap<quint16>(sourceString[i]);
+ for (QChar ch : regularString)
+ *stringStorage++ = qbswap<quint16>(quint16(ch.unicode()));
}
quint32 *p = reinterpret_cast<quint32 *>(newNameTable.data());
@@ -317,15 +279,15 @@ namespace {
{
}
- HRESULT STDMETHODCALLTYPE QueryInterface(REFIID iid, void **object);
- ULONG STDMETHODCALLTYPE AddRef();
- ULONG STDMETHODCALLTYPE Release();
+ HRESULT STDMETHODCALLTYPE QueryInterface(REFIID iid, void **object) override;
+ ULONG STDMETHODCALLTYPE AddRef() override;
+ ULONG STDMETHODCALLTYPE Release() override;
HRESULT STDMETHODCALLTYPE ReadFileFragment(const void **fragmentStart, UINT64 fileOffset,
- UINT64 fragmentSize, OUT void **fragmentContext);
- void STDMETHODCALLTYPE ReleaseFileFragment(void *fragmentContext);
- HRESULT STDMETHODCALLTYPE GetFileSize(OUT UINT64 *fileSize);
- HRESULT STDMETHODCALLTYPE GetLastWriteTime(OUT UINT64 *lastWriteTime);
+ UINT64 fragmentSize, OUT void **fragmentContext) override;
+ void STDMETHODCALLTYPE ReleaseFileFragment(void *fragmentContext) override;
+ HRESULT STDMETHODCALLTYPE GetFileSize(OUT UINT64 *fileSize) override;
+ HRESULT STDMETHODCALLTYPE GetLastWriteTime(OUT UINT64 *lastWriteTime) override;
private:
QByteArray m_fontData;
@@ -397,10 +359,10 @@ namespace {
{
}
- inline void addKey(const void *key, const QByteArray &fontData)
+ inline void addKey(const QByteArray &fontData)
{
- Q_ASSERT(!m_fontDatas.contains(key));
- m_fontDatas.insert(key, fontData);
+ if (!m_fontDatas.contains(fontData.data()))
+ m_fontDatas.insert(fontData.data(), fontData);
}
inline void removeKey(const void *key)
@@ -408,13 +370,18 @@ namespace {
m_fontDatas.remove(key);
}
- HRESULT STDMETHODCALLTYPE QueryInterface(REFIID iid, void **object);
- ULONG STDMETHODCALLTYPE AddRef();
- ULONG STDMETHODCALLTYPE Release();
+ HRESULT STDMETHODCALLTYPE QueryInterface(REFIID iid, void **object) override;
+ ULONG STDMETHODCALLTYPE AddRef() override;
+ ULONG STDMETHODCALLTYPE Release() override;
HRESULT STDMETHODCALLTYPE CreateStreamFromKey(void const *fontFileReferenceKey,
UINT32 fontFileReferenceKeySize,
- OUT IDWriteFontFileStream **fontFileStream);
+ OUT IDWriteFontFileStream **fontFileStream) override;
+
+ void clear()
+ {
+ m_fontDatas.clear();
+ }
private:
ULONG m_referenceCount;
@@ -473,52 +440,62 @@ namespace {
return S_OK;
}
- class CustomFontFileLoader
+} // Anonymous namespace
+
+class QCustomFontFileLoader
+{
+public:
+ QCustomFontFileLoader(IDWriteFactory *factory)
{
- public:
- CustomFontFileLoader(IDWriteFactory *factory)
- {
- m_directWriteFactory = factory;
+ m_directWriteFactory = factory;
- if (m_directWriteFactory) {
- m_directWriteFactory->AddRef();
+ if (m_directWriteFactory) {
+ m_directWriteFactory->AddRef();
- m_directWriteFontFileLoader = new DirectWriteFontFileLoader();
- m_directWriteFactory->RegisterFontFileLoader(m_directWriteFontFileLoader);
- }
+ m_directWriteFontFileLoader = new DirectWriteFontFileLoader();
+ m_directWriteFactory->RegisterFontFileLoader(m_directWriteFontFileLoader);
}
+ }
- ~CustomFontFileLoader()
- {
- if (m_directWriteFactory != nullptr && m_directWriteFontFileLoader != nullptr)
- m_directWriteFactory->UnregisterFontFileLoader(m_directWriteFontFileLoader);
+ ~QCustomFontFileLoader()
+ {
+ clear();
- if (m_directWriteFactory != nullptr)
- m_directWriteFactory->Release();
- }
+ if (m_directWriteFactory != nullptr && m_directWriteFontFileLoader != nullptr)
+ m_directWriteFactory->UnregisterFontFileLoader(m_directWriteFontFileLoader);
- void addKey(const void *key, const QByteArray &fontData)
- {
- if (m_directWriteFontFileLoader != nullptr)
- m_directWriteFontFileLoader->addKey(key, fontData);
- }
+ if (m_directWriteFactory != nullptr)
+ m_directWriteFactory->Release();
+ }
- void removeKey(const void *key)
- {
- if (m_directWriteFontFileLoader != nullptr)
- m_directWriteFontFileLoader->removeKey(key);
- }
+ void addKey(const QByteArray &fontData)
+ {
+ if (m_directWriteFontFileLoader != nullptr)
+ m_directWriteFontFileLoader->addKey(fontData);
+ }
- IDWriteFontFileLoader *loader() const
- {
- return m_directWriteFontFileLoader;
- }
+ void removeKey(const void *key)
+ {
+ if (m_directWriteFontFileLoader != nullptr)
+ m_directWriteFontFileLoader->removeKey(key);
+ }
+
+ IDWriteFontFileLoader *loader() const
+ {
+ return m_directWriteFontFileLoader;
+ }
+
+ void clear()
+ {
+ if (m_directWriteFontFileLoader != nullptr)
+ m_directWriteFontFileLoader->clear();
+ }
+
+private:
+ IDWriteFactory *m_directWriteFactory = nullptr;
+ DirectWriteFontFileLoader *m_directWriteFontFileLoader = nullptr;
+};
- private:
- IDWriteFactory *m_directWriteFactory = nullptr;
- DirectWriteFontFileLoader *m_directWriteFontFileLoader = nullptr;
- };
-} // Anonymous namespace
#endif // directwrite && direct2d
@@ -582,36 +559,34 @@ bool QWindowsFontDatabaseBase::init(QSharedPointer<QWindowsFontEngineData> d)
}
#if QT_CONFIG(directwrite) && QT_CONFIG(direct2d)
-// ### Qt 6: Link directly to dwrite instead
-typedef HRESULT (WINAPI *DWriteCreateFactoryType)(DWRITE_FACTORY_TYPE, const IID &, IUnknown **);
-static inline DWriteCreateFactoryType resolveDWriteCreateFactory()
-{
- QSystemLibrary library(QStringLiteral("dwrite"));
- QFunctionPointer result = library.resolve("DWriteCreateFactory");
- if (Q_UNLIKELY(!result)) {
- qWarning("Unable to load dwrite.dll");
- return nullptr;
- }
- return reinterpret_cast<DWriteCreateFactoryType>(result);
-}
-
void QWindowsFontDatabaseBase::createDirectWriteFactory(IDWriteFactory **factory)
{
*factory = nullptr;
-
- static const DWriteCreateFactoryType dWriteCreateFactory = resolveDWriteCreateFactory();
- if (!dWriteCreateFactory)
- return;
-
IUnknown *result = nullptr;
+
# if QT_CONFIG(directwrite3)
- dWriteCreateFactory(DWRITE_FACTORY_TYPE_SHARED, __uuidof(IDWriteFactory3), &result);
+ qCDebug(lcQpaFonts) << "Trying to create IDWriteFactory6";
+ DWriteCreateFactory(DWRITE_FACTORY_TYPE_SHARED, __uuidof(IDWriteFactory6), &result);
+
+ if (result == nullptr) {
+ qCDebug(lcQpaFonts) << "Trying to create IDWriteFactory5";
+ DWriteCreateFactory(DWRITE_FACTORY_TYPE_SHARED, __uuidof(IDWriteFactory5), &result);
+ }
+
+ if (result == nullptr) {
+ qCDebug(lcQpaFonts) << "Trying to create IDWriteFactory3";
+ DWriteCreateFactory(DWRITE_FACTORY_TYPE_SHARED, __uuidof(IDWriteFactory3), &result);
+ }
# endif
- if (result == nullptr)
- dWriteCreateFactory(DWRITE_FACTORY_TYPE_SHARED, __uuidof(IDWriteFactory2), &result);
if (result == nullptr) {
- if (FAILED(dWriteCreateFactory(DWRITE_FACTORY_TYPE_SHARED, __uuidof(IDWriteFactory), &result))) {
+ qCDebug(lcQpaFonts) << "Trying to create IDWriteFactory2";
+ DWriteCreateFactory(DWRITE_FACTORY_TYPE_SHARED, __uuidof(IDWriteFactory2), &result);
+ }
+
+ if (result == nullptr) {
+ qCDebug(lcQpaFonts) << "Trying to create plain IDWriteFactory";
+ if (FAILED(DWriteCreateFactory(DWRITE_FACTORY_TYPE_SHARED, __uuidof(IDWriteFactory), &result))) {
qErrnoWarning("DWriteCreateFactory failed");
return;
}
@@ -621,16 +596,9 @@ void QWindowsFontDatabaseBase::createDirectWriteFactory(IDWriteFactory **factory
}
#endif // directwrite && direct2d
-static int s_defaultVerticalDPI = 96; // Native Pixels
-
int QWindowsFontDatabaseBase::defaultVerticalDPI()
{
- return s_defaultVerticalDPI;
-}
-
-void QWindowsFontDatabaseBase::setDefaultVerticalDPI(int d)
-{
- s_defaultVerticalDPI = d;
+ return 96;
}
LOGFONT QWindowsFontDatabaseBase::fontDefToLOGFONT(const QFontDef &request, const QString &faceName)
@@ -707,22 +675,12 @@ LOGFONT QWindowsFontDatabaseBase::fontDefToLOGFONT(const QFontDef &request, cons
QString fam = faceName;
if (fam.isEmpty())
- fam = request.families.size() > 0 ? request.families.at(0) : request.family;
+ fam = request.families.first();
if (Q_UNLIKELY(fam.size() >= LF_FACESIZE)) {
qCritical("%s: Family name '%s' is too long.", __FUNCTION__, qPrintable(fam));
fam.truncate(LF_FACESIZE - 1);
}
- if (fam.isEmpty())
- fam = QStringLiteral("MS Sans Serif");
-
- if (fam == QLatin1String("MS Sans Serif")
- && (request.style == QFont::StyleItalic || (-lf.lfHeight > 18 && -lf.lfHeight != 24))) {
- fam = QStringLiteral("Arial"); // MS Sans Serif has bearing problems in italic, and does not scale
- }
- if (fam == QLatin1String("Courier") && !(request.styleStrategy & QFont::PreferBitmap))
- fam = QStringLiteral("Courier New");
-
memcpy(lf.lfFaceName, fam.utf16(), fam.size() * sizeof(wchar_t));
return lf;
@@ -755,36 +713,55 @@ HFONT QWindowsFontDatabaseBase::systemFont()
QFont QWindowsFontDatabaseBase::systemDefaultFont()
{
// Qt 6: Obtain default GUI font (typically "Segoe UI, 9pt", see QTBUG-58610)
- NONCLIENTMETRICS ncm;
- ncm.cbSize = FIELD_OFFSET(NONCLIENTMETRICS, lfMessageFont) + sizeof(LOGFONT);
- SystemParametersInfo(SPI_GETNONCLIENTMETRICS, ncm.cbSize , &ncm, 0);
+ NONCLIENTMETRICS ncm = {};
+ ncm.cbSize = sizeof(ncm);
+ SystemParametersInfoForDpi(SPI_GETNONCLIENTMETRICS, ncm.cbSize, &ncm, 0, defaultVerticalDPI());
const QFont systemFont = QWindowsFontDatabase::LOGFONT_to_QFont(ncm.lfMessageFont);
qCDebug(lcQpaFonts) << __FUNCTION__ << systemFont;
return systemFont;
}
+void QWindowsFontDatabaseBase::invalidate()
+{
+#if QT_CONFIG(directwrite)
+ m_fontFileLoader.reset(nullptr);
+#endif
+}
+
#if QT_CONFIG(directwrite) && QT_CONFIG(direct2d)
-IDWriteFontFace *QWindowsFontDatabaseBase::createDirectWriteFace(const QByteArray &fontData) const
+IDWriteFontFace *QWindowsFontDatabaseBase::createDirectWriteFace(const QByteArray &fontData)
+{
+ QList<IDWriteFontFace *> faces = createDirectWriteFaces(fontData, false);
+ Q_ASSERT(faces.size() <= 1);
+
+ return faces.isEmpty() ? nullptr : faces.first();
+}
+
+QList<IDWriteFontFace *> QWindowsFontDatabaseBase::createDirectWriteFaces(const QByteArray &fontData,
+ bool queryVariations) const
{
+ QList<IDWriteFontFace *> ret;
QSharedPointer<QWindowsFontEngineData> fontEngineData = data();
if (fontEngineData->directWriteFactory == nullptr) {
qCWarning(lcQpaFonts) << "DirectWrite factory not created in QWindowsFontDatabaseBase::createDirectWriteFace()";
- return nullptr;
+ return ret;
}
- CustomFontFileLoader fontFileLoader(fontEngineData->directWriteFactory);
- fontFileLoader.addKey(this, fontData);
+ if (m_fontFileLoader == nullptr)
+ m_fontFileLoader.reset(new QCustomFontFileLoader(fontEngineData->directWriteFactory));
+
+ m_fontFileLoader->addKey(fontData);
IDWriteFontFile *fontFile = nullptr;
- const void *key = this;
+ const void *key = fontData.data();
HRESULT hres = fontEngineData->directWriteFactory->CreateCustomFontFileReference(&key,
sizeof(void *),
- fontFileLoader.loader(),
+ m_fontFileLoader->loader(),
&fontFile);
if (FAILED(hres)) {
qErrnoWarning(hres, "%s: CreateCustomFontFileReference failed", __FUNCTION__);
- return nullptr;
+ return ret;
}
BOOL isSupportedFontType;
@@ -794,28 +771,75 @@ IDWriteFontFace *QWindowsFontDatabaseBase::createDirectWriteFace(const QByteArra
fontFile->Analyze(&isSupportedFontType, &fontFileType, &fontFaceType, &numberOfFaces);
if (!isSupportedFontType) {
fontFile->Release();
- return nullptr;
+ return ret;
+ }
+
+#if QT_CONFIG(directwrite3)
+ IDWriteFactory5 *factory5 = nullptr;
+ if (queryVariations && SUCCEEDED(fontEngineData->directWriteFactory->QueryInterface(__uuidof(IDWriteFactory5),
+ reinterpret_cast<void **>(&factory5)))) {
+
+ IDWriteFontSetBuilder1 *builder;
+ if (SUCCEEDED(factory5->CreateFontSetBuilder(&builder))) {
+ if (SUCCEEDED(builder->AddFontFile(fontFile))) {
+ IDWriteFontSet *fontSet;
+ if (SUCCEEDED(builder->CreateFontSet(&fontSet))) {
+ int count = fontSet->GetFontCount();
+ qCDebug(lcQpaFonts) << "Found" << count << "variations in font file";
+ for (int i = 0; i < count; ++i) {
+ IDWriteFontFaceReference *ref;
+ if (SUCCEEDED(fontSet->GetFontFaceReference(i, &ref))) {
+ IDWriteFontFace3 *face;
+ if (SUCCEEDED(ref->CreateFontFace(&face))) {
+ ret.append(face);
+ }
+ ref->Release();
+ }
+ }
+ fontSet->Release();
+ }
+ }
+
+ builder->Release();
+ }
+
+ factory5->Release();
}
+#else
+ Q_UNUSED(queryVariations);
+#endif
// ### Currently no support for .ttc, but we could easily return a list here.
- IDWriteFontFace *directWriteFontFace = nullptr;
- hres = fontEngineData->directWriteFactory->CreateFontFace(fontFaceType,
- 1,
- &fontFile,
- 0,
- DWRITE_FONT_SIMULATIONS_NONE,
- &directWriteFontFace);
- if (FAILED(hres)) {
- qErrnoWarning(hres, "%s: CreateFontFace failed", __FUNCTION__);
- fontFile->Release();
- return nullptr;
+ if (ret.isEmpty()) {
+ IDWriteFontFace *directWriteFontFace = nullptr;
+ hres = fontEngineData->directWriteFactory->CreateFontFace(fontFaceType,
+ 1,
+ &fontFile,
+ 0,
+ DWRITE_FONT_SIMULATIONS_NONE,
+ &directWriteFontFace);
+ if (FAILED(hres)) {
+ qErrnoWarning(hres, "%s: CreateFontFace failed", __FUNCTION__);
+ fontFile->Release();
+ return ret;
+ } else {
+ ret.append(directWriteFontFace);
+ }
}
fontFile->Release();
- return directWriteFontFace;
+
+ return ret;
}
#endif // directwrite && direct2d
+QFontEngine *QWindowsFontDatabaseBase::fontEngine(const QFontDef &fontDef, void *handle)
+{
+ // This function was apparently not used before, and probably isn't now either,
+ // call the base implementation which just prints that it's not supported.
+ return QPlatformFontDatabase::fontEngine(fontDef, handle);
+}
+
QFontEngine *QWindowsFontDatabaseBase::fontEngine(const QByteArray &fontData, qreal pixelSize, QFont::HintingPreference hintingPreference)
{
QFontEngine *fontEngine = nullptr;
@@ -825,7 +849,10 @@ QFontEngine *QWindowsFontDatabaseBase::fontEngine(const QByteArray &fontData, qr
if (fontEngineData->directWriteFactory == nullptr)
return nullptr;
- IDWriteFontFace *directWriteFontFace = createDirectWriteFace(fontData);
+ IDWriteFontFace * directWriteFontFace = createDirectWriteFace(fontData);
+ if (directWriteFontFace == nullptr)
+ return nullptr;
+
fontEngine = new QWindowsFontEngineDirectWrite(directWriteFontFace,
pixelSize,
fontEngineData);
@@ -833,7 +860,7 @@ QFontEngine *QWindowsFontDatabaseBase::fontEngine(const QByteArray &fontData, qr
// Get font family from font data
EmbeddedFont font(fontData);
font.updateFromOS2Table(fontEngine);
- fontEngine->fontDef.family = font.familyName();
+ fontEngine->fontDef.families = QStringList(font.familyName());
fontEngine->fontDef.hintingPreference = hintingPreference;
directWriteFontFace->Release();
@@ -867,7 +894,7 @@ QString QWindowsFontDatabaseBase::familyForStyleHint(QFont::StyleHint styleHint)
default:
break;
}
- return QStringLiteral("MS Shell Dlg 2");
+ return QStringLiteral("Tahoma");
}
// Creation functions
@@ -883,6 +910,7 @@ static const char *other_tryFonts[] = {
};
static const char *jp_tryFonts [] = {
+ "Yu Gothic UI",
"MS UI Gothic",
"Arial",
"Gulim",
@@ -927,8 +955,7 @@ static const char **tryFonts = nullptr;
QStringList QWindowsFontDatabaseBase::extraTryFontsForFamily(const QString &family)
{
QStringList result;
- QFontDatabase db;
- if (!db.writingSystems(family).contains(QFontDatabase::Symbol)) {
+ if (!QFontDatabase::writingSystems(family).contains(QFontDatabase::Symbol)) {
if (!tryFonts) {
LANGID lid = GetUserDefaultLangID();
switch (lid&0xff) {
@@ -949,13 +976,12 @@ QStringList QWindowsFontDatabaseBase::extraTryFontsForFamily(const QString &fami
break;
}
}
- QFontDatabase db;
- const QStringList families = db.families();
+ const QStringList families = QFontDatabase::families();
const char **tf = tryFonts;
while (tf && *tf) {
// QTBUG-31689, family might be an English alias for a localized font name.
const QString family = QString::fromLatin1(*tf);
- if (families.contains(family) || db.hasFamily(family))
+ if (families.contains(family) || QFontDatabase::hasFamily(family))
result << family;
++tf;
}
@@ -965,4 +991,23 @@ QStringList QWindowsFontDatabaseBase::extraTryFontsForFamily(const QString &fami
return result;
}
+QFontDef QWindowsFontDatabaseBase::sanitizeRequest(QFontDef request) const
+{
+ QFontDef req = request;
+ const QString fam = request.families.front();
+ if (fam.isEmpty())
+ req.families[0] = QStringLiteral("MS Sans Serif");
+
+ if (fam == "MS Sans Serif"_L1) {
+ int height = -qRound(request.pixelSize);
+ // MS Sans Serif has bearing problems in italic, and does not scale
+ if (request.style == QFont::StyleItalic || (height > 18 && height != 24))
+ req.families[0] = QStringLiteral("Arial");
+ }
+
+ if (!(request.styleStrategy & QFont::StyleStrategy::PreferBitmap) && fam == u"Courier")
+ req.families[0] = QStringLiteral("Courier New");
+ return req;
+}
+
QT_END_NAMESPACE
diff --git a/src/gui/text/windows/qwindowsfontdatabasebase_p.h b/src/gui/text/windows/qwindowsfontdatabasebase_p.h
index 873912f8ef..55a3363551 100644
--- a/src/gui/text/windows/qwindowsfontdatabasebase_p.h
+++ b/src/gui/text/windows/qwindowsfontdatabasebase_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2020 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) 2020 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 QWINDOWSFONTDATABASEBASE_P_H
#define QWINDOWSFONTDATABASEBASE_P_H
@@ -65,6 +29,10 @@
QT_BEGIN_NAMESPACE
+#if QT_CONFIG(directwrite)
+ class QCustomFontFileLoader;
+#endif
+
class QWindowsFontEngineData
{
Q_DISABLE_COPY_MOVE(QWindowsFontEngineData)
@@ -89,10 +57,12 @@ public:
QWindowsFontDatabaseBase();
~QWindowsFontDatabaseBase() override;
+ QFontEngine *fontEngine(const QFontDef &fontDef, void *handle) override;
QFontEngine *fontEngine(const QByteArray &fontData, qreal pixelSize, QFont::HintingPreference hintingPreference) override;
+ void invalidate() override;
+
static int defaultVerticalDPI();
- static void setDefaultVerticalDPI(int d);
static QSharedPointer<QWindowsFontEngineData> data();
#if QT_CONFIG(directwrite)
@@ -122,14 +92,22 @@ public:
QByteArray m_fontData;
};
+ QFontDef sanitizeRequest(QFontDef request) const;
+
protected:
#if QT_CONFIG(directwrite)
- IDWriteFontFace *createDirectWriteFace(const QByteArray &fontData) const;
+ QList<IDWriteFontFace *> createDirectWriteFaces(const QByteArray &fontData,
+ bool queryVariations = true) const;
+ IDWriteFontFace *createDirectWriteFace(const QByteArray &fontData);
#endif
private:
static bool init(QSharedPointer<QWindowsFontEngineData> data);
+
+#if QT_CONFIG(directwrite)
+ mutable std::unique_ptr<QCustomFontFileLoader> m_fontFileLoader;
+#endif
};
QT_END_NAMESPACE
diff --git a/src/gui/text/windows/qwindowsfontengine.cpp b/src/gui/text/windows/qwindowsfontengine.cpp
index cbf50f65da..5de80dc8a3 100644
--- a/src/gui/text/windows/qwindowsfontengine.cpp
+++ b/src/gui/text/windows/qwindowsfontengine.cpp
@@ -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 "qwindowsfontengine_p.h"
#include "qwindowsnativeimage_p.h"
@@ -74,6 +38,9 @@
QT_BEGIN_NAMESPACE
+QT_IMPL_METATYPE_EXTERN(HFONT)
+QT_IMPL_METATYPE_EXTERN(LOGFONT)
+
//### mingw needed define
#ifndef TT_PRIM_CSPLINE
#define TT_PRIM_CSPLINE 3
@@ -89,23 +56,11 @@ QT_BEGIN_NAMESPACE
// common DC for all fonts
-typedef BOOL (WINAPI *PtrGetCharWidthI)(HDC, UINT, UINT, LPWORD, LPINT);
-static PtrGetCharWidthI ptrGetCharWidthI = 0;
-static bool resolvedGetCharWidthI = false;
-
-static void resolveGetCharWidthI()
-{
- if (resolvedGetCharWidthI)
- return;
- resolvedGetCharWidthI = true;
- ptrGetCharWidthI = (PtrGetCharWidthI)QSystemLibrary::resolve(QStringLiteral("gdi32"), "GetCharWidthI");
-}
-
// general font engine
QFixed QWindowsFontEngine::lineThickness() const
{
- if(lineWidth > 0)
+ if (lineWidth > 0)
return lineWidth;
return QFontEngine::lineThickness();
@@ -149,7 +104,7 @@ void QWindowsFontEngine::getCMap()
SelectObject(hdc, hfont);
bool symb = false;
if (ttf) {
- cmapTable = getSfntTable(MAKE_TAG('c', 'm', 'a', 'p'));
+ cmapTable = getSfntTable(QFont::Tag("cmap").value());
cmap = QFontEngine::getCMap(reinterpret_cast<const uchar *>(cmapTable.constData()),
cmapTable.size(), &symb, &cmapSize);
}
@@ -160,7 +115,7 @@ void QWindowsFontEngine::getCMap()
symbol = symb;
designToDevice = 1;
_faceId.index = 0;
- if(cmap) {
+ if (cmap) {
OUTLINETEXTMETRIC *otm = getOutlineTextMetric(hdc);
unitsPerEm = int(otm->otmEMSquare);
const QFixed unitsPerEmF(unitsPerEm);
@@ -177,8 +132,9 @@ void QWindowsFontEngine::getCMap()
}
}
-int QWindowsFontEngine::getGlyphIndexes(const QChar *str, int numChars, QGlyphLayout *glyphs) const
+int QWindowsFontEngine::getGlyphIndexes(const QChar *str, int numChars, QGlyphLayout *glyphs, int *mappedGlyphs) const
{
+ *mappedGlyphs = 0;
int glyph_pos = 0;
{
if (symbol) {
@@ -186,8 +142,10 @@ int QWindowsFontEngine::getGlyphIndexes(const QChar *str, int numChars, QGlyphLa
while (it.hasNext()) {
const uint uc = it.next();
glyphs->glyphs[glyph_pos] = getTrueTypeGlyphIndex(cmap, cmapSize, uc);
- if(!glyphs->glyphs[glyph_pos] && uc < 0x100)
+ if (!glyphs->glyphs[glyph_pos] && uc < 0x100)
glyphs->glyphs[glyph_pos] = getTrueTypeGlyphIndex(cmap, cmapSize, uc + 0xf000);
+ if (glyphs->glyphs[glyph_pos] || isIgnorableChar(uc))
+ (*mappedGlyphs)++;
++glyph_pos;
}
} else if (ttf) {
@@ -195,6 +153,8 @@ int QWindowsFontEngine::getGlyphIndexes(const QChar *str, int numChars, QGlyphLa
while (it.hasNext()) {
const uint uc = it.next();
glyphs->glyphs[glyph_pos] = getTrueTypeGlyphIndex(cmap, cmapSize, uc);
+ if (glyphs->glyphs[glyph_pos] || isIgnorableChar(uc))
+ (*mappedGlyphs)++;
++glyph_pos;
}
} else {
@@ -205,6 +165,8 @@ int QWindowsFontEngine::getGlyphIndexes(const QChar *str, int numChars, QGlyphLa
glyphs->glyphs[glyph_pos] = uc;
else
glyphs->glyphs[glyph_pos] = 0;
+ if (glyphs->glyphs[glyph_pos] || isIgnorableChar(uc))
+ (*mappedGlyphs)++;
++glyph_pos;
}
}
@@ -252,16 +214,6 @@ QWindowsFontEngine::QWindowsFontEngine(const QString &name,
cache_cost = tm.tmHeight * tm.tmAveCharWidth * 2000;
getCMap();
- if (!resolvedGetCharWidthI)
- resolveGetCharWidthI();
-
- // ### Properties accessed by QWin32PrintEngine (QtPrintSupport)
- QVariantMap userData;
- userData.insert(QStringLiteral("logFont"), QVariant::fromValue(m_logfont));
- userData.insert(QStringLiteral("hFont"), QVariant::fromValue(hfont));
- userData.insert(QStringLiteral("trueType"), QVariant(bool(ttf)));
- setUserData(userData);
-
hasUnreliableOutline = (tm.tmPitchAndFamily & (TMPF_TRUETYPE | TMPF_VECTOR)) == 0;
}
@@ -314,7 +266,7 @@ HGDIOBJ QWindowsFontEngine::selectDesignFont() const
return SelectObject(m_fontEngineData->hdc, designFont);
}
-bool QWindowsFontEngine::stringToCMap(const QChar *str, int len, QGlyphLayout *glyphs, int *nglyphs, QFontEngine::ShaperFlags flags) const
+int QWindowsFontEngine::stringToCMap(const QChar *str, int len, QGlyphLayout *glyphs, int *nglyphs, QFontEngine::ShaperFlags flags) const
{
Q_ASSERT(glyphs->numGlyphs >= *nglyphs);
if (*nglyphs < len) {
@@ -323,18 +275,18 @@ bool QWindowsFontEngine::stringToCMap(const QChar *str, int len, QGlyphLayout *g
}
glyphs->numGlyphs = *nglyphs;
- *nglyphs = getGlyphIndexes(str, len, glyphs);
+ int mappedGlyphs;
+ *nglyphs = getGlyphIndexes(str, len, glyphs, &mappedGlyphs);
if (!(flags & GlyphIndicesOnly))
recalcAdvances(glyphs, flags);
- return true;
+ return mappedGlyphs;
}
inline void calculateTTFGlyphWidth(HDC hdc, UINT glyph, int &width)
{
- if (ptrGetCharWidthI)
- ptrGetCharWidthI(hdc, glyph, 1, 0, &width);
+ GetCharWidthI(hdc, glyph, 1, 0, &width);
}
void QWindowsFontEngine::recalcAdvances(QGlyphLayout *glyphs, QFontEngine::ShaperFlags flags) const
@@ -344,7 +296,7 @@ void QWindowsFontEngine::recalcAdvances(QGlyphLayout *glyphs, QFontEngine::Shape
if (ttf && (flags & DesignMetrics)) {
for(int i = 0; i < glyphs->numGlyphs; i++) {
unsigned int glyph = glyphs->glyphs[i];
- if(int(glyph) >= designAdvancesSize) {
+ if (int(glyph) >= designAdvancesSize) {
const int newSize = int(glyph + 256) >> 8 << 8;
designAdvances = reinterpret_cast<QFixed *>(realloc(designAdvances, size_t(newSize) * sizeof(QFixed)));
Q_CHECK_PTR(designAdvances);
@@ -362,7 +314,7 @@ void QWindowsFontEngine::recalcAdvances(QGlyphLayout *glyphs, QFontEngine::Shape
}
glyphs->advances[i] = designAdvances[glyph];
}
- if(oldFont)
+ if (oldFont)
DeleteObject(SelectObject(hdc, oldFont));
} else {
for(int i = 0; i < glyphs->numGlyphs; i++) {
@@ -383,7 +335,7 @@ void QWindowsFontEngine::recalcAdvances(QGlyphLayout *glyphs, QFontEngine::Shape
oldFont = SelectObject(hdc, hfont);
if (!ttf) {
- QChar ch[2] = { ushort(glyph), 0 };
+ QChar ch[2] = { ushort(glyph), u'\0' };
int chrLen = 1;
if (QChar::requiresSurrogates(glyph)) {
ch[0] = QChar::highSurrogate(glyph);
@@ -408,18 +360,6 @@ void QWindowsFontEngine::recalcAdvances(QGlyphLayout *glyphs, QFontEngine::Shape
}
}
-glyph_metrics_t QWindowsFontEngine::boundingBox(const QGlyphLayout &glyphs)
-{
- if (glyphs.numGlyphs == 0)
- return glyph_metrics_t();
-
- QFixed w = 0;
- for (int i = 0; i < glyphs.numGlyphs; ++i)
- w += glyphs.effectiveAdvance(i);
-
- return glyph_metrics_t(0, -tm.tmAscent, w - lastRightBearing(glyphs), tm.tmHeight, w, 0);
-}
-
bool QWindowsFontEngine::getOutlineMetrics(glyph_t glyph, const QTransform &t, glyph_metrics_t *metrics) const
{
Q_ASSERT(metrics != 0);
@@ -539,7 +479,7 @@ namespace {
QFixed QWindowsFontEngine::capHeight() const
{
- const QByteArray tableData = getSfntTable(MAKE_TAG('O', 'S', '/', '2'));
+ const QByteArray tableData = getSfntTable(QFont::Tag("OS/2").value());
if (size_t(tableData.size()) >= sizeof(OS2Table)) {
const OS2Table *table = reinterpret_cast<const OS2Table *>(tableData.constData());
if (qFromBigEndian<quint16>(table->version) >= 2) {
@@ -553,7 +493,7 @@ QFixed QWindowsFontEngine::capHeight() const
QFixed QWindowsFontEngine::xHeight() const
{
- if(x_height >= 0)
+ if (x_height >= 0)
return x_height;
return QFontEngine::xHeight();
}
@@ -642,7 +582,7 @@ qreal QWindowsFontEngine::minRightBearing() const
HDC hdc = m_fontEngineData->hdc;
SelectObject(hdc, hfont);
if (ttf) {
- ABC *abc = 0;
+ ABC *abc = nullptr;
int n = tm.tmLastChar - tm.tmFirstChar;
if (n <= max_font_count) {
abc = new ABC[n+1];
@@ -682,8 +622,8 @@ qreal QWindowsFontEngine::minRightBearing() const
fmr = qMin(fmr,abc[i].abcfC);
}
}
- ml = int(fml - 0.9999);
- mr = int(fmr - 0.9999);
+ ml = qFloor(fml);
+ mr = qFloor(fmr);
delete [] abc;
}
lbearing = ml;
@@ -706,7 +646,7 @@ static inline QPointF qt_to_qpointf(const POINTFX &pt, qreal scale, qreal stretc
#endif
static bool addGlyphToPath(glyph_t glyph, const QFixedPoint &position, HDC hdc,
- QPainterPath *path, bool ttf, glyph_metrics_t *metric = 0,
+ QPainterPath *path, bool ttf, glyph_metrics_t *metric = nullptr,
qreal scale = 1.0, qreal stretch = 1.0)
{
MAT2 mat;
@@ -844,7 +784,7 @@ void QWindowsFontEngine::addGlyphsToPath(glyph_t *glyphs, QFixedPoint *positions
void QWindowsFontEngine::addOutlineToPath(qreal x, qreal y, const QGlyphLayout &glyphs,
QPainterPath *path, QTextItem::RenderFlags flags)
{
- if(tm.tmPitchAndFamily & (TMPF_TRUETYPE | TMPF_VECTOR)) {
+ if (tm.tmPitchAndFamily & (TMPF_TRUETYPE | TMPF_VECTOR)) {
hasOutline = true;
QFontEngine::addOutlineToPath(x, y, glyphs, path, flags);
if (hasOutline) {
@@ -868,9 +808,9 @@ QT_END_INCLUDE_NAMESPACE
int QWindowsFontEngine::synthesized() const
{
- if(synthesized_flags == -1) {
+ if (synthesized_flags == -1) {
synthesized_flags = 0;
- if(ttf) {
+ if (ttf) {
const DWORD HEAD = MAKE_LITTLE_ENDIAN_TAG('h', 'e', 'a', 'd');
HDC hdc = m_fontEngineData->hdc;
SelectObject(hdc, hfont);
@@ -927,7 +867,7 @@ void QWindowsFontEngine::getUnscaledGlyph(glyph_t glyph, QPainterPath *path, gly
LOGFONT lf = m_logfont;
lf.lfHeight = -unitsPerEm;
int flags = synthesized();
- if(flags & SynthesizedItalic)
+ if (flags & SynthesizedItalic)
lf.lfItalic = false;
lf.lfWidth = 0;
HFONT hf = CreateFontIndirect(&lf);
@@ -1050,7 +990,10 @@ QWindowsNativeImage *QWindowsFontEngine::drawGDIGlyph(HFONT font, glyph_t glyph,
return ni;
}
-glyph_metrics_t QWindowsFontEngine::alphaMapBoundingBox(glyph_t glyph, QFixed, const QTransform &matrix, GlyphFormat format)
+glyph_metrics_t QWindowsFontEngine::alphaMapBoundingBox(glyph_t glyph,
+ const QFixedPoint &,
+ const QTransform &matrix,
+ GlyphFormat format)
{
int margin = 0;
if (format == QFontEngine::Format_A32 || format == QFontEngine::Format_ARGB)
@@ -1115,7 +1058,9 @@ QImage QWindowsFontEngine::alphaMapForGlyph(glyph_t glyph, const QTransform &xfo
#define SPI_GETFONTSMOOTHINGCONTRAST 0x200C
#define SPI_SETFONTSMOOTHINGCONTRAST 0x200D
-QImage QWindowsFontEngine::alphaRGBMapForGlyph(glyph_t glyph, QFixed, const QTransform &t)
+QImage QWindowsFontEngine::alphaRGBMapForGlyph(glyph_t glyph,
+ const QFixedPoint &,
+ const QTransform &t)
{
HFONT font = hfont;
@@ -1152,9 +1097,9 @@ QImage QWindowsFontEngine::alphaRGBMapForGlyph(glyph_t glyph, QFixed, const QTra
QFontEngine *QWindowsFontEngine::cloneWithSize(qreal pixelSize) const
{
QFontDef request = fontDef;
- QString actualFontName = request.family;
+ QString actualFontName = request.families.constFirst();
if (!uniqueFamilyName.isEmpty())
- request.family = uniqueFamilyName;
+ request.families = QStringList(uniqueFamilyName);
request.pixelSize = pixelSize;
const QString faceName = QString::fromWCharArray(m_logfont.lfFaceName);
@@ -1163,7 +1108,7 @@ QFontEngine *QWindowsFontEngine::cloneWithSize(qreal pixelSize) const
QWindowsFontDatabase::defaultVerticalDPI(),
m_fontEngineData);
if (fontEngine) {
- fontEngine->fontDef.family = actualFontName;
+ fontEngine->fontDef.families = QStringList(actualFontName);
if (!uniqueFamilyName.isEmpty()) {
static_cast<QWindowsFontEngine *>(fontEngine)->setUniqueFamilyName(uniqueFamilyName);
if (QPlatformIntegration *pi = QGuiApplicationPrivate::platformIntegration()) {
@@ -1188,7 +1133,7 @@ void QWindowsFontEngine::initFontInfo(const QFontDef &request,
SelectObject(dc, hfont);
wchar_t n[64];
GetTextFace(dc, 64, n);
- fontDef.family = QString::fromWCharArray(n);
+ fontDef.families = QStringList(QString::fromWCharArray(n));
fontDef.fixedPitch = !(tm.tmPitchAndFamily & TMPF_FIXED_PITCH);
if (fontDef.pointSize < 0) {
fontDef.pointSize = fontDef.pixelSize * 72. / dpi;
diff --git a/src/gui/text/windows/qwindowsfontengine_p.h b/src/gui/text/windows/qwindowsfontengine_p.h
index 8a676affcd..07f4db3c4a 100644
--- a/src/gui/text/windows/qwindowsfontengine_p.h
+++ b/src/gui/text/windows/qwindowsfontengine_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 QWINDOWSFONTENGINE_H
#define QWINDOWSFONTENGINE_H
@@ -84,7 +48,7 @@ public:
QFixed emSquareSize() const override;
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 *glyphs, ShaperFlags) const override;
void addOutlineToPath(qreal x, qreal y, const QGlyphLayout &glyphs, QPainterPath *path, QTextItem::RenderFlags flags) override;
@@ -93,7 +57,6 @@ public:
HGDIOBJ selectDesignFont() const;
- glyph_metrics_t boundingBox(const QGlyphLayout &glyphs) override;
glyph_metrics_t boundingBox(glyph_t g) override { return boundingBox(g, QTransform()); }
glyph_metrics_t boundingBox(glyph_t g, const QTransform &t) override;
@@ -107,20 +70,25 @@ public:
QImage alphaMapForGlyph(glyph_t t) override { return alphaMapForGlyph(t, QTransform()); }
QImage alphaMapForGlyph(glyph_t, const QTransform &xform) override;
- QImage alphaRGBMapForGlyph(glyph_t t, QFixed subPixelPosition, const QTransform &xform) override;
- glyph_metrics_t alphaMapBoundingBox(glyph_t glyph, QFixed, const QTransform &matrix, GlyphFormat) override;
+ QImage alphaRGBMapForGlyph(glyph_t t,
+ const QFixedPoint &subPixelPosition,
+ const QTransform &xform) override;
+ glyph_metrics_t alphaMapBoundingBox(glyph_t glyph,
+ const QFixedPoint &,
+ const QTransform &matrix,
+ GlyphFormat) override;
QFontEngine *cloneWithSize(qreal pixelSize) const override;
Qt::HANDLE handle() const override;
bool supportsTransformation(const QTransform &transform) const override;
#ifndef Q_CC_MINGW
- void getGlyphBearings(glyph_t glyph, qreal *leftBearing = 0, qreal *rightBearing = 0) override;
+ void getGlyphBearings(glyph_t glyph, qreal *leftBearing = nullptr, qreal *rightBearing = nullptr) override;
#endif
bool hasUnreliableGlyphOutline() const override;
- int getGlyphIndexes(const QChar *ch, int numChars, QGlyphLayout *glyphs) const;
+ int getGlyphIndexes(const QChar *ch, int numChars, QGlyphLayout *glyphs, int *mappedGlyphs) const;
void getCMap();
bool getOutlineMetrics(glyph_t glyph, const QTransform &t, glyph_metrics_t *metrics) const;
@@ -169,7 +137,7 @@ private:
QT_END_NAMESPACE
-Q_DECLARE_METATYPE(HFONT)
-Q_DECLARE_METATYPE(LOGFONT)
+QT_DECL_METATYPE_EXTERN(HFONT, Q_GUI_EXPORT)
+QT_DECL_METATYPE_EXTERN(LOGFONT, Q_GUI_EXPORT)
#endif // QWINDOWSFONTENGINE_H
diff --git a/src/gui/text/windows/qwindowsfontenginedirectwrite.cpp b/src/gui/text/windows/qwindowsfontenginedirectwrite.cpp
index c17139ab8b..47b8a7ee3c 100644
--- a/src/gui/text/windows/qwindowsfontenginedirectwrite.cpp
+++ b/src/gui/text/windows/qwindowsfontenginedirectwrite.cpp
@@ -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 "qwindowsfontenginedirectwrite_p.h"
#include "qwindowsfontdatabase_p.h"
@@ -48,10 +12,14 @@
#include <QtCore/private/qwinregistry_p.h>
#include <QtGui/private/qguiapplication_p.h>
#include <qpa/qplatformintegration.h>
-#include <QtGui/private/qhighdpiscaling_p.h>
#include <QtGui/qpainterpath.h>
-#include <dwrite_2.h>
+#if QT_CONFIG(directwrite3)
+# include "qwindowsdirectwritefontdatabase_p.h"
+# include <dwrite_3.h>
+#else
+# include <dwrite_2.h>
+#endif
#include <d2d1.h>
@@ -77,17 +45,17 @@ namespace {
}
virtual ~GeometrySink() = default;
- IFACEMETHOD_(void, AddBeziers)(const D2D1_BEZIER_SEGMENT *beziers, UINT bezierCount);
- IFACEMETHOD_(void, AddLines)(const D2D1_POINT_2F *points, UINT pointCount);
- IFACEMETHOD_(void, BeginFigure)(D2D1_POINT_2F startPoint, D2D1_FIGURE_BEGIN figureBegin);
- IFACEMETHOD(Close)();
- IFACEMETHOD_(void, EndFigure)(D2D1_FIGURE_END figureEnd);
- IFACEMETHOD_(void, SetFillMode)(D2D1_FILL_MODE fillMode);
- IFACEMETHOD_(void, SetSegmentFlags)(D2D1_PATH_SEGMENT vertexFlags);
+ IFACEMETHOD_(void, AddBeziers)(const D2D1_BEZIER_SEGMENT *beziers, UINT bezierCount) override;
+ IFACEMETHOD_(void, AddLines)(const D2D1_POINT_2F *points, UINT pointCount) override;
+ IFACEMETHOD_(void, BeginFigure)(D2D1_POINT_2F startPoint, D2D1_FIGURE_BEGIN figureBegin) override;
+ IFACEMETHOD(Close)() override;
+ IFACEMETHOD_(void, EndFigure)(D2D1_FIGURE_END figureEnd) override;
+ IFACEMETHOD_(void, SetFillMode)(D2D1_FILL_MODE fillMode) override;
+ IFACEMETHOD_(void, SetSegmentFlags)(D2D1_PATH_SEGMENT vertexFlags) override;
- IFACEMETHOD_(unsigned long, AddRef)();
- IFACEMETHOD_(unsigned long, Release)();
- IFACEMETHOD(QueryInterface)(IID const &riid, void **ppvObject);
+ IFACEMETHOD_(unsigned long, AddRef)() override;
+ IFACEMETHOD_(unsigned long, Release)() override;
+ IFACEMETHOD(QueryInterface)(IID const &riid, void **ppvObject) override;
private:
inline static QPointF fromD2D1_POINT_2F(const D2D1_POINT_2F &inp)
@@ -101,7 +69,7 @@ namespace {
};
void GeometrySink::AddBeziers(const D2D1_BEZIER_SEGMENT *beziers,
- UINT bezierCount)
+ UINT bezierCount) noexcept
{
for (uint i=0; i<bezierCount; ++i) {
QPointF c1 = fromD2D1_POINT_2F(beziers[i].point1);
@@ -112,48 +80,48 @@ namespace {
}
}
- void GeometrySink::AddLines(const D2D1_POINT_2F *points, UINT pointsCount)
+ void GeometrySink::AddLines(const D2D1_POINT_2F *points, UINT pointsCount) noexcept
{
for (uint i=0; i<pointsCount; ++i)
m_path->lineTo(fromD2D1_POINT_2F(points[i]));
}
void GeometrySink::BeginFigure(D2D1_POINT_2F startPoint,
- D2D1_FIGURE_BEGIN /*figureBegin*/)
+ D2D1_FIGURE_BEGIN /*figureBegin*/) noexcept
{
m_startPoint = fromD2D1_POINT_2F(startPoint);
m_path->moveTo(m_startPoint);
}
- IFACEMETHODIMP GeometrySink::Close()
+ IFACEMETHODIMP GeometrySink::Close() noexcept
{
return E_NOTIMPL;
}
- void GeometrySink::EndFigure(D2D1_FIGURE_END figureEnd)
+ void GeometrySink::EndFigure(D2D1_FIGURE_END figureEnd) noexcept
{
if (figureEnd == D2D1_FIGURE_END_CLOSED)
m_path->closeSubpath();
}
- void GeometrySink::SetFillMode(D2D1_FILL_MODE fillMode)
+ void GeometrySink::SetFillMode(D2D1_FILL_MODE fillMode) noexcept
{
m_path->setFillRule(fillMode == D2D1_FILL_MODE_ALTERNATE
? Qt::OddEvenFill
: Qt::WindingFill);
}
- void GeometrySink::SetSegmentFlags(D2D1_PATH_SEGMENT /*vertexFlags*/)
+ void GeometrySink::SetSegmentFlags(D2D1_PATH_SEGMENT /*vertexFlags*/) noexcept
{
/* Not implemented */
}
- IFACEMETHODIMP_(unsigned long) GeometrySink::AddRef()
+ IFACEMETHODIMP_(unsigned long) GeometrySink::AddRef() noexcept
{
return InterlockedIncrement(&m_refCount);
}
- IFACEMETHODIMP_(unsigned long) GeometrySink::Release()
+ IFACEMETHODIMP_(unsigned long) GeometrySink::Release() noexcept
{
unsigned long newCount = InterlockedDecrement(&m_refCount);
if (newCount == 0)
@@ -165,7 +133,7 @@ namespace {
return newCount;
}
- IFACEMETHODIMP GeometrySink::QueryInterface(IID const &riid, void **ppvObject)
+ IFACEMETHODIMP GeometrySink::QueryInterface(IID const &riid, void **ppvObject) noexcept
{
if (__uuidof(IDWriteGeometrySink) == riid) {
*ppvObject = this;
@@ -194,10 +162,19 @@ static DWRITE_MEASURING_MODE renderModeToMeasureMode(DWRITE_RENDERING_MODE rende
}
}
-static DWRITE_RENDERING_MODE hintingPreferenceToRenderingMode(QFont::HintingPreference hintingPreference)
+DWRITE_RENDERING_MODE QWindowsFontEngineDirectWrite::hintingPreferenceToRenderingMode(const QFontDef &fontDef) const
{
- if (QHighDpiScaling::isActive() && hintingPreference == QFont::PreferDefaultHinting)
- hintingPreference = QFont::PreferVerticalHinting;
+ if ((fontDef.styleStrategy & QFont::NoAntialias) && glyphFormat != QFontEngine::Format_ARGB)
+ return DWRITE_RENDERING_MODE_ALIASED;
+
+ QFont::HintingPreference hintingPreference = QFont::HintingPreference(fontDef.hintingPreference);
+ if (!qFuzzyCompare(qApp->devicePixelRatio(), 1.0) && hintingPreference == QFont::PreferDefaultHinting) {
+ // Microsoft documentation recommends using asymmetric rendering for small fonts
+ // at pixel size 16 and less, and symmetric for larger fonts.
+ hintingPreference = fontDef.pixelSize > 16.0
+ ? QFont::PreferNoHinting
+ : QFont::PreferVerticalHinting;
+ }
switch (hintingPreference) {
case QFont::PreferNoHinting:
@@ -242,7 +219,7 @@ QWindowsFontEngineDirectWrite::QWindowsFontEngineDirectWrite(IDWriteFontFace *di
fontDef.pixelSize = pixelSize;
collectMetrics();
- cache_cost = (m_ascent.toInt() + m_descent.toInt()) * m_xHeight.toInt() * 2000;
+ cache_cost = m_xHeight.toInt() * m_xHeight.toInt() * 2000;
}
QWindowsFontEngineDirectWrite::~QWindowsFontEngineDirectWrite()
@@ -337,6 +314,22 @@ QString QWindowsFontEngineDirectWrite::filenameFromFontFile(IDWriteFontFile *fon
return ret;
}
+HFONT QWindowsFontEngineDirectWrite::createHFONT() const
+{
+ if (m_fontEngineData == nullptr || m_directWriteFontFace == nullptr)
+ return NULL;
+
+ LOGFONT lf;
+ HRESULT hr = m_fontEngineData->directWriteGdiInterop->ConvertFontFaceToLOGFONT(m_directWriteFontFace,
+ &lf);
+ if (SUCCEEDED(hr)) {
+ lf.lfHeight = -qRound(fontDef.pixelSize);
+ return CreateFontIndirect(&lf);
+ } else {
+ return NULL;
+ }
+}
+
void QWindowsFontEngineDirectWrite::initializeHeightMetrics() const
{
DWRITE_FONT_METRICS metrics;
@@ -368,12 +361,14 @@ void QWindowsFontEngineDirectWrite::collectMetrics()
fontFile->Release();
}
- QByteArray table = getSfntTable(MAKE_TAG('h', 'h', 'e', 'a'));
+ QByteArray table = getSfntTable(QFont::Tag("hhea").value());
const int advanceWidthMaxLocation = 10;
if (table.size() >= advanceWidthMaxLocation + int(sizeof(quint16))) {
quint16 advanceWidthMax = qFromBigEndian<quint16>(table.constData() + advanceWidthMaxLocation);
m_maxAdvanceWidth = DESIGN_TO_LOGICAL(advanceWidthMax);
}
+
+ loadKerningPairs(emSquareSize() / QFixed::fromReal(fontDef.pixelSize));
}
QFixed QWindowsFontEngineDirectWrite::underlinePosition() const
@@ -440,13 +435,13 @@ glyph_t QWindowsFontEngineDirectWrite::glyphIndex(uint ucs4) const
return glyphIndex;
}
-bool QWindowsFontEngineDirectWrite::stringToCMap(const QChar *str, int len, QGlyphLayout *glyphs,
- int *nglyphs, QFontEngine::ShaperFlags flags) const
+int QWindowsFontEngineDirectWrite::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<UINT32> codePoints(len);
@@ -460,11 +455,15 @@ bool QWindowsFontEngineDirectWrite::stringToCMap(const QChar *str, int len, QGly
glyphIndices.data());
if (FAILED(hr)) {
qErrnoWarning("%s: GetGlyphIndicesW failed", __FUNCTION__);
- return false;
+ return -1;
}
- for (int i = 0; i < actualLength; ++i)
+ int mappedGlyphs = 0;
+ for (int i = 0; i < actualLength; ++i) {
glyphs->glyphs[i] = glyphIndices.at(i);
+ if (glyphs->glyphs[i] != 0 || isIgnorableChar(codePoints.at(i)))
+ mappedGlyphs++;
+ }
*nglyphs = actualLength;
glyphs->numGlyphs = actualLength;
@@ -472,7 +471,7 @@ bool QWindowsFontEngineDirectWrite::stringToCMap(const QChar *str, int len, QGly
if (!(flags & GlyphIndicesOnly))
recalcAdvances(glyphs, {});
- return true;
+ return mappedGlyphs;
}
QFontEngine::FaceId QWindowsFontEngineDirectWrite::faceId() const
@@ -480,7 +479,7 @@ QFontEngine::FaceId QWindowsFontEngineDirectWrite::faceId() const
return m_faceId;
}
-void QWindowsFontEngineDirectWrite::recalcAdvances(QGlyphLayout *glyphs, QFontEngine::ShaperFlags) const
+void QWindowsFontEngineDirectWrite::recalcAdvances(QGlyphLayout *glyphs, QFontEngine::ShaperFlags shaperFlags) const
{
QVarLengthArray<UINT16> glyphIndices(glyphs->numGlyphs);
@@ -491,12 +490,15 @@ void QWindowsFontEngineDirectWrite::recalcAdvances(QGlyphLayout *glyphs, QFontEn
QVarLengthArray<DWRITE_GLYPH_METRICS> glyphMetrics(glyphIndices.size());
HRESULT hr;
- DWRITE_RENDERING_MODE renderMode = hintingPreferenceToRenderingMode(QFont::HintingPreference(fontDef.hintingPreference));
- if (renderMode == DWRITE_RENDERING_MODE_GDI_CLASSIC || renderMode == DWRITE_RENDERING_MODE_GDI_NATURAL) {
+ DWRITE_RENDERING_MODE renderMode = hintingPreferenceToRenderingMode(fontDef);
+ bool needsDesignMetrics = shaperFlags & QFontEngine::DesignMetrics;
+ if (!needsDesignMetrics && (renderMode == DWRITE_RENDERING_MODE_GDI_CLASSIC
+ || renderMode == DWRITE_RENDERING_MODE_GDI_NATURAL
+ || renderMode == DWRITE_RENDERING_MODE_ALIASED)) {
hr = m_directWriteFontFace->GetGdiCompatibleGlyphMetrics(float(fontDef.pixelSize),
1.0f,
NULL,
- TRUE,
+ renderMode == DWRITE_RENDERING_MODE_GDI_NATURAL,
glyphIndices.data(),
glyphIndices.size(),
glyphMetrics.data());
@@ -514,9 +516,57 @@ void QWindowsFontEngineDirectWrite::recalcAdvances(QGlyphLayout *glyphs, QFontEn
}
}
+void QWindowsFontEngineDirectWrite::getUnscaledGlyph(glyph_t glyph,
+ QPainterPath *path,
+ glyph_metrics_t *metric)
+{
+ float advance = 0.0f;
+ UINT16 g = glyph;
+ DWRITE_GLYPH_OFFSET offset;
+ offset.advanceOffset = 0;
+ offset.ascenderOffset = 0;
+ GeometrySink geometrySink(path);
+ HRESULT hr = m_directWriteFontFace->GetGlyphRunOutline(m_unitsPerEm,
+ &g,
+ &advance,
+ &offset,
+ 1,
+ false,
+ false,
+ &geometrySink);
+ if (FAILED(hr)) {
+ qErrnoWarning("%s: GetGlyphRunOutline failed", __FUNCTION__);
+ return;
+ }
+
+ DWRITE_GLYPH_METRICS glyphMetrics;
+ hr = m_directWriteFontFace->GetDesignGlyphMetrics(&g, 1, &glyphMetrics);
+ if (FAILED(hr)) {
+ qErrnoWarning("%s: GetDesignGlyphMetrics failed", __FUNCTION__);
+ return;
+ }
+
+ QFixed advanceWidth = QFixed(int(glyphMetrics.advanceWidth));
+ QFixed leftSideBearing = QFixed(glyphMetrics.leftSideBearing);
+ QFixed rightSideBearing = QFixed(glyphMetrics.rightSideBearing);
+ QFixed advanceHeight = QFixed(int(glyphMetrics.advanceHeight));
+ QFixed verticalOriginY = QFixed(glyphMetrics.verticalOriginY);
+ QFixed topSideBearing = QFixed(glyphMetrics.topSideBearing);
+ QFixed bottomSideBearing = QFixed(glyphMetrics.bottomSideBearing);
+ QFixed width = advanceWidth - leftSideBearing - rightSideBearing;
+ QFixed height = advanceHeight - topSideBearing - bottomSideBearing;
+ *metric = glyph_metrics_t(leftSideBearing,
+ -verticalOriginY + topSideBearing,
+ width,
+ height,
+ advanceWidth,
+ 0);
+}
+
void QWindowsFontEngineDirectWrite::addGlyphsToPath(glyph_t *glyphs, QFixedPoint *positions, int nglyphs,
QPainterPath *path, QTextItem::RenderFlags flags)
{
+ Q_UNUSED(flags);
QVarLengthArray<UINT16> glyphIndices(nglyphs);
QVarLengthArray<DWRITE_GLYPH_OFFSET> glyphOffsets(nglyphs);
QVarLengthArray<FLOAT> glyphAdvances(nglyphs);
@@ -536,7 +586,7 @@ void QWindowsFontEngineDirectWrite::addGlyphsToPath(glyph_t *glyphs, QFixedPoint
glyphOffsets.data(),
nglyphs,
false,
- flags & QTextItem::RightToLeft,
+ false,
&geometrySink
);
@@ -552,7 +602,9 @@ glyph_metrics_t QWindowsFontEngineDirectWrite::boundingBox(const QGlyphLayout &g
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);
+ const QFixed leftBearing = firstLeftBearing(glyphs);
+ return glyph_metrics_t(leftBearing, -ascent(), w - leftBearing - lastRightBearing(glyphs),
+ ascent() + descent(), w, 0);
}
glyph_metrics_t QWindowsFontEngineDirectWrite::boundingBox(glyph_t g)
@@ -602,7 +654,9 @@ qreal QWindowsFontEngineDirectWrite::maxCharWidth() const
return m_maxAdvanceWidth.toReal();
}
-QImage QWindowsFontEngineDirectWrite::alphaMapForGlyph(glyph_t glyph, QFixed subPixelPosition, const QTransform &t)
+QImage QWindowsFontEngineDirectWrite::alphaMapForGlyph(glyph_t glyph,
+ const QFixedPoint &subPixelPosition,
+ const QTransform &t)
{
QImage im = imageForGlyph(glyph, subPixelPosition, glyphMargin(Format_A8), t);
@@ -621,21 +675,52 @@ QImage QWindowsFontEngineDirectWrite::alphaMapForGlyph(glyph_t glyph, QFixed sub
return alphaMap;
}
-QImage QWindowsFontEngineDirectWrite::alphaMapForGlyph(glyph_t glyph, QFixed subPixelPosition)
+QImage QWindowsFontEngineDirectWrite::alphaMapForGlyph(glyph_t glyph,
+ const QFixedPoint &subPixelPosition)
{
return alphaMapForGlyph(glyph, subPixelPosition, QTransform());
}
-bool QWindowsFontEngineDirectWrite::supportsSubPixelPositions() const
+bool QWindowsFontEngineDirectWrite::supportsHorizontalSubPixelPositions() const
{
- return true;
+ DWRITE_RENDERING_MODE renderMode = hintingPreferenceToRenderingMode(fontDef);
+ return (renderMode != DWRITE_RENDERING_MODE_GDI_CLASSIC
+ && renderMode != DWRITE_RENDERING_MODE_GDI_NATURAL
+ && renderMode != DWRITE_RENDERING_MODE_ALIASED);
+}
+
+QFontEngine::Properties QWindowsFontEngineDirectWrite::properties() const
+{
+ IDWriteFontFace2 *directWriteFontFace2;
+ if (SUCCEEDED(m_directWriteFontFace->QueryInterface(__uuidof(IDWriteFontFace2),
+ reinterpret_cast<void **>(&directWriteFontFace2)))) {
+ DWRITE_FONT_METRICS1 metrics;
+ directWriteFontFace2->GetMetrics(&metrics);
+
+ Properties p = QFontEngine::properties();
+ p.emSquare = metrics.designUnitsPerEm;
+ p.boundingBox = QRectF(metrics.glyphBoxLeft,
+ -metrics.glyphBoxTop,
+ metrics.glyphBoxRight - metrics.glyphBoxLeft,
+ metrics.glyphBoxTop - metrics.glyphBoxBottom);
+ p.ascent = metrics.ascent;
+ p.descent = metrics.descent;
+ p.leading = metrics.lineGap;
+ p.capHeight = metrics.capHeight;
+ p.lineWidth = metrics.underlineThickness;
+
+ directWriteFontFace2->Release();
+ return p;
+ } else {
+ return QFontEngine::properties();
+ }
}
QImage QWindowsFontEngineDirectWrite::imageForGlyph(glyph_t t,
- QFixed subPixelPosition,
- int margin,
- const QTransform &originalTransform,
- const QColor &color)
+ const QFixedPoint &subPixelPosition,
+ int margin,
+ const QTransform &originalTransform,
+ const QColor &color)
{
UINT16 glyphIndex = t;
FLOAT glyphAdvance = 0;
@@ -659,32 +744,55 @@ QImage QWindowsFontEngineDirectWrite::imageForGlyph(glyph_t t,
xform.scale(fontDef.stretch / 100.0, 1.0);
DWRITE_MATRIX transform;
- transform.dx = subPixelPosition.toReal();
+ transform.dx = subPixelPosition.x.toReal();
transform.dy = 0;
transform.m11 = xform.m11();
transform.m12 = xform.m12();
transform.m21 = xform.m21();
transform.m22 = xform.m22();
- DWRITE_RENDERING_MODE renderMode =
- hintingPreferenceToRenderingMode(QFont::HintingPreference(fontDef.hintingPreference));
+ DWRITE_RENDERING_MODE renderMode = hintingPreferenceToRenderingMode(fontDef);
DWRITE_MEASURING_MODE measureMode =
renderModeToMeasureMode(renderMode);
+ DWRITE_GRID_FIT_MODE gridFitMode = fontDef.hintingPreference == QFont::PreferNoHinting
+ ? DWRITE_GRID_FIT_MODE_DISABLED
+ : DWRITE_GRID_FIT_MODE_DEFAULT;
+
+ IDWriteFactory2 *factory2 = nullptr;
+ HRESULT hr = m_fontEngineData->directWriteFactory->QueryInterface(__uuidof(IDWriteFactory2),
+ reinterpret_cast<void **>(&factory2));
IDWriteGlyphRunAnalysis *glyphAnalysis = NULL;
- HRESULT hr = m_fontEngineData->directWriteFactory->CreateGlyphRunAnalysis(
- &glyphRun,
- 1.0f,
- &transform,
- renderMode,
- measureMode,
- 0.0, 0.0,
- &glyphAnalysis
- );
+ if (!SUCCEEDED(hr)) {
+ qErrnoWarning(hr, "%s: Failed to query IDWriteFactory2 interface.", __FUNCTION__);
+ hr = m_fontEngineData->directWriteFactory->CreateGlyphRunAnalysis(
+ &glyphRun,
+ 1.0f,
+ &transform,
+ renderMode,
+ measureMode,
+ 0.0, 0.0,
+ &glyphAnalysis
+ );
+ } else {
+ hr = factory2->CreateGlyphRunAnalysis(
+ &glyphRun,
+ &transform,
+ renderMode,
+ measureMode,
+ gridFitMode,
+ DWRITE_TEXT_ANTIALIAS_MODE_CLEARTYPE,
+ 0.0, 0.0,
+ &glyphAnalysis
+ );
+ }
if (SUCCEEDED(hr)) {
RECT rect;
- glyphAnalysis->GetAlphaTextureBounds(DWRITE_TEXTURE_CLEARTYPE_3x1, &rect);
+ glyphAnalysis->GetAlphaTextureBounds(renderMode == DWRITE_RENDERING_MODE_ALIASED
+ ? DWRITE_TEXTURE_ALIASED_1x1
+ : DWRITE_TEXTURE_CLEARTYPE_3x1,
+ &rect);
if (rect.top == rect.bottom || rect.left == rect.right)
return QImage();
@@ -701,10 +809,7 @@ QImage QWindowsFontEngineDirectWrite::imageForGlyph(glyph_t t,
QImage image;
HRESULT hr = DWRITE_E_NOCOLOR;
IDWriteColorGlyphRunEnumerator *enumerator = 0;
- IDWriteFactory2 *factory2 = nullptr;
- if (glyphFormat == QFontEngine::Format_ARGB
- && SUCCEEDED(m_fontEngineData->directWriteFactory->QueryInterface(__uuidof(IDWriteFactory2),
- reinterpret_cast<void **>(&factory2)))) {
+ if (glyphFormat == QFontEngine::Format_ARGB && factory2 != nullptr) {
hr = factory2->TranslateColorGlyphRun(0.0f,
0.0f,
&glyphRun,
@@ -732,15 +837,17 @@ QImage QWindowsFontEngineDirectWrite::imageForGlyph(glyph_t t,
}
IDWriteGlyphRunAnalysis *colorGlyphsAnalysis = NULL;
- hr = m_fontEngineData->directWriteFactory->CreateGlyphRunAnalysis(
+ hr = factory2->CreateGlyphRunAnalysis(
&colorGlyphRun->glyphRun,
- 1.0f,
&transform,
renderMode,
measureMode,
+ gridFitMode,
+ DWRITE_TEXT_ANTIALIAS_MODE_CLEARTYPE,
0.0, 0.0,
&colorGlyphsAnalysis
);
+
if (FAILED(hr)) {
qErrnoWarning(hr, "%s: CreateGlyphRunAnalysis failed for color run", __FUNCTION__);
break;
@@ -766,7 +873,8 @@ QImage QWindowsFontEngineDirectWrite::imageForGlyph(glyph_t t,
b,
a,
colorGlyphsAnalysis,
- boundingRect);
+ boundingRect,
+ renderMode);
}
colorGlyphsAnalysis->Release();
@@ -793,7 +901,8 @@ QImage QWindowsFontEngineDirectWrite::imageForGlyph(glyph_t t,
b,
a,
glyphAnalysis,
- boundingRect);
+ boundingRect,
+ renderMode);
}
glyphAnalysis->Release();
@@ -811,7 +920,8 @@ void QWindowsFontEngineDirectWrite::renderGlyphRun(QImage *destination,
float b,
float a,
IDWriteGlyphRunAnalysis *glyphAnalysis,
- const QRect &boundingRect)
+ const QRect &boundingRect,
+ DWRITE_RENDERING_MODE renderMode)
{
const int width = destination->width();
const int height = destination->height();
@@ -832,12 +942,14 @@ void QWindowsFontEngineDirectWrite::renderGlyphRun(QImage *destination,
BYTE *alphaValues = alphaValueArray.data();
memset(alphaValues, 0, size);
- HRESULT hr = glyphAnalysis->CreateAlphaTexture(DWRITE_TEXTURE_CLEARTYPE_3x1,
+ HRESULT hr = glyphAnalysis->CreateAlphaTexture(renderMode == DWRITE_RENDERING_MODE_ALIASED
+ ? DWRITE_TEXTURE_ALIASED_1x1
+ : DWRITE_TEXTURE_CLEARTYPE_3x1,
&rect,
alphaValues,
size);
if (SUCCEEDED(hr)) {
- if (destination->hasAlphaChannel()) {
+ if (destination->hasAlphaChannel()) { // Color glyphs
for (int y = 0; y < height; ++y) {
uint *dest = reinterpret_cast<uint *>(destination->scanLine(y));
BYTE *src = alphaValues + width * 3 * y;
@@ -855,7 +967,16 @@ void QWindowsFontEngineDirectWrite::renderGlyphRun(QImage *destination,
qRound(qAlpha(currentRgb) * (1.0 - averageAlpha) + averageAlpha * 255));
}
}
+ } else if (renderMode == DWRITE_RENDERING_MODE_ALIASED) {
+ for (int y = 0; y < height; ++y) {
+ uint *dest = reinterpret_cast<uint *>(destination->scanLine(y));
+ BYTE *src = alphaValues + width * y;
+ for (int x = 0; x < width; ++x) {
+ int alpha = *(src++);
+ dest[x] = (alpha << 16) + (alpha << 8) + alpha;
+ }
+ }
} else {
for (int y = 0; y < height; ++y) {
uint *dest = reinterpret_cast<uint *>(destination->scanLine(y));
@@ -880,7 +1001,7 @@ void QWindowsFontEngineDirectWrite::renderGlyphRun(QImage *destination,
}
QImage QWindowsFontEngineDirectWrite::alphaRGBMapForGlyph(glyph_t t,
- QFixed subPixelPosition,
+ const QFixedPoint &subPixelPosition,
const QTransform &xform)
{
QImage mask = imageForGlyph(t,
@@ -924,6 +1045,27 @@ void QWindowsFontEngineDirectWrite::initFontInfo(const QFontDef &request,
fontDef.pointSize = fontDef.pixelSize * 72. / dpi;
else if (fontDef.pixelSize == -1)
fontDef.pixelSize = qRound(fontDef.pointSize * dpi / 72.);
+
+ m_faceId.variableAxes = request.variableAxisValues;
+
+#if QT_CONFIG(directwrite3)
+ IDWriteFontFace3 *face3 = nullptr;
+ if (SUCCEEDED(m_directWriteFontFace->QueryInterface(__uuidof(IDWriteFontFace3),
+ reinterpret_cast<void **>(&face3)))) {
+ IDWriteLocalizedStrings *names;
+ if (SUCCEEDED(face3->GetFaceNames(&names))) {
+ wchar_t englishLocale[] = L"en-us";
+ fontDef.styleName = QWindowsDirectWriteFontDatabase::localeString(names, englishLocale);
+ names->Release();
+ }
+
+ // Color font
+ if (face3->GetPaletteEntryCount() > 0)
+ glyphFormat = QFontEngine::Format_ARGB;
+
+ face3->Release();
+ }
+#endif
}
QString QWindowsFontEngineDirectWrite::fontNameSubstitute(const QString &familyName)
@@ -936,7 +1078,7 @@ QString QWindowsFontEngineDirectWrite::fontNameSubstitute(const QString &familyN
}
glyph_metrics_t QWindowsFontEngineDirectWrite::alphaMapBoundingBox(glyph_t glyph,
- QFixed subPixelPosition,
+ const QFixedPoint &subPixelPosition,
const QTransform &originalTransform,
GlyphFormat format)
{
@@ -966,31 +1108,50 @@ glyph_metrics_t QWindowsFontEngineDirectWrite::alphaMapBoundingBox(glyph_t glyph
glyphRun.glyphOffsets = &glyphOffset;
DWRITE_MATRIX transform;
- transform.dx = subPixelPosition.toReal();
+ transform.dx = subPixelPosition.x.toReal();
transform.dy = 0;
transform.m11 = matrix.m11();
transform.m12 = matrix.m12();
transform.m21 = matrix.m21();
transform.m22 = matrix.m22();
- DWRITE_RENDERING_MODE renderMode =
- hintingPreferenceToRenderingMode(QFont::HintingPreference(fontDef.hintingPreference));
+ DWRITE_RENDERING_MODE renderMode = hintingPreferenceToRenderingMode(fontDef);
DWRITE_MEASURING_MODE measureMode = renderModeToMeasureMode(renderMode);
+ DWRITE_GRID_FIT_MODE gridFitMode = fontDef.hintingPreference == QFont::PreferNoHinting
+ ? DWRITE_GRID_FIT_MODE_DISABLED
+ : DWRITE_GRID_FIT_MODE_DEFAULT;
+
+ IDWriteFactory2 *factory2 = nullptr;
+ HRESULT hr = m_fontEngineData->directWriteFactory->QueryInterface(__uuidof(IDWriteFactory2),
+ reinterpret_cast<void **>(&factory2));
IDWriteGlyphRunAnalysis *glyphAnalysis = NULL;
- HRESULT hr = m_fontEngineData->directWriteFactory->CreateGlyphRunAnalysis(
- &glyphRun,
- 1.0f,
- &transform,
- renderMode,
- measureMode,
- 0.0, 0.0,
- &glyphAnalysis
- );
+ if (SUCCEEDED(hr)) {
+ hr = factory2->CreateGlyphRunAnalysis(
+ &glyphRun,
+ &transform,
+ renderMode,
+ measureMode,
+ gridFitMode,
+ DWRITE_TEXT_ANTIALIAS_MODE_CLEARTYPE,
+ 0.0, 0.0,
+ &glyphAnalysis
+ );
+ } else {
+ hr = m_fontEngineData->directWriteFactory->CreateGlyphRunAnalysis(
+ &glyphRun,
+ 1.0f,
+ &transform,
+ renderMode,
+ measureMode,
+ 0.0, 0.0,
+ &glyphAnalysis
+ );
+ }
if (SUCCEEDED(hr)) {
RECT rect;
- glyphAnalysis->GetAlphaTextureBounds(DWRITE_TEXTURE_CLEARTYPE_3x1, &rect);
+ glyphAnalysis->GetAlphaTextureBounds(renderMode == DWRITE_RENDERING_MODE_ALIASED ? DWRITE_TEXTURE_ALIASED_1x1 : DWRITE_TEXTURE_CLEARTYPE_3x1, &rect);
glyphAnalysis->Release();
int margin = glyphMargin(format);
@@ -1008,7 +1169,10 @@ glyph_metrics_t QWindowsFontEngineDirectWrite::alphaMapBoundingBox(glyph_t glyph
}
}
-QImage QWindowsFontEngineDirectWrite::bitmapForGlyph(glyph_t glyph, QFixed subPixelPosition, const QTransform &t, const QColor &color)
+QImage QWindowsFontEngineDirectWrite::bitmapForGlyph(glyph_t glyph,
+ const QFixedPoint &subPixelPosition,
+ const QTransform &t,
+ const QColor &color)
{
return imageForGlyph(glyph, subPixelPosition, glyphMargin(QFontEngine::Format_ARGB), t, color);
}
diff --git a/src/gui/text/windows/qwindowsfontenginedirectwrite_p.h b/src/gui/text/windows/qwindowsfontenginedirectwrite_p.h
index 5be021d37a..d7c9a79267 100644
--- a/src/gui/text/windows/qwindowsfontenginedirectwrite_p.h
+++ b/src/gui/text/windows/qwindowsfontenginedirectwrite_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 QWINDOWSFONTENGINEDIRECTWRITE_H
#define QWINDOWSFONTENGINEDIRECTWRITE_H
@@ -58,6 +22,7 @@ QT_REQUIRE_CONFIG(directwrite);
#include <QtGui/private/qfontengine_p.h>
#include <QtCore/QSharedPointer>
+#include <dwrite.h>
struct IDWriteFont;
struct IDWriteFontFace;
@@ -71,7 +36,7 @@ QT_BEGIN_NAMESPACE
class QWindowsFontEngineData;
-class QWindowsFontEngineDirectWrite : public QFontEngine
+class Q_GUI_EXPORT QWindowsFontEngineDirectWrite : public QFontEngine
{
Q_DISABLE_COPY_MOVE(QWindowsFontEngineDirectWrite)
public:
@@ -88,8 +53,8 @@ public:
QFixed emSquareSize() const override;
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 *glyphs, ShaperFlags) const override;
void addGlyphsToPath(glyph_t *glyphs, QFixedPoint *positions, int nglyphs,
@@ -97,7 +62,7 @@ public:
glyph_metrics_t boundingBox(const QGlyphLayout &glyphs) override;
glyph_metrics_t boundingBox(glyph_t g) override;
- glyph_metrics_t alphaMapBoundingBox(glyph_t glyph, QFixed,
+ glyph_metrics_t alphaMapBoundingBox(glyph_t glyph, const QFixedPoint&,
const QTransform &matrix, GlyphFormat) override;
QFixed capHeight() const override;
@@ -105,12 +70,21 @@ public:
qreal maxCharWidth() const override;
FaceId faceId() const override;
- bool supportsSubPixelPositions() const override;
+ bool supportsHorizontalSubPixelPositions() const override;
- QImage alphaMapForGlyph(glyph_t glyph, QFixed subPixelPosition) override;
- QImage alphaMapForGlyph(glyph_t glyph, QFixed subPixelPosition, const QTransform &t) override;
- QImage alphaRGBMapForGlyph(glyph_t t, QFixed subPixelPosition, const QTransform &xform) override;
- QImage bitmapForGlyph(glyph_t, QFixed subPixelPosition, const QTransform &t, const QColor &color) override;
+ HFONT createHFONT() const;
+
+ QImage alphaMapForGlyph(glyph_t glyph, const QFixedPoint &subPixelPosition) override;
+ QImage alphaMapForGlyph(glyph_t glyph,
+ const QFixedPoint &subPixelPosition,
+ const QTransform &t) override;
+ QImage alphaRGBMapForGlyph(glyph_t t,
+ const QFixedPoint &subPixelPosition,
+ const QTransform &xform) override;
+ QImage bitmapForGlyph(glyph_t,
+ const QFixedPoint &subPixelPosition,
+ const QTransform &t,
+ const QColor &color) override;
QFontEngine *cloneWithSize(qreal pixelSize) const override;
Qt::HANDLE handle() const override;
@@ -125,11 +99,26 @@ public:
void initializeHeightMetrics() const override;
+ Properties properties() const override;
+ void getUnscaledGlyph(glyph_t glyph, QPainterPath *path, glyph_metrics_t *metrics) override;
+
private:
- QImage imageForGlyph(glyph_t t, QFixed subPixelPosition, int margin, const QTransform &xform, const QColor &color = QColor());
+ QImage imageForGlyph(glyph_t t,
+ const QFixedPoint &subPixelPosition,
+ int margin,
+ const QTransform &xform,
+ const QColor &color = QColor());
void collectMetrics();
- void renderGlyphRun(QImage *destination, float r, float g, float b, float a, IDWriteGlyphRunAnalysis *glyphAnalysis, const QRect &boundingRect);
+ void renderGlyphRun(QImage *destination,
+ float r,
+ float g,
+ float b,
+ float a,
+ IDWriteGlyphRunAnalysis *glyphAnalysis,
+ const QRect &boundingRect,
+ DWRITE_RENDERING_MODE renderMode);
static QString filenameFromFontFile(IDWriteFontFile *fontFile);
+ DWRITE_RENDERING_MODE hintingPreferenceToRenderingMode(const QFontDef &fontDef) const;
const QSharedPointer<QWindowsFontEngineData> m_fontEngineData;
diff --git a/src/gui/text/windows/qwindowsnativeimage.cpp b/src/gui/text/windows/qwindowsnativeimage.cpp
index ad277ea7cd..eca51ccfd4 100644
--- a/src/gui/text/windows/qwindowsnativeimage.cpp
+++ b/src/gui/text/windows/qwindowsnativeimage.cpp
@@ -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 "qwindowsnativeimage_p.h"
diff --git a/src/gui/text/windows/qwindowsnativeimage_p.h b/src/gui/text/windows/qwindowsnativeimage_p.h
index 050ecbf03c..d223aabf45 100644
--- a/src/gui/text/windows/qwindowsnativeimage_p.h
+++ b/src/gui/text/windows/qwindowsnativeimage_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) 2020 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 QWINDOWSNATIVEIMAGE_H
#define QWINDOWSNATIVEIMAGE_H
@@ -54,6 +18,7 @@
#include <QtCore/QtGlobal>
#include <QtCore/qt_windows.h>
#include <QtGui/QImage>
+#include <QtCore/private/qglobal_p.h>
QT_BEGIN_NAMESPACE
diff --git a/src/gui/text/windows/windows.pri b/src/gui/text/windows/windows.pri
deleted file mode 100644
index 91f803d740..0000000000
--- a/src/gui/text/windows/windows.pri
+++ /dev/null
@@ -1,35 +0,0 @@
-SOURCES += \
- text/windows/qwindowsfontdatabase.cpp \
- text/windows/qwindowsfontdatabasebase.cpp \
- text/windows/qwindowsfontengine.cpp \
- text/windows/qwindowsnativeimage.cpp
-
-HEADERS += \
- text/windows/qwindowsfontdatabase_p.h \
- text/windows/qwindowsfontdatabasebase_p.h \
- text/windows/qwindowsfontengine_p.h \
- text/windows/qwindowsnativeimage_p.h
-
-qtConfig(freetype) {
- SOURCES += text/windows/qwindowsfontdatabase_ft.cpp
- HEADERS += text/windows/qwindowsfontdatabase_ft_p.h
- QMAKE_USE_PRIVATE += freetype
-}
-
-qtConfig(directwrite):qtConfig(direct2d) {
- qtConfig(directwrite3) {
- QMAKE_USE_PRIVATE += dwrite_3
-
- SOURCES += text/windows/qwindowsdirectwritefontdatabase.cpp
- HEADERS += text/windows/qwindowsdirectwritefontdatabase_p.h
- } else {
- QMAKE_USE_PRIVATE += dwrite_2
- }
- QMAKE_USE_PRIVATE += d2d1
-
- SOURCES += text/windows/qwindowsfontenginedirectwrite.cpp
- HEADERS += text/windows/qwindowsfontenginedirectwrite_p.h
-}
-
-QMAKE_USE_PRIVATE += advapi32 ole32 user32 gdi32
-mingw: QMAKE_USE_PRIVATE += uuid