From 36d92f3a350ef7fe85e30148154cdf98ed0f4a22 Mon Sep 17 00:00:00 2001 From: Khaled Hosny Date: Sun, 19 Jul 2026 15:53:24 +0200 Subject: [PATCH] vcl: Read font data with DirectWrite on Windows Towards migrating off GDI font access. This re-applies the change reverted in bab072a4a10557d031c6010e6c696d5d32974a19, 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 Tested-by: Jenkins --- vcl/inc/win/salgdi.h | 3 + vcl/win/gdi/salfont.cxx | 172 ++++++++++++++++++++++++---------------- 2 files changed, 108 insertions(+), 67 deletions(-) diff --git a/vcl/inc/win/salgdi.h b/vcl/inc/win/salgdi.h index cdec955288fd..1899c195b16d 100644 --- a/vcl/inc/win/salgdi.h +++ b/vcl/inc/win/salgdi.h @@ -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& GetVariations(const LogicalFontInstance&) const override; @@ -80,6 +82,7 @@ private: LOGFONTW maLogFont; sal::systools::COMReference mxDWFont; + mutable sal::systools::COMReference mxDWFontFace; }; /** diff --git a/vcl/win/gdi/salfont.cxx b/vcl/win/gdi/salfont.cxx index 1ec7a2d2c36c..723de7b32e07 100644 --- a/vcl/win/gdi/salfont.cxx +++ b/vcl/win/gdi/salfont.cxx @@ -34,7 +34,6 @@ #include #include -#include #include #include #include @@ -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; +static void releaseTableData(void* pData) +{ + auto* pContext = static_cast(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 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 xFile(aFiles[i], false); + if (pBlob) + continue; + + const void* pKey = nullptr; + UINT32 nKeySize = 0; + sal::systools::COMReference xLoader; + sal::systools::COMReference 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(pData), + static_cast(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(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(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(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 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(pBuffer), nLength, HB_MEMORY_MODE_READONLY, - pBuffer, [](void* data) { delete[] static_cast(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& WinFontInstance::GetDWFontFa HRESULT hr = S_OK; if (eSimulations == DWRITE_FONT_SIMULATIONS_NONE) - hr = pDWFont->CreateFontFace(&mxDWFontFace); + mxDWFontFace + = sal::systools::COMReference(GetFontFace()->GetDWFontFace()); else { auto xDWFont = sal::systools::COMReference(pDWFont)