sd/qa/unit/PNGExportTests.cxx |    1 
 vcl/inc/skia/win/gdiimpl.hxx  |    3 
 vcl/skia/win/gdiimpl.cxx      |  254 +++++++++++++++++++++++++++++++++++-------
 3 files changed, 214 insertions(+), 44 deletions(-)

New commits:
commit 4a32b04fd1d320f9d86bc1979ea491002d2565ac
Author:     Mike Kaganski <[email protected]>
AuthorDate: Mon Aug 19 19:02:49 2024 +0500
Commit:     Mike Kaganski <[email protected]>
CommitDate: Tue Aug 20 04:34:40 2024 +0200

    Explicitly request antialiasing in a test
    
    See commit a8d677035a6565ebc051375206786dab3a353578 (Make sure to
    anti-alias fonts on Windows, when required, 2024-08-19).
    
    Change-Id: I67f863b61002da883a91ca09aa432162985c755b
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/172042
    Tested-by: Jenkins
    Reviewed-by: Mike Kaganski <[email protected]>

diff --git a/sd/qa/unit/PNGExportTests.cxx b/sd/qa/unit/PNGExportTests.cxx
index 253456bc5a5e..873cf6749dd1 100644
--- a/sd/qa/unit/PNGExportTests.cxx
+++ b/sd/qa/unit/PNGExportTests.cxx
@@ -1000,6 +1000,7 @@ CPPUNIT_TEST_FIXTURE(SdPNGExportTest, testTdf162259)
     css::uno::Sequence<css::beans::PropertyValue> aFilterData{
         comphelper::makePropertyValue(u"PixelWidth"_ustr, sal_Int32(101)),
         comphelper::makePropertyValue(u"PixelHeight"_ustr, sal_Int32(151)),
+        comphelper::makePropertyValue(u"AntiAliasing"_ustr, true),
     };
 
     css::uno::Sequence<css::beans::PropertyValue> aDescriptor{
commit a1b18eba354dc773c214fc3b7ee92c3473ec4a5e
Author:     Mike Kaganski <[email protected]>
AuthorDate: Tue Aug 20 04:02:47 2024 +0500
Commit:     Mike Kaganski <[email protected]>
CommitDate: Tue Aug 20 04:34:26 2024 +0200

    Implement Custom Font Collections on pre-Windows 10 systems
    
    Commit 68818db0ec0e9c308c8a0772d46af551f439b32c (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 <[email protected]>

diff --git a/vcl/inc/skia/win/gdiimpl.hxx b/vcl/inc/skia/win/gdiimpl.hxx
index c5b12d08811a..7e399c59effb 100644
--- a/vcl/inc/skia/win/gdiimpl.hxx
+++ b/vcl/inc/skia/win/gdiimpl.hxx
@@ -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;
diff --git a/vcl/skia/win/gdiimpl.cxx b/vcl/skia/win/gdiimpl.cxx
index 1d48fb9bbe71..4a8e6266fe63 100644
--- a/vcl/skia/win/gdiimpl.cxx
+++ b/vcl/skia/win/gdiimpl.cxx
@@ -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

Reply via email to