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 <eirik.aavitsland@qt.io>
This commit is contained in:
Eskil Abrahamsen Blomfeldt 2025-04-04 12:55:14 +02:00
parent 616d33c87a
commit 09805f355c
3 changed files with 62 additions and 11 deletions

View File

@ -371,7 +371,7 @@ QStringList QWindowsDirectWriteFontDatabase::addApplicationFont(const QByteArray
loadedData = file.readAll();
}
QList<IDWriteFontFace *> faces = createDirectWriteFaces(loadedData);
QList<IDWriteFontFace *> faces = createDirectWriteFaces(loadedData, fileName);
if (faces.isEmpty()) {
qCWarning(lcQpaFonts) << "Failed to create DirectWrite face from font data. Font may be unsupported.";
return QStringList();

View File

@ -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<void * const *>(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<void * const *>(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<const void *, QByteArray> m_fontDatas;
QHash<const void *, QPair<QByteArray, QString> > 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<IDWriteFontFace *> faces = createDirectWriteFaces(fontData, false);
QList<IDWriteFontFace *> faces = createDirectWriteFaces(fontData, QString{}, false);
Q_ASSERT(faces.size() <= 1);
return faces.isEmpty() ? nullptr : faces.first();
}
QList<IDWriteFontFace *> QWindowsFontDatabaseBase::createDirectWriteFaces(const QByteArray &fontData,
const QString &filename,
bool queryVariations) const
{
QList<IDWriteFontFace *> ret;
@ -751,7 +801,7 @@ QList<IDWriteFontFace *> 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();

View File

@ -99,6 +99,7 @@ protected:
#if QT_CONFIG(directwrite)
QList<IDWriteFontFace *> createDirectWriteFaces(const QByteArray &fontData,
const QString &filename,
bool queryVariations = true) const;
IDWriteFontFace *createDirectWriteFace(const QByteArray &fontData);
#endif