From 09805f355c7eb2ece70bcc58e731433980e31126 Mon Sep 17 00:00:00 2001 From: Eskil Abrahamsen Blomfeldt Date: Fri, 4 Apr 2025 12:55:14 +0200 Subject: [PATCH] directwrite: Fix embedding fonts in PDF For a reason still unclear to me, the PDF engine refuses to embed fonts if the file name is empty (so fonts that are loaded directly from byte arrays typically). It could be that this in itself is outdated. However, embedding fonts that are loaded from the file system has worked so far, but in Qt 6.8 we moved to the DirectWrite engine on Windows and this stopped working there. The reason is that our custom file loader did not support tracking the file name and thus it would always be empty. This is experienced as a regression and can only be worked around by manually selecting the GDI engine instead (with the limitations that implies). We fix this by implementing the loader as a IDWriteLocalFontFileLoader instead, which supports the API which we currently use to retrieve the paths of system-wide fonts. Pick-to: 6.8 6.9 Fixes: QTBUG-134695 Change-Id: I1411b0617fd1c113c7c28154968c234920f5289e Reviewed-by: Eirik Aavitsland --- .../qwindowsdirectwritefontdatabase.cpp | 2 +- .../text/windows/qwindowsfontdatabasebase.cpp | 70 ++++++++++++++++--- .../text/windows/qwindowsfontdatabasebase_p.h | 1 + 3 files changed, 62 insertions(+), 11 deletions(-) diff --git a/src/gui/text/windows/qwindowsdirectwritefontdatabase.cpp b/src/gui/text/windows/qwindowsdirectwritefontdatabase.cpp index 66990a563f6..25bb1863298 100644 --- a/src/gui/text/windows/qwindowsdirectwritefontdatabase.cpp +++ b/src/gui/text/windows/qwindowsdirectwritefontdatabase.cpp @@ -371,7 +371,7 @@ QStringList QWindowsDirectWriteFontDatabase::addApplicationFont(const QByteArray loadedData = file.readAll(); } - QList faces = createDirectWriteFaces(loadedData); + QList faces = createDirectWriteFaces(loadedData, fileName); if (faces.isEmpty()) { qCWarning(lcQpaFonts) << "Failed to create DirectWrite face from font data. Font may be unsupported."; return QStringList(); diff --git a/src/gui/text/windows/qwindowsfontdatabasebase.cpp b/src/gui/text/windows/qwindowsfontdatabasebase.cpp index 7dfd415ff92..990f20fa447 100644 --- a/src/gui/text/windows/qwindowsfontdatabasebase.cpp +++ b/src/gui/text/windows/qwindowsfontdatabasebase.cpp @@ -352,7 +352,7 @@ namespace { return E_NOTIMPL; } - class DirectWriteFontFileLoader: public IDWriteFontFileLoader + class DirectWriteFontFileLoader: public IDWriteLocalFontFileLoader { public: DirectWriteFontFileLoader() : m_referenceCount(0) {} @@ -360,12 +360,59 @@ namespace { { } - inline void addKey(const QByteArray &fontData) + inline void addKey(const QByteArray &fontData, const QString &filename) { if (!m_fontDatas.contains(fontData.data())) - m_fontDatas.insert(fontData.data(), fontData); + m_fontDatas.insert(fontData.data(), qMakePair(fontData, filename)); } + HRESULT STDMETHODCALLTYPE GetFilePathLengthFromKey(void const* fontFileReferenceKey, + UINT32 fontFileReferenceKeySize, + UINT32* filePathLength) override + { + Q_UNUSED(fontFileReferenceKeySize); + const void *key = *reinterpret_cast(fontFileReferenceKey); + auto it = m_fontDatas.constFind(key); + if (it == m_fontDatas.constEnd()) + return E_FAIL; + + *filePathLength = it.value().second.size(); + return 0; + } + + HRESULT STDMETHODCALLTYPE GetFilePathFromKey(void const* fontFileReferenceKey, + UINT32 fontFileReferenceKeySize, + WCHAR* filePath, + UINT32 filePathSize) override + { + Q_UNUSED(fontFileReferenceKeySize); + const void *key = *reinterpret_cast(fontFileReferenceKey); + const auto it = m_fontDatas.constFind(key); + if (it == m_fontDatas.constEnd()) + return E_FAIL; + + const QString &path = it.value().second; + if (filePathSize < path.size() + 1) + return E_FAIL; + + const qsizetype length = path.toWCharArray(filePath); + filePath[length] = '\0'; + + return 0; + } + + HRESULT STDMETHODCALLTYPE GetLastWriteTimeFromKey(void const* fontFileReferenceKey, + UINT32 fontFileReferenceKeySize, + FILETIME* lastWriteTime) override + { + Q_UNUSED(fontFileReferenceKey); + Q_UNUSED(fontFileReferenceKeySize); + Q_UNUSED(lastWriteTime); + // We never call this, so just fail + return E_FAIL; + } + + inline void removeKey(const void *key) { m_fontDatas.remove(key); @@ -386,13 +433,15 @@ namespace { private: ULONG m_referenceCount; - QHash m_fontDatas; + QHash > m_fontDatas; }; HRESULT STDMETHODCALLTYPE DirectWriteFontFileLoader::QueryInterface(const IID &iid, void **object) { - if (iid == IID_IUnknown || iid == __uuidof(IDWriteFontFileLoader)) { + if (iid == IID_IUnknown + || iid == __uuidof(IDWriteFontFileLoader) + || iid == __uuidof(IDWriteLocalFontFileLoader)) { *object = this; AddRef(); return S_OK; @@ -433,7 +482,7 @@ namespace { if (it == m_fontDatas.constEnd()) return E_FAIL; - QByteArray fontData = it.value(); + QByteArray fontData = it.value().first; DirectWriteFontFileStream *stream = new DirectWriteFontFileStream(fontData); stream->AddRef(); *fontFileStream = stream; @@ -469,10 +518,10 @@ public: m_directWriteFactory->Release(); } - void addKey(const QByteArray &fontData) + void addKey(const QByteArray &fontData, const QString &filename) { if (m_directWriteFontFileLoader != nullptr) - m_directWriteFontFileLoader->addKey(fontData); + m_directWriteFontFileLoader->addKey(fontData, filename); } void removeKey(const void *key) @@ -732,13 +781,14 @@ void QWindowsFontDatabaseBase::invalidate() #if QT_CONFIG(directwrite) && QT_CONFIG(direct2d) IDWriteFontFace *QWindowsFontDatabaseBase::createDirectWriteFace(const QByteArray &fontData) { - QList faces = createDirectWriteFaces(fontData, false); + QList faces = createDirectWriteFaces(fontData, QString{}, false); Q_ASSERT(faces.size() <= 1); return faces.isEmpty() ? nullptr : faces.first(); } QList QWindowsFontDatabaseBase::createDirectWriteFaces(const QByteArray &fontData, + const QString &filename, bool queryVariations) const { QList ret; @@ -751,7 +801,7 @@ QList QWindowsFontDatabaseBase::createDirectWriteFaces(const if (m_fontFileLoader == nullptr) m_fontFileLoader.reset(new QCustomFontFileLoader(fontEngineData->directWriteFactory)); - m_fontFileLoader->addKey(fontData); + m_fontFileLoader->addKey(fontData, filename); IDWriteFontFile *fontFile = nullptr; const void *key = fontData.data(); diff --git a/src/gui/text/windows/qwindowsfontdatabasebase_p.h b/src/gui/text/windows/qwindowsfontdatabasebase_p.h index beb9b52fe48..2c5f8f63c8b 100644 --- a/src/gui/text/windows/qwindowsfontdatabasebase_p.h +++ b/src/gui/text/windows/qwindowsfontdatabasebase_p.h @@ -99,6 +99,7 @@ protected: #if QT_CONFIG(directwrite) QList createDirectWriteFaces(const QByteArray &fontData, + const QString &filename, bool queryVariations = true) const; IDWriteFontFace *createDirectWriteFace(const QByteArray &fontData); #endif