diff options
Diffstat (limited to 'src/gui/text/windows/qwindowsfontdatabase.cpp')
-rw-r--r-- | src/gui/text/windows/qwindowsfontdatabase.cpp | 397 |
1 files changed, 228 insertions, 169 deletions
diff --git a/src/gui/text/windows/qwindowsfontdatabase.cpp b/src/gui/text/windows/qwindowsfontdatabase.cpp index 4db0043b7f..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,13 +56,14 @@ 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 /*! - \struct QWindowsFontEngineData + \class QWindowsFontEngineData \brief Static constant data shared by the font engines. + \internal */ QWindowsFontEngineData::QWindowsFontEngineData() @@ -161,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 @@ -224,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(); @@ -412,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; @@ -433,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; @@ -461,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); @@ -477,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, @@ -498,7 +442,7 @@ namespace { {} QString populatedFontFamily; - QSet<FontAndStyle> foundFontAndStyles; + QDuplicateTracker<FontAndStyle> foundFontAndStyles; QWindowsFontDatabase *windowsFontDatabase; }; } @@ -512,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; @@ -521,11 +465,13 @@ 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; - const QFont::Weight weight = QPlatformFontDatabase::weightFromInteger(textmetric->tmWeight); + const QFont::Weight weight = static_cast<QFont::Weight>(textmetric->tmWeight); const QFont::Stretch stretch = QFont::Unstretched; #ifndef QT_NO_DEBUG_OUTPUT @@ -541,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 @@ -559,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; @@ -579,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); @@ -587,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) @@ -639,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); @@ -650,11 +602,26 @@ static int QT_WIN_CALLBACK storeFont(const LOGFONT *logFont, const TEXTMETRIC *t return 1; } +bool QWindowsFontDatabase::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; +} + 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); @@ -688,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; + } - m_eudcFonts = addApplicationFont(file.readAll(), path); + const QString absolutePath = resolveFontPath(path); + if (absolutePath.isEmpty()) { + qCDebug(lcQpaFonts) << "Unable to locate default EUDC font:" << path; + return; + } + + 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; @@ -714,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) @@ -742,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()); @@ -764,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 @@ -791,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; @@ -802,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; @@ -815,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 @@ -828,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); @@ -852,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; @@ -894,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; @@ -903,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); @@ -912,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; @@ -978,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; @@ -994,7 +1035,7 @@ QStringList QWindowsFontDatabase::addApplicationFont(const QByteArray &fontData, if (applicationFont != nullptr) { QFontDatabasePrivate::ApplicationFont::Properties properties; properties.style = values.isItalic ? QFont::StyleItalic : QFont::StyleNormal; - properties.weight = QPlatformFontDatabase::weightFromInteger(values.weight); + properties.weight = static_cast<int>(values.weight); properties.familyName = familyName; properties.styleName = styleName; @@ -1041,7 +1082,7 @@ QStringList QWindowsFontDatabase::addApplicationFont(const QByteArray &fontData, QFontDatabasePrivate::ApplicationFont::Properties properties; properties.style = values.isItalic ? QFont::StyleItalic : QFont::StyleNormal; - properties.weight = QPlatformFontDatabase::weightFromInteger(values.weight); + properties.weight = static_cast<int>(values.weight); properties.familyName = familyName; properties.styleName = styleName; @@ -1059,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 { @@ -1071,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 @@ -1091,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 @@ -1123,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; @@ -1152,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; } @@ -1170,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), @@ -1177,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, @@ -1193,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; } @@ -1214,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); |