drawinglayer/source/processor2d/vclprocessor2d.cxx |   80 ++++++----
 include/vcl/outdev.hxx                             |    5 
 include/vcl/vcllayout.hxx                          |    6 
 sd/qa/unit/PNGExportTests.cxx                      |   61 +++++++
 sd/qa/unit/data/svg/tdf162259.svg                  |   15 +
 sw/qa/core/text/data/tdf161990-subscripts.fodt     |   44 +++++
 sw/qa/core/text/text.cxx                           |   58 +++++++
 vcl/inc/win/DWriteTextRenderer.hxx                 |   36 +---
 vcl/inc/win/salgdi.h                               |    7 
 vcl/inc/win/winlayout.hxx                          |   10 -
 vcl/qa/cppunit/cjktext.cxx                         |    2 
 vcl/skia/win/gdiimpl.cxx                           |    3 
 vcl/source/outdev/text.cxx                         |    1 
 vcl/win/gdi/DWriteTextRenderer.cxx                 |  161 +++++++--------------
 vcl/win/gdi/salfont.cxx                            |    3 
 vcl/win/gdi/salgdi.cxx                             |   58 ++++---
 vcl/win/gdi/winlayout.cxx                          |   30 ++-
 17 files changed, 362 insertions(+), 218 deletions(-)

New commits:
commit b740fb05965a9857cdbfd287e7bae47b60b8285d
Author:     Mike Kaganski <mike.kagan...@collabora.com>
AuthorDate: Sat Oct 19 11:53:44 2024 +0500
Commit:     Mike Kaganski <mike.kagan...@collabora.com>
CommitDate: Mon Oct 28 19:34:24 2024 +0500

    Windows/--disable-skia: fix D2DWriteTextOutRenderer to properly set AA mode
    
    This simplifies the process; in D2DWriteTextOutRenderer ctor, it
    uses the final mode for the call to CreateRenderTarget, applying
    the correct mode.
    
    Also, pass the AA flag from SalGraphics, and check the setting of
    GetUseFontAAFromSystem to use it, as done in e.g. CairoTextRender
    after commit e6538f5bdd876911ea30f84a6512c03908e620fd (tdf#118966
    vcl: add a flag to determine if AA of fonts is used from the system,
    2018-07-28).
    
    This fixes the failures on Windows with --disable-skia, as seen in
    https://lists.freedesktop.org/archives/libreoffice/2024-October/092541.html
    
    VclCjkTextTest::testVerticalText needed to be fixed by a tweak in
    getCharacterRightSideHeight, to use color's IsDark, instead of
    comparing to the fixed black, because the correct AA mode that is
    used now, makes the vertical part of the character not completely
    black.
    
    Change-Id: Iee8fe98e29a80a242f8e761c9a23c68b34a45699
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/175188
    Tested-by: Jenkins
    Reviewed-by: Mike Kaganski <mike.kagan...@collabora.com>

diff --git a/vcl/inc/win/DWriteTextRenderer.hxx 
b/vcl/inc/win/DWriteTextRenderer.hxx
index 1cdf67d04a39..3d93a463f8ec 100644
--- a/vcl/inc/win/DWriteTextRenderer.hxx
+++ b/vcl/inc/win/DWriteTextRenderer.hxx
@@ -27,33 +27,28 @@
 
 #include <win/winlayout.hxx>
 
-enum class D2DTextAntiAliasMode
-{
-    Default,
-    Aliased,
-    AntiAliased,
-    ClearType,
-};
-
 class D2DWriteTextOutRenderer : public TextOutRenderer
 {
 public:
-    explicit D2DWriteTextOutRenderer(bool bRenderingModeNatural);
+    using MODE = std::pair<D2D1_TEXT_ANTIALIAS_MODE, DWRITE_RENDERING_MODE>;
+
+    explicit D2DWriteTextOutRenderer(MODE mode);
 
     bool operator()(GenericSalLayout const &rLayout,
         SalGraphics &rGraphics,
-        HDC hDC,
-        bool bRenderingModeNatural) override;
+        HDC hDC) override;
 
     HRESULT BindDC(HDC hDC, tools::Rectangle const & rRect = 
tools::Rectangle(0, 0, 1, 1));
 
-    HRESULT CreateRenderTarget(bool bRenderingModeNatural);
+    HRESULT CreateRenderTarget();
 
     bool Ready() const;
 
-    void applyTextAntiAliasMode(bool bRenderingModeNatural);
+    void applyTextAntiAliasMode();
+
+    MODE GetRenderingMode() const { return maRenderingMode; }
 
-    bool GetRenderingModeNatural() const { return mbRenderingModeNatural; }
+    static MODE GetMode(bool bRenderingModeNatural, bool bAntiAlias);
 
 private:
     // This is a singleton object disable copy ctor and assignment operator
@@ -61,14 +56,13 @@ private:
     D2DWriteTextOutRenderer & operator = (const D2DWriteTextOutRenderer &) = 
delete;
 
     IDWriteFontFace* GetDWriteFace(const WinFontInstance& rWinFont, float * 
lfSize) const;
-    bool performRender(GenericSalLayout const &rLayout, SalGraphics 
&rGraphics, HDC hDC, bool& bRetry, bool bRenderingModeNatural);
+    bool performRender(GenericSalLayout const &rLayout, SalGraphics 
&rGraphics, HDC hDC, bool& bRetry);
 
     sal::systools::COMReference<ID2D1Factory> mpD2DFactory;
     sal::systools::COMReference<ID2D1DCRenderTarget> mpRT;
     const D2D1_RENDER_TARGET_PROPERTIES mRTProps;
 
-    bool mbRenderingModeNatural;
-    D2DTextAntiAliasMode meTextAntiAliasMode;
+    MODE maRenderingMode;
 };
 
 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/inc/win/winlayout.hxx b/vcl/inc/win/winlayout.hxx
index cfb36e825b54..6e536d0a8dd8 100644
--- a/vcl/inc/win/winlayout.hxx
+++ b/vcl/inc/win/winlayout.hxx
@@ -74,14 +74,13 @@ protected:
     TextOutRenderer & operator = (const TextOutRenderer &) = delete;
 
 public:
-    static TextOutRenderer & get(bool bUseDWrite, bool bRenderingModeNatural);
+    static TextOutRenderer & get(bool bUseDWrite, bool bRenderingModeNatural, 
bool bAntiAlias);
 
     virtual ~TextOutRenderer() = default;
 
     virtual bool operator ()(GenericSalLayout const &rLayout,
         SalGraphics &rGraphics,
-        HDC hDC,
-        bool bRenderingModeNatural) = 0;
+        HDC hDC) = 0;
 };
 
 class ExTextOutRenderer : public TextOutRenderer
@@ -94,8 +93,7 @@ public:
 
     bool operator ()(GenericSalLayout const &rLayout,
         SalGraphics &rGraphics,
-        HDC hDC,
-        bool bRenderingModeNatural) override;
+        HDC hDC) override;
 };
 
 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/qa/cppunit/cjktext.cxx b/vcl/qa/cppunit/cjktext.cxx
index 8dbed792b97c..273ecc631bb5 100644
--- a/vcl/qa/cppunit/cjktext.cxx
+++ b/vcl/qa/cppunit/cjktext.cxx
@@ -84,7 +84,7 @@ static tools::Long getCharacterRightSideHeight(VirtualDevice* 
device, const Poin
     Bitmap bitmap = device->GetBitmap(Point(), device->GetOutputSizePixel());
     BitmapScopedReadAccess access(bitmap);
     tools::Long x = start.X();
-    while (x >= 0 && access->GetColor(start.Y(), x) != COL_BLACK)
+    while (x >= 0 && !access->GetColor(start.Y(), x).IsDark())
         --x;
     if (x < 0)
         return -1;
diff --git a/vcl/win/gdi/DWriteTextRenderer.cxx 
b/vcl/win/gdi/DWriteTextRenderer.cxx
index bedf0f9fc71f..193817736ab9 100644
--- a/vcl/win/gdi/DWriteTextRenderer.cxx
+++ b/vcl/win/gdi/DWriteTextRenderer.cxx
@@ -38,28 +38,12 @@
 namespace
 {
 
-D2DTextAntiAliasMode lclGetSystemTextAntiAliasMode()
+D2D1_TEXT_ANTIALIAS_MODE lclGetSystemTextAntiAliasType()
 {
-    D2DTextAntiAliasMode eMode = D2DTextAntiAliasMode::Default;
-
-    BOOL bFontSmoothing;
-    if (!SystemParametersInfoW(SPI_GETFONTSMOOTHING, 0, &bFontSmoothing, 0))
-        return eMode;
-
-    if (bFontSmoothing)
-    {
-        eMode = D2DTextAntiAliasMode::AntiAliased;
-
-        UINT nType;
-        if (SystemParametersInfoW(SPI_GETFONTSMOOTHINGTYPE, 0, &nType, 0) && 
nType == FE_FONTSMOOTHINGCLEARTYPE)
-            eMode = D2DTextAntiAliasMode::ClearType;
-    }
-    else
-    {
-        eMode = D2DTextAntiAliasMode::Aliased;
-    }
-
-    return eMode;
+    UINT t;
+    if (SystemParametersInfoW(SPI_GETFONTSMOOTHINGTYPE, 0, &t, 0) && t == 
FE_FONTSMOOTHINGCLEARTYPE)
+        return D2D1_TEXT_ANTIALIAS_MODE_CLEARTYPE;
+    return D2D1_TEXT_ANTIALIAS_MODE_GRAYSCALE;
 }
 
 IDWriteRenderingParams* lclSetRenderingMode(DWRITE_RENDERING_MODE 
eRenderingMode)
@@ -115,59 +99,52 @@ private:
 
 } // end anonymous namespace
 
-D2DWriteTextOutRenderer::D2DWriteTextOutRenderer(bool bRenderingModeNatural)
+// static
+D2DWriteTextOutRenderer::MODE D2DWriteTextOutRenderer::GetMode(bool 
bRenderingModeNatural,
+                                                               bool bAntiAlias)
+{
+    D2D1_TEXT_ANTIALIAS_MODE eTextMode;
+    if 
(!Application::GetSettings().GetStyleSettings().GetUseFontAAFromSystem())
+        eTextMode = bAntiAlias ? lclGetSystemTextAntiAliasType() : 
D2D1_TEXT_ANTIALIAS_MODE_ALIASED;
+    else if (BOOL bSmoothing; SystemParametersInfoW(SPI_GETFONTSMOOTHING, 0, 
&bSmoothing, 0))
+        eTextMode = bSmoothing ? lclGetSystemTextAntiAliasType() : 
D2D1_TEXT_ANTIALIAS_MODE_ALIASED;
+    else
+        eTextMode = D2D1_TEXT_ANTIALIAS_MODE_DEFAULT;
+
+    DWRITE_RENDERING_MODE eRenderingMode;
+    if (eTextMode == D2D1_TEXT_ANTIALIAS_MODE_ALIASED)
+        eRenderingMode = DWRITE_RENDERING_MODE_ALIASED; // no way to use 
bRenderingModeNatural
+    else if (bRenderingModeNatural)
+        eRenderingMode = DWRITE_RENDERING_MODE_NATURAL;
+    else if (eTextMode == D2D1_TEXT_ANTIALIAS_MODE_DEFAULT)
+        eRenderingMode = DWRITE_RENDERING_MODE_DEFAULT;
+    else // D2D1_TEXT_ANTIALIAS_MODE_GRAYSCALE || 
D2D1_TEXT_ANTIALIAS_MODE_CLEARTYPE
+        eRenderingMode = DWRITE_RENDERING_MODE_GDI_CLASSIC;
+
+    return { eTextMode, eRenderingMode };
+}
+
+D2DWriteTextOutRenderer::D2DWriteTextOutRenderer(MODE mode)
     : mpD2DFactory(nullptr),
     mpRT(nullptr),
     mRTProps(D2D1::RenderTargetProperties(D2D1_RENDER_TARGET_TYPE_DEFAULT,
                                           
D2D1::PixelFormat(DXGI_FORMAT_B8G8R8A8_UNORM, D2D1_ALPHA_MODE_PREMULTIPLIED),
                                           0, 0)),
-    mbRenderingModeNatural(bRenderingModeNatural),
-    meTextAntiAliasMode(D2DTextAntiAliasMode::Default)
+    maRenderingMode(mode)
 {
     HRESULT hr = D2D1CreateFactory(D2D1_FACTORY_TYPE_SINGLE_THREADED, 
__uuidof(ID2D1Factory), nullptr, IID_PPV_ARGS_Helper(&mpD2DFactory));
     if (SUCCEEDED(hr))
-        hr = CreateRenderTarget(bRenderingModeNatural);
-    meTextAntiAliasMode = lclGetSystemTextAntiAliasMode();
+        hr = CreateRenderTarget();
 }
 
-void D2DWriteTextOutRenderer::applyTextAntiAliasMode(bool 
bRenderingModeNatural)
-{
-    D2D1_TEXT_ANTIALIAS_MODE eTextAAMode = D2D1_TEXT_ANTIALIAS_MODE_DEFAULT;
-    DWRITE_RENDERING_MODE eRenderingMode = DWRITE_RENDERING_MODE_DEFAULT;
-    switch (meTextAntiAliasMode)
-    {
-        case D2DTextAntiAliasMode::Default:
-            eRenderingMode = DWRITE_RENDERING_MODE_DEFAULT;
-            eTextAAMode = D2D1_TEXT_ANTIALIAS_MODE_DEFAULT;
-            break;
-        case D2DTextAntiAliasMode::Aliased:
-            eRenderingMode = DWRITE_RENDERING_MODE_ALIASED;
-            eTextAAMode = D2D1_TEXT_ANTIALIAS_MODE_ALIASED;
-            break;
-        case D2DTextAntiAliasMode::AntiAliased:
-            eRenderingMode = DWRITE_RENDERING_MODE_CLEARTYPE_GDI_CLASSIC;
-            eTextAAMode = D2D1_TEXT_ANTIALIAS_MODE_GRAYSCALE;
-            break;
-        case D2DTextAntiAliasMode::ClearType:
-            eRenderingMode = DWRITE_RENDERING_MODE_CLEARTYPE_GDI_CLASSIC;
-            eTextAAMode = D2D1_TEXT_ANTIALIAS_MODE_CLEARTYPE;
-            break;
-        default:
-            break;
-    }
-
-    if (bRenderingModeNatural)
-        eRenderingMode = DWRITE_RENDERING_MODE_CLEARTYPE_NATURAL;
-
-    mpRT->SetTextRenderingParams(lclSetRenderingMode(eRenderingMode));
-    mpRT->SetTextAntialiasMode(eTextAAMode);
-}
-
-HRESULT D2DWriteTextOutRenderer::CreateRenderTarget(bool bRenderingModeNatural)
+HRESULT D2DWriteTextOutRenderer::CreateRenderTarget()
 {
     HRESULT hr = CHECKHR(mpD2DFactory->CreateDCRenderTarget(&mRTProps, &mpRT));
     if (SUCCEEDED(hr))
-        applyTextAntiAliasMode(bRenderingModeNatural);
+    {
+        
mpRT->SetTextRenderingParams(lclSetRenderingMode(maRenderingMode.second));
+        mpRT->SetTextAntialiasMode(maRenderingMode.first);
+    }
     return hr;
 }
 
@@ -184,7 +161,7 @@ HRESULT D2DWriteTextOutRenderer::BindDC(HDC hDC, 
tools::Rectangle const & rRect)
     return CHECKHR(mpRT->BindDC(hDC, &rc));
 }
 
-bool D2DWriteTextOutRenderer::operator()(GenericSalLayout const & rLayout, 
SalGraphics& rGraphics, HDC hDC, bool bRenderingModeNatural)
+bool D2DWriteTextOutRenderer::operator()(GenericSalLayout const & rLayout, 
SalGraphics& rGraphics, HDC hDC)
 {
     bool bRetry = false;
     bool bResult = false;
@@ -192,13 +169,13 @@ bool D2DWriteTextOutRenderer::operator()(GenericSalLayout 
const & rLayout, SalGr
     do
     {
        bRetry = false;
-       bResult = performRender(rLayout, rGraphics, hDC, bRetry, 
bRenderingModeNatural);
+       bResult = performRender(rLayout, rGraphics, hDC, bRetry);
        nCount++;
     } while (bRetry && nCount < 3);
     return bResult;
 }
 
-bool D2DWriteTextOutRenderer::performRender(GenericSalLayout const & rLayout, 
SalGraphics& rGraphics, HDC hDC, bool& bRetry, bool bRenderingModeNatural)
+bool D2DWriteTextOutRenderer::performRender(GenericSalLayout const & rLayout, 
SalGraphics& rGraphics, HDC hDC, bool& bRetry)
 {
     if (!Ready())
         return false;
@@ -207,14 +184,14 @@ bool 
D2DWriteTextOutRenderer::performRender(GenericSalLayout const & rLayout, Sa
 
     if (hr == D2DERR_RECREATE_TARGET)
     {
-        CreateRenderTarget(bRenderingModeNatural);
+        CreateRenderTarget();
         bRetry = true;
         return false;
     }
     if (FAILED(hr))
     {
         // If for any reason we can't bind fallback to legacy APIs.
-        return ExTextOutRenderer()(rLayout, rGraphics, hDC, 
bRenderingModeNatural);
+        return ExTextOutRenderer()(rLayout, rGraphics, hDC);
     }
 
     const WinFontInstance& rWinFont = static_cast<const 
WinFontInstance&>(rLayout.GetFont());
@@ -282,7 +259,7 @@ bool 
D2DWriteTextOutRenderer::performRender(GenericSalLayout const & rLayout, Sa
 
     if (hr == D2DERR_RECREATE_TARGET)
     {
-        CreateRenderTarget(bRenderingModeNatural);
+        CreateRenderTarget();
         bRetry = true;
     }
 
diff --git a/vcl/win/gdi/winlayout.cxx b/vcl/win/gdi/winlayout.cxx
index 19eaae2ecee7..83c3a653b1ae 100644
--- a/vcl/win/gdi/winlayout.cxx
+++ b/vcl/win/gdi/winlayout.cxx
@@ -50,7 +50,7 @@
 #include <shlwapi.h>
 #include <winver.h>
 
-TextOutRenderer& TextOutRenderer::get(bool bUseDWrite, bool 
bRenderingModeNatural)
+TextOutRenderer& TextOutRenderer::get(bool bUseDWrite, bool 
bRenderingModeNatural, bool bAntiAlias)
 {
     SalData* const pSalData = GetSalData();
 
@@ -62,14 +62,16 @@ TextOutRenderer& TextOutRenderer::get(bool bUseDWrite, bool 
bRenderingModeNatura
 
     if (bUseDWrite)
     {
-        if (!pSalData->m_pD2DWriteTextOutRenderer
-            || 
static_cast<D2DWriteTextOutRenderer*>(pSalData->m_pD2DWriteTextOutRenderer.get())
-                       ->GetRenderingModeNatural()
-                   != bRenderingModeNatural)
+        const auto mode = 
D2DWriteTextOutRenderer::GetMode(bRenderingModeNatural, bAntiAlias);
+        if (pSalData->m_pD2DWriteTextOutRenderer)
         {
-            pSalData->m_pD2DWriteTextOutRenderer.reset(
-                new D2DWriteTextOutRenderer(bRenderingModeNatural));
+            auto pRenderer
+                = 
static_cast<D2DWriteTextOutRenderer*>(pSalData->m_pD2DWriteTextOutRenderer.get());
+            if (pRenderer->GetRenderingMode() == mode)
+                return *pSalData->m_pD2DWriteTextOutRenderer;
         }
+
+        pSalData->m_pD2DWriteTextOutRenderer.reset(new 
D2DWriteTextOutRenderer(mode));
         return *pSalData->m_pD2DWriteTextOutRenderer;
     }
     if (!pSalData->m_pExTextOutRenderer)
@@ -80,7 +82,7 @@ TextOutRenderer& TextOutRenderer::get(bool bUseDWrite, bool 
bRenderingModeNatura
 }
 
 bool ExTextOutRenderer::operator()(GenericSalLayout const& rLayout, 
SalGraphics& /*rGraphics*/,
-                                   HDC hDC, bool /*bRenderingModeNatural*/)
+                                   HDC hDC)
 {
     int nStart = 0;
     basegfx::B2DPoint aPos;
@@ -197,8 +199,8 @@ void WinFontInstance::SetGraphics(WinSalGraphics* pGraphics)
 void WinSalGraphics::DrawTextLayout(const GenericSalLayout& rLayout, HDC hDC, 
bool bUseDWrite,
                                     bool bRenderingModeNatural)
 {
-    TextOutRenderer& render = TextOutRenderer::get(bUseDWrite, 
bRenderingModeNatural);
-    render(rLayout, *this, hDC, bRenderingModeNatural);
+    auto& render = TextOutRenderer::get(bUseDWrite, bRenderingModeNatural, 
getAntiAlias());
+    render(rLayout, *this, hDC);
 }
 
 void WinSalGraphics::DrawTextLayout(const GenericSalLayout& rLayout)
commit d7a3b1ff05223b76fd11db779b8e60de474cfa24
Author:     Mike Kaganski <mike.kagan...@collabora.com>
AuthorDate: Wed Apr 10 14:01:53 2024 +0500
Commit:     Mike Kaganski <mike.kagan...@collabora.com>
CommitDate: Mon Oct 28 19:34:21 2024 +0500

    Use COMReference in D2DWriteTextOutRenderer
    
    ... and simplify the related code.
    
    Change-Id: Idaef7c9d725273e202948158e45ded7e7a2f85a0
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/165985
    Tested-by: Jenkins
    Reviewed-by: Mike Kaganski <mike.kagan...@collabora.com>

diff --git a/vcl/inc/win/DWriteTextRenderer.hxx 
b/vcl/inc/win/DWriteTextRenderer.hxx
index b822a6bca488..1cdf67d04a39 100644
--- a/vcl/inc/win/DWriteTextRenderer.hxx
+++ b/vcl/inc/win/DWriteTextRenderer.hxx
@@ -23,6 +23,8 @@
 #include <d2d1.h>
 #include <dwrite.h>
 
+#include <systools/win32/comtools.hxx>
+
 #include <win/winlayout.hxx>
 
 enum class D2DTextAntiAliasMode
@@ -37,7 +39,6 @@ class D2DWriteTextOutRenderer : public TextOutRenderer
 {
 public:
     explicit D2DWriteTextOutRenderer(bool bRenderingModeNatural);
-    virtual ~D2DWriteTextOutRenderer() override;
 
     bool operator()(GenericSalLayout const &rLayout,
         SalGraphics &rGraphics,
@@ -62,9 +63,8 @@ private:
     IDWriteFontFace* GetDWriteFace(const WinFontInstance& rWinFont, float * 
lfSize) const;
     bool performRender(GenericSalLayout const &rLayout, SalGraphics 
&rGraphics, HDC hDC, bool& bRetry, bool bRenderingModeNatural);
 
-    ID2D1Factory        * mpD2DFactory;
-    IDWriteFactory      * mpDWriteFactory;
-    ID2D1DCRenderTarget * mpRT;
+    sal::systools::COMReference<ID2D1Factory> mpD2DFactory;
+    sal::systools::COMReference<ID2D1DCRenderTarget> mpRT;
     const D2D1_RENDER_TARGET_PROPERTIES mRTProps;
 
     bool mbRenderingModeNatural;
diff --git a/vcl/inc/win/salgdi.h b/vcl/inc/win/salgdi.h
index 80fafdeba56f..19edee0f4440 100644
--- a/vcl/inc/win/salgdi.h
+++ b/vcl/inc/win/salgdi.h
@@ -166,10 +166,6 @@ private:
     RGNDATA*                mpStdClipRgnData;   // Cache 
Standard-ClipRegion-Data
     int                     mnPenWidth;         // line width
 
-    inline static sal::systools::COMReference<IDWriteFactory> mxDWriteFactory;
-    inline static sal::systools::COMReference<IDWriteGdiInterop> 
mxDWriteGdiInterop;
-    inline static bool bDWriteDone = false;
-
     // just call both from setHDC!
     void InitGraphics();
     void DeInitGraphics();
@@ -198,7 +194,8 @@ public:
         SCREEN
     };
 
-    static void getDWriteFactory(IDWriteFactory** pFactory, 
IDWriteGdiInterop** pInterop = nullptr);
+    static IDWriteFactory* getDWriteFactory();
+    static IDWriteGdiInterop* getDWriteGdiInterop();
 
 public:
 
diff --git a/vcl/skia/win/gdiimpl.cxx b/vcl/skia/win/gdiimpl.cxx
index 0451307b7bdf..1d48fb9bbe71 100644
--- a/vcl/skia/win/gdiimpl.cxx
+++ b/vcl/skia/win/gdiimpl.cxx
@@ -126,8 +126,7 @@ sk_sp<SkTypeface>
 WinSkiaSalGraphicsImpl::createDirectWriteTypeface(const WinFontInstance* 
pWinFont) try
 {
     using sal::systools::ThrowIfFailed;
-    IDWriteFactory* dwriteFactory;
-    WinSalGraphics::getDWriteFactory(&dwriteFactory);
+    IDWriteFactory* dwriteFactory = WinSalGraphics::getDWriteFactory();
     if (!dwriteDone)
     {
         dwriteFontMgr = SkFontMgr_New_DirectWrite(dwriteFactory);
diff --git a/vcl/win/gdi/DWriteTextRenderer.cxx 
b/vcl/win/gdi/DWriteTextRenderer.cxx
index 377874f1586f..bedf0f9fc71f 100644
--- a/vcl/win/gdi/DWriteTextRenderer.cxx
+++ b/vcl/win/gdi/DWriteTextRenderer.cxx
@@ -62,8 +62,10 @@ D2DTextAntiAliasMode lclGetSystemTextAntiAliasMode()
     return eMode;
 }
 
-IDWriteRenderingParams* lclSetRenderingMode(IDWriteFactory* pDWriteFactory, 
DWRITE_RENDERING_MODE eRenderingMode)
+IDWriteRenderingParams* lclSetRenderingMode(DWRITE_RENDERING_MODE 
eRenderingMode)
 {
+    IDWriteFactory* pDWriteFactory = WinSalGraphics::getDWriteFactory();
+
     IDWriteRenderingParams* pDefaultParameters = nullptr;
     pDWriteFactory->CreateRenderingParams(&pDefaultParameters);
 
@@ -122,22 +124,12 @@ D2DWriteTextOutRenderer::D2DWriteTextOutRenderer(bool 
bRenderingModeNatural)
     mbRenderingModeNatural(bRenderingModeNatural),
     meTextAntiAliasMode(D2DTextAntiAliasMode::Default)
 {
-    WinSalGraphics::getDWriteFactory(&mpDWriteFactory);
-    HRESULT hr = S_OK;
-    hr = D2D1CreateFactory(D2D1_FACTORY_TYPE_SINGLE_THREADED, 
__uuidof(ID2D1Factory), nullptr, reinterpret_cast<void **>(&mpD2DFactory));
+    HRESULT hr = D2D1CreateFactory(D2D1_FACTORY_TYPE_SINGLE_THREADED, 
__uuidof(ID2D1Factory), nullptr, IID_PPV_ARGS_Helper(&mpD2DFactory));
     if (SUCCEEDED(hr))
         hr = CreateRenderTarget(bRenderingModeNatural);
     meTextAntiAliasMode = lclGetSystemTextAntiAliasMode();
 }
 
-D2DWriteTextOutRenderer::~D2DWriteTextOutRenderer()
-{
-    if (mpRT)
-        mpRT->Release();
-    if (mpD2DFactory)
-        mpD2DFactory->Release();
-}
-
 void D2DWriteTextOutRenderer::applyTextAntiAliasMode(bool 
bRenderingModeNatural)
 {
     D2D1_TEXT_ANTIALIAS_MODE eTextAAMode = D2D1_TEXT_ANTIALIAS_MODE_DEFAULT;
@@ -167,17 +159,12 @@ void D2DWriteTextOutRenderer::applyTextAntiAliasMode(bool 
bRenderingModeNatural)
     if (bRenderingModeNatural)
         eRenderingMode = DWRITE_RENDERING_MODE_CLEARTYPE_NATURAL;
 
-    mpRT->SetTextRenderingParams(lclSetRenderingMode(mpDWriteFactory, 
eRenderingMode));
+    mpRT->SetTextRenderingParams(lclSetRenderingMode(eRenderingMode));
     mpRT->SetTextAntialiasMode(eTextAAMode);
 }
 
 HRESULT D2DWriteTextOutRenderer::CreateRenderTarget(bool bRenderingModeNatural)
 {
-    if (mpRT)
-    {
-        mpRT->Release();
-        mpRT = nullptr;
-    }
     HRESULT hr = CHECKHR(mpD2DFactory->CreateDCRenderTarget(&mRTProps, &mpRT));
     if (SUCCEEDED(hr))
         applyTextAntiAliasMode(bRenderingModeNatural);
@@ -216,8 +203,7 @@ bool 
D2DWriteTextOutRenderer::performRender(GenericSalLayout const & rLayout, Sa
     if (!Ready())
         return false;
 
-    HRESULT hr = S_OK;
-    hr = BindDC(hDC);
+    HRESULT hr = BindDC(hDC);
 
     if (hr == D2DERR_RECREATE_TARGET)
     {
@@ -253,7 +239,7 @@ bool 
D2DWriteTextOutRenderer::performRender(GenericSalLayout const & rLayout, Sa
         succeeded &= SUCCEEDED(hr);
     }
 
-    ID2D1SolidColorBrush* pBrush = nullptr;
+    sal::systools::COMReference<ID2D1SolidColorBrush> pBrush;
     if (succeeded)
     {
         COLORREF bgrTextColor = GetTextColor(hDC);
@@ -294,9 +280,6 @@ bool 
D2DWriteTextOutRenderer::performRender(GenericSalLayout const & rLayout, Sa
         hr = CHECKHR(mpRT->EndDraw());
     }
 
-    if (pBrush)
-        pBrush->Release();
-
     if (hr == D2DERR_RECREATE_TARGET)
     {
         CreateRenderTarget(bRenderingModeNatural);
diff --git a/vcl/win/gdi/salfont.cxx b/vcl/win/gdi/salfont.cxx
index 51ddcce741b7..f4574c800cf5 100644
--- a/vcl/win/gdi/salfont.cxx
+++ b/vcl/win/gdi/salfont.cxx
@@ -1359,8 +1359,7 @@ IDWriteFontFace* WinFontInstance::GetDWFontFace() const
                 SelectObject(hDC, hOrigFont);
         });
 
-        IDWriteGdiInterop* pDWriteGdiInterop;
-        WinSalGraphics::getDWriteFactory(nullptr, &pDWriteGdiInterop);
+        IDWriteGdiInterop* pDWriteGdiInterop = 
WinSalGraphics::getDWriteGdiInterop();
 
         HRESULT hr = pDWriteGdiInterop->CreateFontFaceFromHdc(hDC, 
&mxDWFontFace);
         if (FAILED(hr))
diff --git a/vcl/win/gdi/salgdi.cxx b/vcl/win/gdi/salgdi.cxx
index cb4b500a4a43..8da350105afd 100644
--- a/vcl/win/gdi/salgdi.cxx
+++ b/vcl/win/gdi/salgdi.cxx
@@ -758,35 +758,43 @@ void WinSalGraphics::GetResolution( sal_Int32& rDPIX, 
sal_Int32& rDPIY )
         rDPIX = rDPIY = 600;
 }
 
-void WinSalGraphics::getDWriteFactory(IDWriteFactory** pFactory, 
IDWriteGdiInterop** pInterop)
+// static
+IDWriteFactory* WinSalGraphics::getDWriteFactory()
 {
-    if (!bDWriteDone)
-    {
-        HRESULT hr = S_OK;
-        hr = DWriteCreateFactory(DWRITE_FACTORY_TYPE_SHARED, 
__uuidof(IDWriteFactory),
-                                 
reinterpret_cast<IUnknown**>(&mxDWriteFactory));
-        if (FAILED(hr))
+    static sal::systools::COMReference<IDWriteFactory> pDWriteFactory(
+        []()
         {
-            SAL_WARN("vcl.fonts", "HRESULT 0x" << OUString::number(hr, 16) << 
": "
-                                               << 
WindowsErrorStringFromHRESULT(hr));
-            abort();
-        }
+            sal::systools::COMReference<IDWriteFactory> pResult;
+            HRESULT hr = DWriteCreateFactory(DWRITE_FACTORY_TYPE_SHARED, 
__uuidof(IDWriteFactory),
+                                             
reinterpret_cast<IUnknown**>(&pResult));
+            if (FAILED(hr))
+            {
+                SAL_WARN("vcl.fonts", "HRESULT 0x" << OUString::number(hr, 16) 
<< ": "
+                                                   << 
WindowsErrorStringFromHRESULT(hr));
+                abort();
+            }
+            return pResult;
+        }());
+    return pDWriteFactory.get();
+}
 
-        hr = mxDWriteFactory->GetGdiInterop(&mxDWriteGdiInterop);
-        if (FAILED(hr))
+// static
+IDWriteGdiInterop* WinSalGraphics::getDWriteGdiInterop()
+{
+    static sal::systools::COMReference<IDWriteGdiInterop> pDWriteGdiInterop(
+        []()
         {
-            SAL_WARN("vcl.fonts", "HRESULT 0x" << OUString::number(hr, 16) << 
": "
-                                               << 
WindowsErrorStringFromHRESULT(hr));
-            abort();
-        }
-
-        bDWriteDone = true;
-    }
-
-    if (pFactory)
-        *pFactory = mxDWriteFactory.get();
-    if (pInterop)
-        *pInterop = mxDWriteGdiInterop.get();
+            sal::systools::COMReference<IDWriteGdiInterop> pResult;
+            HRESULT hr = getDWriteFactory()->GetGdiInterop(&pResult);
+            if (FAILED(hr))
+            {
+                SAL_WARN("vcl.fonts", "HRESULT 0x" << OUString::number(hr, 16) 
<< ": "
+                                                   << 
WindowsErrorStringFromHRESULT(hr));
+                abort();
+            }
+            return pResult;
+        }());
+    return pDWriteGdiInterop.get();
 }
 
 sal_uInt16 WinSalGraphics::GetBitCount() const
commit 5d99c97ced459ff250177d42e364676bed0a5555
Author:     Mike Kaganski <mike.kagan...@collabora.com>
AuthorDate: Thu Aug 1 19:31:41 2024 +0500
Commit:     Mike Kaganski <mike.kagan...@collabora.com>
CommitDate: Mon Oct 28 17:42:45 2024 +0500

    tdf#162259: correctly handle font width on Windows
    
    Unlike other platforms, on Windows, the font width is not relative to
    font height, but to average width of font's glyphs. This is mentioned
    in LogicalFontInstance::GetScale.
    
    1. In VclProcessor2D::RenderTextSimpleOrDecoratedPortionPrimitive2D,
    when calculating the correction for width / height (introduced in
    commit cc3663bbaed4f65d64154e5f9abb51a5f622f710, 2024-04-20), the
    already applied X scale is now calculated using unscaled font's width.
    
    2. Commit 8557ea84c9336ba8061246f1f46ddb6e02f413a1 (Exclude getHScale
    from DirectWrite font rendering, 2024-04-08) was effectively reverted,
    because I was wrong assuming that the code there was unnecessary.
    
    3. Commit 2092df2a9044f1c2ae4379f48a3201e5867575a8 (tdf#161154: pass
    "scaling is done externally" information down the stack, 2024-05-18)
    was also reverted.
    
    Change-Id: I8cff39b67a6efd380f7807f5655f401bdb62cc3a
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/171382
    Reviewed-by: Mike Kaganski <mike.kagan...@collabora.com>
    Tested-by: Jenkins
    Signed-off-by: Xisco Fauli <xiscofa...@libreoffice.org>
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/174978
    Reviewed-by: Michael Stahl <michael.st...@allotropia.de>

diff --git a/drawinglayer/source/processor2d/vclprocessor2d.cxx 
b/drawinglayer/source/processor2d/vclprocessor2d.cxx
index f5d3153dd6f9..c85669817746 100644
--- a/drawinglayer/source/processor2d/vclprocessor2d.cxx
+++ b/drawinglayer/source/processor2d/vclprocessor2d.cxx
@@ -420,6 +420,25 @@ void 
VclProcessor2D::RenderTextSimpleOrDecoratedPortionPrimitive2D(
                           / (aResultFontSize.Width() ? aResultFontSize.Width()
                                                      : 
aResultFontSize.Height());
 
+#ifdef _WIN32
+                    if (aResultFontSize.Width()
+                        && aResultFontSize.Width() != aResultFontSize.Height())
+                    {
+                        // See getVclFontFromFontAttribute in 
drawinglayer/source/primitive2d/textlayoutdevice.cxx
+                        vcl::Font aUnscaledTest(aFont);
+                        aUnscaledTest.SetFontSize({ 0, 
aResultFontSize.Height() });
+                        const FontMetric aUnscaledFontMetric(
+                            
Application::GetDefaultDevice()->GetFontMetric(aUnscaledTest));
+                        if (aUnscaledFontMetric.GetAverageFontWidth() > 0)
+                        {
+                            double nExistingXScale = 
static_cast<double>(aResultFontSize.Width())
+                                                     / 
aUnscaledFontMetric.GetAverageFontWidth();
+                            nFontScalingFixX
+                                = aFontScaling.getX() / aFontScaling.getY() / 
nExistingXScale;
+                        }
+                    }
+#endif
+
                     if (!rtl_math_approxEqual(nFontScalingFixY, 1.0)
                         || !rtl_math_approxEqual(nFontScalingFixX, 1.0))
                     {
@@ -446,21 +465,17 @@ void 
VclProcessor2D::RenderTextSimpleOrDecoratedPortionPrimitive2D(
             mpOutputDevice->SetFont(aFont);
             mpOutputDevice->SetTextColor(Color(aRGBFontColor));
 
+            if (!aDXArray.empty())
             {
-                // For D2DWriteTextOutRenderer, we must pass a flag to not use 
font scaling
-                auto guard = mpOutputDevice->ScopedNoFontScaling();
-                if (!aDXArray.empty())
-                {
-                    const SalLayoutGlyphs* pGlyphs = 
SalLayoutGlyphsCache::self()->GetLayoutGlyphs(
-                        mpOutputDevice, aText, nPos, nLen);
-                    mpOutputDevice->DrawTextArray(aStartPoint, aText, aDXArray,
-                                                  
rTextCandidate.getKashidaArray(), nPos, nLen,
-                                                  SalLayoutFlags::NONE, 
pGlyphs);
-                }
-                else
-                {
-                    mpOutputDevice->DrawText(aStartPoint, aText, nPos, nLen);
-                }
+                const SalLayoutGlyphs* pGlyphs = 
SalLayoutGlyphsCache::self()->GetLayoutGlyphs(
+                    mpOutputDevice, aText, nPos, nLen);
+                mpOutputDevice->DrawTextArray(aStartPoint, aText, aDXArray,
+                                              
rTextCandidate.getKashidaArray(), nPos, nLen,
+                                              SalLayoutFlags::NONE, pGlyphs);
+            }
+            else
+            {
+                mpOutputDevice->DrawText(aStartPoint, aText, nPos, nLen);
             }
 
             if (rTextCandidate.getFontAttribute().getRTL())
diff --git a/include/vcl/outdev.hxx b/include/vcl/outdev.hxx
index 15c6cd130f71..bdbb2ecd1847 100644
--- a/include/vcl/outdev.hxx
+++ b/include/vcl/outdev.hxx
@@ -21,7 +21,6 @@
 
 #include <sal/config.h>
 
-#include <comphelper/flagguard.hxx>
 #include <tools/gen.hxx>
 #include <tools/ref.hxx>
 #include <tools/solar.h>
@@ -260,8 +259,6 @@ private:
     mutable bool                    mbRefPoint : 1;
     mutable bool                    mbEnableRTL : 1;
 
-    bool mbNoFontScaling = false; // Used only by D2DWriteTextOutRenderer
-
 protected:
     mutable std::shared_ptr<vcl::font::PhysicalFontCollection> 
mxFontCollection;
     mutable std::shared_ptr<ImplFontCache> mxFontCache;
@@ -344,8 +341,6 @@ public:
     /// request XSpriteCanvas render interface
     css::uno::Reference< css::rendering::XSpriteCanvas > GetSpriteCanvas() 
const;
 
-    auto ScopedNoFontScaling() { return 
comphelper::FlagRestorationGuard(mbNoFontScaling, true); }
-
 protected:
 
     /** Acquire a graphics device that the output device uses to draw on.
diff --git a/include/vcl/vcllayout.hxx b/include/vcl/vcllayout.hxx
index b37e8ff6e45b..c946f6f67884 100644
--- a/include/vcl/vcllayout.hxx
+++ b/include/vcl/vcllayout.hxx
@@ -22,7 +22,6 @@
 #include <basegfx/point/b2dpoint.hxx>
 #include <basegfx/polygon/b2dpolypolygon.hxx>
 #include <basegfx/range/b2drectangle.hxx>
-#include <comphelper/flagguard.hxx>
 #include <i18nlangtag/languagetag.hxx>
 #include <tools/gen.hxx>
 #include <tools/degree.hxx>
@@ -111,9 +110,6 @@ public:
 
     virtual SalLayoutGlyphs GetGlyphs() const;
 
-    auto ScopedFontScaling(bool v) { return 
comphelper::FlagRestorationGuard(mbScaleFont, v); }
-    bool ScaleFont() const { return mbScaleFont; }
-
 protected:
     // used by layout engines
     SalLayout();
@@ -122,8 +118,6 @@ private:
     SalLayout(const SalLayout&) = delete;
     SalLayout& operator=(const SalLayout&) = delete;
 
-    bool mbScaleFont = true; // Used only by D2DWriteTextOutRenderer
-
 protected:
     int             mnMinCharPos;
     int             mnEndCharPos;
diff --git a/sd/qa/unit/PNGExportTests.cxx b/sd/qa/unit/PNGExportTests.cxx
index 69343b8b208d..cc2174acb70e 100644
--- a/sd/qa/unit/PNGExportTests.cxx
+++ b/sd/qa/unit/PNGExportTests.cxx
@@ -888,4 +888,65 @@ CPPUNIT_TEST_FIXTURE(SdPNGExportTest, testTdf155048)
     }
 }
 
+CPPUNIT_TEST_FIXTURE(SdPNGExportTest, testTdf162259)
+{
+    // The top X in the SVG, having no skew, used a fast rendering path, and 
was output much wider
+    // than the bottom one, which has a skew. Test the rendered pixels inside 
the known boundaries.
+
+    loadFromFile(u"svg/tdf162259.svg");
+
+    auto xGraphicExporter = 
drawing::GraphicExportFilter::create(getComponentContext());
+    CPPUNIT_ASSERT(xGraphicExporter);
+
+    auto xSupplier = 
mxComponent.queryThrow<css::drawing::XDrawPagesSupplier>();
+    auto xPage = 
xSupplier->getDrawPages()->getByIndex(0).queryThrow<css::lang::XComponent>();
+    xGraphicExporter->setSourceDocument(xPage);
+
+    // 101 x 151 is current width x height ratio of the loaded SVG. FIXME: it 
should be 100 x 150.
+    css::uno::Sequence<css::beans::PropertyValue> aFilterData{
+        comphelper::makePropertyValue(u"PixelWidth"_ustr, sal_Int32(101)),
+        comphelper::makePropertyValue(u"PixelHeight"_ustr, sal_Int32(151)),
+    };
+
+    css::uno::Sequence<css::beans::PropertyValue> aDescriptor{
+        comphelper::makePropertyValue(u"URL"_ustr, maTempFile.GetURL()),
+        comphelper::makePropertyValue(u"FilterName"_ustr, u"PNG"_ustr),
+        comphelper::makePropertyValue(u"FilterData"_ustr, aFilterData)
+    };
+
+    xGraphicExporter->filter(aDescriptor);
+    BitmapEx bmp = 
vcl::PngImageReader(*maTempFile.GetStream(StreamMode::READ)).read();
+
+    tools::Rectangle topX(12, 21, 37, 60);
+    int topNonWhites = 0;
+    tools::Rectangle bottomX(13, 83, 37, 126);
+    int bottomNonWhites = 0;
+
+    // Check that there is nothing outside the X recrangles
+    for (tools::Long x = 0; x < bmp.GetSizePixel().Width(); ++x)
+    {
+        for (tools::Long y = 0; y < bmp.GetSizePixel().Height(); ++y)
+        {
+            if (topX.Contains(Point{ x, y }))
+            {
+                if (bmp.GetPixelColor(x, y) != COL_WHITE)
+                    ++topNonWhites;
+            }
+            else if (bottomX.Contains(Point{ x, y }))
+            {
+                if (bmp.GetPixelColor(x, y) != COL_WHITE)
+                    ++bottomNonWhites;
+            }
+            else
+            {
+                OString msg("Pixel: "_ostr + OString::number(x) + "," + 
OString::number(y));
+                CPPUNIT_ASSERT_EQUAL_MESSAGE(msg.getStr(), COL_WHITE, 
bmp.GetPixelColor(x, y));
+            }
+        }
+    }
+
+    CPPUNIT_ASSERT_GREATER(350, topNonWhites); // 399 in my testing
+    CPPUNIT_ASSERT_GREATER(350, bottomNonWhites); // 362 in my testing
+}
+
 CPPUNIT_PLUGIN_IMPLEMENT();
diff --git a/sd/qa/unit/data/svg/tdf162259.svg 
b/sd/qa/unit/data/svg/tdf162259.svg
new file mode 100644
index 000000000000..96e7bd930c8d
--- /dev/null
+++ b/sd/qa/unit/data/svg/tdf162259.svg
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<svg xmlns="http://www.w3.org/2000/svg"; version="1.1" width="100" height="150" 
viewBox="0 0 100 150">
+ <g>
+  <text style="font-size:5.9px;font-family:Liberation Serif"
+        transform="scale(6,10)"
+        x="2" y="6">
+   <tspan>X</tspan>
+  </text>
+  <text style="font-size:5.9px;font-family:Liberation Serif"
+        transform="scale(6,10) skewY(5)"
+        x="2" y="12">
+   <tspan>X</tspan>
+  </text>
+ </g>
+</svg>
diff --git a/vcl/inc/win/winlayout.hxx b/vcl/inc/win/winlayout.hxx
index 31066a7db28a..cfb36e825b54 100644
--- a/vcl/inc/win/winlayout.hxx
+++ b/vcl/inc/win/winlayout.hxx
@@ -36,6 +36,8 @@ class WinFontInstance : public LogicalFontInstance
 public:
     ~WinFontInstance() override;
 
+    float getHScale() const;
+
     void SetGraphics(WinSalGraphics*);
     WinSalGraphics* GetGraphics() const { return m_pGraphics; }
 
diff --git a/vcl/source/outdev/text.cxx b/vcl/source/outdev/text.cxx
index edd215943a3c..356243b1e398 100644
--- a/vcl/source/outdev/text.cxx
+++ b/vcl/source/outdev/text.cxx
@@ -449,7 +449,6 @@ void OutputDevice::ImplDrawSpecialText( SalLayout& 
rSalLayout )
 
 void OutputDevice::ImplDrawText( SalLayout& rSalLayout )
 {
-    auto guard = rSalLayout.ScopedFontScaling(!mbNoFontScaling);
     if( mbInitClipRegion )
         InitClipRegion();
     if( mbOutputClipped )
diff --git a/vcl/win/gdi/DWriteTextRenderer.cxx 
b/vcl/win/gdi/DWriteTextRenderer.cxx
index 6a28cb4b4d98..377874f1586f 100644
--- a/vcl/win/gdi/DWriteTextRenderer.cxx
+++ b/vcl/win/gdi/DWriteTextRenderer.cxx
@@ -101,7 +101,7 @@ HRESULT checkResult(HRESULT hr, const char* file, size_t 
line)
 class WinFontTransformGuard
 {
 public:
-    WinFontTransformGuard(ID2D1RenderTarget* pRenderTarget,
+    WinFontTransformGuard(ID2D1RenderTarget* pRenderTarget, float hscale,
                           const GenericSalLayout& rLayout, const 
D2D1_POINT_2F& rBaseline,
                           bool bIsVertical);
     ~WinFontTransformGuard();
@@ -265,17 +265,18 @@ bool 
D2DWriteTextOutRenderer::performRender(GenericSalLayout const & rLayout, Sa
     {
         mpRT->BeginDraw();
 
+        const float hscale = rWinFont.getHScale();
         int nStart = 0;
         basegfx::B2DPoint aPos;
         const GlyphItem* pGlyph;
         while (rLayout.GetNextGlyph(&pGlyph, aPos, nStart))
         {
             UINT16 glyphIndices[] = { static_cast<UINT16>(pGlyph->glyphId()) };
-            FLOAT glyphAdvances[] = { static_cast<FLOAT>(pGlyph->newWidth()) };
+            FLOAT glyphAdvances[] = { static_cast<FLOAT>(pGlyph->newWidth()) / 
hscale };
             DWRITE_GLYPH_OFFSET glyphOffsets[] = { { 0.0f, 0.0f }, };
-            D2D1_POINT_2F baseline = { static_cast<FLOAT>(aPos.getX() - 
bounds.Left()),
+            D2D1_POINT_2F baseline = { static_cast<FLOAT>(aPos.getX() - 
bounds.Left()) / hscale,
                                        static_cast<FLOAT>(aPos.getY() - 
bounds.Top()) };
-            WinFontTransformGuard aTransformGuard(mpRT, rLayout, baseline, 
pGlyph->IsVertical());
+            WinFontTransformGuard aTransformGuard(mpRT, hscale, rLayout, 
baseline, pGlyph->IsVertical());
             DWRITE_GLYPH_RUN glyphs = {
                 pFontFace,
                 lfEmHeight,
@@ -326,22 +327,12 @@ IDWriteFontFace* 
D2DWriteTextOutRenderer::GetDWriteFace(const WinFontInstance& r
     return pFontFace;
 }
 
-WinFontTransformGuard::WinFontTransformGuard(ID2D1RenderTarget* pRenderTarget,
+WinFontTransformGuard::WinFontTransformGuard(ID2D1RenderTarget* pRenderTarget, 
float hscale,
                                              const GenericSalLayout& rLayout,
                                              const D2D1_POINT_2F& rBaseline,
                                              bool bIsVertical)
     : mpRenderTarget(pRenderTarget)
 {
-    const float hscale = [&rLayout]
-    {
-        if (!rLayout.ScaleFont())
-            return 1.0;
-        const auto& rPattern = rLayout.GetFont().GetFontSelectPattern();
-        if (!rPattern.mnHeight || !rPattern.mnWidth)
-            return 1.0;
-        return rPattern.mnWidth * rLayout.GetFont().GetAverageWidthFactor() / 
rPattern.mnHeight;
-    }();
-
     Degree10 angle = rLayout.GetOrientation();
     if (bIsVertical)
         angle += 900_deg10;
diff --git a/vcl/win/gdi/winlayout.cxx b/vcl/win/gdi/winlayout.cxx
index 0c64759e1ab8..19eaae2ecee7 100644
--- a/vcl/win/gdi/winlayout.cxx
+++ b/vcl/win/gdi/winlayout.cxx
@@ -146,6 +146,14 @@ WinFontInstance::~WinFontInstance()
         ::DeleteFont(m_hFont);
 }
 
+float WinFontInstance::getHScale() const
+{
+    const vcl::font::FontSelectPattern& rPattern = GetFontSelectPattern();
+    if (!rPattern.mnHeight || !rPattern.mnWidth)
+        return 1.0;
+    return rPattern.mnWidth * GetAverageWidthFactor() / rPattern.mnHeight;
+}
+
 void WinFontInstance::ImplInitHbFont(hb_font_t* /*pHbFont*/)
 {
     assert(m_pGraphics);
commit 63d0dcdd6535c7c20e193893142a5d2036c48c4b
Author:     Mike Kaganski <mike.kagan...@collabora.com>
AuthorDate: Sat Jul 13 22:54:05 2024 +0500
Commit:     Mike Kaganski <mike.kagan...@collabora.com>
CommitDate: Mon Oct 28 17:42:06 2024 +0500

    tdf#161990: take MapMode's origin point into account
    
    Regression from commit cc3663bbaed4f65d64154e5f9abb51a5f622f710 (tdf#160702:
    improve text positioning, 2024-04-20).
    
    Change-Id: I03f461d7f03c3e67ccdb8c4376720feb69903d6b
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/170432
    Tested-by: Jenkins
    Reviewed-by: Mike Kaganski <mike.kagan...@collabora.com>
    (cherry picked from commit ccc3996cfcbebe14e9d5f3511906cfc64ddf3452)
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/170466
    Reviewed-by: Adolfo Jayme Barrientos <fit...@ubuntu.com>

diff --git a/drawinglayer/source/processor2d/vclprocessor2d.cxx 
b/drawinglayer/source/processor2d/vclprocessor2d.cxx
index 872d0fc913fb..f5d3153dd6f9 100644
--- a/drawinglayer/source/processor2d/vclprocessor2d.cxx
+++ b/drawinglayer/source/processor2d/vclprocessor2d.cxx
@@ -426,13 +426,14 @@ void 
VclProcessor2D::RenderTextSimpleOrDecoratedPortionPrimitive2D(
                         MapMode aMapMode = mpOutputDevice->GetMapMode();
                         aMapMode.SetScaleX(aMapMode.GetScaleX() * 
nFontScalingFixX);
                         aMapMode.SetScaleY(aMapMode.GetScaleY() * 
nFontScalingFixY);
+                        Point origin = aMapMode.GetOrigin();
 
                         mpOutputDevice->Push(vcl::PushFlags::MAPMODE);
                         mpOutputDevice->SetRelativeMapMode(aMapMode);
                         bChangeMapMode = true;
 
-                        aPointX /= nFontScalingFixX;
-                        aPointY /= nFontScalingFixY;
+                        aPointX = (aPointX + origin.X()) / nFontScalingFixX - 
origin.X();
+                        aPointY = (aPointY + origin.Y()) / nFontScalingFixY - 
origin.Y();
                     }
                 }
 
diff --git a/sw/qa/core/text/data/tdf161990-subscripts.fodt 
b/sw/qa/core/text/data/tdf161990-subscripts.fodt
new file mode 100644
index 000000000000..c9f172aa6d56
--- /dev/null
+++ b/sw/qa/core/text/data/tdf161990-subscripts.fodt
@@ -0,0 +1,44 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<office:document 
xmlns:office="urn:oasis:names:tc:opendocument:xmlns:office:1.0" 
xmlns:fo="urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0" 
xmlns:style="urn:oasis:names:tc:opendocument:xmlns:style:1.0" 
xmlns:text="urn:oasis:names:tc:opendocument:xmlns:text:1.0" 
xmlns:draw="urn:oasis:names:tc:opendocument:xmlns:drawing:1.0" 
xmlns:svg="urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0" 
office:version="1.3" office:mimetype="application/vnd.oasis.opendocument.text">
+ <office:font-face-decls>
+  <style:font-face style:name="Liberation Sans" 
svg:font-family="&apos;Liberation Sans&apos;" style:font-family-generic="swiss" 
style:font-pitch="variable"/>
+ </office:font-face-decls>
+ <office:styles>
+  <style:default-style style:family="graphic">
+   <style:graphic-properties draw:start-line-spacing-horizontal="2.83mm" 
draw:start-line-spacing-vertical="2.83mm" 
draw:end-line-spacing-horizontal="2.83mm" 
draw:end-line-spacing-vertical="2.83mm" style:writing-mode="lr-tb" 
style:flow-with-text="false"/>
+   <style:paragraph-properties style:text-autospace="ideograph-alpha" 
style:line-break="strict" style:font-independent-line-spacing="false"/>
+   <style:text-properties style:use-window-font-color="true" 
style:font-name="Liberation Sans" fo:font-size="12pt" 
style:letter-kerning="true"/>
+  </style:default-style>
+ </office:styles>
+ <office:automatic-styles>
+  <style:style style:name="T1" style:family="text">
+   <style:text-properties style:text-position="-33% 58%" fo:font-size="11pt"/>
+  </style:style>
+  <style:style style:name="gr1" style:family="graphic">
+   <style:graphic-properties draw:stroke="none" draw:fill="none" 
draw:textarea-horizontal-align="left" draw:auto-grow-height="true" 
draw:auto-grow-width="true" fo:min-height="5mm" fo:min-width="4mm" 
fo:padding-top="0" fo:padding-bottom="0" fo:padding-left="0" 
fo:padding-right="0" fo:wrap-option="wrap" draw:shadow="hidden" 
style:run-through="foreground" style:vertical-pos="top" 
style:vertical-rel="page-content" style:horizontal-pos="left" 
style:horizontal-rel="page-content"/>
+  </style:style>
+  <style:page-layout style:name="pm1">
+   <style:page-layout-properties fo:page-width="210mm" fo:page-height="297mm" 
style:print-orientation="portrait" fo:margin-top="20mm" fo:margin-bottom="20mm" 
fo:margin-left="20mm" fo:margin-right="20mm" style:writing-mode="lr-tb"/>
+  </style:page-layout>
+  <style:style style:name="dp1" style:family="drawing-page"/>
+ </office:automatic-styles>
+ <office:master-styles>
+  <style:master-page style:name="Standard" style:page-layout-name="pm1" 
draw:style-name="dp1"/>
+ </office:master-styles>
+ <office:body>
+  <office:text>
+   <draw:frame text:anchor-type="page" text:anchor-page-number="1" 
draw:name="Shape_p1" draw:style-name="gr1" svg:width="9.16mm" 
svg:height="8.4mm" svg:x="0" svg:y="0">
+    <draw:text-box>
+     <text:p>P<text:span text:style-name="T1">1</text:span></text:p>
+    </draw:text-box>
+   </draw:frame>
+   <draw:frame text:anchor-type="page" text:anchor-page-number="6" 
draw:name="Shape_p6" draw:style-name="gr1" svg:width="9.16mm" 
svg:height="8.4mm" svg:x="0" svg:y="0">
+    <draw:text-box>
+     <text:p>P<text:span text:style-name="T1">1</text:span></text:p>
+    </draw:text-box>
+   </draw:frame>
+   <text:p/>
+  </office:text>
+ </office:body>
+</office:document>
\ No newline at end of file
diff --git a/sw/qa/core/text/text.cxx b/sw/qa/core/text/text.cxx
index f18c4fd531f8..722ca5ccd1d0 100644
--- a/sw/qa/core/text/text.cxx
+++ b/sw/qa/core/text/text.cxx
@@ -1660,6 +1660,64 @@ CPPUNIT_TEST_FIXTURE(SwCoreTextTest, testTdf112594)
                 u"\u202F\u1824"_ustr);
 }
 
+CPPUNIT_TEST_FIXTURE(SwCoreTextTest, testTdf161990)
+{
+    auto pPDFium = vcl::pdf::PDFiumLibrary::get();
+    if (!pPDFium)
+        return;
+
+    // Given a file with two frames, each having a subscript run, on pages 1 
and 6:
+    createSwDoc("tdf161990-subscripts.fodt");
+
+    // When exporting to PDF:
+    save(u"writer_pdf_Export"_ustr);
+    auto pPdfDocument = parsePDFExport();
+
+    // Check that both subscripts are positioned correctly relative to the 
non-subscript runs
+    double expectedOffset = 0;
+
+    // Page 1
+    {
+        auto pPage = pPdfDocument->openPage(0);
+        auto pTextPage = pPage->getTextPage();
+
+        CPPUNIT_ASSERT_EQUAL(2, pPage->getObjectCount());
+
+        auto pObject = pPage->getObject(0);
+        CPPUNIT_ASSERT_EQUAL(u"P"_ustr, pObject->getText(pTextPage));
+        auto textPPos = pObject->getBounds();
+        pObject = pPage->getObject(1);
+        CPPUNIT_ASSERT_EQUAL(u"1"_ustr, pObject->getText(pTextPage));
+        auto text1Pos = pObject->getBounds();
+        expectedOffset = textPPos.getMaxY() - text1Pos.getMaxY();
+        // Without the fix, this would fail with
+        // - Expected: 7.49
+        // - Actual  : 7.54150390625
+        // But if it fails in some configurations because of different page 
units, then this
+        // check is not as important as that this value is the same as on the 
6th page below.
+        CPPUNIT_ASSERT_DOUBLES_EQUAL(7.49, expectedOffset, 0.01);
+    }
+
+    // Page 6
+    {
+        auto pPage = pPdfDocument->openPage(5);
+        auto pTextPage = pPage->getTextPage();
+
+        CPPUNIT_ASSERT_EQUAL(2, pPage->getObjectCount());
+
+        auto pObject = pPage->getObject(0);
+        CPPUNIT_ASSERT_EQUAL(u"P"_ustr, pObject->getText(pTextPage));
+        auto textPPos = pObject->getBounds();
+        pObject = pPage->getObject(1);
+        CPPUNIT_ASSERT_EQUAL(u"1"_ustr, pObject->getText(pTextPage));
+        auto text1Pos = pObject->getBounds();
+        // Without the fix, this would fail with
+        // - Expected: 7.4925537109375
+        // - Actual  : 20.9005126953125
+        CPPUNIT_ASSERT_DOUBLES_EQUAL(expectedOffset, textPPos.getMaxY() - 
text1Pos.getMaxY(), 0.01);
+    }
+}
+
 CPPUNIT_PLUGIN_IMPLEMENT();
 
 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
commit c21521daeea400173c4069b621acfb81510bcf42
Author:     Mike Kaganski <mike.kagan...@collabora.com>
AuthorDate: Sun May 26 10:21:59 2024 +0500
Commit:     Mike Kaganski <mike.kagan...@collabora.com>
CommitDate: Mon Oct 28 17:42:03 2024 +0500

    tdf#161222: LOK: do not fine-tune text scaling for tile rendering
    
    For unclear reason, this shifts text on all tiles except the top left one.
    I wasn't able to track where the coordinate is changed: it seems that the
    pixel offset of the virtual device, its MapMode's origin, as well as the
    start point passed to the DrawTextArray method, are all only changed by a
    tiny amount, if at all, so it should be no more than a pixel off compared
    to before commit cc3663bbaed4f65d64154e5f9abb51a5f622f710 (tdf#160702:
    improve text positioning, 2024-04-20). However, it is already more than
    half the tile size (more than 128 pixel) offset in 75% scale case for the
    second tile; and it's completely off the tile for all the rest (third+ in
    a row), and for greater scales (100%+) even for second tile.
    
    Change-Id: I64dc24bea4bab0cac90f11f2500bba0fd9bc7855
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/168041
    Reviewed-by: Mike Kaganski <mike.kagan...@collabora.com>
    Tested-by: Jenkins
    (cherry picked from commit c9571914b8170128a68496ec2dd299e21243d1c1)
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/168054
    Reviewed-by: Christian Lohmaier <lohmaier+libreoff...@googlemail.com>

diff --git a/drawinglayer/source/processor2d/vclprocessor2d.cxx 
b/drawinglayer/source/processor2d/vclprocessor2d.cxx
index f6b8dbc8f953..872d0fc913fb 100644
--- a/drawinglayer/source/processor2d/vclprocessor2d.cxx
+++ b/drawinglayer/source/processor2d/vclprocessor2d.cxx
@@ -411,25 +411,29 @@ void 
VclProcessor2D::RenderTextSimpleOrDecoratedPortionPrimitive2D(
                 const basegfx::B2DPoint aPoint(aLocalTransform * 
basegfx::B2DPoint(0.0, 0.0));
                 double aPointX = aPoint.getX(), aPointY = aPoint.getY();
 
-                // aFont has an integer size; we must scale a bit for precision
-                double nFontScalingFixY = aFontScaling.getY() / 
aResultFontSize.Height();
-                double nFontScalingFixX = aFontScaling.getX()
-                                          / (aResultFontSize.Width() ? 
aResultFontSize.Width()
-                                                                     : 
aResultFontSize.Height());
-
-                if (!rtl_math_approxEqual(nFontScalingFixY, 1.0)
-                    || !rtl_math_approxEqual(nFontScalingFixX, 1.0))
+                if (!comphelper::LibreOfficeKit::isActive())
                 {
-                    MapMode aMapMode = mpOutputDevice->GetMapMode();
-                    aMapMode.SetScaleX(aMapMode.GetScaleX() * 
nFontScalingFixX);
-                    aMapMode.SetScaleY(aMapMode.GetScaleY() * 
nFontScalingFixY);
+                    // aFont has an integer size; we must scale a bit for 
precision
+                    double nFontScalingFixY = aFontScaling.getY() / 
aResultFontSize.Height();
+                    double nFontScalingFixX
+                        = aFontScaling.getX()
+                          / (aResultFontSize.Width() ? aResultFontSize.Width()
+                                                     : 
aResultFontSize.Height());
+
+                    if (!rtl_math_approxEqual(nFontScalingFixY, 1.0)
+                        || !rtl_math_approxEqual(nFontScalingFixX, 1.0))
+                    {
+                        MapMode aMapMode = mpOutputDevice->GetMapMode();
+                        aMapMode.SetScaleX(aMapMode.GetScaleX() * 
nFontScalingFixX);
+                        aMapMode.SetScaleY(aMapMode.GetScaleY() * 
nFontScalingFixY);
 
-                    mpOutputDevice->Push(vcl::PushFlags::MAPMODE);
-                    mpOutputDevice->SetRelativeMapMode(aMapMode);
-                    bChangeMapMode = true;
+                        mpOutputDevice->Push(vcl::PushFlags::MAPMODE);
+                        mpOutputDevice->SetRelativeMapMode(aMapMode);
+                        bChangeMapMode = true;
 
-                    aPointX /= nFontScalingFixX;
-                    aPointY /= nFontScalingFixY;
+                        aPointX /= nFontScalingFixX;
+                        aPointY /= nFontScalingFixY;
+                    }
                 }
 
                 aStartPoint = Point(basegfx::fround<tools::Long>(aPointX),

Reply via email to