Skip to content

Commit

Permalink
Implement missing features in DirectWrite font backend
Browse files Browse the repository at this point in the history
The DirectWrite font backend is an optional backend which
is planned to take over as the default on Windows. In order
to do this, though, a few gaps need to be filled in order
for it to pass all autotests.

The following things are covered by this:

1. Bitmap fonts are unsupported in DirectWrite. We enumerate
   these using GDI and fall back to the GDI font engine when
   loading them. As part of this, we introduce a new handle
   type for fonts on Windows which can represent both
   the DirectWrite and GDI engines.
2. "Legacy font names" where sub-family is embedded in the
   family name is now enumerated together with the
   typographic font name.
3. The DirectWrite font engine was not loading kerning pairs
   from the font, like the other engines (omission which was
   detected by the test)
4. Turning off antialiasing does not work with DirectWrite, so
   we fall back to GDI for this.
5. Loading supported writing systems from application fonts
   was not supported.

Task-number: QTBUG-119420
Change-Id: Icf6c351afb0d7487b2f4634199088d701a324aae
Reviewed-by: Tor Arne Vestbø <tor.arne.vestbo@qt.io>
  • Loading branch information
eskilblomfeldt committed Feb 2, 2024
1 parent 16bcdba commit 0916415
Show file tree
Hide file tree
Showing 8 changed files with 452 additions and 105 deletions.
2 changes: 1 addition & 1 deletion src/gui/configure.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -580,7 +580,7 @@ qt_config_compile_test(directwrite3
int main(int, char **)
{
IUnknown *factory = nullptr;
DWriteCreateFactory(DWRITE_FACTORY_TYPE_SHARED, __uuidof(IDWriteFactory3),
DWriteCreateFactory(DWRITE_FACTORY_TYPE_SHARED, __uuidof(IDWriteFactory6),
&factory);
return 0;
}
Expand Down
457 changes: 383 additions & 74 deletions src/gui/text/windows/qwindowsdirectwritefontdatabase.cpp

Large diffs are not rendered by default.

22 changes: 18 additions & 4 deletions src/gui/text/windows/qwindowsdirectwritefontdatabase_p.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,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:
Expand All @@ -39,21 +40,34 @@ class Q_GUI_EXPORT QWindowsDirectWriteFontDatabase : public QWindowsFontDatabase

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
Expand Down
41 changes: 21 additions & 20 deletions src/gui/text/windows/qwindowsfontdatabase.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -434,18 +434,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.data(), sizeof(wchar_t) * nameLength);
faceNamePtr[nameLength] = u'\0';
}
return faceNamePtr;
}

namespace {
struct StoreFontPayload {
StoreFontPayload(const QString &family,
Expand Down Expand Up @@ -554,19 +542,19 @@ static bool addFontToDatabase(QString familyName,

const bool wasPopulated = QPlatformFontDatabase::isFamilyPopulated(familyName);
QPlatformFontDatabase::registerFont(familyName, styleName, foundryName, weight,
style, stretch, antialias, scalable, size, fixed, writingSystems, createFontFile(faceName));
style, stretch, antialias, scalable, size, fixed, writingSystems, new QWindowsFontDatabase::FontHandle(faceName));


// add fonts windows can generate for us:
if (weight <= QFont::DemiBold && styleName.isEmpty())
QPlatformFontDatabase::registerFont(familyName, QString(), foundryName, QFont::Bold,
style, stretch, antialias, scalable, size, fixed, writingSystems, createFontFile(faceName));
style, stretch, antialias, scalable, size, fixed, writingSystems, new QWindowsFontDatabase::FontHandle(faceName));
if (style != QFont::StyleItalic && styleName.isEmpty())
QPlatformFontDatabase::registerFont(familyName, QString(), foundryName, weight,
QFont::StyleItalic, stretch, antialias, scalable, size, fixed, writingSystems, createFontFile(faceName));
QFont::StyleItalic, stretch, antialias, scalable, size, fixed, writingSystems, new QWindowsFontDatabase::FontHandle(faceName));
if (weight <= QFont::DemiBold && style != QFont::StyleItalic && styleName.isEmpty())
QPlatformFontDatabase::registerFont(familyName, QString(), foundryName, QFont::Bold,
QFont::StyleItalic, stretch, antialias, scalable, size, fixed, writingSystems, createFontFile(faceName));
QFont::StyleItalic, stretch, antialias, scalable, size, fixed, writingSystems, new QWindowsFontDatabase::FontHandle(faceName));

// We came here from populating a different font family, so we have
// to ensure the entire typographic family is populated before we
Expand All @@ -580,7 +568,7 @@ static bool addFontToDatabase(QString familyName,

if (!subFamilyName.isEmpty() && familyName != subFamilyName) {
QPlatformFontDatabase::registerFont(subFamilyName, subFamilyStyle, foundryName, weight,
style, stretch, antialias, scalable, size, fixed, writingSystems, createFontFile(faceName));
style, stretch, antialias, scalable, size, fixed, writingSystems, new QWindowsFontDatabase::FontHandle(faceName));
}

if (!englishName.isEmpty() && englishName != familyName)
Expand Down Expand Up @@ -752,7 +740,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());
Expand Down Expand Up @@ -1124,10 +1113,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
Expand Down
9 changes: 8 additions & 1 deletion src/gui/text/windows/qwindowsfontdatabase_p.h
Original file line number Diff line number Diff line change
Expand Up @@ -75,9 +75,16 @@ class Q_GUI_EXPORT QWindowsFontDatabase : public QWindowsFontDatabaseBase
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();

private:
IDWriteFontFace *fontFace = nullptr;
QString faceName;
};

private:
void addDefaultEUDCFont();

struct WinApplicationFont {
Expand Down
17 changes: 14 additions & 3 deletions src/gui/text/windows/qwindowsfontdatabasebase.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -565,16 +565,27 @@ void QWindowsFontDatabaseBase::createDirectWriteFactory(IDWriteFactory **factory
IUnknown *result = nullptr;

# if QT_CONFIG(directwrite3)
DWriteCreateFactory(DWRITE_FACTORY_TYPE_SHARED, __uuidof(IDWriteFactory5), &result);
qCDebug(lcQpaFonts) << "Trying to create IDWriteFactory6";
DWriteCreateFactory(DWRITE_FACTORY_TYPE_SHARED, __uuidof(IDWriteFactory6), &result);

if (result == nullptr)
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)
if (result == nullptr) {
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;
Expand Down
5 changes: 4 additions & 1 deletion src/gui/text/windows/qwindowsfontenginedirectwrite.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -365,6 +365,8 @@ void QWindowsFontEngineDirectWrite::collectMetrics()
quint16 advanceWidthMax = qFromBigEndian<quint16>(table.constData() + advanceWidthMaxLocation);
m_maxAdvanceWidth = DESIGN_TO_LOGICAL(advanceWidthMax);
}

loadKerningPairs(emSquareSize() / QFixed::fromReal(fontDef.pixelSize));
}

QFixed QWindowsFontEngineDirectWrite::underlinePosition() const
Expand Down Expand Up @@ -672,7 +674,8 @@ 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);
}

QFontEngine::Properties QWindowsFontEngineDirectWrite::properties() const
Expand Down
4 changes: 3 additions & 1 deletion tests/auto/gui/text/qfontdatabase/tst_qfontdatabase.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -423,8 +423,10 @@ void tst_QFontDatabase::condensedFontMatching()
QFont f;
f.setStyleStrategy(QFont::NoFontMerging);
QFontPrivate *font_d = QFontPrivate::get(f);
if (font_d->engineForScript(QChar::Script_Common)->type() != QFontEngine::Freetype)
if (font_d->engineForScript(QChar::Script_Common)->type() != QFontEngine::Freetype
&& font_d->engineForScript(QChar::Script_Common)->type() != QFontEngine::DirectWrite) {
QEXPECT_FAIL("","No matching of sub-family by stretch on Windows", Continue);
}
#endif

QCOMPARE(QFontMetrics(tfcByStretch).horizontalAdvance(testString()),
Expand Down

0 comments on commit 0916415

Please sign in to comment.