vcl: Read font data with DirectWrite on Windows

Towards migrating off GDI font access.

This re-applies the change reverted in
bab072a4a1, after aligning the code with
hb_directwrite_face_create() to fix TTC handling and emmry mangement
issues.

Change-Id: I5954838bb41ab34cb89bab86e4dbd2ae1916c99f
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/208257
Reviewed-by: Khaled Hosny <khaled@libreoffice.org>
Tested-by: Jenkins
This commit is contained in:
Khaled Hosny
2026-07-21 09:15:24 +02:00
committed by Khaled Hosny
parent 49044478b3
commit 36d92f3a35
2 changed files with 108 additions and 67 deletions
+3
View File
@@ -70,7 +70,9 @@ public:
const LOGFONTW& GetLogFont() const { return maLogFont; }
IDWriteFont* GetDWFont() const { return mxDWFont.get(); }
IDWriteFontFace* GetDWFontFace() const;
hb_face_t* GetHbFace() const override;
hb_blob_t* GetHbTable(hb_tag_t nTag) const override;
const std::vector<vcl::font::Variation>& GetVariations(const LogicalFontInstance&) const override;
@@ -80,6 +82,7 @@ private:
LOGFONTW maLogFont;
sal::systools::COMReference<IDWriteFont> mxDWFont;
mutable sal::systools::COMReference<IDWriteFontFace> mxDWFontFace;
};
/**
+105 -67
View File
@@ -34,7 +34,6 @@
#include <d2d1.h>
#include <dwrite_3.h>
#include <o3tl/lru_map.hxx>
#include <basegfx/matrix/b2dhommatrixtools.hxx>
#include <basegfx/polygon/b2dpolygon.hxx>
#include <i18nlangtag/mslangid.hxx>
@@ -525,6 +524,20 @@ WinFontFace::~WinFontFace()
{
}
IDWriteFontFace* WinFontFace::GetDWFontFace() const
{
if (!mxDWFontFace && mxDWFont)
{
HRESULT hr = mxDWFont->CreateFontFace(&mxDWFontFace);
if (FAILED(hr))
{
SAL_WARN("vcl.fonts", "HRESULT 0x" << OUString::number(hr, 16) << ": "
<< comphelper::WindowsErrorStringFromHRESULT(hr));
}
}
return mxDWFontFace.get();
}
sal_IntPtr WinFontFace::GetFontId() const
{
return mnId;
@@ -562,83 +575,107 @@ WinFontFace::GetVariations(const LogicalFontInstance& rFont) const
namespace
{
struct BlobReference
// Keeps the DirectWrite font table alive until HarfBuzz destroys the blob.
struct DWFontTableContext
{
hb_blob_t* mpBlob;
BlobReference(hb_blob_t* pBlob)
: mpBlob(pBlob)
{
hb_blob_reference(mpBlob);
}
BlobReference(BlobReference&& other) noexcept
: mpBlob(other.mpBlob)
{
other.mpBlob = nullptr;
}
BlobReference& operator=(BlobReference&& other)
{
std::swap(mpBlob, other.mpBlob);
return *this;
}
BlobReference(const BlobReference& other) = delete;
BlobReference& operator=(BlobReference& other) = delete;
~BlobReference() { hb_blob_destroy(mpBlob); }
IDWriteFontFace* mpFontFace;
void* mpTableContext;
};
}
using BlobCacheKey = std::pair<sal_IntPtr, hb_tag_t>;
static void releaseTableData(void* pData)
{
auto* pContext = static_cast<DWFontTableContext*>(pData);
pContext->mpFontFace->ReleaseFontTable(pContext->mpTableContext);
delete pContext;
}
namespace
static hb_blob_t* getFontFile(IDWriteFontFace* pFontFace)
{
struct BlobCacheKeyHash
{
std::size_t operator()(BlobCacheKey const& rKey) const
UINT32 nFileCount = 0;
if (FAILED(pFontFace->GetFiles(&nFileCount, nullptr)))
return nullptr;
std::vector<IDWriteFontFile*> aFiles(nFileCount, nullptr);
if (FAILED(pFontFace->GetFiles(&nFileCount, aFiles.data())))
return nullptr;
hb_blob_t* pBlob = nullptr;
for (UINT32 i = 0; i < nFileCount; ++i)
{
std::size_t seed = 0;
o3tl::hash_combine(seed, rKey.first);
o3tl::hash_combine(seed, rKey.second);
return seed;
sal::systools::COMReference<IDWriteFontFile> xFile(aFiles[i], false);
if (pBlob)
continue;
const void* pKey = nullptr;
UINT32 nKeySize = 0;
sal::systools::COMReference<IDWriteFontFileLoader> xLoader;
sal::systools::COMReference<IDWriteFontFileStream> xStream;
UINT64 nSize = 0;
const void* pData = nullptr;
void* pContext = nullptr;
if (SUCCEEDED(xFile->GetReferenceKey(&pKey, &nKeySize))
&& SUCCEEDED(xFile->GetLoader(&xLoader))
&& SUCCEEDED(xLoader->CreateStreamFromKey(pKey, nKeySize, &xStream))
&& SUCCEEDED(xStream->GetFileSize(&nSize))
&& SUCCEEDED(xStream->ReadFileFragment(&pData, 0, nSize, &pContext)))
{
pBlob = hb_blob_create(static_cast<const char*>(pData),
static_cast<unsigned int>(nSize), HB_MEMORY_MODE_DUPLICATE,
nullptr, nullptr);
xStream->ReleaseFileFragment(pContext);
}
}
};
return pBlob;
}
static hb_blob_t* getFontTable(hb_face_t*, hb_tag_t nTag, void* pUserData)
{
auto pFontFace = static_cast<IDWriteFontFace*>(pUserData);
if (nTag == 0)
return getFontFile(pFontFace);
const void* pData = nullptr;
UINT32 nSize = 0;
void* pContext = nullptr;
BOOL bExists = FALSE;
if (FAILED(pFontFace->TryGetFontTable(OSL_NETDWORD(nTag), &pData, &nSize, &pContext, &bExists)))
return nullptr;
if (!pData || !bExists || !nSize)
{
pFontFace->ReleaseFontTable(pContext);
return nullptr;
}
return hb_blob_create(static_cast<const char*>(pData), nSize, HB_MEMORY_MODE_READONLY,
new DWFontTableContext{ pFontFace, pContext }, releaseTableData);
}
hb_face_t* WinFontFace::GetHbFace() const
{
if (!mpHbFace)
{
if (IDWriteFontFace* pFontFace = GetDWFontFace())
{
pFontFace->AddRef();
mpHbFace = hb_face_create_for_tables(getFontTable, pFontFace, [](void* pUserData) {
static_cast<IDWriteFontFace*>(pUserData)->Release();
});
hb_face_set_index(mpHbFace, pFontFace->GetIndex());
hb_face_set_glyph_count(mpHbFace, pFontFace->GetGlyphCount());
}
else
mpHbFace = hb_face_get_empty();
}
return mpHbFace;
}
hb_blob_t* WinFontFace::GetHbTable(hb_tag_t nTag) const
{
static o3tl::lru_map<BlobCacheKey, BlobReference, BlobCacheKeyHash> gCache(50);
BlobCacheKey aCacheKey{ GetFontId(), nTag };
auto it = gCache.find(aCacheKey);
if (it != gCache.end())
{
hb_blob_reference(it->second.mpBlob);
return it->second.mpBlob;
}
sal_uLong nLength = 0;
unsigned char* pBuffer = nullptr;
HDC hDC(::GetDC(nullptr));
HFONT hFont = ::CreateFontIndirectW(&maLogFont);
HFONT hOldFont = ::SelectFont(hDC, hFont);
nLength = ::GetFontData(hDC, OSL_NETDWORD(nTag), 0, nullptr, 0);
if (nLength > 0 && nLength != GDI_ERROR)
{
pBuffer = new unsigned char[nLength];
::GetFontData(hDC, OSL_NETDWORD(nTag), 0, pBuffer, nLength);
}
::SelectFont(hDC, hOldFont);
::DeleteFont(hFont);
::ReleaseDC(nullptr, hDC);
hb_blob_t* pBlob = nullptr;
if (pBuffer)
pBlob = hb_blob_create(reinterpret_cast<const char*>(pBuffer), nLength, HB_MEMORY_MODE_READONLY,
pBuffer, [](void* data) { delete[] static_cast<unsigned char*>(data); });
gCache.insert({ aCacheKey, BlobReference(pBlob) });
return pBlob;
return hb_face_reference_table(GetHbFace(), nTag);
}
void WinSalGraphics::SetTextColor( Color nColor )
@@ -1123,7 +1160,8 @@ const sal::systools::COMReference<IDWriteFontFace>& WinFontInstance::GetDWFontFa
HRESULT hr = S_OK;
if (eSimulations == DWRITE_FONT_SIMULATIONS_NONE)
hr = pDWFont->CreateFontFace(&mxDWFontFace);
mxDWFontFace
= sal::systools::COMReference<IDWriteFontFace>(GetFontFace()->GetDWFontFace());
else
{
auto xDWFont = sal::systools::COMReference<IDWriteFont>(pDWFont)