Implement Custom Font Collections on pre-Windows 10 systems
Commit 68818db0ec
(build a
IDWriteFontCollection1 of our FR_PRIVATE fonts, 2022-01-11) used
dwrite_3.h, which has API available only starting from Windows 10.
For pre-Windows 10 versions, there is a different way to implement
this, as explained at
https://learn.microsoft.com/en-us/windows/win32/directwrite/custom-font-collections
This change implements that more complex way as a fallback, until
we bump the baseline. Allows to not fall back to gdi in Skia, like
with the original commit, just on older Windows versions.
Change-Id: Ieca13e4a04bc72ce877ab9b512c7821d5466cb70
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/172090
Tested-by: Jenkins
Reviewed-by: Mike Kaganski <mike.kaganski@collabora.com>
This commit is contained in:
parent
a05432f02b
commit
a1b18eba35
2 changed files with 213 additions and 44 deletions
|
@ -65,8 +65,7 @@ protected:
|
|||
virtual void createWindowSurfaceInternal(bool forceRaster = false) override;
|
||||
static sk_sp<SkTypeface> createDirectWriteTypeface(const WinFontInstance* pWinFont);
|
||||
static void initFontInfo();
|
||||
inline static sal::systools::COMReference<IDWriteFontSetBuilder> dwriteFontSetBuilder;
|
||||
inline static sal::systools::COMReference<IDWriteFontCollection1> dwritePrivateCollection;
|
||||
inline static sal::systools::COMReference<IDWriteFontCollection> dwritePrivateCollection;
|
||||
inline static sk_sp<SkFontMgr> dwriteFontMgr;
|
||||
inline static bool dwriteDone = false;
|
||||
static SkFont::Edging fontEdging;
|
||||
|
|
|
@ -32,6 +32,217 @@
|
|||
|
||||
#include <windows.h>
|
||||
|
||||
#include <type_traits>
|
||||
|
||||
namespace
|
||||
{
|
||||
sal::systools::COMReference<IDWriteFontCollection>
|
||||
getDWritePrivateFontCollection_w10(IDWriteFontFile* fontFile)
|
||||
{
|
||||
static sal::systools::COMReference<IDWriteFactory3> dwriteFactory3 = [] {
|
||||
IDWriteFactory* dwriteFactory = WinSalGraphics::getDWriteFactory();
|
||||
sal::systools::COMReference<IDWriteFactory3> factory3;
|
||||
dwriteFactory->QueryInterface(&factory3);
|
||||
return factory3;
|
||||
}();
|
||||
if (!dwriteFactory3)
|
||||
return {};
|
||||
|
||||
static sal::systools::COMReference<IDWriteFontSetBuilder> dwriteFontSetBuilder = [] {
|
||||
sal::systools::COMReference<IDWriteFontSetBuilder> builder;
|
||||
dwriteFactory3->CreateFontSetBuilder(&dwriteFontSetBuilder);
|
||||
return builder;
|
||||
}();
|
||||
if (!dwriteFontSetBuilder)
|
||||
return {};
|
||||
|
||||
BOOL isSupported;
|
||||
DWRITE_FONT_FILE_TYPE fileType;
|
||||
UINT32 numberOfFonts;
|
||||
sal::systools::ThrowIfFailed(
|
||||
fontFile->Analyze(&isSupported, &fileType, nullptr, &numberOfFonts), SAL_WHERE);
|
||||
if (!isSupported)
|
||||
return {};
|
||||
|
||||
// For each font within the font file, get a font face reference and add to the builder.
|
||||
for (UINT32 fontIndex = 0; fontIndex < numberOfFonts; ++fontIndex)
|
||||
{
|
||||
sal::systools::COMReference<IDWriteFontFaceReference> fontFaceReference;
|
||||
if (FAILED(dwriteFactory3->CreateFontFaceReference(
|
||||
fontFile, fontIndex, DWRITE_FONT_SIMULATIONS_NONE, &fontFaceReference)))
|
||||
continue;
|
||||
|
||||
// Leave it to DirectWrite to read properties directly out of the font files
|
||||
dwriteFontSetBuilder->AddFontFaceReference(fontFaceReference);
|
||||
}
|
||||
|
||||
sal::systools::COMReference<IDWriteFontSet> fontSet;
|
||||
sal::systools::ThrowIfFailed(dwriteFontSetBuilder->CreateFontSet(&fontSet), SAL_WHERE);
|
||||
|
||||
sal::systools::COMReference<IDWriteFontCollection1> fc1;
|
||||
sal::systools::ThrowIfFailed(dwriteFactory3->CreateFontCollectionFromFontSet(fontSet, &fc1),
|
||||
SAL_WHERE);
|
||||
return { fc1.get() };
|
||||
}
|
||||
|
||||
// The following is only needed until we bump baseline to Windows 10
|
||||
|
||||
template <class I> requires std::is_base_of_v<IUnknown, I> class IUnknown_Impl : public I
|
||||
{
|
||||
public:
|
||||
virtual ~IUnknown_Impl() {}
|
||||
|
||||
// IUnknown
|
||||
HRESULT STDMETHODCALLTYPE QueryInterface(REFIID iid, void** ppvObject) override
|
||||
{
|
||||
if (iid == IID_IUnknown || iid == __uuidof(I))
|
||||
{
|
||||
*ppvObject = this;
|
||||
AddRef();
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
*ppvObject = nullptr;
|
||||
return E_NOINTERFACE;
|
||||
}
|
||||
ULONG STDMETHODCALLTYPE AddRef() override { return ++m_nRef; }
|
||||
ULONG STDMETHODCALLTYPE Release() override
|
||||
{
|
||||
ULONG n = --m_nRef;
|
||||
if (n == 0)
|
||||
delete this;
|
||||
return n;
|
||||
};
|
||||
|
||||
private:
|
||||
std::atomic<ULONG> m_nRef = 0;
|
||||
};
|
||||
|
||||
// A simple loader class, which only stores the font files (to fulfill the requirement that
|
||||
// "each key is ... valid until the loader is unregistered using the factory"), and creates
|
||||
// instances of enumerator
|
||||
class FontCollectionLoader_w7 : public IUnknown_Impl<IDWriteFontCollectionLoader>
|
||||
{
|
||||
public:
|
||||
// IDWriteFontCollectionLoader
|
||||
HRESULT STDMETHODCALLTYPE CreateEnumeratorFromKey(
|
||||
IDWriteFactory* factory, void const* collectionKey, UINT32 collectionKeySize,
|
||||
/* OUT */ IDWriteFontFileEnumerator** fontFileEnumerator) override;
|
||||
|
||||
private:
|
||||
std::vector<sal::systools::COMReference<IDWriteFontFile>> m_fontFiles;
|
||||
};
|
||||
|
||||
// A singleton class, that (1) caches IDWriteFactory, to avoid destruction order problems,
|
||||
// (2) holds the FontCollectionLoader_w7 singleton, and (3) calls IDWriteFactory's
|
||||
// (Un)RegisterFontCollectionLoader, because these can't be called from destructor of
|
||||
// FontCollectionLoader_w7, because RegisterFontCollectionLoader calls AddRef.
|
||||
struct FontCollectionLoader_w7_singleton_t
|
||||
{
|
||||
sal::systools::COMReference<IDWriteFactory> factory;
|
||||
sal::systools::COMReference<FontCollectionLoader_w7> loader;
|
||||
FontCollectionLoader_w7_singleton_t()
|
||||
: factory(WinSalGraphics::getDWriteFactory())
|
||||
, loader(new FontCollectionLoader_w7)
|
||||
{
|
||||
factory->RegisterFontCollectionLoader(loader);
|
||||
}
|
||||
~FontCollectionLoader_w7_singleton_t() { factory->UnregisterFontCollectionLoader(loader); }
|
||||
};
|
||||
|
||||
// A simple enumerator class, which only operates on a single font file.
|
||||
class FontFileEnumerator_w7 : public IUnknown_Impl<IDWriteFontFileEnumerator>
|
||||
{
|
||||
public:
|
||||
FontFileEnumerator_w7(IDWriteFontFile* collectionKey)
|
||||
: m_fontFile(collectionKey)
|
||||
{
|
||||
assert(collectionKey);
|
||||
AddRef();
|
||||
}
|
||||
|
||||
// IDWriteFontFileEnumerator
|
||||
HRESULT STDMETHODCALLTYPE MoveNext(BOOL* hasCurrentFile) override;
|
||||
HRESULT STDMETHODCALLTYPE GetCurrentFontFile(IDWriteFontFile** fontFile) override;
|
||||
|
||||
private:
|
||||
sal::systools::COMReference<IDWriteFontFile> m_fontFile;
|
||||
size_t m_nextIndex = 0;
|
||||
};
|
||||
|
||||
HRESULT STDMETHODCALLTYPE FontCollectionLoader_w7::CreateEnumeratorFromKey(
|
||||
IDWriteFactory* /*factory*/, void const* collectionKey, UINT32 collectionKeySize,
|
||||
/* OUT */ IDWriteFontFileEnumerator** fontFileEnumerator)
|
||||
{
|
||||
if (!fontFileEnumerator)
|
||||
return E_INVALIDARG;
|
||||
*fontFileEnumerator = nullptr;
|
||||
if (!collectionKey || collectionKeySize != sizeof(IDWriteFontFile*))
|
||||
return E_INVALIDARG;
|
||||
|
||||
auto pFontFile = *static_cast<IDWriteFontFile* const*>(collectionKey);
|
||||
auto it = std::find_if(m_fontFiles.begin(), m_fontFiles.end(),
|
||||
[pFontFile](const auto& el) { return el.get() == pFontFile; });
|
||||
if (it == m_fontFiles.end())
|
||||
m_fontFiles.emplace_back(pFontFile); // cals AddRef
|
||||
|
||||
*fontFileEnumerator = new (std::nothrow) FontFileEnumerator_w7(pFontFile);
|
||||
return *fontFileEnumerator ? S_OK : E_OUTOFMEMORY;
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE FontFileEnumerator_w7::MoveNext(BOOL* hasCurrentFile)
|
||||
{
|
||||
if (!hasCurrentFile)
|
||||
return E_INVALIDARG;
|
||||
*hasCurrentFile = m_nextIndex == 0 ? TRUE : FALSE;
|
||||
++m_nextIndex;
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE FontFileEnumerator_w7::GetCurrentFontFile(IDWriteFontFile** fontFile)
|
||||
{
|
||||
if (!fontFile)
|
||||
return E_INVALIDARG;
|
||||
if (m_nextIndex == 1)
|
||||
{
|
||||
*fontFile = m_fontFile;
|
||||
m_fontFile->AddRef();
|
||||
return S_OK;
|
||||
}
|
||||
*fontFile = nullptr;
|
||||
return E_FAIL;
|
||||
}
|
||||
|
||||
sal::systools::COMReference<IDWriteFontCollection>
|
||||
getDWritePrivateFontCollection_w7(IDWriteFontFile* fontFile)
|
||||
{
|
||||
static FontCollectionLoader_w7_singleton_t singleton;
|
||||
sal::systools::COMReference<IDWriteFontCollection> collection;
|
||||
sal::systools::ThrowIfFailed(singleton.factory->CreateCustomFontCollection(
|
||||
singleton.loader, &fontFile, sizeof(fontFile), &collection),
|
||||
SAL_WHERE);
|
||||
return collection;
|
||||
}
|
||||
|
||||
// End of pre-Windows 10 compatibility code
|
||||
|
||||
sal::systools::COMReference<IDWriteFontCollection>
|
||||
getDWritePrivateFontCollection(IDWriteFontFace* fontFace)
|
||||
{
|
||||
UINT32 numberOfFiles;
|
||||
sal::systools::ThrowIfFailed(fontFace->GetFiles(&numberOfFiles, nullptr), SAL_WHERE);
|
||||
if (numberOfFiles != 1)
|
||||
return {};
|
||||
|
||||
sal::systools::COMReference<IDWriteFontFile> fontFile;
|
||||
sal::systools::ThrowIfFailed(fontFace->GetFiles(&numberOfFiles, &fontFile), SAL_WHERE);
|
||||
|
||||
if (auto collection = getDWritePrivateFontCollection_w10(fontFile))
|
||||
return collection;
|
||||
return getDWritePrivateFontCollection_w7(fontFile);
|
||||
}
|
||||
}
|
||||
|
||||
using namespace SkiaHelper;
|
||||
|
||||
WinSkiaSalGraphicsImpl::WinSkiaSalGraphicsImpl(WinSalGraphics& rGraphics,
|
||||
|
@ -155,47 +366,7 @@ WinSkiaSalGraphicsImpl::createDirectWriteTypeface(const WinFontInstance* pWinFon
|
|||
// collection. For such cases attempt to update a collection of
|
||||
// private fonts with this newly used font.
|
||||
|
||||
sal::systools::COMReference<IDWriteFactory3> dwriteFactory3;
|
||||
ThrowIfFailed(dwriteFactory->QueryInterface(&dwriteFactory3), SAL_WHERE);
|
||||
|
||||
if (!dwriteFontSetBuilder)
|
||||
ThrowIfFailed(dwriteFactory3->CreateFontSetBuilder(&dwriteFontSetBuilder),
|
||||
SAL_WHERE);
|
||||
|
||||
UINT32 numberOfFiles;
|
||||
ThrowIfFailed(fontFace->GetFiles(&numberOfFiles, nullptr), SAL_WHERE);
|
||||
if (numberOfFiles != 1)
|
||||
return nullptr;
|
||||
|
||||
sal::systools::COMReference<IDWriteFontFile> fontFile;
|
||||
ThrowIfFailed(fontFace->GetFiles(&numberOfFiles, &fontFile), SAL_WHERE);
|
||||
|
||||
BOOL isSupported;
|
||||
DWRITE_FONT_FILE_TYPE fileType;
|
||||
UINT32 numberOfFonts;
|
||||
ThrowIfFailed(fontFile->Analyze(&isSupported, &fileType, nullptr, &numberOfFonts),
|
||||
SAL_WHERE);
|
||||
if (!isSupported)
|
||||
return nullptr;
|
||||
|
||||
// For each font within the font file, get a font face reference and add to the builder.
|
||||
for (UINT32 fontIndex = 0; fontIndex < numberOfFonts; ++fontIndex)
|
||||
{
|
||||
sal::systools::COMReference<IDWriteFontFaceReference> fontFaceReference;
|
||||
if (FAILED(dwriteFactory3->CreateFontFaceReference(fontFile.get(), fontIndex,
|
||||
DWRITE_FONT_SIMULATIONS_NONE,
|
||||
&fontFaceReference)))
|
||||
continue;
|
||||
|
||||
// Leave it to DirectWrite to read properties directly out of the font files
|
||||
dwriteFontSetBuilder->AddFontFaceReference(fontFaceReference.get());
|
||||
}
|
||||
|
||||
sal::systools::COMReference<IDWriteFontSet> fontSet;
|
||||
ThrowIfFailed(dwriteFontSetBuilder->CreateFontSet(&fontSet), SAL_WHERE);
|
||||
ThrowIfFailed(dwriteFactory3->CreateFontCollectionFromFontSet(fontSet.get(),
|
||||
&dwritePrivateCollection),
|
||||
SAL_WHERE);
|
||||
dwritePrivateCollection = getDWritePrivateFontCollection(fontFace);
|
||||
ThrowIfFailed(dwritePrivateCollection->GetFontFromFontFace(fontFace, &font), SAL_WHERE);
|
||||
}
|
||||
}
|
||||
|
@ -323,7 +494,6 @@ void WinSkiaSalGraphicsImpl::initFontInfo()
|
|||
void WinSkiaSalGraphicsImpl::ClearDevFontCache()
|
||||
{
|
||||
dwriteFontMgr.reset();
|
||||
dwriteFontSetBuilder.clear();
|
||||
dwritePrivateCollection.clear();
|
||||
dwriteDone = false;
|
||||
initFontInfo(); // get font info again, just in case
|
||||
|
|
Loading…
Reference in a new issue