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 <volker.hilsheimer@qt.io>
(cherry picked from commit 6854ea63365ae419a04a470eb17e5f7662f00931)
Reviewed-by: Qt Cherry-pick Bot <cherrypick_bot@qt-project.org>
This commit is contained in:
Eskil Abrahamsen Blomfeldt 2025-04-09 09:38:57 +02:00 committed by Qt Cherry-pick Bot
parent 938d5fd6d2
commit 3915684fb8
2 changed files with 171 additions and 218 deletions

View File

@ -357,6 +357,79 @@ QStringList QWindowsDirectWriteFontDatabase::fallbacksForFamily(const QString &f
return result;
}
template<typename T>
void QWindowsDirectWriteFontDatabase::collectAdditionalNames(T *font,
wchar_t *defaultLocale,
wchar_t *englishLocale,
std::function<void(const std::pair<QString, QString> &)> 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<std::pair<QString, QString> > registeredFonts;
QSet<QString> ret;
for (int i = 0; i < faces.size(); ++i) {
IDWriteFontFace *face = faces.at(i);
@ -393,6 +467,46 @@ QStringList QWindowsDirectWriteFontDatabase::addApplicationFont(const QByteArray
DirectWriteScope<IDWriteFontFace3> face3;
if (SUCCEEDED(face->QueryInterface(__uuidof(IDWriteFontFace3),
reinterpret_cast<void **>(&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<QString, QString> &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);
{
const auto key = std::make_pair(englishLocaleFamilyName, englishLocaleStyleName);
if (!englishLocaleFamilyName.isEmpty())
registerFamilyAndStyle(key);
}
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(defaultLocaleFamilyName, defaultLocaleStyleName);
if (!defaultLocaleFamilyName.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));
}
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<QString> registeredFamilies;
for (uint i = 0; i < fontCollection->GetFontFamilyCount(); ++i) {
DirectWriteScope<IDWriteFontFamily2> fontFamily;
if (SUCCEEDED(fontCollection->GetFontFamily(i, &fontFamily))) {
auto registerFamily = [&](const std::pair<QString, QString> &familyAndStyle) {
const QString registeredFamily = familyAndStyle.first;
if (registeredFamilies.contains(registeredFamily))
return;
registeredFamilies.insert(registeredFamily);
qCDebug(lcQpaFonts) << "Registering font family" << registeredFamily;
registerFontFamily(registeredFamily);
m_populatedFonts.insert(registeredFamily, *fontFamily);
fontFamily->AddRef();
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<IDWriteLocalizedStrings> 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);
fontFamily->AddRef();
if (defaultLocaleName == defaultFontName && defaultFontName != systemDefaultFontName) {
qCDebug(lcQpaFonts) << "Adding default font" << systemDefaultFontName << "as alternative to" << defaultLocaleName;
m_populatedFonts.insert(systemDefaultFontName, *fontFamily);
fontFamily->AddRef();
}
{
const auto key = std::make_pair(defaultLocaleName, QString{});
if (!defaultLocaleName.isEmpty())
registerFamily(key);
}
if (!englishLocaleName.isEmpty() && englishLocaleName != defaultLocaleName) {
registerFontFamily(englishLocaleName);
m_populatedFonts.insert(englishLocaleName, *fontFamily);
fontFamily->AddRef();
{
const auto key = std::make_pair(englishLocaleName, QString{});
if (!englishLocaleName.isEmpty())
registerFamily(key);
}
if (englishLocaleName == defaultFontName && defaultFontName != systemDefaultFontName) {
qCDebug(lcQpaFonts) << "Adding default font" << systemDefaultFontName << "as alternative to" << englishLocaleName;
m_populatedFonts.insert(systemDefaultFontName, *fontFamily);
fontFamily->AddRef();
for (uint j = 0; j < fontFamily->GetFontCount(); ++j) {
DirectWriteScope<IDWriteFont3> font;
if (SUCCEEDED(fontFamily->GetFont(j, &font))) {
collectAdditionalNames(*font,
hasDefaultLocale ? defaultLocale : nullptr,
englishLocale,
registerFamily);
}
}
}

View File

@ -69,6 +69,12 @@ private:
friend class QWindowsFontEngineDirectWrite;
static QString localeString(IDWriteLocalizedStrings *names, wchar_t localeName[]);
template<typename T>
static void collectAdditionalNames(T *fontFace,
wchar_t *defaultLocale,
wchar_t *englishLocale,
std::function<void(const std::pair<QString, QString> &)> registerFamily);
QSupportedWritingSystems supportedWritingSystems(IDWriteFontFace *face) const;
QHash<QString, IDWriteFontFamily *> m_populatedFonts;