Support variable applications fonts with DirectWrite

For GDI, there doesn't seem to be any way to do this, so it depends
on selecting the DirectWrite font database explicitly.

This moves the supportsVariableApplicationFonts() check into the
QPlatformFontDatabase instead of the font engine, since that's
where it belongs.

[ChangeLog][Fonts] Added support for selecting named instances in
variable application fonts when using the DirectWrite backend.

Task-number: QTBUG-108624
Change-Id: I51e0fedd7a9616088a06453a1d17f48bd18fa5a7
Reviewed-by: Allan Sandfeld Jensen <allan.jensen@qt.io>
This commit is contained in:
Eskil Abrahamsen Blomfeldt 2023-08-18 09:00:21 +02:00
parent 0138d910b2
commit 500be123f4
16 changed files with 208 additions and 115 deletions

View File

@ -2241,15 +2241,6 @@ Qt::HANDLE QFontEngineFT::handle() const
return non_locked_face(); return non_locked_face();
} }
bool QFontEngineFT::supportsVariableApplicationFonts() const
{
#if (FREETYPE_MAJOR*10000 + FREETYPE_MINOR*100 + FREETYPE_PATCH) >= 20900
return true;
#else
return false;
#endif
}
QT_END_NAMESPACE QT_END_NAMESPACE
#endif // QT_NO_FREETYPE #endif // QT_NO_FREETYPE

View File

@ -159,8 +159,6 @@ private:
return supportsHorizontalSubPixelPositions(); return supportsHorizontalSubPixelPositions();
} }
bool supportsVariableApplicationFonts() const override;
bool getSfntTableData(uint tag, uchar *buffer, uint *length) const override; bool getSfntTableData(uint tag, uchar *buffer, uint *length) const override;
int synthesized() const override; int synthesized() const override;

View File

@ -335,4 +335,13 @@ QStringList QFreeTypeFontDatabase::addTTFile(const QByteArray &fontData, const Q
return families; return families;
} }
bool QFreeTypeFontDatabase::supportsVariableApplicationFonts() const
{
#if (FREETYPE_MAJOR*10000 + FREETYPE_MINOR*100 + FREETYPE_PATCH) >= 20900
return true;
#else
return false;
#endif
}
QT_END_NAMESPACE QT_END_NAMESPACE

View File

@ -42,6 +42,7 @@ public:
QFontEngine *fontEngine(const QByteArray &fontData, qreal pixelSize, QFont::HintingPreference hintingPreference) override; QFontEngine *fontEngine(const QByteArray &fontData, qreal pixelSize, QFont::HintingPreference hintingPreference) override;
QStringList addApplicationFont(const QByteArray &fontData, const QString &fileName, QFontDatabasePrivate::ApplicationFont *applicationFont = nullptr) override; QStringList addApplicationFont(const QByteArray &fontData, const QString &fileName, QFontDatabasePrivate::ApplicationFont *applicationFont = nullptr) override;
void releaseHandle(void *handle) override; void releaseHandle(void *handle) override;
bool supportsVariableApplicationFonts() const override;
static void addNamedInstancesForFace(void *face, int faceIndex, static void addNamedInstancesForFace(void *face, int faceIndex,
const QString &family, const QString &styleName, const QString &family, const QString &styleName,

View File

@ -247,11 +247,6 @@ bool QFontEngine::supportsTransformation(const QTransform &transform) const
return transform.type() < QTransform::TxProject; return transform.type() < QTransform::TxProject;
} }
bool QFontEngine::supportsVariableApplicationFonts() const
{
return false;
}
bool QFontEngine::expectsGammaCorrectedBlending() const bool QFontEngine::expectsGammaCorrectedBlending() const
{ {
return true; return true;

View File

@ -213,7 +213,6 @@ public:
inline bool canRender(uint ucs4) const { return glyphIndex(ucs4) != 0; } inline bool canRender(uint ucs4) const { return glyphIndex(ucs4) != 0; }
virtual bool canRender(const QChar *str, int len) const; virtual bool canRender(const QChar *str, int len) const;
virtual bool supportsVariableApplicationFonts() const;
virtual bool supportsTransformation(const QTransform &transform) const; virtual bool supportsTransformation(const QTransform &transform) const;

View File

@ -645,6 +645,18 @@ bool QPlatformFontDatabase::isFamilyPopulated(const QString &familyName)
return qt_isFontFamilyPopulated(familyName); return qt_isFontFamilyPopulated(familyName);
} }
/*!
Returns true if this font database supports loading named instances from variable application
fonts.
\since 6.7
*/
bool QPlatformFontDatabase::supportsVariableApplicationFonts() const
{
return false;
}
/*! /*!
\class QPlatformFontDatabase \class QPlatformFontDatabase
\since 5.0 \since 5.0

View File

@ -89,6 +89,8 @@ public:
virtual bool fontsAlwaysScalable() const; virtual bool fontsAlwaysScalable() const;
virtual QList<int> standardSizes() const; virtual QList<int> standardSizes() const;
virtual bool supportsVariableApplicationFonts() const;
// helper // helper
static QSupportedWritingSystems writingSystemsFromTrueTypeBits(quint32 unicodeRange[4], quint32 codePageRange[2]); static QSupportedWritingSystems writingSystemsFromTrueTypeBits(quint32 unicodeRange[4], quint32 codePageRange[2]);
static QSupportedWritingSystems writingSystemsFromOS2Table(const char *os2Table, size_t length); static QSupportedWritingSystems writingSystemsFromOS2Table(const char *os2Table, size_t length);

View File

@ -1042,4 +1042,13 @@ void QFontconfigDatabase::setupFontEngine(QFontEngineFT *engine, const QFontDef
engine->glyphFormat = format; engine->glyphFormat = format;
} }
bool QFontconfigDatabase::supportsVariableApplicationFonts() const
{
#if (FREETYPE_MAJOR*10000 + FREETYPE_MINOR*100 + FREETYPE_PATCH) >= 20900
return true;
#else
return false;
#endif
}
QT_END_NAMESPACE QT_END_NAMESPACE

View File

@ -28,6 +28,7 @@ public:
~QFontconfigDatabase() override; ~QFontconfigDatabase() override;
void populateFontDatabase() override; void populateFontDatabase() override;
void invalidate() override; void invalidate() override;
bool supportsVariableApplicationFonts() const override;
QFontEngineMulti *fontEngineMulti(QFontEngine *fontEngine, QChar::Script script) override; QFontEngineMulti *fontEngineMulti(QFontEngine *fontEngine, QChar::Script script) override;
QFontEngine *fontEngine(const QFontDef &fontDef, void *handle) override; QFontEngine *fontEngine(const QFontDef &fontDef, void *handle) override;
QFontEngine *fontEngine(const QByteArray &fontData, qreal pixelSize, QFont::HintingPreference hintingPreference) override; QFontEngine *fontEngine(const QByteArray &fontData, qreal pixelSize, QFont::HintingPreference hintingPreference) override;

View File

@ -249,99 +249,102 @@ QStringList QWindowsDirectWriteFontDatabase::addApplicationFont(const QByteArray
loadedData = file.readAll(); loadedData = file.readAll();
} }
IDWriteFontFace *face = createDirectWriteFace(loadedData); QList<IDWriteFontFace *> faces = createDirectWriteFaces(loadedData);
if (face == nullptr) { if (faces.isEmpty()) {
qCWarning(lcQpaFonts) << "Failed to create DirectWrite face from font data. Font may be unsupported."; qCWarning(lcQpaFonts) << "Failed to create DirectWrite face from font data. Font may be unsupported.";
return QStringList(); return QStringList();
} }
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 QString foundryName; // No such concept.
const bool scalable = true;
const bool antialias = false;
const int size = SMOOTH_SCALABLE;
QSupportedWritingSystems writingSystems;
writingSystems.setSupported(QFontDatabase::Any);
writingSystems.setSupported(QFontDatabase::Latin);
QStringList ret; QStringList ret;
IDWriteFontFace3 *face3 = nullptr; for (int i = 0; i < faces.size(); ++i) {
if (SUCCEEDED(face->QueryInterface(__uuidof(IDWriteFontFace3), IDWriteFontFace *face = faces.at(i);
reinterpret_cast<void **>(&face3)))) { wchar_t defaultLocale[LOCALE_NAME_MAX_LENGTH];
QString defaultLocaleFamilyName; bool hasDefaultLocale = GetUserDefaultLocaleName(defaultLocale, LOCALE_NAME_MAX_LENGTH) != 0;
QString englishLocaleFamilyName; wchar_t englishLocale[] = L"en-us";
IDWriteLocalizedStrings *names; static const int SMOOTH_SCALABLE = 0xffff;
if (SUCCEEDED(face3->GetFamilyNames(&names))) { const QString foundryName; // No such concept.
defaultLocaleFamilyName = hasDefaultLocale ? localeString(names, defaultLocale) : QString(); const bool scalable = true;
englishLocaleFamilyName = localeString(names, englishLocale); const bool antialias = false;
const int size = SMOOTH_SCALABLE;
names->Release(); QSupportedWritingSystems writingSystems;
} writingSystems.setSupported(QFontDatabase::Any);
writingSystems.setSupported(QFontDatabase::Latin);
QString defaultLocaleStyleName; IDWriteFontFace3 *face3 = nullptr;
QString englishLocaleStyleName; if (SUCCEEDED(face->QueryInterface(__uuidof(IDWriteFontFace3),
if (SUCCEEDED(face3->GetFaceNames(&names))) { reinterpret_cast<void **>(&face3)))) {
defaultLocaleStyleName = hasDefaultLocale ? localeString(names, defaultLocale) : QString(); QString defaultLocaleFamilyName;
englishLocaleStyleName = localeString(names, englishLocale); QString englishLocaleFamilyName;
names->Release(); IDWriteLocalizedStrings *names;
} if (SUCCEEDED(face3->GetFamilyNames(&names))) {
defaultLocaleFamilyName = hasDefaultLocale ? localeString(names, defaultLocale) : QString();
englishLocaleFamilyName = localeString(names, englishLocale);
QFont::Stretch stretch = fromDirectWriteStretch(face3->GetStretch()); names->Release();
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);
} }
ret.append(englishLocaleFamilyName); QString defaultLocaleStyleName;
QPlatformFontDatabase::registerFont(englishLocaleFamilyName, englishLocaleStyleName, QString(), weight, style, stretch, antialias, scalable, size, fixed, writingSystems, face); QString englishLocaleStyleName;
face->AddRef(); if (SUCCEEDED(face3->GetFaceNames(&names))) {
} defaultLocaleStyleName = hasDefaultLocale ? localeString(names, defaultLocale) : QString();
englishLocaleStyleName = localeString(names, englishLocale);
if (!defaultLocaleFamilyName.isEmpty() && defaultLocaleFamilyName != englishLocaleFamilyName) { names->Release();
if (applicationFont != nullptr) {
QFontDatabasePrivate::ApplicationFont::Properties properties;
properties.style = style;
properties.weight = weight;
properties.familyName = englishLocaleFamilyName;
properties.styleName = englishLocaleStyleName;
applicationFont->properties.append(properties);
} }
ret.append(defaultLocaleFamilyName); QFont::Stretch stretch = fromDirectWriteStretch(face3->GetStretch());
QPlatformFontDatabase::registerFont(defaultLocaleFamilyName, defaultLocaleStyleName, QString(), weight, style, stretch, antialias, scalable, size, fixed, writingSystems, face); QFont::Style style = fromDirectWriteStyle(face3->GetStyle());
face->AddRef(); 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);
}
ret.append(englishLocaleFamilyName);
QPlatformFontDatabase::registerFont(englishLocaleFamilyName, englishLocaleStyleName, QString(), weight, style, stretch, antialias, scalable, size, fixed, writingSystems, face);
face->AddRef();
}
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);
}
ret.append(defaultLocaleFamilyName);
QPlatformFontDatabase::registerFont(defaultLocaleFamilyName, defaultLocaleStyleName, QString(), weight, style, stretch, antialias, scalable, size, fixed, writingSystems, face);
face->AddRef();
}
face3->Release();
} else {
qCWarning(lcQpaFonts) << "Unable to query IDWriteFontFace3 interface from font face.";
} }
face3->Release(); face->Release();
} else {
qCWarning(lcQpaFonts) << "Unable to query IDWriteFontFace3 interface from font face.";
} }
face->Release();
return ret; return ret;
} }
@ -425,4 +428,17 @@ QFont QWindowsDirectWriteFontDatabase::defaultFont() const
return QFont(QStringLiteral("Segoe UI")); return QFont(QStringLiteral("Segoe UI"));
} }
bool QWindowsDirectWriteFontDatabase::supportsVariableApplicationFonts() const
{
QSharedPointer<QWindowsFontEngineData> fontEngineData = data();
IDWriteFactory5 *factory5 = nullptr;
if (SUCCEEDED(fontEngineData->directWriteFactory->QueryInterface(__uuidof(IDWriteFactory5),
reinterpret_cast<void **>(&factory5)))) {
factory5->Release();
return true;
}
return false;
}
QT_END_NAMESPACE QT_END_NAMESPACE

View File

@ -47,6 +47,7 @@ public:
bool fontsAlwaysScalable() const override; bool fontsAlwaysScalable() const override;
bool isPrivateFontFamily(const QString &family) const override; bool isPrivateFontFamily(const QString &family) const override;
bool supportsVariableApplicationFonts() const override;
private: private:
friend class QWindowsFontEngineDirectWrite; friend class QWindowsFontEngineDirectWrite;

View File

@ -550,8 +550,12 @@ void QWindowsFontDatabaseBase::createDirectWriteFactory(IDWriteFactory **factory
IUnknown *result = nullptr; IUnknown *result = nullptr;
# if QT_CONFIG(directwrite3) # if QT_CONFIG(directwrite3)
DWriteCreateFactory(DWRITE_FACTORY_TYPE_SHARED, __uuidof(IDWriteFactory3), &result); DWriteCreateFactory(DWRITE_FACTORY_TYPE_SHARED, __uuidof(IDWriteFactory5), &result);
if (result == nullptr)
DWriteCreateFactory(DWRITE_FACTORY_TYPE_SHARED, __uuidof(IDWriteFactory3), &result);
# endif # endif
if (result == nullptr) if (result == nullptr)
DWriteCreateFactory(DWRITE_FACTORY_TYPE_SHARED, __uuidof(IDWriteFactory2), &result); DWriteCreateFactory(DWRITE_FACTORY_TYPE_SHARED, __uuidof(IDWriteFactory2), &result);
@ -692,12 +696,22 @@ QFont QWindowsFontDatabaseBase::systemDefaultFont()
} }
#if QT_CONFIG(directwrite) && QT_CONFIG(direct2d) #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(); QSharedPointer<QWindowsFontEngineData> fontEngineData = data();
if (fontEngineData->directWriteFactory == nullptr) { if (fontEngineData->directWriteFactory == nullptr) {
qCWarning(lcQpaFonts) << "DirectWrite factory not created in QWindowsFontDatabaseBase::createDirectWriteFace()"; qCWarning(lcQpaFonts) << "DirectWrite factory not created in QWindowsFontDatabaseBase::createDirectWriteFace()";
return nullptr; return ret;
} }
CustomFontFileLoader fontFileLoader(fontEngineData->directWriteFactory); CustomFontFileLoader fontFileLoader(fontEngineData->directWriteFactory);
@ -712,7 +726,7 @@ IDWriteFontFace *QWindowsFontDatabaseBase::createDirectWriteFace(const QByteArra
&fontFile); &fontFile);
if (FAILED(hres)) { if (FAILED(hres)) {
qErrnoWarning(hres, "%s: CreateCustomFontFileReference failed", __FUNCTION__); qErrnoWarning(hres, "%s: CreateCustomFontFileReference failed", __FUNCTION__);
return nullptr; return ret;
} }
BOOL isSupportedFontType; BOOL isSupportedFontType;
@ -722,25 +736,64 @@ IDWriteFontFace *QWindowsFontDatabaseBase::createDirectWriteFace(const QByteArra
fontFile->Analyze(&isSupportedFontType, &fontFileType, &fontFaceType, &numberOfFaces); fontFile->Analyze(&isSupportedFontType, &fontFileType, &fontFaceType, &numberOfFaces);
if (!isSupportedFontType) { if (!isSupportedFontType) {
fontFile->Release(); 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. // ### Currently no support for .ttc, but we could easily return a list here.
IDWriteFontFace *directWriteFontFace = nullptr; if (ret.isEmpty()) {
hres = fontEngineData->directWriteFactory->CreateFontFace(fontFaceType, IDWriteFontFace *directWriteFontFace = nullptr;
1, hres = fontEngineData->directWriteFactory->CreateFontFace(fontFaceType,
&fontFile, 1,
0, &fontFile,
DWRITE_FONT_SIMULATIONS_NONE, 0,
&directWriteFontFace); DWRITE_FONT_SIMULATIONS_NONE,
if (FAILED(hres)) { &directWriteFontFace);
qErrnoWarning(hres, "%s: CreateFontFace failed", __FUNCTION__); if (FAILED(hres)) {
fontFile->Release(); qErrnoWarning(hres, "%s: CreateFontFace failed", __FUNCTION__);
return nullptr; fontFile->Release();
return ret;
} else {
ret.append(directWriteFontFace);
}
} }
fontFile->Release(); fontFile->Release();
return directWriteFontFace; return ret;
} }
#endif // directwrite && direct2d #endif // directwrite && direct2d
@ -760,7 +813,10 @@ QFontEngine *QWindowsFontDatabaseBase::fontEngine(const QByteArray &fontData, qr
if (fontEngineData->directWriteFactory == nullptr) if (fontEngineData->directWriteFactory == nullptr)
return nullptr; return nullptr;
IDWriteFontFace *directWriteFontFace = createDirectWriteFace(fontData); IDWriteFontFace * directWriteFontFace = createDirectWriteFace(fontData);
if (directWriteFontFace == nullptr)
return nullptr;
fontEngine = new QWindowsFontEngineDirectWrite(directWriteFontFace, fontEngine = new QWindowsFontEngineDirectWrite(directWriteFontFace,
pixelSize, pixelSize,
fontEngineData); fontEngineData);

View File

@ -91,7 +91,9 @@ public:
protected: protected:
#if QT_CONFIG(directwrite) #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 #endif
private: private:

View File

@ -12,6 +12,9 @@
#include <private/qfont_p.h> #include <private/qfont_p.h>
#include <private/qfontengine_p.h> #include <private/qfontengine_p.h>
#include <qpa/qplatformfontdatabase.h> #include <qpa/qplatformfontdatabase.h>
#include <qpa/qplatformintegration.h>
#include <QtGui/private/qguiapplication_p.h>
using namespace Qt::StringLiterals; using namespace Qt::StringLiterals;
@ -513,11 +516,9 @@ void tst_QFontDatabase::findCourier()
void tst_QFontDatabase::variableFont() void tst_QFontDatabase::variableFont()
{ {
{ {
QFont f; QPlatformFontDatabase *pfdb = QGuiApplicationPrivate::platformIntegration()->fontDatabase();
f.setStyleStrategy(QFont::NoFontMerging); if (!pfdb->supportsVariableApplicationFonts())
QFontPrivate *font_d = QFontPrivate::get(f); QSKIP("Variable application fonts not supported on this platform");
if (!font_d->engineForScript(QChar::Script_Common)->supportsVariableApplicationFonts())
QSKIP("Variable application fonts only supported on Freetype currently");
} }
int id = QFontDatabase::addApplicationFont(m_testFontVariable); int id = QFontDatabase::addApplicationFont(m_testFontVariable);