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="'Liberation Sans'" 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),