From 3915684fb8005b76e11d75b15a5d860a20b6d77c Mon Sep 17 00:00:00 2001 From: Eskil Abrahamsen Blomfeldt Date: Wed, 9 Apr 2025 09:38:57 +0200 Subject: [PATCH] dwrite: Support additional font names for system fonts Fonts have multiple name fields that users may expect to be able to use for referring to them. In particular, on Windows it has become common to use a legacy font family name which contains the sub-family of the specific font, because of backwards-compatibility reasons. This is not technically the family of the font, but native applications will typically list them as such anyway, and Qt would also do this prior to Qt 6.8 because the GDI backend prioritizes these. When we moved to DirectWrite as the new default, these legacy names for system fonts disappeared from the families list, which is perceived as a regression. Now, there was already an implementation for getting the legacy names for fonts for application fonts, but this had not been implemented for system fonts when populating the font database. We rectify this by sharing the code which gets the additional font names and also calling this when populating system fonts. [ChangeLog][Windows] Fixed an issue where legacy names such as 'Arial Narrow' would no longer be listed as separate font families. Fixes: QTBUG-135817 Pick-to: 6.8 Change-Id: I2e1b42b7be926e3b5af7bb2aab3ebe5a1fbe29de Reviewed-by: Volker Hilsheimer (cherry picked from commit 6854ea63365ae419a04a470eb17e5f7662f00931) Reviewed-by: Qt Cherry-pick Bot --- .../qwindowsdirectwritefontdatabase.cpp | 383 ++++++++---------- .../qwindowsdirectwritefontdatabase_p.h | 6 + 2 files changed, 171 insertions(+), 218 deletions(-) diff --git a/src/gui/text/windows/qwindowsdirectwritefontdatabase.cpp b/src/gui/text/windows/qwindowsdirectwritefontdatabase.cpp index 25bb1863298..594fc18f9c6 100644 --- a/src/gui/text/windows/qwindowsdirectwritefontdatabase.cpp +++ b/src/gui/text/windows/qwindowsdirectwritefontdatabase.cpp @@ -357,6 +357,79 @@ QStringList QWindowsDirectWriteFontDatabase::fallbacksForFamily(const QString &f return result; } +template +void QWindowsDirectWriteFontDatabase::collectAdditionalNames(T *font, + wchar_t *defaultLocale, + wchar_t *englishLocale, + std::function &)> registerFamily) +{ + BOOL ok; + QString defaultLocaleGdiCompatibleFamilyName; + QString englishLocaleGdiCompatibleFamilyName; + + const bool hasDefaultLocale = defaultLocale != nullptr; + Q_ASSERT(englishLocale != nullptr); + + IDWriteLocalizedStrings *names = nullptr; + if (SUCCEEDED(font->GetInformationalStrings(DWRITE_INFORMATIONAL_STRING_WIN32_FAMILY_NAMES, &names, &ok)) && ok) { + defaultLocaleGdiCompatibleFamilyName = hasDefaultLocale ? localeString(names, defaultLocale) : QString(); + englishLocaleGdiCompatibleFamilyName = localeString(names, englishLocale); + + names->Release(); + } + + QString defaultLocaleGdiCompatibleStyleName; + QString englishLocaleGdiCompatibleStyleName; + if (SUCCEEDED(font->GetInformationalStrings(DWRITE_INFORMATIONAL_STRING_WIN32_SUBFAMILY_NAMES, &names, &ok)) && ok) { + defaultLocaleGdiCompatibleStyleName = hasDefaultLocale ? localeString(names, defaultLocale) : QString(); + englishLocaleGdiCompatibleStyleName = localeString(names, englishLocale); + + names->Release(); + } + + QString defaultLocaleTypographicFamilyName; + QString englishLocaleTypographicFamilyName; + if (SUCCEEDED(font->GetInformationalStrings(DWRITE_INFORMATIONAL_STRING_TYPOGRAPHIC_FAMILY_NAMES, &names, &ok)) && ok) { + defaultLocaleTypographicFamilyName = hasDefaultLocale ? localeString(names, defaultLocale) : QString(); + englishLocaleTypographicFamilyName = localeString(names, englishLocale); + + names->Release(); + } + + QString defaultLocaleTypographicStyleName; + QString englishLocaleTypographicStyleName; + if (SUCCEEDED(font->GetInformationalStrings(DWRITE_INFORMATIONAL_STRING_TYPOGRAPHIC_SUBFAMILY_NAMES, &names, &ok)) && ok) { + defaultLocaleTypographicStyleName = hasDefaultLocale ? localeString(names, defaultLocale) : QString(); + englishLocaleTypographicStyleName = localeString(names, englishLocale); + + names->Release(); + } + + { + const auto key = std::make_pair(englishLocaleGdiCompatibleFamilyName, englishLocaleGdiCompatibleStyleName); + if (!englishLocaleGdiCompatibleFamilyName.isEmpty()) + registerFamily(key); + } + + { + const auto key = std::make_pair(defaultLocaleGdiCompatibleFamilyName, defaultLocaleGdiCompatibleStyleName); + if (!defaultLocaleGdiCompatibleFamilyName.isEmpty()) + registerFamily(key); + } + + { + const auto key = std::make_pair(englishLocaleTypographicFamilyName, englishLocaleTypographicStyleName); + if (!englishLocaleTypographicFamilyName.isEmpty()) + registerFamily(key); + } + + { + const auto key = std::make_pair(defaultLocaleTypographicFamilyName, defaultLocaleTypographicStyleName); + if (!defaultLocaleTypographicFamilyName.isEmpty()) + registerFamily(key); + } +} + QStringList QWindowsDirectWriteFontDatabase::addApplicationFont(const QByteArray &fontData, const QString &fileName, QFontDatabasePrivate::ApplicationFont *applicationFont) { qCDebug(lcQpaFonts) << "Adding application font" << fileName; @@ -377,6 +450,7 @@ QStringList QWindowsDirectWriteFontDatabase::addApplicationFont(const QByteArray return QStringList(); } + QSet > registeredFonts; QSet ret; for (int i = 0; i < faces.size(); ++i) { IDWriteFontFace *face = faces.at(i); @@ -393,6 +467,46 @@ QStringList QWindowsDirectWriteFontDatabase::addApplicationFont(const QByteArray DirectWriteScope face3; if (SUCCEEDED(face->QueryInterface(__uuidof(IDWriteFontFace3), reinterpret_cast(&face3)))) { + + QFont::Stretch stretch = fromDirectWriteStretch(face3->GetStretch()); + QFont::Style style = fromDirectWriteStyle(face3->GetStyle()); + QFont::Weight weight = fromDirectWriteWeight(face3->GetWeight()); + bool fixed = face3->IsMonospacedFont(); + bool color = face3->IsColorFont(); + + auto registerFamilyAndStyle = [&](const std::pair &familyAndStyle) + { + if (registeredFonts.contains(familyAndStyle)) + return; + registeredFonts.insert(familyAndStyle); + ret.insert(familyAndStyle.first); + + qCDebug(lcQpaFonts) << "\tRegistering alternative:" << familyAndStyle.first + << ":" << familyAndStyle.second; + if (applicationFont != nullptr) { + QFontDatabasePrivate::ApplicationFont::Properties properties; + properties.style = style; + properties.weight = weight; + properties.familyName = familyAndStyle.first; + properties.styleName = familyAndStyle.second; + applicationFont->properties.append(properties); + } + + QPlatformFontDatabase::registerFont(familyAndStyle.first, + familyAndStyle.second, + QString(), + weight, + style, + stretch, + antialias, + scalable, + size, + fixed, + color, + writingSystems, + new FontHandle(face, familyAndStyle.first)); + }; + QString defaultLocaleFamilyName; QString englishLocaleFamilyName; @@ -413,49 +527,6 @@ QStringList QWindowsDirectWriteFontDatabase::addApplicationFont(const QByteArray names->Release(); } - 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); - - 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); - - 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); - - names->Release(); - } - - 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); - - names->Release(); - } - - QFont::Stretch stretch = fromDirectWriteStretch(face3->GetStretch()); - QFont::Style style = fromDirectWriteStyle(face3->GetStyle()); - QFont::Weight weight = fromDirectWriteWeight(face3->GetWeight()); - bool fixed = face3->IsMonospacedFont(); - bool color = face3->IsColorFont(); - qCDebug(lcQpaFonts) << "\tFont names:" << englishLocaleFamilyName << ", " << defaultLocaleFamilyName << ", style names:" << englishLocaleStyleName << ", " << defaultLocaleStyleName << ", stretch:" << stretch @@ -463,161 +534,22 @@ QStringList QWindowsDirectWriteFontDatabase::addApplicationFont(const QByteArray << ", 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); - } - - ret.insert(englishLocaleFamilyName); - QPlatformFontDatabase::registerFont(englishLocaleFamilyName, - englishLocaleStyleName, - QString(), - weight, - style, - stretch, - antialias, - scalable, - size, - fixed, - color, - writingSystems, - new FontHandle(face, englishLocaleFamilyName)); + { + const auto key = std::make_pair(englishLocaleFamilyName, englishLocaleStyleName); + if (!englishLocaleFamilyName.isEmpty()) + registerFamilyAndStyle(key); } - 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); - } - - ret.insert(defaultLocaleFamilyName); - QPlatformFontDatabase::registerFont(defaultLocaleFamilyName, - defaultLocaleStyleName, - QString(), - weight, - style, - stretch, - antialias, - scalable, - size, - fixed, - color, - writingSystems, - new FontHandle(face, defaultLocaleFamilyName)); + { + const auto key = std::make_pair(defaultLocaleFamilyName, defaultLocaleStyleName); + if (!defaultLocaleFamilyName.isEmpty()) + registerFamilyAndStyle(key); } - 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); - } - - ret.insert(englishLocaleGdiCompatibleFamilyName); - QPlatformFontDatabase::registerFont(englishLocaleGdiCompatibleFamilyName, - englishLocaleGdiCompatibleStyleName, - QString(), - weight, - style, - stretch, - antialias, - scalable, - size, - fixed, - color, - 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, - color, - 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, - color, - 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, - color, - writingSystems, - new FontHandle(face, defaultLocaleTypographicFamilyName)); - } + collectAdditionalNames(*face3, + hasDefaultLocale ? defaultLocale : nullptr, + englishLocale, + registerFamilyAndStyle); } else { qCWarning(lcQpaFonts) << "Unable to query IDWriteFontFace3 interface from font face."; @@ -678,44 +610,59 @@ void QWindowsDirectWriteFontDatabase::populateFontDatabase() if (SUCCEEDED(factory6->GetSystemFontCollection(false, DWRITE_FONT_FAMILY_MODEL_TYPOGRAPHIC, &fontCollection))) { + QSet registeredFamilies; for (uint i = 0; i < fontCollection->GetFontFamilyCount(); ++i) { DirectWriteScope fontFamily; if (SUCCEEDED(fontCollection->GetFontFamily(i, &fontFamily))) { - QString defaultLocaleName; - QString englishLocaleName; + auto registerFamily = [&](const std::pair &familyAndStyle) { + const QString registeredFamily = familyAndStyle.first; + if (registeredFamilies.contains(registeredFamily)) + return; + registeredFamilies.insert(registeredFamily); - DirectWriteScope names; - if (SUCCEEDED(fontFamily->GetFamilyNames(&names))) { - if (hasDefaultLocale) - defaultLocaleName = localeString(*names, defaultLocale); - - 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); + qCDebug(lcQpaFonts) << "Registering font family" << registeredFamily; + registerFontFamily(registeredFamily); + m_populatedFonts.insert(registeredFamily, *fontFamily); fontFamily->AddRef(); - if (defaultLocaleName == defaultFontName && defaultFontName != systemDefaultFontName) { - qCDebug(lcQpaFonts) << "Adding default font" << systemDefaultFontName << "as alternative to" << defaultLocaleName; + if (registeredFamily == defaultFontName + && defaultFontName != systemDefaultFontName) { + qCDebug(lcQpaFonts) << "Adding default font" << systemDefaultFontName + << "as alternative to" << registeredFamily; m_populatedFonts.insert(systemDefaultFontName, *fontFamily); fontFamily->AddRef(); } + }; + + QString defaultLocaleName; + QString englishLocaleName; + DirectWriteScope names; + if (SUCCEEDED(fontFamily->GetFamilyNames(&names))) { + if (hasDefaultLocale) + defaultLocaleName = localeString(*names, defaultLocale); + englishLocaleName = localeString(*names, englishLocale); } - if (!englishLocaleName.isEmpty() && englishLocaleName != defaultLocaleName) { - registerFontFamily(englishLocaleName); - m_populatedFonts.insert(englishLocaleName, *fontFamily); - fontFamily->AddRef(); + { + const auto key = std::make_pair(defaultLocaleName, QString{}); + if (!defaultLocaleName.isEmpty()) + registerFamily(key); + } - if (englishLocaleName == defaultFontName && defaultFontName != systemDefaultFontName) { - qCDebug(lcQpaFonts) << "Adding default font" << systemDefaultFontName << "as alternative to" << englishLocaleName; + { + const auto key = std::make_pair(englishLocaleName, QString{}); + if (!englishLocaleName.isEmpty()) + registerFamily(key); + } - m_populatedFonts.insert(systemDefaultFontName, *fontFamily); - fontFamily->AddRef(); + for (uint j = 0; j < fontFamily->GetFontCount(); ++j) { + DirectWriteScope font; + if (SUCCEEDED(fontFamily->GetFont(j, &font))) { + collectAdditionalNames(*font, + hasDefaultLocale ? defaultLocale : nullptr, + englishLocale, + registerFamily); } } } diff --git a/src/gui/text/windows/qwindowsdirectwritefontdatabase_p.h b/src/gui/text/windows/qwindowsdirectwritefontdatabase_p.h index 1ea01cbd88e..c8ebdef3561 100644 --- a/src/gui/text/windows/qwindowsdirectwritefontdatabase_p.h +++ b/src/gui/text/windows/qwindowsdirectwritefontdatabase_p.h @@ -69,6 +69,12 @@ private: friend class QWindowsFontEngineDirectWrite; static QString localeString(IDWriteLocalizedStrings *names, wchar_t localeName[]); + template + static void collectAdditionalNames(T *fontFace, + wchar_t *defaultLocale, + wchar_t *englishLocale, + std::function &)> registerFamily); + QSupportedWritingSystems supportedWritingSystems(IDWriteFontFace *face) const; QHash m_populatedFonts;