diff options
Diffstat (limited to 'src/gui/text/windows')
-rw-r--r-- | src/gui/text/windows/qwindowsdirectwritefontdatabase.cpp | 741 | ||||
-rw-r--r-- | src/gui/text/windows/qwindowsdirectwritefontdatabase_p.h | 64 | ||||
-rw-r--r-- | src/gui/text/windows/qwindowsfontdatabase.cpp | 341 | ||||
-rw-r--r-- | src/gui/text/windows/qwindowsfontdatabase_ft.cpp | 90 | ||||
-rw-r--r-- | src/gui/text/windows/qwindowsfontdatabase_ft_p.h | 43 | ||||
-rw-r--r-- | src/gui/text/windows/qwindowsfontdatabase_p.h | 57 | ||||
-rw-r--r-- | src/gui/text/windows/qwindowsfontdatabasebase.cpp | 347 | ||||
-rw-r--r-- | src/gui/text/windows/qwindowsfontdatabasebase_p.h | 58 | ||||
-rw-r--r-- | src/gui/text/windows/qwindowsfontengine.cpp | 99 | ||||
-rw-r--r-- | src/gui/text/windows/qwindowsfontengine_p.h | 49 | ||||
-rw-r--r-- | src/gui/text/windows/qwindowsfontenginedirectwrite.cpp | 361 | ||||
-rw-r--r-- | src/gui/text/windows/qwindowsfontenginedirectwrite_p.h | 62 | ||||
-rw-r--r-- | src/gui/text/windows/qwindowsnativeimage.cpp | 40 | ||||
-rw-r--r-- | src/gui/text/windows/qwindowsnativeimage_p.h | 41 |
14 files changed, 1340 insertions, 1053 deletions
diff --git a/src/gui/text/windows/qwindowsdirectwritefontdatabase.cpp b/src/gui/text/windows/qwindowsdirectwritefontdatabase.cpp index 1779872f7d..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"; @@ -116,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"; @@ -134,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, @@ -142,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"; @@ -152,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()); @@ -181,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(); } } + } + } + } +} - font1->Release(); - font->Release(); +QSupportedWritingSystems QWindowsDirectWriteFontDatabase::supportedWritingSystems(IDWriteFontFace *face) const +{ + QSupportedWritingSystems writingSystems; + writingSystems.setSupported(QFontDatabase::Any); + + 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()); @@ -285,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 @@ -398,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"; - const QString defaultFontName = defaultFont().families().first(); - const QString systemDefaultFontName = systemDefaultFont().families().first(); + const QString defaultFontName = defaultFont().families().constFirst(); + const QString systemDefaultFontName = systemDefaultFont().families().constFirst(); + + 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; + } - IDWriteFontCollection *fontCollection; - if (SUCCEEDED(data()->directWriteFactory->GetSystemFontCollection(&fontCollection))) { + 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 @@ -461,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 94a152abed..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 @@ -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] = u'\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; @@ -544,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 @@ -562,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; @@ -582,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); @@ -590,33 +539,35 @@ static bool addFontToDatabase(QString familyName, writingSystems.setSupported(ws); } + const bool wasPopulated = QPlatformFontDatabase::isFamilyPopulated(familyName); + QPlatformFontDatabase::registerFont(familyName, styleName, foundryName, weight, + 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, new QWindowsFontDatabase::FontHandle(faceName)); + if (style != QFont::StyleItalic && styleName.isEmpty()) + QPlatformFontDatabase::registerFont(familyName, QString(), foundryName, weight, + 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, 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 - && !QPlatformFontDatabase::isFamilyPopulated(familyName)) { + && !wasPopulated) { sfp->windowsFontDatabase->populateFamily(familyName); } - QPlatformFontDatabase::registerFont(familyName, styleName, foundryName, weight, - style, stretch, antialias, scalable, size, fixed, writingSystems, createFontFile(faceName)); - - // add fonts windows can generate for us: - if (weight <= QFont::DemiBold) - QPlatformFontDatabase::registerFont(familyName, QString(), foundryName, QFont::Bold, - style, stretch, antialias, scalable, size, fixed, writingSystems, createFontFile(faceName)); - if (style != QFont::StyleItalic) - QPlatformFontDatabase::registerFont(familyName, QString(), foundryName, weight, - QFont::StyleItalic, stretch, antialias, scalable, size, fixed, writingSystems, createFontFile(faceName)); - if (weight <= QFont::DemiBold && style != QFont::StyleItalic) - QPlatformFontDatabase::registerFont(familyName, QString(), foundryName, QFont::Bold, - QFont::StyleItalic, stretch, antialias, scalable, size, fixed, writingSystems, createFontFile(faceName)); - 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) @@ -642,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); @@ -706,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; @@ -732,12 +705,18 @@ void QWindowsFontDatabase::populateFontDatabase() EnumFontFamiliesEx(dummy, &lf, populateFontFamilies, 0, 0); ReleaseDC(0, dummy); // Work around EnumFontFamiliesEx() not listing the system font. - const QString systemDefaultFamily = QWindowsFontDatabase::systemDefaultFont().families().first(); + 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) @@ -760,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()); @@ -782,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 @@ -822,7 +802,7 @@ QT_WARNING_POP if (fontEngine) { if (request.families != fontEngine->fontDef.families) { qWarning("%s: Failed to load font. Got fallback instead: %s", __FUNCTION__, - qPrintable(fontEngine->fontDef.families.first())); + qPrintable(fontEngine->fontDef.families.constFirst())); if (fontEngine->ref.loadRelaxed() == 0) delete fontEngine; fontEngine = 0; @@ -846,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); @@ -870,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; @@ -912,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; @@ -921,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); @@ -930,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; @@ -996,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; @@ -1077,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 { @@ -1089,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 @@ -1109,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 @@ -1170,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; } @@ -1188,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), @@ -1195,10 +1235,12 @@ 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); + useDw = useDw || useDirectWrite(hintingPreference, fam, isColorFont) || needsSimulation; qCDebug(lcQpaFonts) << __FUNCTION__ << request.families.first() << request.pointSize << "pt" << "hintingPreference=" << hintingPreference << "color=" << isColorFont @@ -1214,9 +1256,6 @@ QFontEngine *QWindowsFontDatabase::createEngine(const QFontDef &request, const Q QFontDef fontDef = request; fontDef.families = QStringList(QString::fromWCharArray(n)); - - if (isColorFont) - fedw->glyphFormat = QFontEngine::Format_ARGB; fedw->initFontInfo(fontDef, dpi); fe = fedw; } diff --git a/src/gui/text/windows/qwindowsfontdatabase_ft.cpp b/src/gui/text/windows/qwindowsfontdatabase_ft.cpp index c8dbba015c..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) @@ -279,15 +246,15 @@ static bool addFontToDatabase(QString familyName, antialias, scalable, size, fixed, writingSystems, createFontFile(value, index)); // add fonts windows can generate for us: - if (weight <= QFont::DemiBold) + if (weight <= QFont::DemiBold && styleName.isEmpty()) QPlatformFontDatabase::registerFont(familyName, QString(), foundryName, QFont::Bold, style, stretch, antialias, scalable, size, fixed, writingSystems, createFontFile(value, index)); - if (style != QFont::StyleItalic) + if (style != QFont::StyleItalic && styleName.isEmpty()) QPlatformFontDatabase::registerFont(familyName, QString(), foundryName, weight, QFont::StyleItalic, stretch, antialias, scalable, size, fixed, writingSystems, createFontFile(value, index)); - if (weight <= QFont::DemiBold && style != QFont::StyleItalic) + 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(value, index)); @@ -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 - const QString systemDefaultFamily = QWindowsFontDatabase::systemDefaultFont().families().first(); + 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.families.first() << 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 81042c1284..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()); @@ -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) @@ -416,6 +378,11 @@ namespace { UINT32 fontFileReferenceKeySize, OUT IDWriteFontFileStream **fontFileStream) override; + void clear() + { + m_fontDatas.clear(); + } + private: ULONG m_referenceCount; QHash<const void *, QByteArray> m_fontDatas; @@ -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) @@ -713,16 +681,6 @@ LOGFONT QWindowsFontDatabaseBase::fontDefToLOGFONT(const QFontDef &request, cons 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); @@ -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", @@ -963,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 f4ef9f8a23..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,18 +56,6 @@ 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 @@ -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); } @@ -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) { @@ -188,6 +144,8 @@ int QWindowsFontEngine::getGlyphIndexes(const QChar *str, int numChars, QGlyphLa glyphs->glyphs[glyph_pos] = getTrueTypeGlyphIndex(cmap, cmapSize, uc); 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,9 +214,6 @@ QWindowsFontEngine::QWindowsFontEngine(const QString &name, cache_cost = tm.tmHeight * tm.tmAveCharWidth * 2000; getCMap(); - if (!resolvedGetCharWidthI) - resolveGetCharWidthI(); - hasUnreliableOutline = (tm.tmPitchAndFamily & (TMPF_TRUETYPE | TMPF_VECTOR)) == 0; } @@ -307,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) { @@ -316,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 @@ -401,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); @@ -532,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) { @@ -675,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; @@ -1150,7 +1097,7 @@ QImage QWindowsFontEngine::alphaRGBMapForGlyph(glyph_t glyph, QFontEngine *QWindowsFontEngine::cloneWithSize(qreal pixelSize) const { QFontDef request = fontDef; - QString actualFontName = request.families.first(); + QString actualFontName = request.families.constFirst(); if (!uniqueFamilyName.isEmpty()) request.families = QStringList(uniqueFamilyName); request.pixelSize = pixelSize; diff --git a/src/gui/text/windows/qwindowsfontengine_p.h b/src/gui/text/windows/qwindowsfontengine_p.h index 57b619bc1c..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; @@ -125,7 +88,7 @@ public: 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; @@ -174,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 0dac9769ef..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> @@ -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,6 +516,53 @@ 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) { @@ -553,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) @@ -632,7 +683,37 @@ QImage QWindowsFontEngineDirectWrite::alphaMapForGlyph(glyph_t glyph, 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, @@ -670,25 +751,48 @@ QImage QWindowsFontEngineDirectWrite::imageForGlyph(glyph_t t, 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(); @@ -705,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, @@ -736,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; @@ -770,7 +873,8 @@ QImage QWindowsFontEngineDirectWrite::imageForGlyph(glyph_t t, b, a, colorGlyphsAnalysis, - boundingRect); + boundingRect, + renderMode); } colorGlyphsAnalysis->Release(); @@ -797,7 +901,8 @@ QImage QWindowsFontEngineDirectWrite::imageForGlyph(glyph_t t, b, a, glyphAnalysis, - boundingRect); + boundingRect, + renderMode); } glyphAnalysis->Release(); @@ -815,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(); @@ -836,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; @@ -859,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)); @@ -928,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) @@ -977,24 +1115,43 @@ glyph_metrics_t QWindowsFontEngineDirectWrite::alphaMapBoundingBox(glyph_t glyph 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); diff --git a/src/gui/text/windows/qwindowsfontenginedirectwrite_p.h b/src/gui/text/windows/qwindowsfontenginedirectwrite_p.h index 4d19c3908a..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, @@ -107,6 +72,8 @@ public: bool supportsHorizontalSubPixelPositions() const override; + HFONT createHFONT() const; + QImage alphaMapForGlyph(glyph_t glyph, const QFixedPoint &subPixelPosition) override; QImage alphaMapForGlyph(glyph_t glyph, const QFixedPoint &subPixelPosition, @@ -132,6 +99,9 @@ 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, const QFixedPoint &subPixelPosition, @@ -139,8 +109,16 @@ private: 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 |