From f8eaa8d6f5105d394778345f7e3b0701c44b8fb6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Caol=C3=A1n=20McNamara?= Date: Mon, 14 Mar 2022 15:10:31 +0000 Subject: [PATCH] tdf#144862 use resolution independent text rendering --- ...esolution-independent-positions-for-.patch | 2868 +++++++++++++++++ libreoffice.spec | 6 +- 2 files changed, 2873 insertions(+), 1 deletion(-) create mode 100644 0001-tdf-144862-use-resolution-independent-positions-for-.patch diff --git a/0001-tdf-144862-use-resolution-independent-positions-for-.patch b/0001-tdf-144862-use-resolution-independent-positions-for-.patch new file mode 100644 index 0000000..60c191c --- /dev/null +++ b/0001-tdf-144862-use-resolution-independent-positions-for-.patch @@ -0,0 +1,2868 @@ +From abaae9bf594e183f16a20305e86330977fbf8758 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Caol=C3=A1n=20McNamara?= +Date: Mon, 20 Dec 2021 11:38:47 +0000 +Subject: [PATCH] tdf#144862 use resolution independent positions for writer's + screen-rendering +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +in favor of pushing it down to the text renderers and leave +it to them to optimized as best they can the the rendering +to make it look as well as possible. + +Change-Id: Ic0849c091a36e1a90453771b1c91b8ff706b679e +Reviewed-on: https://gerrit.libreoffice.org/c/core/+/128418 +Tested-by: Caolán McNamara +Reviewed-by: Caolán McNamara +(cherry picked from commit 4ed26badfd6fd9190cb6e54078b41eb38cb37dca) +--- + include/basegfx/tuple/Tuple2D.hxx | 6 + + include/vcl/devicecoordinate.hxx | 11 +- + include/vcl/outdev.hxx | 10 +- + include/vcl/vcllayout.hxx | 17 +- + sw/qa/extras/layout/layout.cxx | 2 +- + sw/source/core/inc/fntcache.hxx | 8 +- + sw/source/core/inc/scriptinfo.hxx | 10 +- + sw/source/core/text/itradj.cxx | 6 +- + sw/source/core/text/porlay.cxx | 10 +- + sw/source/core/text/portxt.cxx | 4 +- + sw/source/core/txtnode/fntcache.cxx | 201 ++++++++--------------- + sw/source/uibase/config/usrpref.cxx | 2 +- + vcl/inc/ImplLayoutArgs.hxx | 6 +- + vcl/inc/impglyphitem.hxx | 4 +- + vcl/inc/quartz/salgdi.h | 4 +- + vcl/inc/salgdi.hxx | 11 ++ + vcl/inc/sallayout.hxx | 18 +- + vcl/inc/skia/osx/gdiimpl.hxx | 3 +- + vcl/inc/skia/win/gdiimpl.hxx | 4 + + vcl/inc/win/DWriteTextRenderer.hxx | 17 +- + vcl/inc/win/salgdi.h | 2 +- + vcl/inc/win/winlayout.hxx | 8 +- + vcl/qt5/QtGraphics_Text.cxx | 21 ++- + vcl/quartz/salgdi.cxx | 16 +- + vcl/skia/gdiimpl.cxx | 4 +- + vcl/skia/osx/gdiimpl.cxx | 11 +- + vcl/skia/win/gdiimpl.cxx | 78 ++++++++- + vcl/skia/x11/textrender.cxx | 18 +- + vcl/source/gdi/CommonSalLayout.cxx | 35 ++-- + vcl/source/gdi/pdfwriter_impl.cxx | 28 ++-- + vcl/source/gdi/salgdilayout.cxx | 3 +- + vcl/source/gdi/sallayout.cxx | 102 +++++++----- + vcl/source/gdi/virdev.cxx | 2 + + vcl/source/outdev/font.cxx | 10 +- + vcl/source/outdev/map.cxx | 25 ++- + vcl/source/outdev/outdev.cxx | 19 ++- + vcl/source/outdev/text.cxx | 150 ++++++++++------- + vcl/source/outdev/textline.cxx | 18 +- + vcl/source/text/ImplLayoutArgs.cxx | 18 +- + vcl/unx/generic/gdi/cairotextrender.cxx | 23 ++- + vcl/unx/generic/print/genpspgraphics.cxx | 4 +- + vcl/win/gdi/DWriteTextRenderer.cxx | 42 +++-- + vcl/win/gdi/winlayout.cxx | 31 ++-- + 43 files changed, 618 insertions(+), 404 deletions(-) + +diff --git a/include/basegfx/tuple/Tuple2D.hxx b/include/basegfx/tuple/Tuple2D.hxx +index 2007732543b6..b173ad3033c1 100644 +--- a/include/basegfx/tuple/Tuple2D.hxx ++++ b/include/basegfx/tuple/Tuple2D.hxx +@@ -73,6 +73,12 @@ public: + /// Set Y-Coordinate of 2D Tuple + void setY(TYPE fY) { mnY = fY; } + ++ /// Adjust X-Coordinate of 2D Tuple ++ void adjustX(TYPE fX) { mnX += fX; } ++ ++ /// Adjust Y-Coordinate of 2D Tuple ++ void adjustY(TYPE fY) { mnY += fY; } ++ + // comparators with tolerance + + template , int> = 0> +diff --git a/include/vcl/devicecoordinate.hxx b/include/vcl/devicecoordinate.hxx +index acece32cc204..bd0bf1ee7963 100644 +--- a/include/vcl/devicecoordinate.hxx ++++ b/include/vcl/devicecoordinate.hxx +@@ -7,23 +7,22 @@ + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +-#ifndef INCLUDED_VCL_DEVICE_COORDINATE_HXX +-#define INCLUDED_VCL_DEVICE_COORDINATE_HXX ++#pragma once + + #include + #include + +-#if VCL_FLOAT_DEVICE_PIXEL + #include ++typedef basegfx::B2DPoint DevicePoint; ++ ++#if VCL_FLOAT_DEVICE_PIXEL ++ + typedef double DeviceCoordinate; + + #else /* !VCL_FLOAT_DEVICE_PIXEL */ + +-#include + typedef sal_Int32 DeviceCoordinate; + + #endif /* ! Carpet Cushion */ + +-#endif /* NDef INCLUDED_VCL_DEVICE_COORDINATE_HXX */ +- + /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ +diff --git a/include/vcl/outdev.hxx b/include/vcl/outdev.hxx +index e8011412955c..85670e44dec3 100644 +--- a/include/vcl/outdev.hxx ++++ b/include/vcl/outdev.hxx +@@ -242,6 +242,7 @@ private: + Point maRefPoint; + AntialiasingFlags mnAntialiasing; + LanguageType meTextLanguage; ++ bool mbTextRenderModeForResolutionIndependentLayout; + + mutable bool mbMap : 1; + mutable bool mbClipRegion : 1; +@@ -489,6 +490,10 @@ public: + void SetAntialiasing( AntialiasingFlags nMode ); + AntialiasingFlags GetAntialiasing() const { return mnAntialiasing; } + ++ // Render glyphs with a mode suitable for rendering of resolution-independent layout positions. ++ void SetTextRenderModeForResolutionIndependentLayout(bool bMode); ++ bool GetTextRenderModeForResolutionIndependentLayout() const { return mbTextRenderModeForResolutionIndependentLayout; } ++ + void SetDrawMode( DrawModeFlags nDrawMode ); + DrawModeFlags GetDrawMode() const { return mnDrawMode; } + +@@ -1244,8 +1249,9 @@ public: + o3tl::span pLogicDXArray={}, SalLayoutFlags flags = SalLayoutFlags::NONE, + vcl::text::TextLayoutCache const* = nullptr, + const SalLayoutGlyphs* pGlyphs = nullptr) const; ++ + SAL_DLLPRIVATE vcl::text::ImplLayoutArgs ImplPrepareLayoutArgs( OUString&, const sal_Int32 nIndex, const sal_Int32 nLen, +- DeviceCoordinate nPixelWidth, const DeviceCoordinate* pPixelDXArray, ++ DeviceCoordinate nPixelWidth, + SalLayoutFlags flags = SalLayoutFlags::NONE, + vcl::text::TextLayoutCache const* = nullptr) const; + SAL_DLLPRIVATE std::unique_ptr +@@ -1693,6 +1699,7 @@ public: + @returns Physical point on the device. + */ + SAL_DLLPRIVATE Point ImplLogicToDevicePixel( const Point& rLogicPt ) const; ++ SAL_DLLPRIVATE DevicePoint ImplLogicToDeviceFontCoordinate(const Point& rLogicPt) const; + + /** Convert a logical width to a width in units of device pixels. + +@@ -1705,6 +1712,7 @@ public: + @returns Width in units of device pixels. + */ + SAL_DLLPRIVATE tools::Long ImplLogicWidthToDevicePixel( tools::Long nWidth ) const; ++ SAL_DLLPRIVATE double ImplLogicWidthToDeviceFontWidth(tools::Long nWidth) const; + + SAL_DLLPRIVATE DeviceCoordinate LogicWidthToDeviceCoordinate( tools::Long nWidth ) const; + +diff --git a/include/vcl/vcllayout.hxx b/include/vcl/vcllayout.hxx +index 6ea9bc61bfbb..18d4da907375 100644 +--- a/include/vcl/vcllayout.hxx ++++ b/include/vcl/vcllayout.hxx +@@ -70,11 +70,11 @@ class VCL_DLLPUBLIC SalLayout + public: + virtual ~SalLayout(); + // used by upper layers +- Point& DrawBase() { return maDrawBase; } +- const Point& DrawBase() const { return maDrawBase; } ++ DevicePoint& DrawBase() { return maDrawBase; } ++ const DevicePoint& DrawBase() const { return maDrawBase; } + Point& DrawOffset() { return maDrawOffset; } + const Point& DrawOffset() const { return maDrawOffset; } +- Point GetDrawPosition( const Point& rRelative = Point(0,0) ) const; ++ DevicePoint GetDrawPosition( const DevicePoint& rRelative = DevicePoint(0,0) ) const; + + virtual bool LayoutText( vcl::text::ImplLayoutArgs&, const SalLayoutGlyphsImpl* ) = 0; // first step of layouting + virtual void AdjustLayout( vcl::text::ImplLayoutArgs& ); // adjusting after fallback etc. +@@ -84,6 +84,11 @@ public: + int GetUnitsPerPixel() const { return mnUnitsPerPixel; } + Degree10 GetOrientation() const { return mnOrientation; } + ++ void SetTextRenderModeForResolutionIndependentLayout(bool bTextRenderModeForResolutionIndependentLayout) ++ { ++ mbTextRenderModeForResolutionIndependentLayout = bTextRenderModeForResolutionIndependentLayout; ++ } ++ + // methods using string indexing + virtual sal_Int32 GetTextBreak(DeviceCoordinate nMaxWidth, DeviceCoordinate nCharExtra, int nFactor) const = 0; + virtual DeviceCoordinate FillDXArray( std::vector* pDXArray ) const = 0; +@@ -92,7 +97,7 @@ public: + virtual bool IsKashidaPosValid ( int /*nCharPos*/ ) const { return true; } // i60594 + + // methods using glyph indexing +- virtual bool GetNextGlyph(const GlyphItem** pGlyph, Point& rPos, int& nStart, ++ virtual bool GetNextGlyph(const GlyphItem** pGlyph, DevicePoint& rPos, int& nStart, + const LogicalFontInstance** ppGlyphFont = nullptr, + const vcl::font::PhysicalFontFace** pFallbackFont = nullptr) const = 0; + virtual bool GetOutline(basegfx::B2DPolyPolygonVector&) const; +@@ -116,7 +121,9 @@ protected: + Degree10 mnOrientation; + + mutable Point maDrawOffset; +- Point maDrawBase; ++ DevicePoint maDrawBase; ++ ++ bool mbTextRenderModeForResolutionIndependentLayout; + }; + + /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ +diff --git a/sw/qa/extras/layout/layout.cxx b/sw/qa/extras/layout/layout.cxx +index 94edf650a359..267c20770bef 100644 +--- a/sw/qa/extras/layout/layout.cxx ++++ b/sw/qa/extras/layout/layout.cxx +@@ -2823,7 +2823,7 @@ CPPUNIT_TEST_FIXTURE(SwLayoutWriter, testBtlrCell) + // Without the accompanying fix in place, this test would have failed with 'Expected: 1979; + // Actual : 2129', i.e. the gray background of the "AAA2." text was too close to the right edge + // of the text portion. Now it's exactly behind the text portion. +- assertXPath(pXmlDoc, "//rect[@top='2159']", "left", "1979"); ++ assertXPath(pXmlDoc, "(//rect)[2]", "left", "1979"); + + // Without the accompanying fix in place, this test would have failed with 'Expected: 269; + // Actual : 0', i.e. the AAA2 frame was not visible due to 0 width. +diff --git a/sw/source/core/inc/fntcache.hxx b/sw/source/core/inc/fntcache.hxx +index 0bdc4757d9b3..4285165a3336 100644 +--- a/sw/source/core/inc/fntcache.hxx ++++ b/sw/source/core/inc/fntcache.hxx +@@ -66,13 +66,13 @@ extern SwFntObj *pLastFont; + */ + struct SwTextGlyphsKey + { +- VclPtr m_pOutputDevice; ++ VclPtr m_pOutputDevice; + OUString m_aText; + sal_Int32 m_nIndex; + sal_Int32 m_nLength; + size_t mnHashCode; + +- SwTextGlyphsKey(VclPtr const& pOutputDevice, const OUString & sText, sal_Int32 nIndex, sal_Int32 nLength); ++ SwTextGlyphsKey(const OutputDevice* pOutputDevice, const OUString & sText, sal_Int32 nIndex, sal_Int32 nLength); + bool operator==(SwTextGlyphsKey const & rhs) const; + }; + struct SwTextGlyphsKeyHash +@@ -113,6 +113,10 @@ class SwFntObj final : public SwCacheObj + /// Cache of already calculated layout glyphs and text widths. + SwTextGlyphsMap m_aTextGlyphs; + ++ void GetTextArray(const OutputDevice& rOutputDevice, const OUString& rStr, ++ std::vector& rDXAry, sal_Int32 nIndex, sal_Int32 nLen, ++ bool bCaching); ++ + static tools::Long s_nPixWidth; + static MapMode *s_pPixMap; + +diff --git a/sw/source/core/inc/scriptinfo.hxx b/sw/source/core/inc/scriptinfo.hxx +index cd479034ed06..cfe9ef3e55fb 100644 +--- a/sw/source/core/inc/scriptinfo.hxx ++++ b/sw/source/core/inc/scriptinfo.hxx +@@ -281,8 +281,6 @@ public: + positions in the kerning array. + @param pKernArray + The printers kerning array. Optional. +- @param pScrArray +- The screen kerning array. Optional. + @param nStt + Start referring to the paragraph. + @param nLen +@@ -291,7 +289,7 @@ public: + The value which has to be added to a kashida opportunity. + @return The number of kashida opportunities in the given range + */ +- sal_Int32 KashidaJustify( sal_Int32* pKernArray, sal_Int32* pScrArray, ++ sal_Int32 KashidaJustify( sal_Int32* pKernArray, + TextFrameIndex nStt, TextFrameIndex nLen, tools::Long nSpaceAdd = 0) const; + + /** Clears array of kashidas marked as invalid +@@ -354,8 +352,6 @@ public: + The String + @param pKernArray + The printers kerning array. Optional. +- @param pScrArray +- The screen kerning array. Optional. + @param nIdx + Start referring to the paragraph. + @param nLen +@@ -365,7 +361,7 @@ public: + @return The number of extra spaces in the given range + */ + static TextFrameIndex ThaiJustify( const OUString& rText, sal_Int32* pKernArray, +- sal_Int32* pScrArray, TextFrameIndex nIdx, ++ TextFrameIndex nIdx, + TextFrameIndex nLen, + TextFrameIndex nNumberOfBlanks = TextFrameIndex(0), + tools::Long nSpaceAdd = 0 ); +@@ -374,7 +370,7 @@ public: + TextFrameIndex nPos, TextFrameIndex nEnd, LanguageType aLang); + + static void CJKJustify( const OUString& rText, sal_Int32* pKernArray, +- sal_Int32* pScrArray, TextFrameIndex nStt, ++ TextFrameIndex nStt, + TextFrameIndex nLen, LanguageType aLang, + tools::Long nSpaceAdd, bool bIsSpaceStop ); + +diff --git a/sw/source/core/text/itradj.cxx b/sw/source/core/text/itradj.cxx +index d664602bf3ce..a952ce7649c2 100644 +--- a/sw/source/core/text/itradj.cxx ++++ b/sw/source/core/text/itradj.cxx +@@ -122,7 +122,7 @@ static bool lcl_CheckKashidaPositions( SwScriptInfo& rSI, SwTextSizeInfo& rInf, + // total number of kashida positions, or the number of kashida positions after some positions + // have been dropped. + // Here we want the clean total, which is OK: We have called ClearKashidaInvalid() before. +- rKashidas = rSI.KashidaJustify ( nullptr, nullptr, rItr.GetStart(), rItr.GetLength() ); ++ rKashidas = rSI.KashidaJustify(nullptr, rItr.GetStart(), rItr.GetLength()); + + if (rKashidas <= 0) // nothing to do + return true; +@@ -147,7 +147,7 @@ static bool lcl_CheckKashidaPositions( SwScriptInfo& rSI, SwTextSizeInfo& rInf, + + if (nNext == TextFrameIndex(COMPLETE_STRING) || nNext > nEnd) + nNext = nEnd; +- sal_Int32 nKashidasInAttr = rSI.KashidaJustify ( nullptr, nullptr, nIdx, nNext - nIdx ); ++ sal_Int32 nKashidasInAttr = rSI.KashidaJustify(nullptr, nIdx, nNext - nIdx); + if (nKashidasInAttr > 0) + { + // Kashida glyph looks suspicious, skip Kashida justification +@@ -212,7 +212,7 @@ static bool lcl_CheckKashidaWidth ( SwScriptInfo& rSI, SwTextSizeInfo& rInf, SwT + + if (nNext == TextFrameIndex(COMPLETE_STRING) || nNext > nEnd) + nNext = nEnd; +- sal_Int32 nKashidasInAttr = rSI.KashidaJustify ( nullptr, nullptr, nIdx, nNext - nIdx ); ++ sal_Int32 nKashidasInAttr = rSI.KashidaJustify(nullptr, nIdx, nNext - nIdx); + + tools::Long nFontMinKashida = rInf.GetOut()->GetMinKashida(); + if ( nFontMinKashida && nKashidasInAttr > 0 && SwScriptInfo::IsArabicText( rInf.GetText(), nIdx, nNext - nIdx ) ) +diff --git a/sw/source/core/text/porlay.cxx b/sw/source/core/text/porlay.cxx +index 3db5d2ab0ac3..bd9d6a0e143c 100644 +--- a/sw/source/core/text/porlay.cxx ++++ b/sw/source/core/text/porlay.cxx +@@ -2183,7 +2183,6 @@ tools::Long SwScriptInfo::Compress(sal_Int32* pKernArray, TextFrameIndex nIdx, T + // have been dropped, depending on the state of the m_KashidaInvalid set. + + sal_Int32 SwScriptInfo::KashidaJustify( sal_Int32* pKernArray, +- sal_Int32* pScrArray, + TextFrameIndex const nStt, + TextFrameIndex const nLen, + tools::Long nSpaceAdd ) const +@@ -2253,8 +2252,6 @@ sal_Int32 SwScriptInfo::KashidaJustify( sal_Int32* pKernArray, + while ( nArrayPos < nArrayEnd ) + { + pKernArray[ sal_Int32(nArrayPos) ] += nKashAdd; +- if ( pScrArray ) +- pScrArray[ sal_Int32(nArrayPos) ] += nKashAdd; + ++nArrayPos; + } + nKashAdd += nSpaceAdd; +@@ -2442,7 +2439,7 @@ void SwScriptInfo::MarkKashidasInvalid(sal_Int32 const nCnt, + } + + TextFrameIndex SwScriptInfo::ThaiJustify( const OUString& rText, sal_Int32* pKernArray, +- sal_Int32* pScrArray, TextFrameIndex const nStt, ++ TextFrameIndex const nStt, + TextFrameIndex const nLen, + TextFrameIndex nNumberOfBlanks, + tools::Long nSpaceAdd ) +@@ -2474,7 +2471,6 @@ TextFrameIndex SwScriptInfo::ThaiJustify( const OUString& rText, sal_Int32* pKer + } + + if ( pKernArray ) pKernArray[ nI ] += nSpaceSum; +- if ( pScrArray ) pScrArray[ nI ] += nSpaceSum; + } + + return nCnt; +@@ -2775,7 +2771,7 @@ TextFrameIndex SwScriptInfo::CountCJKCharacters(const OUString &rText, + } + + void SwScriptInfo::CJKJustify( const OUString& rText, sal_Int32* pKernArray, +- sal_Int32* pScrArray, TextFrameIndex const nStt, ++ TextFrameIndex const nStt, + TextFrameIndex const nLen, LanguageType aLang, + tools::Long nSpaceAdd, bool bIsSpaceStop ) + { +@@ -2798,8 +2794,6 @@ void SwScriptInfo::CJKJustify( const OUString& rText, sal_Int32* pKernArray, + nSpaceSum += nSpaceAdd; + } + pKernArray[ nI ] += nSpaceSum; +- if ( pScrArray ) +- pScrArray[ nI ] += nSpaceSum; + } + } + /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ +diff --git a/sw/source/core/text/portxt.cxx b/sw/source/core/text/portxt.cxx +index 7def7badcc7a..219036aa834d 100644 +--- a/sw/source/core/text/portxt.cxx ++++ b/sw/source/core/text/portxt.cxx +@@ -113,7 +113,7 @@ static TextFrameIndex lcl_AddSpace(const SwTextSizeInfo &rInf, + { + if ( SwScriptInfo::IsArabicText( *pStr, nPos, nEnd - nPos ) && pSI->CountKashida() ) + { +- const sal_Int32 nKashRes = pSI->KashidaJustify( nullptr, nullptr, nPos, nEnd - nPos ); ++ const sal_Int32 nKashRes = pSI->KashidaJustify(nullptr, nPos, nEnd - nPos); + // i60591: need to check result of KashidaJustify + // determine if kashida justification is applicable + if (nKashRes != -1) +@@ -129,7 +129,7 @@ static TextFrameIndex lcl_AddSpace(const SwTextSizeInfo &rInf, + + if ( LANGUAGE_THAI == aLang ) + { +- nCnt = SwScriptInfo::ThaiJustify( *pStr, nullptr, nullptr, nPos, nEnd - nPos ); ++ nCnt = SwScriptInfo::ThaiJustify(*pStr, nullptr, nPos, nEnd - nPos); + + const SwLinePortion* pPor = rPor.GetNextPortion(); + if ( pPor && ( pPor->IsKernPortion() || +diff --git a/sw/source/core/txtnode/fntcache.cxx b/sw/source/core/txtnode/fntcache.cxx +index 93e83c2013e9..6ceaeaf99206 100644 +--- a/sw/source/core/txtnode/fntcache.cxx ++++ b/sw/source/core/txtnode/fntcache.cxx +@@ -77,11 +77,11 @@ static vcl::DeleteOnDeinit< VclPtr > s_pFntObjPixOut {}; + * Defines a substring on a given output device, to be used as an std::unordered_map<> + * key. + */ +-SwTextGlyphsKey::SwTextGlyphsKey(VclPtr const& pOutputDevice, const OUString & sText, sal_Int32 nIndex, sal_Int32 nLength) ++SwTextGlyphsKey::SwTextGlyphsKey(const OutputDevice* pOutputDevice, const OUString & sText, sal_Int32 nIndex, sal_Int32 nLength) + : m_pOutputDevice(pOutputDevice), m_aText(sText), m_nIndex(nIndex), m_nLength(nLength) + { + mnHashCode = 0; +- o3tl::hash_combine(mnHashCode, pOutputDevice.get()); ++ o3tl::hash_combine(mnHashCode, pOutputDevice); + o3tl::hash_combine(mnHashCode, m_nIndex); + o3tl::hash_combine(mnHashCode, m_nLength); + if(m_nLength >= 0 && m_nIndex >= 0 && m_nIndex + m_nLength <= m_aText.getLength()) +@@ -855,6 +855,18 @@ static void lcl_DrawLineForWrongListData( + rInf.GetOut().Pop(); + } + ++void SwFntObj::GetTextArray(const OutputDevice& rDevice, const OUString& rStr, std::vector& rDXAry, ++ sal_Int32 nIndex, sal_Int32 nLen, bool bCaching) ++{ ++ SalLayoutGlyphs* pLayoutCache = nullptr; ++ if (bCaching) ++ { ++ SwTextGlyphsKey aGlyphsKey{&rDevice, rStr, nIndex, nLen}; ++ pLayoutCache = GetCachedSalLayoutGlyphs(aGlyphsKey); ++ } ++ rDevice.GetTextArray(rStr, &rDXAry, nIndex, nLen, nullptr, pLayoutCache); ++} ++ + void SwFntObj::DrawText( SwDrawTextInfo &rInf ) + { + OSL_ENSURE( rInf.GetShell(), "SwFntObj::DrawText without shell" ); +@@ -1031,11 +1043,11 @@ void SwFntObj::DrawText( SwDrawTextInfo &rInf ) + std::vector aKernArray; + + if ( m_pPrinter ) +- m_pPrinter->GetTextArray( rInf.GetText(), &aKernArray, +- sal_Int32(rInf.GetIdx()), sal_Int32(rInf.GetLen())); ++ GetTextArray(*m_pPrinter, rInf.GetText(), aKernArray, ++ sal_Int32(rInf.GetIdx()), sal_Int32(rInf.GetLen()), false); + else +- rInf.GetOut().GetTextArray( rInf.GetText(), &aKernArray, +- sal_Int32(rInf.GetIdx()), sal_Int32(rInf.GetLen())); ++ GetTextArray(rInf.GetOut(), rInf.GetText(), aKernArray, ++ sal_Int32(rInf.GetIdx()), sal_Int32(rInf.GetLen()), false); + + // Change the average width per character to an appropriate grid width + // basically get the ratio of the avg width to the grid unit width, then +@@ -1138,11 +1150,11 @@ void SwFntObj::DrawText( SwDrawTextInfo &rInf ) + std::vector aKernArray; + + if ( m_pPrinter ) +- m_pPrinter->GetTextArray( rInf.GetText(), &aKernArray, +- sal_Int32(rInf.GetIdx()), sal_Int32(rInf.GetLen())); ++ GetTextArray(*m_pPrinter, rInf.GetText(), aKernArray, ++ sal_Int32(rInf.GetIdx()), sal_Int32(rInf.GetLen()), false); + else +- rInf.GetOut().GetTextArray( rInf.GetText(), &aKernArray, +- sal_Int32(rInf.GetIdx()), sal_Int32(rInf.GetLen())); ++ GetTextArray(rInf.GetOut(), rInf.GetText(), aKernArray, ++ sal_Int32(rInf.GetIdx()), sal_Int32(rInf.GetLen()), false); + if ( bSwitchH2V ) + rInf.GetFrame()->SwitchHorizontalToVertical( aTextOriginPos ); + if ( rInf.GetSpace() || rInf.GetKanaComp()) +@@ -1278,8 +1290,8 @@ void SwFntObj::DrawText( SwDrawTextInfo &rInf ) + if( rInf.GetSpace() || rInf.GetKanaComp() ) + { + std::vector aKernArray; +- rInf.GetOut().GetTextArray( rInf.GetText(), &aKernArray, +- sal_Int32(rInf.GetIdx()), sal_Int32(rInf.GetLen())); ++ GetTextArray(rInf.GetOut(), rInf.GetText(), aKernArray, ++ sal_Int32(rInf.GetIdx()), sal_Int32(rInf.GetLen()), false); + + if( bStretch ) + { +@@ -1337,7 +1349,7 @@ void SwFntObj::DrawText( SwDrawTextInfo &rInf ) + + if (!MsLangId::isKorean(aLang)) + { +- SwScriptInfo::CJKJustify( rInf.GetText(), aKernArray.data(), nullptr, ++ SwScriptInfo::CJKJustify( rInf.GetText(), aKernArray.data(), + rInf.GetIdx(), rInf.GetLen(), aLang, nSpaceAdd, rInf.IsSpaceStop() ); + + bSpecialJust = true; +@@ -1351,7 +1363,7 @@ void SwFntObj::DrawText( SwDrawTextInfo &rInf ) + if ( SwScriptInfo::IsArabicText( rInf.GetText(), rInf.GetIdx(), rInf.GetLen() ) ) + { + if ( pSI && pSI->CountKashida() && +- pSI->KashidaJustify( aKernArray.data(), nullptr, rInf.GetIdx(), ++ pSI->KashidaJustify( aKernArray.data(), rInf.GetIdx(), + rInf.GetLen(), nSpaceAdd ) != -1 ) + { + bSpecialJust = true; +@@ -1369,7 +1381,7 @@ void SwFntObj::DrawText( SwDrawTextInfo &rInf ) + { + // Use rInf.GetSpace() because it has more precision than + // nSpaceAdd: +- SwScriptInfo::ThaiJustify( rInf.GetText(), aKernArray.data(), nullptr, ++ SwScriptInfo::ThaiJustify( rInf.GetText(), aKernArray.data(), + rInf.GetIdx(), rInf.GetLen(), + rInf.GetNumberOfBlanks(), + rInf.GetSpace() ); +@@ -1478,6 +1490,10 @@ void SwFntObj::DrawText( SwDrawTextInfo &rInf ) + + else + { ++ const bool bOrigTextRenderModeForResolutionIndependentLayout(rInf.GetOut().GetTextRenderModeForResolutionIndependentLayout()); ++ // set text render mode to suit use of resolution independent text layout ++ rInf.GetOut().SetTextRenderModeForResolutionIndependentLayout(true); ++ + const OUString* pStr = &rInf.GetText(); + + OUString aStr; +@@ -1487,14 +1503,6 @@ void SwFntObj::DrawText( SwDrawTextInfo &rInf ) + bBullet = false; + std::vector aKernArray; + CreateScrFont( *rInf.GetShell(), rInf.GetOut() ); +- tools::Long nScrPos; +- +- // get screen array +- std::vector aScrArray; +- SwTextGlyphsKey aGlyphsKey{ &rInf.GetOut(), rInf.GetText(), sal_Int32(rInf.GetIdx()), sal_Int32(rInf.GetLen()) }; +- SalLayoutGlyphs* pGlyphs = GetCachedSalLayoutGlyphs(aGlyphsKey); +- rInf.GetOut().GetTextArray( rInf.GetText(), &aScrArray, +- sal_Int32(rInf.GetIdx()), sal_Int32(rInf.GetLen()), nullptr, pGlyphs); + + // OLE: no printer available + // OSL_ENSURE( pPrinter, "DrawText needs pPrinter" ) +@@ -1506,15 +1514,13 @@ void SwFntObj::DrawText( SwDrawTextInfo &rInf ) + if( !m_pPrtFont->IsSameInstance( m_pPrinter->GetFont() ) ) + m_pPrinter->SetFont( *m_pPrtFont ); + } +- aGlyphsKey = SwTextGlyphsKey{ m_pPrinter, rInf.GetText(), sal_Int32(rInf.GetIdx()), sal_Int32(rInf.GetLen()) }; +- pGlyphs = GetCachedSalLayoutGlyphs(aGlyphsKey); +- m_pPrinter->GetTextArray(rInf.GetText(), &aKernArray, +- sal_Int32(rInf.GetIdx()), sal_Int32(rInf.GetLen()), nullptr, pGlyphs); ++ GetTextArray(*m_pPrinter, rInf.GetText(), aKernArray, ++ sal_Int32(rInf.GetIdx()), sal_Int32(rInf.GetLen()), true); + } + else + { +- rInf.GetOut().GetTextArray( rInf.GetText(), &aKernArray, +- sal_Int32(rInf.GetIdx()), sal_Int32(rInf.GetLen())); ++ GetTextArray(rInf.GetOut(), rInf.GetText(), aKernArray, ++ sal_Int32(rInf.GetIdx()), sal_Int32(rInf.GetLen()), true); + } + + // Modify Printer and ScreenArrays for special justifications +@@ -1532,10 +1538,6 @@ void SwFntObj::DrawText( SwDrawTextInfo &rInf ) + pSI && pSI->CountCompChg() && + lcl_IsMonoSpaceFont( rInf.GetOut() ) ) + { +- Point aTmpPos( aTextOriginPos ); +- pSI->Compress( aScrArray.data(), rInf.GetIdx(), rInf.GetLen(), +- rInf.GetKanaComp(), +- o3tl::narrowing(m_aFont.GetFontSize().Height()), lcl_IsFullstopCentered( rInf.GetOut() ), &aTmpPos ); + pSI->Compress( aKernArray.data(), rInf.GetIdx(), rInf.GetLen(), + rInf.GetKanaComp(), + o3tl::narrowing(m_aFont.GetFontSize().Height()), lcl_IsFullstopCentered( rInf.GetOut() ), &aTextOriginPos ); +@@ -1548,7 +1550,7 @@ void SwFntObj::DrawText( SwDrawTextInfo &rInf ) + + if (!MsLangId::isKorean(aLang)) + { +- SwScriptInfo::CJKJustify( rInf.GetText(), aKernArray.data(), aScrArray.data(), ++ SwScriptInfo::CJKJustify( rInf.GetText(), aKernArray.data(), + rInf.GetIdx(), rInf.GetLen(), aLang, nSpaceAdd, rInf.IsSpaceStop() ); + + nSpaceAdd = 0; +@@ -1561,7 +1563,7 @@ void SwFntObj::DrawText( SwDrawTextInfo &rInf ) + if ( SwScriptInfo::IsArabicText( rInf.GetText(), rInf.GetIdx(), rInf.GetLen() ) ) + { + if ( pSI && pSI->CountKashida() && +- pSI->KashidaJustify( aKernArray.data(), aScrArray.data(), rInf.GetIdx(), ++ pSI->KashidaJustify( aKernArray.data(), rInf.GetIdx(), + rInf.GetLen(), nSpaceAdd ) != -1 ) + nSpaceAdd = 0; + else +@@ -1577,7 +1579,7 @@ void SwFntObj::DrawText( SwDrawTextInfo &rInf ) + if ( LANGUAGE_THAI == aLang ) + { + SwScriptInfo::ThaiJustify( rInf.GetText(), aKernArray.data(), +- aScrArray.data(), rInf.GetIdx(), ++ rInf.GetIdx(), + rInf.GetLen(), + rInf.GetNumberOfBlanks(), + rInf.GetSpace() ); +@@ -1588,8 +1590,6 @@ void SwFntObj::DrawText( SwDrawTextInfo &rInf ) + } + } + +- nScrPos = aScrArray[ 0 ]; +- + if( bBullet ) + { + // !!! HACK !!! +@@ -1661,13 +1661,6 @@ void SwFntObj::DrawText( SwDrawTextInfo &rInf ) + } + else + { +- sal_Unicode nCh; +- +- // In case of Pair Kerning the printer influence on the positioning +- // grows +- const int nMul = m_pPrtFont->GetKerning() != FontKerning::NONE ? 1 : 3; +- const int nDiv = nMul+1; +- + // nSpaceSum contains the sum of the intermediate space distributed + // among Spaces by the Justification. + // The Spaces themselves will be positioned in the middle of the +@@ -1686,43 +1679,25 @@ void SwFntObj::DrawText( SwDrawTextInfo &rInf ) + nSpaceSum = nHalfSpace; + for (sal_Int32 i = 1; i < sal_Int32(nCnt); ++i, nKernSum += rInf.GetKern()) + { +- nCh = rInf.GetText()[sal_Int32(rInf.GetIdx()) + i]; +- +- tools::Long nScr = aScrArray[ i ] - aScrArray[ i - 1 ]; ++ sal_Unicode nCh = rInf.GetText()[sal_Int32(rInf.GetIdx()) + i]; + +- // If there is an (ex-)Space before us, position optimally, +- // i.e., our right margin to the 100% printer position; +- // if we _are_ an ex-Space, position us left-aligned to the +- // printer position. +- if ( nCh == CH_BLANK ) ++ // Apply SpaceSum ++ if (cChPrev == CH_BLANK) + { +- nScrPos = aKernArray[i-1] + nScr; ++ // no Pixel is lost: ++ nSpaceSum += nOtherHalf; ++ } + +- if ( cChPrev == CH_BLANK ) +- nSpaceSum += nOtherHalf; ++ if (nCh == CH_BLANK) ++ { + if (i + 1 == sal_Int32(nCnt)) + nSpaceSum += nSpaceAdd; + else + nSpaceSum += nHalfSpace; + } +- else +- { +- if ( cChPrev == CH_BLANK ) +- { +- nScrPos = aKernArray[i-1] + nScr; +- // no Pixel is lost: +- nSpaceSum += nOtherHalf; +- } +- else if ( cChPrev == '-' ) +- nScrPos = aKernArray[i-1] + nScr; +- else +- { +- nScrPos += nScr; +- nScrPos = ( nMul * nScrPos + aKernArray[i] ) / nDiv; +- } +- } ++ + cChPrev = nCh; +- aKernArray[i-1] = nScrPos - nScr + nKernSum + nSpaceSum; ++ aKernArray[i-1] += nKernSum + nSpaceSum; + // In word line mode and for Arabic, we disabled the half space trick. If a portion + // ends with a blank, the full nSpaceAdd value has been added to the character in + // front of the blank. This leads to painting artifacts, therefore we remove the +@@ -1843,8 +1818,8 @@ void SwFntObj::DrawText( SwDrawTextInfo &rInf ) + sal_Int32 nTmpIdx = bBullet + ? (rInf.GetIdx() ? 1 : 0) + : sal_Int32(rInf.GetIdx()); +- aGlyphsKey = SwTextGlyphsKey{ &rInf.GetOut(), *pStr, nTmpIdx, nLen }; +- pGlyphs = GetCachedSalLayoutGlyphs(aGlyphsKey); ++ SwTextGlyphsKey aGlyphsKey{ &rInf.GetOut(), *pStr, nTmpIdx, nLen }; ++ SalLayoutGlyphs* pGlyphs = GetCachedSalLayoutGlyphs(aGlyphsKey); + rInf.GetOut().DrawTextArray( aTextOriginPos, *pStr, aKernArray, + nTmpIdx , nLen, SalLayoutFlags::NONE, pGlyphs ); + if (bBullet) +@@ -1894,6 +1869,8 @@ void SwFntObj::DrawText( SwDrawTextInfo &rInf ) + } + } + } ++ ++ rInf.GetOut().SetTextRenderModeForResolutionIndependentLayout(bOrigTextRenderModeForResolutionIndependentLayout); + } + } + +@@ -1997,17 +1974,14 @@ Size SwFntObj::GetTextSize( SwDrawTextInfo& rInf ) + { + if( !m_pPrtFont->IsSameInstance( m_pPrinter->GetFont() ) ) + m_pPrinter->SetFont(*m_pPrtFont); +- aTextSize.setWidth( m_pPrinter->GetTextWidth( rInf.GetText(), +- sal_Int32(rInf.GetIdx()), sal_Int32(nLn))); + aTextSize.setHeight( m_pPrinter->GetTextHeight() ); + std::vector aKernArray; + CreateScrFont( *rInf.GetShell(), rInf.GetOut() ); + if( !GetScrFont()->IsSameInstance( rInf.GetOut().GetFont() ) ) + rInf.GetOut().SetFont( *m_pScrFont ); +- tools::Long nScrPos; + +- m_pPrinter->GetTextArray(rInf.GetText(), &aKernArray, +- sal_Int32(rInf.GetIdx()), sal_Int32(nLn)); ++ GetTextArray(*m_pPrinter, rInf.GetText(), aKernArray, ++ sal_Int32(rInf.GetIdx()), sal_Int32(nLn), false); + if( bCompress ) + rInf.SetKanaDiff( rInf.GetScriptInfo()->Compress( aKernArray.data(), + rInf.GetIdx(), nLn, rInf.GetKanaComp(), +@@ -2015,50 +1989,7 @@ Size SwFntObj::GetTextSize( SwDrawTextInfo& rInf ) + else + rInf.SetKanaDiff( 0 ); + +- if ( rInf.GetKanaDiff() ) +- nScrPos = aKernArray[ sal_Int32(nLn) - 1 ]; +- else +- { +- std::vector aScrArray; +- rInf.GetOut().GetTextArray( rInf.GetText(), &aScrArray, +- sal_Int32(rInf.GetIdx()), sal_Int32(rInf.GetLen())); +- nScrPos = aScrArray[ 0 ]; +- TextFrameIndex nCnt(rInf.GetText().getLength()); +- if ( nCnt < rInf.GetIdx() ) +- nCnt = TextFrameIndex(0); // assert??? +- else +- nCnt = nCnt - rInf.GetIdx(); +- nCnt = std::min(nCnt, nLn); +- sal_Unicode nChPrev = rInf.GetText()[ sal_Int32(rInf.GetIdx()) ]; +- +- sal_Unicode nCh; +- +- // In case of Pair Kerning the printer influence on the positioning +- // grows +- const int nMul = m_pPrtFont->GetKerning() != FontKerning::NONE ? 1 : 3; +- const int nDiv = nMul+1; +- for (sal_Int32 i = 1; i < sal_Int32(nCnt); i++) +- { +- nCh = rInf.GetText()[ sal_Int32(rInf.GetIdx()) + i ]; +- tools::Long nScr = aScrArray[ i ] - aScrArray[ i - 1 ]; +- if ( nCh == CH_BLANK ) +- nScrPos = aKernArray[i-1]+nScr; +- else +- { +- if ( nChPrev == CH_BLANK || nChPrev == '-' ) +- nScrPos = aKernArray[i-1]+nScr; +- else +- { +- nScrPos += nScr; +- nScrPos = ( nMul * nScrPos + aKernArray[i] ) / nDiv; +- } +- } +- nChPrev = nCh; +- aKernArray[i-1] = nScrPos - nScr; +- } +- } +- +- aTextSize.setWidth( nScrPos ); ++ aTextSize.setWidth(aKernArray[sal_Int32(nLn) - 1]); + } + else + { +@@ -2067,8 +1998,8 @@ Size SwFntObj::GetTextSize( SwDrawTextInfo& rInf ) + if( bCompress ) + { + std::vector aKernArray; +- rInf.GetOut().GetTextArray( rInf.GetText(), &aKernArray, +- sal_Int32(rInf.GetIdx()), sal_Int32(nLn)); ++ GetTextArray(rInf.GetOut(), rInf.GetText(), aKernArray, ++ sal_Int32(rInf.GetIdx()), sal_Int32(nLn), false); + rInf.SetKanaDiff( rInf.GetScriptInfo()->Compress( aKernArray.data(), + rInf.GetIdx(), nLn, rInf.GetKanaComp(), + o3tl::narrowing(m_aFont.GetFontSize().Height()) ,lcl_IsFullstopCentered( rInf.GetOut() ) ) ); +@@ -2110,14 +2041,14 @@ TextFrameIndex SwFntObj::GetModelPositionForViewPoint(SwDrawTextInfo &rInf) + { + m_pPrinter->SetLayoutMode( rInf.GetOut().GetLayoutMode() ); + m_pPrinter->SetDigitLanguage( rInf.GetOut().GetDigitLanguage() ); +- SwTextGlyphsKey aGlyphsKey{ m_pPrinter, rInf.GetText(), sal_Int32(rInf.GetIdx()), sal_Int32(rInf.GetLen()) }; +- SalLayoutGlyphs* pGlyphs = GetCachedSalLayoutGlyphs(aGlyphsKey); +- m_pPrinter->GetTextArray( rInf.GetText(), &aKernArray, +- sal_Int32(rInf.GetIdx()), sal_Int32(rInf.GetLen()), nullptr, pGlyphs); ++ GetTextArray(*m_pPrinter, rInf.GetText(), aKernArray, ++ sal_Int32(rInf.GetIdx()), sal_Int32(rInf.GetLen()), true); + } + else +- rInf.GetOut().GetTextArray( rInf.GetText(), &aKernArray, +- sal_Int32(rInf.GetIdx()), sal_Int32(rInf.GetLen())); ++ { ++ GetTextArray(rInf.GetOut(), rInf.GetText(), aKernArray, ++ sal_Int32(rInf.GetIdx()), sal_Int32(rInf.GetLen()), false); ++ } + + const SwScriptInfo* pSI = rInf.GetScriptInfo(); + if ( rInf.GetFont() && rInf.GetLen() ) +@@ -2142,7 +2073,7 @@ TextFrameIndex SwFntObj::GetModelPositionForViewPoint(SwDrawTextInfo &rInf) + + if (!MsLangId::isKorean(aLang)) + { +- SwScriptInfo::CJKJustify( rInf.GetText(), aKernArray.data(), nullptr, ++ SwScriptInfo::CJKJustify( rInf.GetText(), aKernArray.data(), + rInf.GetIdx(), rInf.GetLen(), aLang, nSpaceAdd, rInf.IsSpaceStop() ); + + nSpaceAdd = 0; +@@ -2156,7 +2087,7 @@ TextFrameIndex SwFntObj::GetModelPositionForViewPoint(SwDrawTextInfo &rInf) + if ( SwScriptInfo::IsArabicText( rInf.GetText(), rInf.GetIdx(), rInf.GetLen() ) ) + { + if ( pSI && pSI->CountKashida() && +- pSI->KashidaJustify( aKernArray.data(), nullptr, rInf.GetIdx(), rInf.GetLen(), ++ pSI->KashidaJustify( aKernArray.data(), rInf.GetIdx(), rInf.GetLen(), + nSpaceAdd ) != -1 ) + nSpaceAdd = 0; + } +@@ -2169,7 +2100,7 @@ TextFrameIndex SwFntObj::GetModelPositionForViewPoint(SwDrawTextInfo &rInf) + + if ( LANGUAGE_THAI == aLang ) + { +- SwScriptInfo::ThaiJustify( rInf.GetText(), aKernArray.data(), nullptr, ++ SwScriptInfo::ThaiJustify( rInf.GetText(), aKernArray.data(), + rInf.GetIdx(), rInf.GetLen(), + rInf.GetNumberOfBlanks(), + rInf.GetSpace() ); +diff --git a/sw/source/uibase/config/usrpref.cxx b/sw/source/uibase/config/usrpref.cxx +index 0c759a78cf94..e7b43df54c34 100644 +--- a/sw/source/uibase/config/usrpref.cxx ++++ b/sw/source/uibase/config/usrpref.cxx +@@ -388,7 +388,7 @@ void SwLayoutViewConfig::Load() + case 16: m_rParent.SetViewLayoutBookMode(bSet); break;// "ViewLayout/BookMode", + case 17: m_rParent.SetDefaultPageMode(bSet,true); break;// "Other/IsSquaredPageMode", + case 18: m_rParent.SetApplyCharUnit(bSet, true); break;// "Other/ApplyUserChar" +- case 19: m_rParent.SetShowScrollBarTips(bSet); break;// "Window/ShowScrollBarTips", ++ case 29: m_rParent.SetShowScrollBarTips(bSet); break;// "Window/ShowScrollBarTips", + } + } + } +diff --git a/vcl/inc/ImplLayoutArgs.hxx b/vcl/inc/ImplLayoutArgs.hxx +index 865470b7897a..fa94562ca86c 100644 +--- a/vcl/inc/ImplLayoutArgs.hxx ++++ b/vcl/inc/ImplLayoutArgs.hxx +@@ -35,7 +35,8 @@ public: + vcl::text::TextLayoutCache const* m_pTextLayoutCache; + + // positioning related inputs +- const DeviceCoordinate* mpDXArray; // in pixel units ++ const DeviceCoordinate* mpDXArray; // in integer pixel units ++ const double* mpAltNaturalDXArray; // in floating point pixel units + DeviceCoordinate mnLayoutWidth; // in pixel units + Degree10 mnOrientation; // in 0-3600 system + +@@ -48,12 +49,15 @@ public: + + void SetLayoutWidth(DeviceCoordinate nWidth); + void SetDXArray(const DeviceCoordinate* pDXArray); ++ void SetAltNaturalDXArray(const double* pDXArray); + void SetOrientation(Degree10 nOrientation); + + void ResetPos(); + bool GetNextPos(int* nCharPos, bool* bRTL); + bool GetNextRun(int* nMinRunPos, int* nEndRunPos, bool* bRTL); + void AddFallbackRun(int nMinRunPos, int nEndRunPos, bool bRTL); ++ bool HasDXArray() const { return mpDXArray || mpAltNaturalDXArray; } ++ + // methods used by BiDi and glyph fallback + bool HasFallbackRun() const; + bool PrepareFallback(const SalLayoutGlyphsImpl* pGlyphsImpl); +diff --git a/vcl/inc/impglyphitem.hxx b/vcl/inc/impglyphitem.hxx +index ca9f7cf5c052..4fc48f1ca2a2 100644 +--- a/vcl/inc/impglyphitem.hxx ++++ b/vcl/inc/impglyphitem.hxx +@@ -58,10 +58,10 @@ class VCL_DLLPUBLIC GlyphItem + GlyphItemFlags m_nFlags; + + public: +- Point m_aLinearPos; // absolute position of non rotated string ++ DevicePoint m_aLinearPos; // absolute position of non rotated string + sal_Int32 m_nNewWidth; // width after adjustments + +- GlyphItem(int nCharPos, int nCharCount, sal_GlyphId aGlyphId, const Point& rLinearPos, ++ GlyphItem(int nCharPos, int nCharCount, sal_GlyphId aGlyphId, const DevicePoint& rLinearPos, + GlyphItemFlags nFlags, int nOrigWidth, int nXOffset) + : m_nOrigWidth(nOrigWidth) + , m_nCharPos(nCharPos) +diff --git a/vcl/inc/quartz/salgdi.h b/vcl/inc/quartz/salgdi.h +index f0aa925c6083..569a14a6d3fc 100644 +--- a/vcl/inc/quartz/salgdi.h ++++ b/vcl/inc/quartz/salgdi.h +@@ -297,7 +297,7 @@ public: + const tools::Rectangle &rControlRegion, + ControlState nState, + const ImplControlValue &aValue) = 0; +- virtual void drawTextLayout(const GenericSalLayout& layout) = 0; ++ virtual void drawTextLayout(const GenericSalLayout& layout, bool bTextRenderModeForResolutionIndependentLayout) = 0; + virtual void Flush() {} + virtual void Flush( const tools::Rectangle& ) {} + protected: +@@ -446,7 +446,7 @@ public: + ControlState nState, + const ImplControlValue &aValue) override; + +- virtual void drawTextLayout(const GenericSalLayout& layout) override; ++ virtual void drawTextLayout(const GenericSalLayout& layout, bool bTextRenderModeForResolutionIndependentLayout) override; + + bool supportsOperation(OutDevSupportType eType) const override; + }; +diff --git a/vcl/inc/salgdi.hxx b/vcl/inc/salgdi.hxx +index 716c9aa934a6..6f9ee67f69a4 100644 +--- a/vcl/inc/salgdi.hxx ++++ b/vcl/inc/salgdi.hxx +@@ -96,6 +96,16 @@ public: + return m_bAntiAlias; + } + ++ void setTextRenderModeForResolutionIndependentLayout(bool bNew) ++ { ++ m_bTextRenderModeForResolutionIndependentLayout = bNew; ++ } ++ ++ bool getTextRenderModeForResolutionIndependentLayoutEnabled() const ++ { ++ return m_bTextRenderModeForResolutionIndependentLayout; ++ } ++ + // public SalGraphics methods, the interface to the independent vcl part + + // get device resolution +@@ -631,6 +641,7 @@ private: + protected: + /// flags which hold the SetAntialiasing() value from OutputDevice + bool m_bAntiAlias : 1; ++ bool m_bTextRenderModeForResolutionIndependentLayout : 1; + + inline tools::Long GetDeviceWidth(const OutputDevice& rOutDev) const; + +diff --git a/vcl/inc/sallayout.hxx b/vcl/inc/sallayout.hxx +index ab29a2022985..847c22ace091 100644 +--- a/vcl/inc/sallayout.hxx ++++ b/vcl/inc/sallayout.hxx +@@ -64,7 +64,7 @@ public: + sal_Int32 GetTextBreak(DeviceCoordinate nMaxWidth, DeviceCoordinate nCharExtra, int nFactor) const override; + DeviceCoordinate FillDXArray(std::vector* pDXArray) const override; + void GetCaretPositions(int nArraySize, sal_Int32* pCaretXArray) const override; +- bool GetNextGlyph(const GlyphItem** pGlyph, Point& rPos, int& nStart, ++ bool GetNextGlyph(const GlyphItem** pGlyph, DevicePoint& rPos, int& nStart, + const LogicalFontInstance** ppGlyphFont = nullptr, + const vcl::font::PhysicalFontFace** pFallbackFont = nullptr) const override; + bool GetOutline(basegfx::B2DPolyPolygonVector&) const override; +@@ -82,7 +82,11 @@ public: + + void SetIncomplete(bool bIncomplete); + +-public: ++ template ++ void ImplAdjustMultiLayout(vcl::text::ImplLayoutArgs& rArgs, ++ vcl::text::ImplLayoutArgs& rMultiArgs, ++ const DC* pMultiDXArray); ++ + virtual ~MultiSalLayout() override; + + private: +@@ -97,7 +101,10 @@ private: + + class VCL_DLLPUBLIC GenericSalLayout : public SalLayout + { +- friend void MultiSalLayout::AdjustLayout(vcl::text::ImplLayoutArgs&); ++ template friend void MultiSalLayout::ImplAdjustMultiLayout( ++ vcl::text::ImplLayoutArgs& rArgs, ++ vcl::text::ImplLayoutArgs& rMultiArgs, ++ const DC* pMultiDXArray); + + public: + GenericSalLayout(LogicalFontInstance&); +@@ -121,7 +128,7 @@ public: + LogicalFontInstance& GetFont() const + { return *m_GlyphItems.GetFont(); } + +- bool GetNextGlyph(const GlyphItem** pGlyph, Point& rPos, int& nStart, ++ bool GetNextGlyph(const GlyphItem** pGlyph, DevicePoint& rPos, int& nStart, + const LogicalFontInstance** ppGlyphFont = nullptr, + const vcl::font::PhysicalFontFace** pFallbackFont = nullptr) const override; + +@@ -136,7 +143,8 @@ private: + GenericSalLayout( const GenericSalLayout& ) = delete; + GenericSalLayout& operator=( const GenericSalLayout& ) = delete; + +- void ApplyDXArray(const DeviceCoordinate*, SalLayoutFlags nLayoutFlags); ++ template ++ void ApplyDXArray(const DC*, SalLayoutFlags nLayoutFlags); + void Justify(DeviceCoordinate nNewWidth); + void ApplyAsianKerning(const OUString& rStr); + +diff --git a/vcl/inc/skia/osx/gdiimpl.hxx b/vcl/inc/skia/osx/gdiimpl.hxx +index e59aa60f56df..71baf24625fc 100644 +--- a/vcl/inc/skia/osx/gdiimpl.hxx ++++ b/vcl/inc/skia/osx/gdiimpl.hxx +@@ -38,7 +38,8 @@ public: + const tools::Rectangle& rControlRegion, ControlState nState, + const ImplControlValue& aValue) override; + +- virtual void drawTextLayout(const GenericSalLayout& layout) override; ++ virtual void drawTextLayout(const GenericSalLayout& layout, ++ bool bTextRenderModeForResolutionIndependentLayout) override; + + virtual void Flush() override; + virtual void Flush(const tools::Rectangle&) override; +diff --git a/vcl/inc/skia/win/gdiimpl.hxx b/vcl/inc/skia/win/gdiimpl.hxx +index 58043e5f6a83..a8897d0d7c20 100644 +--- a/vcl/inc/skia/win/gdiimpl.hxx ++++ b/vcl/inc/skia/win/gdiimpl.hxx +@@ -25,6 +25,8 @@ + #include + #include + ++#include ++ + class SkTypeface; + class ControlCacheKey; + +@@ -64,6 +66,8 @@ protected: + static void initFontInfo(); + inline static sal::systools::COMReference dwriteFactory; + inline static sal::systools::COMReference dwriteGdiInterop; ++ inline static sal::systools::COMReference dwriteFontSetBuilder; ++ inline static sal::systools::COMReference dwritePrivateCollection; + inline static sk_sp dwriteFontMgr; + inline static bool dwriteDone = false; + static SkFont::Edging fontEdging; +diff --git a/vcl/inc/win/DWriteTextRenderer.hxx b/vcl/inc/win/DWriteTextRenderer.hxx +index 6e097546d1e2..b64cc48a1c6a 100644 +--- a/vcl/inc/win/DWriteTextRenderer.hxx ++++ b/vcl/inc/win/DWriteTextRenderer.hxx +@@ -37,12 +37,13 @@ enum class D2DTextAntiAliasMode + class D2DWriteTextOutRenderer : public TextOutRenderer + { + public: +- explicit D2DWriteTextOutRenderer(); ++ explicit D2DWriteTextOutRenderer(bool bRenderingModeNatural); + virtual ~D2DWriteTextOutRenderer() override; + +- bool operator ()(GenericSalLayout const &rLayout, ++ bool operator()(GenericSalLayout const &rLayout, + SalGraphics &rGraphics, +- HDC hDC) override; ++ HDC hDC, ++ bool bRenderingModeNatural) override; + + HRESULT BindDC(HDC hDC, tools::Rectangle const & rRect = tools::Rectangle(0, 0, 1, 1)); + +@@ -54,12 +55,13 @@ public: + IDWriteFontFace * GetFontFace() const { return mpFontFace; } + float GetEmHeight() const { return mlfEmHeight; } + +- HRESULT CreateRenderTarget(); ++ HRESULT CreateRenderTarget(bool bRenderingModeNatural); + + bool Ready() const; + +- void applyTextAntiAliasMode(); +- void changeTextAntiAliasMode(D2DTextAntiAliasMode eMode); ++ void applyTextAntiAliasMode(bool bRenderingModeNatural); ++ ++ bool GetRenderingModeNatural() const { return mbRenderingModeNatural; } + + private: + // This is a singleton object disable copy ctor and assignment operator +@@ -67,7 +69,7 @@ private: + D2DWriteTextOutRenderer & operator = (const D2DWriteTextOutRenderer &) = delete; + + bool GetDWriteFaceFromHDC(HDC hDC, IDWriteFontFace ** ppFontFace, float * lfSize) const; +- bool performRender(GenericSalLayout const &rLayout, SalGraphics &rGraphics, HDC hDC, bool& bRetry); ++ bool performRender(GenericSalLayout const &rLayout, SalGraphics &rGraphics, HDC hDC, bool& bRetry, bool bRenderingModeNatural); + + ID2D1Factory * mpD2DFactory; + IDWriteFactory * mpDWriteFactory; +@@ -78,6 +80,7 @@ private: + IDWriteFontFace * mpFontFace; + float mlfEmHeight; + HDC mhDC; ++ bool mbRenderingModeNatural; + D2DTextAntiAliasMode meTextAntiAliasMode; + }; + +diff --git a/vcl/inc/win/salgdi.h b/vcl/inc/win/salgdi.h +index b472ece0a256..7833f988bd18 100644 +--- a/vcl/inc/win/salgdi.h ++++ b/vcl/inc/win/salgdi.h +@@ -316,7 +316,7 @@ public: + private: + // local helpers + +- void DrawTextLayout(const GenericSalLayout&, HDC, bool bUseDWrite); ++ void DrawTextLayout(const GenericSalLayout&, HDC, bool bUseDWrite, bool bRenderingModeNatural); + + public: + // public SalGraphics methods, the interface to the independent vcl part +diff --git a/vcl/inc/win/winlayout.hxx b/vcl/inc/win/winlayout.hxx +index 0b43ef4eeca1..5f56fe6b0c5e 100644 +--- a/vcl/inc/win/winlayout.hxx ++++ b/vcl/inc/win/winlayout.hxx +@@ -75,13 +75,14 @@ protected: + TextOutRenderer & operator = (const TextOutRenderer &) = delete; + + public: +- static TextOutRenderer & get(bool bUseDWrite); ++ static TextOutRenderer & get(bool bUseDWrite, bool bRenderingModeNatural); + + virtual ~TextOutRenderer() = default; + + virtual bool operator ()(GenericSalLayout const &rLayout, + SalGraphics &rGraphics, +- HDC hDC) = 0; ++ HDC hDC, ++ bool bRenderingModeNatural) = 0; + }; + + class ExTextOutRenderer : public TextOutRenderer +@@ -94,7 +95,8 @@ public: + + bool operator ()(GenericSalLayout const &rLayout, + SalGraphics &rGraphics, +- HDC hDC) override; ++ HDC hDC, ++ bool bRenderingModeNatural) override; + }; + + /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ +diff --git a/vcl/qt5/QtGraphics_Text.cxx b/vcl/qt5/QtGraphics_Text.cxx +index b509c2a946bd..02158fca29db 100644 +--- a/vcl/qt5/QtGraphics_Text.cxx ++++ b/vcl/qt5/QtGraphics_Text.cxx +@@ -294,11 +294,26 @@ std::unique_ptr QtGraphics::GetTextLayout(int nFallbackLevel) + return std::make_unique(*m_pTextStyle[nFallbackLevel]); + } + ++static QRawFont GetRawFont(const QFont& rFont, bool bWithoutHintingInTextDirection) ++{ ++ QFont::HintingPreference eHinting = rFont.hintingPreference(); ++ bool bAllowedHintStyle ++ = !bWithoutHintingInTextDirection ++ || (eHinting == QFont::PreferNoHinting || eHinting == QFont::PreferVerticalHinting); ++ if (bWithoutHintingInTextDirection && !bAllowedHintStyle) ++ { ++ QFont aFont(rFont); ++ aFont.setHintingPreference(QFont::PreferVerticalHinting); ++ return QRawFont::fromFont(aFont); ++ } ++ return QRawFont::fromFont(rFont); ++} ++ + void QtGraphics::DrawTextLayout(const GenericSalLayout& rLayout) + { + const QtFont* pFont = static_cast(&rLayout.GetFont()); + assert(pFont); +- QRawFont aRawFont(QRawFont::fromFont(*pFont)); ++ QRawFont aRawFont(GetRawFont(*pFont, getTextRenderModeForResolutionIndependentLayoutEnabled())); + + QVector glyphIndexes; + QVector positions; +@@ -311,13 +326,13 @@ void QtGraphics::DrawTextLayout(const GenericSalLayout& rLayout) + if (nOrientation) + pQtLayout->SetOrientation(0_deg10); + +- Point aPos; ++ DevicePoint aPos; + const GlyphItem* pGlyph; + int nStart = 0; + while (rLayout.GetNextGlyph(&pGlyph, aPos, nStart)) + { + glyphIndexes.push_back(pGlyph->glyphId()); +- positions.push_back(QPointF(aPos.X(), aPos.Y())); ++ positions.push_back(QPointF(aPos.getX(), aPos.getY())); + } + + // seems to be common to try to layout an empty string... +diff --git a/vcl/quartz/salgdi.cxx b/vcl/quartz/salgdi.cxx +index 9928cc4df3a2..b8a3ac375655 100644 +--- a/vcl/quartz/salgdi.cxx ++++ b/vcl/quartz/salgdi.cxx +@@ -363,10 +363,10 @@ bool AquaSalGraphics::AddTempDevFont(vcl::font::PhysicalFontCollection*, + + void AquaSalGraphics::DrawTextLayout(const GenericSalLayout& rLayout) + { +- mpBackend->drawTextLayout(rLayout); ++ mpBackend->drawTextLayout(rLayout, getTextRenderModeForResolutionIndependentLayoutEnabled()); + } + +-void AquaGraphicsBackend::drawTextLayout(const GenericSalLayout& rLayout) ++void AquaGraphicsBackend::drawTextLayout(const GenericSalLayout& rLayout, bool bTextRenderModeForResolutionIndependentLayout) + { + #ifdef IOS + if (!mrShared.checkContext()) +@@ -387,7 +387,7 @@ void AquaGraphicsBackend::drawTextLayout(const GenericSalLayout& rLayout) + CTFontRef pFont = static_cast(CFDictionaryGetValue(rStyle.GetStyleDict(), kCTFontAttributeName)); + CGAffineTransform aRotMatrix = CGAffineTransformMakeRotation(-rStyle.mfFontRotation); + +- Point aPos; ++ DevicePoint aPos; + const GlyphItem* pGlyph; + std::vector aGlyphIds; + std::vector aGlyphPos; +@@ -395,7 +395,7 @@ void AquaGraphicsBackend::drawTextLayout(const GenericSalLayout& rLayout) + int nStart = 0; + while (rLayout.GetNextGlyph(&pGlyph, aPos, nStart)) + { +- CGPoint aGCPos = CGPointMake(aPos.X(), -aPos.Y()); ++ CGPoint aGCPos = CGPointMake(aPos.getX(), -aPos.getY()); + + // Whether the glyph should be upright in vertical mode or not + bool bUprightGlyph = false; +@@ -460,6 +460,14 @@ void AquaGraphicsBackend::drawTextLayout(const GenericSalLayout& rLayout) + CGContextSetTextDrawingMode(mrShared.maContextHolder.get(), kCGTextFillStroke); + } + ++ if (bTextRenderModeForResolutionIndependentLayout) ++ { ++ CGContextSetAllowsFontSubpixelQuantization(mrShared.maContextHolder.get(), false); ++ CGContextSetShouldSubpixelQuantizeFonts(mrShared.maContextHolder.get(), false); ++ CGContextSetAllowsFontSubpixelPositioning(mrShared.maContextHolder.get(), true); ++ CGContextSetShouldSubpixelPositionFonts(mrShared.maContextHolder.get(), true); ++ } ++ + auto aIt = aGlyphOrientation.cbegin(); + while (aIt != aGlyphOrientation.cend()) + { +diff --git a/vcl/skia/gdiimpl.cxx b/vcl/skia/gdiimpl.cxx +index 55fed5b84e72..6d027a6f860a 100644 +--- a/vcl/skia/gdiimpl.cxx ++++ b/vcl/skia/gdiimpl.cxx +@@ -2206,7 +2206,7 @@ void SkiaSalGraphicsImpl::drawGenericLayout(const GenericSalLayout& layout, Colo + glyphIds.reserve(256); + glyphForms.reserve(256); + verticals.reserve(256); +- Point aPos; ++ DevicePoint aPos; + const GlyphItem* pGlyph; + int nStart = 0; + while (layout.GetNextGlyph(&pGlyph, aPos, nStart)) +@@ -2215,7 +2215,7 @@ void SkiaSalGraphicsImpl::drawGenericLayout(const GenericSalLayout& layout, Colo + Degree10 angle = layout.GetOrientation(); + if (pGlyph->IsVertical()) + angle += 900_deg10; +- SkRSXform form = SkRSXform::Make(toCos(angle), toSin(angle), aPos.X(), aPos.Y()); ++ SkRSXform form = SkRSXform::Make(toCos(angle), toSin(angle), aPos.getX(), aPos.getY()); + glyphForms.emplace_back(std::move(form)); + verticals.emplace_back(pGlyph->IsVertical()); + } +diff --git a/vcl/skia/osx/gdiimpl.cxx b/vcl/skia/osx/gdiimpl.cxx +index 126f43bb6ac3..4c9ae86dbadf 100644 +--- a/vcl/skia/osx/gdiimpl.cxx ++++ b/vcl/skia/osx/gdiimpl.cxx +@@ -261,7 +261,8 @@ bool AquaSkiaSalGraphicsImpl::drawNativeControl(ControlType nType, ControlPart n + return bOK; + } + +-void AquaSkiaSalGraphicsImpl::drawTextLayout(const GenericSalLayout& rLayout) ++void AquaSkiaSalGraphicsImpl::drawTextLayout(const GenericSalLayout& rLayout, ++ bool bSubpixelPositioning) + { + const CoreTextStyle& rStyle = *static_cast(&rLayout.GetFont()); + const vcl::font::FontSelectPattern& rFontSelect = rStyle.GetFontSelectPattern(); +@@ -295,8 +296,12 @@ void AquaSkiaSalGraphicsImpl::drawTextLayout(const GenericSalLayout& rLayout) + // font.setScaleX(rStyle.mfFontStretch); TODO + if (rStyle.mbFauxBold) + font.setEmbolden(true); +- font.setEdging(!mrShared.mbNonAntialiasedText ? SkFont::Edging::kAntiAlias +- : SkFont::Edging::kAlias); ++ ++ SkFont::Edging ePreferredAliasing ++ = bSubpixelPositioning ? SkFont::Edging::kSubpixelAntiAlias : SkFont::Edging::kAntiAlias; ++ if (bSubpixelPositioning) ++ font.setSubpixel(true); ++ font.setEdging(mrShared.mbNonAntialiasedText ? SkFont::Edging::kAlias : ePreferredAliasing); + + // Vertical font, use width as "height". + SkFont verticalFont(font); +diff --git a/vcl/skia/win/gdiimpl.cxx b/vcl/skia/win/gdiimpl.cxx +index a10351888eac..e9074340e66e 100644 +--- a/vcl/skia/win/gdiimpl.cxx ++++ b/vcl/skia/win/gdiimpl.cxx +@@ -163,11 +163,7 @@ sk_sp WinSkiaSalGraphicsImpl::createDirectWriteTypeface(HDC hdc, HFO + // different version of the same font installed system-wide). + // For that CreateFromFaceFromHdc() is necessary. The simpler + // CreateFontFromLOGFONT() seems to search for the best matching font, +- // which may not be the exact font. Our private fonts are installed +- // using AddFontResourceExW( FR_PRIVATE ) and that apparently does +- // not make them available to DirectWrite (at least, they are not +- // included the DWrite system font collection). For such cases, we'll +- // need to fall back to Skia's GDI-based font rendering. ++ // which may not be the exact font. + HFONT oldFont = SelectFont(hdc, hfont); + sal::systools::COMReference fontFace; + if (FAILED(CHECKHR(dwriteGdiInterop->CreateFontFaceFromHdc(hdc, &fontFace)))) +@@ -175,6 +171,7 @@ sk_sp WinSkiaSalGraphicsImpl::createDirectWriteTypeface(HDC hdc, HFO + SelectFont(hdc, oldFont); + return nullptr; + } ++ + SelectFont(hdc, oldFont); + sal::systools::COMReference collection; + if (FAILED(CHECKHR(dwriteFactory->GetSystemFontCollection(&collection)))) +@@ -182,7 +179,66 @@ sk_sp WinSkiaSalGraphicsImpl::createDirectWriteTypeface(HDC hdc, HFO + sal::systools::COMReference font; + // Do not use CHECKHR() here, as said above, this fails for our fonts. + if (FAILED(collection->GetFontFromFontFace(fontFace.get(), &font))) +- return nullptr; ++ { ++ // If not found in system collection, try our private font collection. ++ // If that's not possible we'll fall back to Skia's GDI-based font rendering. ++ if (!dwritePrivateCollection ++ || FAILED(dwritePrivateCollection->GetFontFromFontFace(fontFace.get(), &font))) ++ { ++ // Our private fonts are installed using AddFontResourceExW( FR_PRIVATE ) ++ // and that does not make them available to the DWrite system font ++ // collection. For such cases attempt to update a collection of ++ // private fonts with this newly used font. ++ ++ sal::systools::COMReference dwriteFactory3; ++ if (FAILED(dwriteFactory->QueryInterface(&dwriteFactory3))) ++ return nullptr; ++ ++ if (!dwriteFontSetBuilder ++ && FAILED(dwriteFactory3->CreateFontSetBuilder(&dwriteFontSetBuilder))) ++ return nullptr; ++ ++ UINT32 numberOfFiles; ++ if (FAILED(fontFace->GetFiles(&numberOfFiles, nullptr)) || numberOfFiles != 1) ++ return nullptr; ++ ++ sal::systools::COMReference fontFile; ++ if (FAILED(fontFace->GetFiles(&numberOfFiles, &fontFile))) ++ return nullptr; ++ ++ BOOL isSupported; ++ DWRITE_FONT_FILE_TYPE fileType; ++ UINT32 numberOfFonts; ++ if (FAILED(fontFile->Analyze(&isSupported, &fileType, nullptr, &numberOfFonts)) ++ || !isSupported) ++ return nullptr; ++ ++ // For each font within the font file, get a font face reference and add to the builder. ++ for (UINT32 fontIndex = 0; fontIndex < numberOfFonts; ++fontIndex) ++ { ++ sal::systools::COMReference fontFaceReference; ++ if (FAILED(dwriteFactory3->CreateFontFaceReference(fontFile.get(), fontIndex, ++ DWRITE_FONT_SIMULATIONS_NONE, ++ &fontFaceReference))) ++ continue; ++ ++ // Leave it to DirectWrite to read properties directly out of the font files ++ dwriteFontSetBuilder->AddFontFaceReference(fontFaceReference.get()); ++ } ++ ++ dwritePrivateCollection.clear(); ++ sal::systools::COMReference fontSet; ++ if (SUCCEEDED(CHECKHR(dwriteFontSetBuilder->CreateFontSet(&fontSet)))) ++ dwriteFactory3->CreateFontCollectionFromFontSet(fontSet.get(), ++ &dwritePrivateCollection); ++ } ++ ++ if (!dwritePrivateCollection) ++ return nullptr; ++ // CHECKHR because we expect to succeed here ++ if (FAILED(CHECKHR(dwritePrivateCollection->GetFontFromFontFace(fontFace.get(), &font)))) ++ return nullptr; ++ } + sal::systools::COMReference fontFamily; + if (FAILED(CHECKHR(font->GetFontFamily(&fontFamily)))) + return nullptr; +@@ -231,8 +287,14 @@ bool WinSkiaSalGraphicsImpl::DrawTextLayout(const GenericSalLayout& rLayout) + } + + SkFont font(typeface); ++ ++ bool bSubpixelPositioning = mWinParent.getTextRenderModeForResolutionIndependentLayoutEnabled(); ++ SkFont::Edging ePreferredAliasing ++ = bSubpixelPositioning ? SkFont::Edging::kSubpixelAntiAlias : fontEdging; ++ if (bSubpixelPositioning) ++ font.setSubpixel(true); + font.setEdging(logFont.lfQuality == NONANTIALIASED_QUALITY ? SkFont::Edging::kAlias +- : fontEdging); ++ : ePreferredAliasing); + + const vcl::font::FontSelectPattern& rFSD = pWinFont->GetFontSelectPattern(); + int nHeight = rFSD.mnHeight; +@@ -296,6 +358,8 @@ void WinSkiaSalGraphicsImpl::initFontInfo() + void WinSkiaSalGraphicsImpl::ClearDevFontCache() + { + dwriteFontMgr.reset(); ++ dwriteFontSetBuilder.clear(); ++ dwritePrivateCollection.clear(); + dwriteFactory.clear(); + dwriteGdiInterop.clear(); + dwriteDone = false; +diff --git a/vcl/skia/x11/textrender.cxx b/vcl/skia/x11/textrender.cxx +index a2d0dcbb36f6..9fda8ba6601c 100644 +--- a/vcl/skia/x11/textrender.cxx ++++ b/vcl/skia/x11/textrender.cxx +@@ -57,8 +57,22 @@ void SkiaTextRender::DrawTextLayout(const GenericSalLayout& rLayout, const SalGr + font.setSkewX(1.0 * -0x4000L / 0x10000L); + if (rFont.NeedsArtificialBold()) + font.setEmbolden(true); +- font.setEdging(rFont.GetAntialiasAdvice() ? SkFont::Edging::kAntiAlias +- : SkFont::Edging::kAlias); ++ ++ bool bSubpixelPositioning = rGraphics.getTextRenderModeForResolutionIndependentLayoutEnabled(); ++ SkFont::Edging ePreferredAliasing ++ = bSubpixelPositioning ? SkFont::Edging::kSubpixelAntiAlias : SkFont::Edging::kAntiAlias; ++ if (bSubpixelPositioning) ++ { ++ font.setSubpixel(true); ++ ++ SkFontHinting eHinting = font.getHinting(); ++ bool bAllowedHintStyle ++ = eHinting == SkFontHinting::kNone || eHinting == SkFontHinting::kSlight; ++ if (!bAllowedHintStyle) ++ font.setHinting(SkFontHinting::kSlight); ++ } ++ ++ font.setEdging(rFont.GetAntialiasAdvice() ? ePreferredAliasing : SkFont::Edging::kAlias); + + // Vertical font, use width as "height". + SkFont verticalFont(font); +diff --git a/vcl/source/gdi/CommonSalLayout.cxx b/vcl/source/gdi/CommonSalLayout.cxx +index 13bc53ebbdab..811849309d67 100644 +--- a/vcl/source/gdi/CommonSalLayout.cxx ++++ b/vcl/source/gdi/CommonSalLayout.cxx +@@ -197,7 +197,9 @@ void GenericSalLayout::AdjustLayout(vcl::text::ImplLayoutArgs& rArgs) + { + SalLayout::AdjustLayout(rArgs); + +- if (rArgs.mpDXArray) ++ if (rArgs.mpAltNaturalDXArray) // Used when "TextRenderModeForResolutionIndependentLayout" is set ++ ApplyDXArray(rArgs.mpAltNaturalDXArray, rArgs.mnFlags); ++ else if (rArgs.mpDXArray) // Normal case + ApplyDXArray(rArgs.mpDXArray, rArgs.mnFlags); + else if (rArgs.mnLayoutWidth) + Justify(rArgs.mnLayoutWidth); +@@ -331,7 +333,7 @@ bool GenericSalLayout::LayoutText(vcl::text::ImplLayoutArgs& rArgs, const SalLay + double nYScale = 0; + GetFont().GetScale(&nXScale, &nYScale); + +- Point aCurrPos(0, 0); ++ DevicePoint aCurrPos(0, 0); + while (true) + { + int nBidiMinRunPos, nBidiEndRunPos; +@@ -584,12 +586,12 @@ bool GenericSalLayout::LayoutText(vcl::text::ImplLayoutArgs& rArgs, const SalLay + nXOffset = std::lround(nXOffset * nXScale); + nYOffset = std::lround(nYOffset * nYScale); + +- Point aNewPos(aCurrPos.X() + nXOffset, aCurrPos.Y() + nYOffset); ++ DevicePoint aNewPos(aCurrPos.getX() + nXOffset, aCurrPos.getY() + nYOffset); + const GlyphItem aGI(nCharPos, nCharCount, nGlyphIndex, aNewPos, nGlyphFlags, + nAdvance, nXOffset); + m_GlyphItems.push_back(aGI); + +- aCurrPos.AdjustX(nAdvance ); ++ aCurrPos.adjustX(nAdvance); + } + } + } +@@ -635,12 +637,12 @@ void GenericSalLayout::GetCharWidths(std::vector& rCharWidths) + // * Check the above flag to decide whether to insert Kashidas or not. + // * For any RTL glyph that has DX adjustment, insert enough Kashidas to + // fill in the added space. +- +-void GenericSalLayout::ApplyDXArray(const DeviceCoordinate* pDXArray, SalLayoutFlags nLayoutFlags) ++template ++void GenericSalLayout::ApplyDXArray(const DC* pDXArray, SalLayoutFlags nLayoutFlags) + { + int nCharCount = mnEndCharPos - mnMinCharPos; + std::vector aOldCharWidths; +- std::unique_ptr const pNewCharWidths(new DeviceCoordinate[nCharCount]); ++ std::unique_ptr const pNewCharWidths(new DC[nCharCount]); + + // Get the natural character widths (i.e. before applying DX adjustments). + GetCharWidths(aOldCharWidths); +@@ -671,7 +673,7 @@ void GenericSalLayout::ApplyDXArray(const DeviceCoordinate* pDXArray, SalLayoutF + std::map pKashidas; + + // The accumulated difference in X position. +- DeviceCoordinate nDelta = 0; ++ DC nDelta = 0; + + // Apply the DX adjustments to glyph positions and widths. + size_t i = 0; +@@ -680,7 +682,7 @@ void GenericSalLayout::ApplyDXArray(const DeviceCoordinate* pDXArray, SalLayoutF + // Accumulate the width difference for all characters corresponding to + // this glyph. + int nCharPos = m_GlyphItems[i].charPos() - mnMinCharPos; +- DeviceCoordinate nDiff = 0; ++ DC nDiff = 0; + for (int j = 0; j < m_GlyphItems[i].charCount(); j++) + nDiff += pNewCharWidths[nCharPos + j] - aOldCharWidths[nCharPos + j]; + +@@ -689,14 +691,14 @@ void GenericSalLayout::ApplyDXArray(const DeviceCoordinate* pDXArray, SalLayoutF + // Adjust the width and position of the first (leftmost) glyph in + // the cluster. + m_GlyphItems[i].m_nNewWidth += nDiff; +- m_GlyphItems[i].m_aLinearPos.AdjustX(nDelta); ++ m_GlyphItems[i].m_aLinearPos.adjustX(nDelta); + + // Adjust the position of the rest of the glyphs in the cluster. + while (++i < m_GlyphItems.size()) + { + if (!m_GlyphItems[i].IsInCluster()) + break; +- m_GlyphItems[i].m_aLinearPos.AdjustX(nDelta); ++ m_GlyphItems[i].m_aLinearPos.adjustX(nDelta); + } + } + else if (m_GlyphItems[i].IsInCluster()) +@@ -711,7 +713,7 @@ void GenericSalLayout::ApplyDXArray(const DeviceCoordinate* pDXArray, SalLayoutF + // the cluster. + // For RTL, we put all the adjustment to the left of the glyph. + m_GlyphItems[i].m_nNewWidth += nDiff; +- m_GlyphItems[i].m_aLinearPos.AdjustX(nDelta + nDiff); ++ m_GlyphItems[i].m_aLinearPos.adjustX(nDelta + nDiff); + + // Adjust the X position of all glyphs in the cluster. + size_t j = i; +@@ -720,7 +722,7 @@ void GenericSalLayout::ApplyDXArray(const DeviceCoordinate* pDXArray, SalLayoutF + --j; + if (!m_GlyphItems[j].IsInCluster()) + break; +- m_GlyphItems[j].m_aLinearPos.AdjustX(nDelta + nDiff); ++ m_GlyphItems[j].m_aLinearPos.adjustX(nDelta + nDiff); + } + + // If this glyph is Kashida-justifiable, then mark this as a +@@ -737,7 +739,7 @@ void GenericSalLayout::ApplyDXArray(const DeviceCoordinate* pDXArray, SalLayoutF + { + if (!m_GlyphItems[j].IsDiacritic()) + break; +- m_GlyphItems[j--].m_aLinearPos.AdjustX(nDiff); ++ m_GlyphItems[j--].m_aLinearPos.adjustX(nDiff); + } + } + i++; +@@ -779,15 +781,14 @@ void GenericSalLayout::ApplyDXArray(const DeviceCoordinate* pDXArray, SalLayoutF + nOverlap = nExcess / (nCopies - 1); + } + +- Point aPos(pGlyphIter->m_aLinearPos.getX() - nTotalWidth, 0); ++ DevicePoint aPos(pGlyphIter->m_aLinearPos.getX() - nTotalWidth, 0); + int nCharPos = pGlyphIter->charPos(); + GlyphItemFlags const nFlags = GlyphItemFlags::IS_IN_CLUSTER | GlyphItemFlags::IS_RTL_GLYPH; + while (nCopies--) + { + GlyphItem aKashida(nCharPos, 0, nKashidaIndex, aPos, nFlags, nKashidaWidth, 0); + pGlyphIter = m_GlyphItems.insert(pGlyphIter, aKashida); +- aPos.AdjustX(nKashidaWidth ); +- aPos.AdjustX( -nOverlap ); ++ aPos.adjustX(nKashidaWidth - nOverlap); + ++pGlyphIter; + ++nInserted; + } +diff --git a/vcl/source/gdi/pdfwriter_impl.cxx b/vcl/source/gdi/pdfwriter_impl.cxx +index 0a971622d5bc..a3ad137230fd 100644 +--- a/vcl/source/gdi/pdfwriter_impl.cxx ++++ b/vcl/source/gdi/pdfwriter_impl.cxx +@@ -3899,7 +3899,7 @@ void PDFWriterImpl::createDefaultCheckBoxAppearance( PDFWidget& rBox, const PDFW + // make sure OpenSymbol is embedded, and includes our checkmark + const sal_Unicode cMark=0x2713; + const GlyphItem aItem(0, 0, pMap->GetGlyphIndex(cMark), +- Point(), GlyphItemFlags::NONE, 0, 0); ++ DevicePoint(), GlyphItemFlags::NONE, 0, 0); + const std::vector aCodeUnits={ cMark }; + sal_uInt8 nMappedGlyph; + sal_Int32 nMappedFontObject; +@@ -5808,9 +5808,9 @@ void PDFWriterImpl::drawShadow( SalLayout& rLayout, const OUString& rText, bool + tools::Long nOff = 1 + ((GetFontInstance()->mnLineHeight-24)/24); + if( rFont.IsOutline() ) + nOff++; +- rLayout.DrawBase() += Point( nOff, nOff ); ++ rLayout.DrawBase() += DevicePoint(nOff, nOff); + drawLayout( rLayout, rText, bTextLines ); +- rLayout.DrawBase() -= Point( nOff, nOff ); ++ rLayout.DrawBase() -= DevicePoint(nOff, nOff); + + setFont( aSaveFont ); + setTextLineColor( aSaveTextLineColor ); +@@ -6133,7 +6133,7 @@ void PDFWriterImpl::drawLayout( SalLayout& rLayout, const OUString& rText, bool + std::vector< PDFGlyph > aGlyphs; + aGlyphs.reserve( nMaxGlyphs ); + // first get all the glyphs and register them; coordinates still in Pixel +- Point aPos; ++ DevicePoint aPos; + while (rLayout.GetNextGlyph(&pGlyph, aPos, nIndex, nullptr, &pFallbackFont)) + { + const auto* pFont = pFallbackFont ? pFallbackFont : pDevFont; +@@ -6206,7 +6206,7 @@ void PDFWriterImpl::drawLayout( SalLayout& rLayout, const OUString& rText, bool + if (bUseActualText || pGlyph->IsInCluster()) + nCharPos = pGlyph->charPos(); + +- aGlyphs.emplace_back(aPos, ++ aGlyphs.emplace_back(Point(aPos.getX(), aPos.getY()), + pGlyph, + nGlyphWidth, + nMappedFontObject, +@@ -6229,7 +6229,8 @@ void PDFWriterImpl::drawLayout( SalLayout& rLayout, const OUString& rText, bool + // The rectangle is the bounding box of the text, but also includes + // ascent / descent to match the on-screen rendering. + // This is the top left of the text without ascent / descent. +- tools::Rectangle aRectangle(PixelToLogic(rLayout.GetDrawPosition()), ++ DevicePoint aDrawPosition(rLayout.GetDrawPosition()); ++ tools::Rectangle aRectangle(PixelToLogic(Point(aDrawPosition.getX(), aDrawPosition.getY())), + Size(ImplDevicePixelToLogicWidth(rLayout.GetTextWidth()), 0)); + aRectangle.AdjustTop(-aRefDevFontMetric.GetAscent()); + // This includes ascent / descent. +@@ -6240,7 +6241,7 @@ void PDFWriterImpl::drawLayout( SalLayout& rLayout, const OUString& rText, bool + { + // Adapt rectangle for rotated text. + tools::Polygon aPolygon(aRectangle); +- aPolygon.Rotate(PixelToLogic(rLayout.GetDrawPosition()), pFontInstance->mnOrientation); ++ aPolygon.Rotate(PixelToLogic(Point(aDrawPosition.getX(), aDrawPosition.getY())), pFontInstance->mnOrientation); + drawPolygon(aPolygon); + } + else +@@ -6337,7 +6338,7 @@ void PDFWriterImpl::drawLayout( SalLayout& rLayout, const OUString& rText, bool + if (!pGlyph->IsSpacing()) + { + if( !nWidth ) +- aStartPt = aPos; ++ aStartPt = Point(aPos.getX(), aPos.getY()); + + nWidth += pGlyph->m_nNewWidth; + } +@@ -6359,9 +6360,9 @@ void PDFWriterImpl::drawLayout( SalLayout& rLayout, const OUString& rText, bool + } + else + { +- Point aStartPt = rLayout.GetDrawPosition(); ++ DevicePoint aStartPt = rLayout.GetDrawPosition(); + int nWidth = rLayout.GetTextWidth() / rLayout.GetUnitsPerPixel(); +- drawTextLine( PixelToLogic( aStartPt ), ++ drawTextLine( PixelToLogic(Point(aStartPt.getX(), aStartPt.getY()) ), + ImplDevicePixelToLogicWidth( nWidth ), + eStrikeout, eUnderline, eOverline, bUnderlineAbove ); + } +@@ -6437,9 +6438,10 @@ void PDFWriterImpl::drawLayout( SalLayout& rLayout, const OUString& rText, bool + + aAdjOffset -= Point( nEmphWidth2, nEmphHeight2 ); + +- aPos += aAdjOffset; +- aPos = PixelToLogic( aPos ); +- drawEmphasisMark( aPos.X(), aPos.Y(), ++ Point aMarkPos(aPos.getX(), aPos.getY()); ++ aMarkPos += aAdjOffset; ++ aMarkPos = PixelToLogic(aMarkPos); ++ drawEmphasisMark( aMarkPos.X(), aMarkPos.Y(), + aEmphPoly, bEmphPolyLine, + aEmphRect1, aEmphRect2 ); + } +diff --git a/vcl/source/gdi/salgdilayout.cxx b/vcl/source/gdi/salgdilayout.cxx +index aafa8f157e70..b22d4594cafe 100644 +--- a/vcl/source/gdi/salgdilayout.cxx ++++ b/vcl/source/gdi/salgdilayout.cxx +@@ -54,7 +54,8 @@ SalGraphics::SalGraphics() + m_aLastMirrorW(0), + m_nLastMirrorDeviceLTRButBiDiRtlTranslate(0), + m_bLastMirrorDeviceLTRButBiDiRtlSet(false), +- m_bAntiAlias(false) ++ m_bAntiAlias(false), ++ m_bTextRenderModeForResolutionIndependentLayout(false) + { + // read global RTL settings + if( AllSettings::GetLayoutRTL() ) +diff --git a/vcl/source/gdi/sallayout.cxx b/vcl/source/gdi/sallayout.cxx +index ebf10bf8e17c..abad66427491 100644 +--- a/vcl/source/gdi/sallayout.cxx ++++ b/vcl/source/gdi/sallayout.cxx +@@ -135,7 +135,8 @@ SalLayout::SalLayout() + mnEndCharPos( -1 ), + mnUnitsPerPixel( 1 ), + mnOrientation( 0 ), +- maDrawOffset( 0, 0 ) ++ maDrawOffset( 0, 0 ), ++ mbTextRenderModeForResolutionIndependentLayout(false) + {} + + SalLayout::~SalLayout() +@@ -148,10 +149,11 @@ void SalLayout::AdjustLayout( vcl::text::ImplLayoutArgs& rArgs ) + mnOrientation = rArgs.mnOrientation; + } + +-Point SalLayout::GetDrawPosition( const Point& rRelative ) const ++DevicePoint SalLayout::GetDrawPosition(const DevicePoint& rRelative) const + { +- Point aPos = maDrawBase; +- Point aOfs = rRelative + maDrawOffset; ++ DevicePoint aPos(maDrawBase); ++ DevicePoint aOfs(rRelative.getX() + maDrawOffset.X(), ++ rRelative.getY() + maDrawOffset.Y()); + + if( mnOrientation == 0_deg10 ) + aPos += aOfs; +@@ -168,11 +170,20 @@ Point SalLayout::GetDrawPosition( const Point& rRelative ) const + fSin = sin( fRad ); + } + +- double fX = aOfs.X(); +- double fY = aOfs.Y(); +- tools::Long nX = static_cast( +fCos * fX + fSin * fY ); +- tools::Long nY = static_cast( +fCos * fY - fSin * fX ); +- aPos += Point( nX, nY ); ++ double fX = aOfs.getX(); ++ double fY = aOfs.getY(); ++ if (mbTextRenderModeForResolutionIndependentLayout) ++ { ++ double nX = +fCos * fX + fSin * fY; ++ double nY = +fCos * fY - fSin * fX; ++ aPos += DevicePoint(nX, nY); ++ } ++ else ++ { ++ tools::Long nX = static_cast( +fCos * fX + fSin * fY ); ++ tools::Long nY = static_cast( +fCos * fY - fSin * fX ); ++ aPos += DevicePoint(nX, nY); ++ } + } + + return aPos; +@@ -185,7 +196,7 @@ bool SalLayout::GetOutline(basegfx::B2DPolyPolygonVector& rVector) const + + basegfx::B2DPolyPolygon aGlyphOutline; + +- Point aPos; ++ DevicePoint aPos; + const GlyphItem* pGlyph; + int nStart = 0; + const LogicalFontInstance* pGlyphFont; +@@ -198,9 +209,9 @@ bool SalLayout::GetOutline(basegfx::B2DPolyPolygonVector& rVector) const + // only add non-empty outlines + if( bSuccess && (aGlyphOutline.count() > 0) ) + { +- if( aPos.X() || aPos.Y() ) ++ if( aPos.getX() || aPos.getY() ) + { +- aGlyphOutline.transform(basegfx::utils::createTranslateB2DHomMatrix(aPos.X(), aPos.Y())); ++ aGlyphOutline.transform(basegfx::utils::createTranslateB2DHomMatrix(aPos.getX(), aPos.getY())); + } + + // insert outline at correct position +@@ -218,7 +229,7 @@ bool SalLayout::GetBoundRect(tools::Rectangle& rRect) const + + tools::Rectangle aRectangle; + +- Point aPos; ++ DevicePoint aPos; + const GlyphItem* pGlyph; + int nStart = 0; + const LogicalFontInstance* pGlyphFont; +@@ -228,7 +239,7 @@ bool SalLayout::GetBoundRect(tools::Rectangle& rRect) const + if (pGlyph->GetGlyphBoundRect(pGlyphFont, aRectangle)) + { + // merge rectangle +- aRectangle += aPos; ++ aRectangle += Point(aPos.getX(), aPos.getY()); + if (rRect.IsEmpty()) + rRect = aRectangle; + else +@@ -322,7 +333,7 @@ void GenericSalLayout::Justify( DeviceCoordinate nNewWidth ) + for( pGlyphIter = m_GlyphItems.begin(); pGlyphIter != pGlyphIterRight; ++pGlyphIter ) + { + // move glyph to justified position +- pGlyphIter->m_aLinearPos.AdjustX(nDeltaSum ); ++ pGlyphIter->m_aLinearPos.adjustX(nDeltaSum); + + // do not stretch non-stretchable glyphs + if( pGlyphIter->IsDiacritic() || (nStretchable <= 0) ) +@@ -438,7 +449,7 @@ void GenericSalLayout::ApplyAsianKerning(const OUString& rStr) + + // adjust the glyph positions to the new glyph widths + if( pGlyphIter+1 != pGlyphIterEnd ) +- pGlyphIter->m_aLinearPos.AdjustX(nOffset); ++ pGlyphIter->m_aLinearPos.adjustX(nOffset); + } + } + +@@ -491,7 +502,7 @@ sal_Int32 GenericSalLayout::GetTextBreak( DeviceCoordinate nMaxWidth, DeviceCoor + } + + bool GenericSalLayout::GetNextGlyph(const GlyphItem** pGlyph, +- Point& rPos, int& nStart, ++ DevicePoint& rPos, int& nStart, + const LogicalFontInstance** ppGlyphFont, + const vcl::font::PhysicalFontFace**) const + { +@@ -521,10 +532,10 @@ bool GenericSalLayout::GetNextGlyph(const GlyphItem** pGlyph, + *ppGlyphFont = m_GlyphItems.GetFont().get(); + + // calculate absolute position in pixel units +- Point aRelativePos = pGlyphIter->m_aLinearPos; ++ DevicePoint aRelativePos = pGlyphIter->m_aLinearPos; + +- aRelativePos.setX( aRelativePos.X() / mnUnitsPerPixel ); +- aRelativePos.setY( aRelativePos.Y() / mnUnitsPerPixel ); ++ aRelativePos.setX( aRelativePos.getX() / mnUnitsPerPixel ); ++ aRelativePos.setY( aRelativePos.getY() / mnUnitsPerPixel ); + rPos = GetDrawPosition( aRelativePos ); + + return true; +@@ -550,7 +561,7 @@ void GenericSalLayout::MoveGlyph( int nStart, tools::Long nNewXPos ) + { + for( std::vector::iterator pGlyphIterEnd = m_GlyphItems.end(); pGlyphIter != pGlyphIterEnd; ++pGlyphIter ) + { +- pGlyphIter->m_aLinearPos.AdjustX(nXDelta ); ++ pGlyphIter->m_aLinearPos.adjustX(nXDelta); + } + } + } +@@ -637,7 +648,7 @@ void MultiSalLayout::AdjustLayout( vcl::text::ImplLayoutArgs& rArgs ) + vcl::text::ImplLayoutArgs aMultiArgs = rArgs; + std::vector aJustificationArray; + +- if( !rArgs.mpDXArray && rArgs.mnLayoutWidth ) ++ if( !rArgs.HasDXArray() && rArgs.mnLayoutWidth ) + { + // for stretched text in a MultiSalLayout the target width needs to be + // distributed by individually adjusting its virtual character widths +@@ -702,6 +713,17 @@ void MultiSalLayout::AdjustLayout( vcl::text::ImplLayoutArgs& rArgs ) + } + } + ++ if (aMultiArgs.mpAltNaturalDXArray) ++ ImplAdjustMultiLayout(rArgs, aMultiArgs, aMultiArgs.mpAltNaturalDXArray); ++ else ++ ImplAdjustMultiLayout(rArgs, aMultiArgs, aMultiArgs.mpDXArray); ++} ++ ++template ++void MultiSalLayout::ImplAdjustMultiLayout(vcl::text::ImplLayoutArgs& rArgs, ++ vcl::text::ImplLayoutArgs& rMultiArgs, ++ const DC* pMultiDXArray) ++{ + // Compute rtl flags, since in some scripts glyphs/char order can be + // reversed for a few character sequences e.g. Myanmar + std::vector vRtl(rArgs.mnEndCharPos - rArgs.mnMinCharPos, false); +@@ -721,17 +743,17 @@ void MultiSalLayout::AdjustLayout( vcl::text::ImplLayoutArgs& rArgs ) + const GlyphItem* pGlyphs[MAX_FALLBACK]; + bool bValid[MAX_FALLBACK] = { false }; + +- Point aPos; ++ DevicePoint aPos; + int nLevel = 0, n; + for( n = 0; n < mnLevel; ++n ) + { + // now adjust the individual components + if( n > 0 ) + { +- aMultiArgs.maRuns = maFallbackRuns[ n-1 ]; +- aMultiArgs.mnFlags |= SalLayoutFlags::ForFallback; ++ rMultiArgs.maRuns = maFallbackRuns[ n-1 ]; ++ rMultiArgs.mnFlags |= SalLayoutFlags::ForFallback; + } +- mpLayouts[n]->AdjustLayout( aMultiArgs ); ++ mpLayouts[n]->AdjustLayout( rMultiArgs ); + + // remove unused parts of component + if( n > 0 ) +@@ -830,7 +852,7 @@ void MultiSalLayout::AdjustLayout( vcl::text::ImplLayoutArgs& rArgs ) + } + + // skip to end of layout run and calculate its advance width +- DeviceCoordinate nRunAdvance = 0; ++ DC nRunAdvance = 0; + bool bKeepNotDef = (nFBLevel >= nLevel); + for(;;) + { +@@ -886,7 +908,7 @@ void MultiSalLayout::AdjustLayout( vcl::text::ImplLayoutArgs& rArgs ) + bKeepNotDef = bNeedFallback; + } + // check for reordered glyphs +- if (aMultiArgs.mpDXArray && ++ if (pMultiDXArray && + nRunVisibleEndChar < mnEndCharPos && + nRunVisibleEndChar >= mnMinCharPos && + pGlyphs[n]->charPos() < mnEndCharPos && +@@ -894,14 +916,14 @@ void MultiSalLayout::AdjustLayout( vcl::text::ImplLayoutArgs& rArgs ) + { + if (vRtl[nActiveCharPos - mnMinCharPos]) + { +- if (aMultiArgs.mpDXArray[nRunVisibleEndChar-mnMinCharPos] +- >= aMultiArgs.mpDXArray[pGlyphs[n]->charPos() - mnMinCharPos]) ++ if (pMultiDXArray[nRunVisibleEndChar-mnMinCharPos] ++ >= pMultiDXArray[pGlyphs[n]->charPos() - mnMinCharPos]) + { + nRunVisibleEndChar = pGlyphs[n]->charPos(); + } + } +- else if (aMultiArgs.mpDXArray[nRunVisibleEndChar-mnMinCharPos] +- <= aMultiArgs.mpDXArray[pGlyphs[n]->charPos() - mnMinCharPos]) ++ else if (pMultiDXArray[nRunVisibleEndChar-mnMinCharPos] ++ <= pMultiDXArray[pGlyphs[n]->charPos() - mnMinCharPos]) + { + nRunVisibleEndChar = pGlyphs[n]->charPos(); + } +@@ -910,7 +932,7 @@ void MultiSalLayout::AdjustLayout( vcl::text::ImplLayoutArgs& rArgs ) + + // if a justification array is available + // => use it directly to calculate the corresponding run width +- if( aMultiArgs.mpDXArray ) ++ if (pMultiDXArray) + { + // the run advance is the width from the first char + // in the run to the first char in the next run +@@ -919,16 +941,16 @@ void MultiSalLayout::AdjustLayout( vcl::text::ImplLayoutArgs& rArgs ) + if (nActiveCharIndex >= 0 && vRtl[nActiveCharIndex]) + { + if (nRunVisibleEndChar > mnMinCharPos && nRunVisibleEndChar <= mnEndCharPos) +- nRunAdvance -= aMultiArgs.mpDXArray[nRunVisibleEndChar - 1 - mnMinCharPos]; ++ nRunAdvance -= pMultiDXArray[nRunVisibleEndChar - 1 - mnMinCharPos]; + if (nLastRunEndChar > mnMinCharPos && nLastRunEndChar <= mnEndCharPos) +- nRunAdvance += aMultiArgs.mpDXArray[nLastRunEndChar - 1 - mnMinCharPos]; ++ nRunAdvance += pMultiDXArray[nLastRunEndChar - 1 - mnMinCharPos]; + } + else + { + if (nRunVisibleEndChar >= mnMinCharPos) +- nRunAdvance += aMultiArgs.mpDXArray[nRunVisibleEndChar - mnMinCharPos]; ++ nRunAdvance += pMultiDXArray[nRunVisibleEndChar - mnMinCharPos]; + if (nLastRunEndChar >= mnMinCharPos) +- nRunAdvance -= aMultiArgs.mpDXArray[nLastRunEndChar - mnMinCharPos]; ++ nRunAdvance -= pMultiDXArray[nLastRunEndChar - mnMinCharPos]; + } + nLastRunEndChar = nRunVisibleEndChar; + nRunVisibleEndChar = pGlyphs[nFirstValid]->charPos(); +@@ -1105,7 +1127,7 @@ void MultiSalLayout::GetCaretPositions( int nMaxIndex, sal_Int32* pCaretXArray ) + } + + bool MultiSalLayout::GetNextGlyph(const GlyphItem** pGlyph, +- Point& rPos, int& nStart, ++ DevicePoint& rPos, int& nStart, + const LogicalFontInstance** ppGlyphFont, + const vcl::font::PhysicalFontFace** pFallbackFont) const + { +@@ -1123,8 +1145,8 @@ bool MultiSalLayout::GetNextGlyph(const GlyphItem** pGlyph, + nStart |= nFontTag; + if (pFallbackFont) + *pFallbackFont = pFontFace; +- rPos += maDrawBase; +- rPos += maDrawOffset; ++ rPos.adjustX(maDrawBase.getX() + maDrawOffset.X()); ++ rPos.adjustY(maDrawBase.getY() + maDrawOffset.Y()); + return true; + } + } +diff --git a/vcl/source/gdi/virdev.cxx b/vcl/source/gdi/virdev.cxx +index 6a44cc1cd136..87721c683d77 100644 +--- a/vcl/source/gdi/virdev.cxx ++++ b/vcl/source/gdi/virdev.cxx +@@ -379,6 +379,8 @@ bool VirtualDevice::ImplSetOutputSizePixel( const Size& rNewSize, bool bErase, + mpAlphaVDev->SetMapMode( GetMapMode() ); + + mpAlphaVDev->SetAntialiasing( GetAntialiasing() ); ++ ++ mpAlphaVDev->SetTextRenderModeForResolutionIndependentLayout(GetTextRenderModeForResolutionIndependentLayout()); + } + + return true; +diff --git a/vcl/source/outdev/font.cxx b/vcl/source/outdev/font.cxx +index 5139f1fc03a4..71e4091e754e 100644 +--- a/vcl/source/outdev/font.cxx ++++ b/vcl/source/outdev/font.cxx +@@ -1117,7 +1117,7 @@ void OutputDevice::ImplDrawEmphasisMarks( SalLayout& rSalLayout ) + tools::Long nEmphasisHeight2 = nEmphasisHeight / 2; + aOffset += Point( nEmphasisWidth2, nEmphasisHeight2 ); + +- Point aOutPoint; ++ DevicePoint aOutPoint; + tools::Rectangle aRectangle; + const GlyphItem* pGlyph; + const LogicalFontInstance* pGlyphFont; +@@ -1136,10 +1136,10 @@ void OutputDevice::ImplDrawEmphasisMarks( SalLayout& rSalLayout ) + Point aOriginPt(0, 0); + aOriginPt.RotateAround( aAdjPoint, mpFontInstance->mnOrientation ); + } +- aOutPoint += aAdjPoint; +- aOutPoint -= Point( nEmphasisWidth2, nEmphasisHeight2 ); +- ImplDrawEmphasisMark( rSalLayout.DrawBase().X(), +- aOutPoint.X(), aOutPoint.Y(), ++ aOutPoint.adjustX(aAdjPoint.X() - nEmphasisWidth2); ++ aOutPoint.adjustY(aAdjPoint.Y() - nEmphasisHeight2); ++ ImplDrawEmphasisMark( rSalLayout.DrawBase().getX(), ++ aOutPoint.getX(), aOutPoint.getY(), + aPolyPoly, bPolyLine, aRect1, aRect2 ); + } + } +diff --git a/vcl/source/outdev/map.cxx b/vcl/source/outdev/map.cxx +index 227905f075a8..bb4683f37a19 100644 +--- a/vcl/source/outdev/map.cxx ++++ b/vcl/source/outdev/map.cxx +@@ -1879,11 +1879,32 @@ DeviceCoordinate OutputDevice::LogicWidthToDeviceCoordinate( tools::Long nWidth + return static_cast(nWidth); + + #if VCL_FLOAT_DEVICE_PIXEL +- return (double)nWidth * maMapRes.mfScaleX * mnDPIX; ++ return ImplLogicToPixel(static_cast(nWidth), mnDPIX, maMapRes.mnMapScNumX, maMapRes.mnMapScDenomX); + #else +- + return ImplLogicToPixel(nWidth, mnDPIX, maMapRes.mnMapScNumX, maMapRes.mnMapScDenomX); + #endif + } + ++double OutputDevice::ImplLogicWidthToDeviceFontWidth(tools::Long nWidth) const ++{ ++ if (!mbMap) ++ return nWidth; ++ ++ return ImplLogicToPixel(static_cast(nWidth), mnDPIX, ++ maMapRes.mnMapScNumX, maMapRes.mnMapScDenomX); ++} ++ ++DevicePoint OutputDevice::ImplLogicToDeviceFontCoordinate(const Point& rPoint) const ++{ ++ if (!mbMap) ++ return DevicePoint(rPoint.X() + mnOutOffX, rPoint.Y() + mnOutOffY); ++ ++ return DevicePoint(ImplLogicToPixel(static_cast(rPoint.X() + maMapRes.mnMapOfsX), mnDPIX, ++ maMapRes.mnMapScNumX, maMapRes.mnMapScDenomX) ++ + mnOutOffX + mnOutOffOrigX, ++ ImplLogicToPixel(static_cast(rPoint.Y() + maMapRes.mnMapOfsY), mnDPIY, ++ maMapRes.mnMapScNumY, maMapRes.mnMapScDenomY) ++ + mnOutOffY + mnOutOffOrigY); ++} ++ + /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ +diff --git a/vcl/source/outdev/outdev.cxx b/vcl/source/outdev/outdev.cxx +index 9231f7779a6b..1ad7a2e71dd0 100644 +--- a/vcl/source/outdev/outdev.cxx ++++ b/vcl/source/outdev/outdev.cxx +@@ -105,6 +105,7 @@ OutputDevice::OutputDevice(OutDevType eOutDevType) : + meRasterOp = RasterOp::OverPaint; + mnAntialiasing = AntialiasingFlags::NONE; + meTextLanguage = LANGUAGE_SYSTEM; // TODO: get default from configuration? ++ mbTextRenderModeForResolutionIndependentLayout = false; + mbLineColor = true; + mbFillColor = true; + mbInitLineColor = true; +@@ -354,16 +355,28 @@ void OutputDevice::SetAntialiasing( AntialiasingFlags nMode ) + mnAntialiasing = nMode; + mbInitFont = true; + +- if(mpGraphics) +- { ++ if (mpGraphics) + mpGraphics->setAntiAlias(bool(mnAntialiasing & AntialiasingFlags::Enable)); +- } + } + + if( mpAlphaVDev ) + mpAlphaVDev->SetAntialiasing( nMode ); + } + ++void OutputDevice::SetTextRenderModeForResolutionIndependentLayout(bool bMode) ++{ ++ if (mbTextRenderModeForResolutionIndependentLayout!= bMode) ++ { ++ mbTextRenderModeForResolutionIndependentLayout = bMode; ++ ++ if (mpGraphics) ++ mpGraphics->setTextRenderModeForResolutionIndependentLayout(bMode); ++ } ++ ++ if (mpAlphaVDev) ++ mpAlphaVDev->SetTextRenderModeForResolutionIndependentLayout(bMode); ++} ++ + void OutputDevice::SetDrawMode(DrawModeFlags nDrawMode) + { + mnDrawMode = nDrawMode; +diff --git a/vcl/source/outdev/text.cxx b/vcl/source/outdev/text.cxx +index a28dc49e5ebd..3d773e56d488 100644 +--- a/vcl/source/outdev/text.cxx ++++ b/vcl/source/outdev/text.cxx +@@ -168,9 +168,9 @@ void OutputDevice::ImplDrawTextRect( tools::Long nBaseX, tools::Long nBaseY, + void OutputDevice::ImplDrawTextBackground( const SalLayout& rSalLayout ) + { + const tools::Long nWidth = rSalLayout.GetTextWidth() / rSalLayout.GetUnitsPerPixel(); +- const Point aBase = rSalLayout.DrawBase(); +- const tools::Long nX = aBase.X(); +- const tools::Long nY = aBase.Y(); ++ const DevicePoint aBase = rSalLayout.DrawBase(); ++ const tools::Long nX = aBase.getX(); ++ const tools::Long nY = aBase.getY(); + + if ( mbLineColor || mbInitLineColor ) + { +@@ -187,9 +187,9 @@ void OutputDevice::ImplDrawTextBackground( const SalLayout& rSalLayout ) + + tools::Rectangle OutputDevice::ImplGetTextBoundRect( const SalLayout& rSalLayout ) const + { +- Point aPoint = rSalLayout.GetDrawPosition(); +- tools::Long nX = aPoint.X(); +- tools::Long nY = aPoint.Y(); ++ DevicePoint aPoint = rSalLayout.GetDrawPosition(); ++ tools::Long nX = aPoint.getX(); ++ tools::Long nY = aPoint.getY(); + + tools::Long nWidth = rSalLayout.GetTextWidth(); + tools::Long nHeight = mpFontInstance->mnLineHeight + mnEmphasisAscent + mnEmphasisDescent; +@@ -225,11 +225,11 @@ tools::Rectangle OutputDevice::ImplGetTextBoundRect( const SalLayout& rSalLayout + + bool OutputDevice::ImplDrawRotateText( SalLayout& rSalLayout ) + { +- tools::Long nX = rSalLayout.DrawBase().X(); +- tools::Long nY = rSalLayout.DrawBase().Y(); ++ tools::Long nX = rSalLayout.DrawBase().getX(); ++ tools::Long nY = rSalLayout.DrawBase().getY(); + + tools::Rectangle aBoundRect; +- rSalLayout.DrawBase() = Point( 0, 0 ); ++ rSalLayout.DrawBase() = DevicePoint( 0, 0 ); + rSalLayout.DrawOffset() = Point( 0, 0 ); + if (!rSalLayout.GetBoundRect(aBoundRect)) + { +@@ -261,7 +261,8 @@ bool OutputDevice::ImplDrawRotateText( SalLayout& rSalLayout ) + pVDev->ImplInitTextColor(); + + // draw text into upper left corner +- rSalLayout.DrawBase() -= aBoundRect.TopLeft(); ++ rSalLayout.DrawBase().adjustX(-aBoundRect.Left()); ++ rSalLayout.DrawBase().adjustY(-aBoundRect.Top()); + rSalLayout.DrawText( *pVDev->mpGraphics ); + + Bitmap aBmp = pVDev->GetBitmap( Point(), aBoundRect.GetSize() ); +@@ -302,18 +303,18 @@ void OutputDevice::ImplDrawTextDirect( SalLayout& rSalLayout, + if( ImplDrawRotateText( rSalLayout ) ) + return; + +- tools::Long nOldX = rSalLayout.DrawBase().X(); ++ auto nOldX = rSalLayout.DrawBase().getX(); + if( HasMirroredGraphics() ) + { + tools::Long w = IsVirtual() ? mnOutWidth : mpGraphics->GetGraphicsWidth(); +- tools::Long x = rSalLayout.DrawBase().X(); ++ auto x = rSalLayout.DrawBase().getX(); + rSalLayout.DrawBase().setX( w - 1 - x ); + if( !IsRTLEnabled() ) + { + OutputDevice *pOutDevRef = this; + // mirror this window back + tools::Long devX = w-pOutDevRef->mnOutWidth-pOutDevRef->mnOutOffX; // re-mirrored mnOutOffX +- rSalLayout.DrawBase().setX( devX + ( pOutDevRef->mnOutWidth - 1 - (rSalLayout.DrawBase().X() - devX) ) ) ; ++ rSalLayout.DrawBase().setX( devX + ( pOutDevRef->mnOutWidth - 1 - (rSalLayout.DrawBase().getX() - devX) ) ) ; + } + } + else if( IsRTLEnabled() ) +@@ -322,7 +323,7 @@ void OutputDevice::ImplDrawTextDirect( SalLayout& rSalLayout, + + // mirror this window back + tools::Long devX = pOutDevRef->mnOutOffX; // re-mirrored mnOutOffX +- rSalLayout.DrawBase().setX( pOutDevRef->mnOutWidth - 1 - (rSalLayout.DrawBase().X() - devX) + devX ); ++ rSalLayout.DrawBase().setX( pOutDevRef->mnOutWidth - 1 - (rSalLayout.DrawBase().getX() - devX) + devX ); + } + + rSalLayout.DrawText( *mpGraphics ); +@@ -345,7 +346,7 @@ void OutputDevice::ImplDrawSpecialText( SalLayout& rSalLayout ) + Color aOldOverlineColor = GetOverlineColor(); + FontRelief eRelief = maFont.GetRelief(); + +- Point aOrigPos = rSalLayout.DrawBase(); ++ DevicePoint aOrigPos = rSalLayout.DrawBase(); + if ( eRelief != FontRelief::NONE ) + { + Color aReliefColor( COL_LIGHTGRAY ); +@@ -413,9 +414,9 @@ void OutputDevice::ImplDrawSpecialText( SalLayout& rSalLayout ) + else + SetTextColor( COL_BLACK ); + ImplInitTextColor(); +- rSalLayout.DrawBase() += Point( nOff, nOff ); ++ rSalLayout.DrawBase() += DevicePoint( nOff, nOff ); + ImplDrawTextDirect( rSalLayout, mbTextLines ); +- rSalLayout.DrawBase() -= Point( nOff, nOff ); ++ rSalLayout.DrawBase() -= DevicePoint( nOff, nOff ); + SetTextColor( aOldColor ); + SetTextLineColor( aOldTextLineColor ); + SetOverlineColor( aOldOverlineColor ); +@@ -427,21 +428,21 @@ void OutputDevice::ImplDrawSpecialText( SalLayout& rSalLayout ) + + if ( maFont.IsOutline() ) + { +- rSalLayout.DrawBase() = aOrigPos + Point(-1,-1); ++ rSalLayout.DrawBase() = aOrigPos + DevicePoint(-1,-1); + ImplDrawTextDirect( rSalLayout, mbTextLines ); +- rSalLayout.DrawBase() = aOrigPos + Point(+1,+1); ++ rSalLayout.DrawBase() = aOrigPos + DevicePoint(+1,+1); + ImplDrawTextDirect( rSalLayout, mbTextLines ); +- rSalLayout.DrawBase() = aOrigPos + Point(-1,+0); ++ rSalLayout.DrawBase() = aOrigPos + DevicePoint(-1,+0); + ImplDrawTextDirect( rSalLayout, mbTextLines ); +- rSalLayout.DrawBase() = aOrigPos + Point(-1,+1); ++ rSalLayout.DrawBase() = aOrigPos + DevicePoint(-1,+1); + ImplDrawTextDirect( rSalLayout, mbTextLines ); +- rSalLayout.DrawBase() = aOrigPos + Point(+0,+1); ++ rSalLayout.DrawBase() = aOrigPos + DevicePoint(+0,+1); + ImplDrawTextDirect( rSalLayout, mbTextLines ); +- rSalLayout.DrawBase() = aOrigPos + Point(+0,-1); ++ rSalLayout.DrawBase() = aOrigPos + DevicePoint(+0,-1); + ImplDrawTextDirect( rSalLayout, mbTextLines ); +- rSalLayout.DrawBase() = aOrigPos + Point(+1,-1); ++ rSalLayout.DrawBase() = aOrigPos + DevicePoint(+1,-1); + ImplDrawTextDirect( rSalLayout, mbTextLines ); +- rSalLayout.DrawBase() = aOrigPos + Point(+1,+0); ++ rSalLayout.DrawBase() = aOrigPos + DevicePoint(+1,+0); + ImplDrawTextDirect( rSalLayout, mbTextLines ); + rSalLayout.DrawBase() = aOrigPos; + +@@ -468,7 +469,7 @@ void OutputDevice::ImplDrawText( SalLayout& rSalLayout ) + if( mbInitTextColor ) + ImplInitTextColor(); + +- rSalLayout.DrawBase() += Point( mnTextOffX, mnTextOffY ); ++ rSalLayout.DrawBase() += DevicePoint(mnTextOffX, mnTextOffY); + + if( IsTextFillColor() ) + ImplDrawTextBackground( rSalLayout ); +@@ -986,12 +987,13 @@ tools::Long OutputDevice::GetTextArray( const OUString& rStr, std::vector pDXPixelArray; ++ std::unique_ptr> xDXPixelArray; + if(pDXAry) + { +- pDXPixelArray.reset(new DeviceCoordinate[nLen]); ++ xDXPixelArray.reset(new std::vector(nLen)); + } +- DeviceCoordinate nWidth = pSalLayout->FillDXArray( pDXPixelArray.get() ); ++ std::vector* pDXPixelArray = xDXPixelArray.get(); ++ DeviceCoordinate nWidth = pSalLayout->FillDXArray(pDXPixelArray); + int nWidthFactor = pSalLayout->GetUnitsPerPixel(); + + // convert virtual char widths to virtual absolute positions +@@ -999,7 +1001,7 @@ tools::Long OutputDevice::GetTextArray( const OUString& rStr, std::vectorresize(nLen); + for( int i = 0; i < nLen; ++i ) + { +- (*pDXAry)[i] = basegfx::fround(pDXPixelArray[i]); ++ (*pDXAry)[i] = basegfx::fround((*pDXPixelArray)[i]); + } + } + return basegfx::fround(nWidth); +@@ -1148,7 +1150,7 @@ void OutputDevice::DrawStretchText( const Point& rStartPt, sal_uLong nWidth, + + vcl::text::ImplLayoutArgs OutputDevice::ImplPrepareLayoutArgs( OUString& rStr, + const sal_Int32 nMinIndex, const sal_Int32 nLen, +- DeviceCoordinate nPixelWidth, const DeviceCoordinate* pDXArray, ++ DeviceCoordinate nPixelWidth, + SalLayoutFlags nLayoutFlags, + vcl::text::TextLayoutCache const*const pLayoutCache) const + { +@@ -1241,7 +1243,6 @@ vcl::text::ImplLayoutArgs OutputDevice::ImplPrepareLayoutArgs( OUString& rStr, + aLayoutArgs.SetOrientation( nOrientation ); + + aLayoutArgs.SetLayoutWidth( nPixelWidth ); +- aLayoutArgs.SetDXArray( pDXArray ); + + return aLayoutArgs; + } +@@ -1338,21 +1339,47 @@ std::unique_ptr OutputDevice::ImplLayout(const OUString& rOrigStr, + nPixelWidth = LogicWidthToDeviceCoordinate( nLogicalWidth ); + } + ++ vcl::text::ImplLayoutArgs aLayoutArgs = ImplPrepareLayoutArgs( aStr, nMinIndex, nLen, ++ nPixelWidth, flags, pLayoutCache); ++ ++ bool bTextRenderModeForResolutionIndependentLayout(false); ++ DeviceCoordinate nEndGlyphCoord(0); + std::unique_ptr xDXPixelArray; +- DeviceCoordinate* pDXPixelArray(nullptr); ++ std::unique_ptr xNaturalDXPixelArray; + if( !pDXArray.empty() ) + { +- if(mbMap) ++ DeviceCoordinate* pDXPixelArray(nullptr); ++ if (mbMap) + { +- // convert from logical units to font units using a temporary array +- xDXPixelArray.reset(new DeviceCoordinate[nLen]); +- pDXPixelArray = xDXPixelArray.get(); +- // using base position for better rounding a.k.a. "dancing characters" +- DeviceCoordinate nPixelXOfs2 = LogicWidthToDeviceCoordinate(rLogicalPos.X() * 2); +- for( int i = 0; i < nLen; ++i ) ++ if (GetTextRenderModeForResolutionIndependentLayout()) + { +- pDXPixelArray[i] = (LogicWidthToDeviceCoordinate((rLogicalPos.X() + pDXArray[i]) * 2) - nPixelXOfs2) / 2; ++ bTextRenderModeForResolutionIndependentLayout = true; ++ ++ // convert from logical units to font units using a temporary array ++ xNaturalDXPixelArray.reset(new double[nLen]); ++ ++ for (int i = 0; i < nLen; ++i) ++ xNaturalDXPixelArray[i] = ImplLogicWidthToDeviceFontWidth(pDXArray[i]); ++ ++ aLayoutArgs.SetAltNaturalDXArray(xNaturalDXPixelArray.get()); ++ nEndGlyphCoord = std::lround(xNaturalDXPixelArray[nLen - 1]); + } ++ else ++ { ++ // convert from logical units to font units using a temporary array ++ xDXPixelArray.reset(new DeviceCoordinate[nLen]); ++ pDXPixelArray = xDXPixelArray.get(); ++ // using base position for better rounding a.k.a. "dancing characters" ++ DeviceCoordinate nPixelXOfs2 = LogicWidthToDeviceCoordinate(rLogicalPos.X() * 2); ++ for( int i = 0; i < nLen; ++i ) ++ { ++ pDXPixelArray[i] = (LogicWidthToDeviceCoordinate((rLogicalPos.X() + pDXArray[i]) * 2) - nPixelXOfs2) / 2; ++ } ++ ++ aLayoutArgs.SetDXArray(pDXPixelArray); ++ nEndGlyphCoord = pDXPixelArray[nLen - 1]; ++ } ++ + } + else + { +@@ -1361,17 +1388,17 @@ std::unique_ptr OutputDevice::ImplLayout(const OUString& rOrigStr, + pDXPixelArray = xDXPixelArray.get(); + for( int i = 0; i < nLen; ++i ) + { +- pDXPixelArray[i] = (*pDXArray)[i]; ++ pDXPixelArray[i] = pDXArray[i]; + } + #else /* !VCL_FLOAT_DEVICE_PIXEL */ + pDXPixelArray = const_cast(pDXArray.data()); + #endif /* !VCL_FLOAT_DEVICE_PIXEL */ ++ ++ aLayoutArgs.SetDXArray(pDXPixelArray); ++ nEndGlyphCoord = pDXPixelArray[nLen - 1]; + } + } + +- vcl::text::ImplLayoutArgs aLayoutArgs = ImplPrepareLayoutArgs( aStr, nMinIndex, nLen, +- nPixelWidth, pDXPixelArray, flags, pLayoutCache); +- + // get matching layout object for base font + std::unique_ptr pSalLayout = mpGraphics->GetTextLayout(0); + +@@ -1384,6 +1411,8 @@ std::unique_ptr OutputDevice::ImplLayout(const OUString& rOrigStr, + if( !pSalLayout ) + return nullptr; + ++ pSalLayout->SetTextRenderModeForResolutionIndependentLayout(bTextRenderModeForResolutionIndependentLayout); ++ + // do glyph fallback if needed + // #105768# avoid fallback for very small font sizes + if (aLayoutArgs.HasFallbackRun() && mpFontInstance->GetFontSelectPattern().mnHeight >= 3) +@@ -1396,13 +1425,21 @@ std::unique_ptr OutputDevice::ImplLayout(const OUString& rOrigStr, + + // position, justify, etc. the layout + pSalLayout->AdjustLayout( aLayoutArgs ); +- pSalLayout->DrawBase() = ImplLogicToDevicePixel( rLogicalPos ); ++ ++ if (bTextRenderModeForResolutionIndependentLayout) ++ pSalLayout->DrawBase() = ImplLogicToDeviceFontCoordinate(rLogicalPos); ++ else ++ { ++ Point aDevicePos = ImplLogicToDevicePixel(rLogicalPos); ++ pSalLayout->DrawBase() = DevicePoint(aDevicePos.X(), aDevicePos.Y()); ++ } ++ + // adjust to right alignment if necessary + if( aLayoutArgs.mnFlags & SalLayoutFlags::RightAlign ) + { + DeviceCoordinate nRTLOffset; +- if( pDXPixelArray ) +- nRTLOffset = pDXPixelArray[ nLen - 1 ]; ++ if (!pDXArray.empty()) ++ nRTLOffset = nEndGlyphCoord; + else if( nPixelWidth ) + nRTLOffset = nPixelWidth; + else +@@ -1425,7 +1462,7 @@ std::shared_ptr OutputDevice::CreateTextLayoutCache( + bool OutputDevice::GetTextIsRTL( const OUString& rString, sal_Int32 nIndex, sal_Int32 nLen ) const + { + OUString aStr( rString ); +- vcl::text::ImplLayoutArgs aArgs = ImplPrepareLayoutArgs( aStr, nIndex, nLen, 0, nullptr ); ++ vcl::text::ImplLayoutArgs aArgs = ImplPrepareLayoutArgs(aStr, nIndex, nLen, 0); + bool bRTL = false; + int nCharPos = -1; + if (!aArgs.GetNextPos(&nCharPos, &bRTL)) +@@ -2365,7 +2402,8 @@ bool OutputDevice::GetTextBoundRect( tools::Rectangle& rRect, + } + + Point aRotatedOfs( mnTextOffX, mnTextOffY ); +- aRotatedOfs -= pSalLayout->GetDrawPosition( Point( nXOffset, 0 ) ); ++ DevicePoint aPos = pSalLayout->GetDrawPosition(DevicePoint(nXOffset, 0)); ++ aRotatedOfs -= Point(aPos.getX(), aPos.getY()); + aPixelRect += aRotatedOfs; + rRect = PixelToLogic( aPixelRect ); + if( mbMap ) +@@ -2432,9 +2470,9 @@ bool OutputDevice::GetTextOutlines( basegfx::B2DPolyPolygonVector& rVector, + int nWidthFactor = pSalLayout->GetUnitsPerPixel(); + if( nXOffset | mnTextOffX | mnTextOffY ) + { +- Point aRotatedOfs( mnTextOffX*nWidthFactor, mnTextOffY*nWidthFactor ); +- aRotatedOfs -= pSalLayout->GetDrawPosition( Point( nXOffset, 0 ) ); +- aMatrix.translate( aRotatedOfs.X(), aRotatedOfs.Y() ); ++ DevicePoint aRotatedOfs( mnTextOffX*nWidthFactor, mnTextOffY*nWidthFactor ); ++ aRotatedOfs -= pSalLayout->GetDrawPosition(DevicePoint(nXOffset, 0)); ++ aMatrix.translate( aRotatedOfs.getX(), aRotatedOfs.getY() ); + } + + if( nWidthFactor > 1 ) +diff --git a/vcl/source/outdev/textline.cxx b/vcl/source/outdev/textline.cxx +index c4fcb33bf5d0..7c8af3db9220 100644 +--- a/vcl/source/outdev/textline.cxx ++++ b/vcl/source/outdev/textline.cxx +@@ -710,7 +710,7 @@ void OutputDevice::ImplDrawStrikeoutChar( tools::Long nBaseX, tools::Long nBaseY + SetTextColor( aColor ); + ImplInitTextColor(); + +- pLayout->DrawBase() = Point( nBaseX+mnTextOffX, nBaseY+mnTextOffY ); ++ pLayout->DrawBase() = DevicePoint(nBaseX + mnTextOffX, nBaseY + mnTextOffY); + + tools::Rectangle aPixelRect; + aPixelRect.SetLeft( nBaseX+mnTextOffX ); +@@ -811,10 +811,10 @@ void OutputDevice::ImplDrawTextLines( SalLayout& rSalLayout, FontStrikeout eStri + if( bWordLine ) + { + // draw everything relative to the layout base point +- const Point aStartPt = rSalLayout.DrawBase(); ++ const DevicePoint aStartPt = rSalLayout.DrawBase(); + + // calculate distance of each word from the base point +- Point aPos; ++ DevicePoint aPos; + DeviceCoordinate nDist = 0; + DeviceCoordinate nWidth = 0; + const GlyphItem* pGlyph; +@@ -827,10 +827,10 @@ void OutputDevice::ImplDrawTextLines( SalLayout& rSalLayout, FontStrikeout eStri + if( !nWidth ) + { + // get the distance to the base point (as projected to baseline) +- nDist = aPos.X() - aStartPt.X(); ++ nDist = aPos.getX() - aStartPt.getX(); + if( mpFontInstance->mnOrientation ) + { +- const tools::Long nDY = aPos.Y() - aStartPt.Y(); ++ const DeviceCoordinate nDY = aPos.getY() - aStartPt.getY(); + const double fRad = toRadians(mpFontInstance->mnOrientation); + nDist = FRound( nDist*cos(fRad) - nDY*sin(fRad) ); + } +@@ -842,7 +842,7 @@ void OutputDevice::ImplDrawTextLines( SalLayout& rSalLayout, FontStrikeout eStri + else if( nWidth > 0 ) + { + // draw the textline for each word +- ImplDrawTextLine( aStartPt.X(), aStartPt.Y(), nDist, nWidth, ++ ImplDrawTextLine( aStartPt.getX(), aStartPt.getY(), nDist, nWidth, + eStrikeout, eUnderline, eOverline, bUnderlineAbove ); + nWidth = 0; + } +@@ -851,14 +851,14 @@ void OutputDevice::ImplDrawTextLines( SalLayout& rSalLayout, FontStrikeout eStri + // draw textline for the last word + if( nWidth > 0 ) + { +- ImplDrawTextLine( aStartPt.X(), aStartPt.Y(), nDist, nWidth, ++ ImplDrawTextLine( aStartPt.getX(), aStartPt.getY(), nDist, nWidth, + eStrikeout, eUnderline, eOverline, bUnderlineAbove ); + } + } + else + { +- Point aStartPt = rSalLayout.GetDrawPosition(); +- ImplDrawTextLine( aStartPt.X(), aStartPt.Y(), 0, ++ DevicePoint aStartPt = rSalLayout.GetDrawPosition(); ++ ImplDrawTextLine( aStartPt.getX(), aStartPt.getY(), 0, + rSalLayout.GetTextWidth() / rSalLayout.GetUnitsPerPixel(), + eStrikeout, eUnderline, eOverline, bUnderlineAbove ); + } +diff --git a/vcl/source/text/ImplLayoutArgs.cxx b/vcl/source/text/ImplLayoutArgs.cxx +index 7957d1ace824..55e01d2737b8 100644 +--- a/vcl/source/text/ImplLayoutArgs.cxx ++++ b/vcl/source/text/ImplLayoutArgs.cxx +@@ -37,6 +37,7 @@ ImplLayoutArgs::ImplLayoutArgs(const OUString& rStr, int nMinCharPos, int nEndCh + , mnEndCharPos(nEndCharPos) + , m_pTextLayoutCache(pLayoutCache) + , mpDXArray(nullptr) ++ , mpAltNaturalDXArray(nullptr) + , mnLayoutWidth(0) + , mnOrientation(0) + { +@@ -101,6 +102,11 @@ void ImplLayoutArgs::SetLayoutWidth(DeviceCoordinate nWidth) { mnLayoutWidth = n + + void ImplLayoutArgs::SetDXArray(DeviceCoordinate const* pDXArray) { mpDXArray = pDXArray; } + ++void ImplLayoutArgs::SetAltNaturalDXArray(double const* pDXArray) ++{ ++ mpAltNaturalDXArray = pDXArray; ++} ++ + void ImplLayoutArgs::SetOrientation(Degree10 nOrientation) { mnOrientation = nOrientation; } + + void ImplLayoutArgs::ResetPos() { maRuns.ResetPos(); } +@@ -304,7 +310,7 @@ std::ostream& operator<<(std::ostream& s, vcl::text::ImplLayoutArgs const& rArgs + s << "\""; + + s << ",DXArray="; +- if (rArgs.mpDXArray) ++ if (rArgs.mpDXArray || rArgs.mpAltNaturalDXArray) + { + s << "["; + int count = rArgs.mnEndCharPos - rArgs.mnMinCharPos; +@@ -313,7 +319,10 @@ std::ostream& operator<<(std::ostream& s, vcl::text::ImplLayoutArgs const& rArgs + lim = 7; + for (int i = 0; i < lim; i++) + { +- s << rArgs.mpDXArray[i]; ++ if (rArgs.mpDXArray) ++ s << rArgs.mpDXArray[i]; ++ else ++ s << rArgs.mpAltNaturalDXArray[i]; + if (i < lim - 1) + s << ","; + } +@@ -321,7 +330,10 @@ std::ostream& operator<<(std::ostream& s, vcl::text::ImplLayoutArgs const& rArgs + { + if (count > lim + 1) + s << "..."; +- s << rArgs.mpDXArray[count - 1]; ++ if (rArgs.mpDXArray) ++ s << rArgs.mpDXArray[count - 1]; ++ else ++ s << rArgs.mpAltNaturalDXArray[count - 1]; + } + s << "]"; + } +diff --git a/vcl/unx/generic/gdi/cairotextrender.cxx b/vcl/unx/generic/gdi/cairotextrender.cxx +index b14c018652a4..c403c2f3246b 100644 +--- a/vcl/unx/generic/gdi/cairotextrender.cxx ++++ b/vcl/unx/generic/gdi/cairotextrender.cxx +@@ -125,15 +125,15 @@ void CairoTextRender::DrawTextLayout(const GenericSalLayout& rLayout, const SalG + std::vector glyph_extrarotation; + cairo_glyphs.reserve( 256 ); + +- Point aPos; ++ DevicePoint aPos; + const GlyphItem* pGlyph; + int nStart = 0; + while (rLayout.GetNextGlyph(&pGlyph, aPos, nStart)) + { + cairo_glyph_t aGlyph; + aGlyph.index = pGlyph->glyphId(); +- aGlyph.x = aPos.X(); +- aGlyph.y = aPos.Y(); ++ aGlyph.x = aPos.getX(); ++ aGlyph.y = aPos.getY(); + cairo_glyphs.push_back(aGlyph); + + if (pGlyph->IsVertical()) +@@ -172,12 +172,25 @@ void CairoTextRender::DrawTextLayout(const GenericSalLayout& rLayout, const SalG + if (const cairo_font_options_t* pFontOptions = pSVData->mpDefInst->GetCairoFontOptions()) + { + const StyleSettings& rStyleSettings = Application::GetSettings().GetStyleSettings(); +- if (!rStyleSettings.GetUseFontAAFromSystem() && !rGraphics.getAntiAlias()) ++ bool bDisableAA = !rStyleSettings.GetUseFontAAFromSystem() && !rGraphics.getAntiAlias(); ++ ++ const bool bResolutionIndependentLayoutEnabled = rGraphics.getTextRenderModeForResolutionIndependentLayoutEnabled(); ++ cairo_hint_style_t eHintStyle = cairo_font_options_get_hint_style(pFontOptions); ++ cairo_hint_metrics_t eHintMetricsStyle = cairo_font_options_get_hint_metrics(pFontOptions); ++ bool bAllowedHintStyle = !bResolutionIndependentLayoutEnabled || (eHintStyle == CAIRO_HINT_STYLE_NONE || eHintStyle == CAIRO_HINT_STYLE_SLIGHT); ++ bool bAllowedHintMetricStyle = !bResolutionIndependentLayoutEnabled || (eHintMetricsStyle == CAIRO_HINT_METRICS_OFF); ++ ++ if (bDisableAA || !bAllowedHintStyle || !bAllowedHintMetricStyle) + { + // Disable font AA in case global AA setting is supposed to affect + // font rendering (not the default) and AA is disabled. + cairo_font_options_t* pOptions = cairo_font_options_copy(pFontOptions); +- cairo_font_options_set_antialias(pOptions, CAIRO_ANTIALIAS_NONE); ++ if (bDisableAA) ++ cairo_font_options_set_antialias(pOptions, CAIRO_ANTIALIAS_NONE); ++ if (!bAllowedHintStyle) ++ cairo_font_options_set_hint_style(pOptions, CAIRO_HINT_STYLE_SLIGHT); ++ if (!bAllowedHintMetricStyle) ++ cairo_font_options_set_hint_metrics(pOptions, CAIRO_HINT_METRICS_OFF); + cairo_set_font_options(cr, pOptions); + cairo_font_options_destroy(pOptions); + } +diff --git a/vcl/unx/generic/print/genpspgraphics.cxx b/vcl/unx/generic/print/genpspgraphics.cxx +index 359c33026cfd..da0a406dea0b 100644 +--- a/vcl/unx/generic/print/genpspgraphics.cxx ++++ b/vcl/unx/generic/print/genpspgraphics.cxx +@@ -149,10 +149,10 @@ void PspSalLayout::InitFont() const + void GenPspGraphics::DrawTextLayout(const GenericSalLayout& rLayout) + { + const GlyphItem* pGlyph; +- Point aPos; ++ DevicePoint aPos; + int nStart = 0; + while (rLayout.GetNextGlyph(&pGlyph, aPos, nStart)) +- m_pPrinterGfx->DrawGlyph(aPos, *pGlyph); ++ m_pPrinterGfx->DrawGlyph(Point(aPos.getX(), aPos.getY()), *pGlyph); + } + + FontCharMapRef GenPspGraphics::GetFontCharMap() const +diff --git a/vcl/win/gdi/DWriteTextRenderer.cxx b/vcl/win/gdi/DWriteTextRenderer.cxx +index edd0b700bdf6..1ec441b00592 100644 +--- a/vcl/win/gdi/DWriteTextRenderer.cxx ++++ b/vcl/win/gdi/DWriteTextRenderer.cxx +@@ -99,7 +99,7 @@ HRESULT checkResult(HRESULT hr, const char* file, size_t line) + + } // end anonymous namespace + +-D2DWriteTextOutRenderer::D2DWriteTextOutRenderer() ++D2DWriteTextOutRenderer::D2DWriteTextOutRenderer(bool bRenderingModeNatural) + : mpD2DFactory(nullptr), + mpDWriteFactory(nullptr), + mpGdiInterop(nullptr), +@@ -110,6 +110,7 @@ D2DWriteTextOutRenderer::D2DWriteTextOutRenderer() + mpFontFace(nullptr), + mlfEmHeight(0.0f), + mhDC(nullptr), ++ mbRenderingModeNatural(bRenderingModeNatural), + meTextAntiAliasMode(D2DTextAntiAliasMode::Default) + { + HRESULT hr = S_OK; +@@ -118,7 +119,7 @@ D2DWriteTextOutRenderer::D2DWriteTextOutRenderer() + if (SUCCEEDED(hr)) + { + hr = mpDWriteFactory->GetGdiInterop(&mpGdiInterop); +- hr = CreateRenderTarget(); ++ hr = CreateRenderTarget(bRenderingModeNatural); + } + meTextAntiAliasMode = lclGetSystemTextAntiAliasMode(); + } +@@ -135,7 +136,7 @@ D2DWriteTextOutRenderer::~D2DWriteTextOutRenderer() + mpD2DFactory->Release(); + } + +-void D2DWriteTextOutRenderer::applyTextAntiAliasMode() ++void D2DWriteTextOutRenderer::applyTextAntiAliasMode(bool bRenderingModeNatural) + { + D2D1_TEXT_ANTIALIAS_MODE eTextAAMode = D2D1_TEXT_ANTIALIAS_MODE_DEFAULT; + DWRITE_RENDERING_MODE eRenderingMode = DWRITE_RENDERING_MODE_DEFAULT; +@@ -160,11 +161,15 @@ void D2DWriteTextOutRenderer::applyTextAntiAliasMode() + default: + break; + } ++ ++ if (bRenderingModeNatural) ++ eRenderingMode = DWRITE_RENDERING_MODE_CLEARTYPE_NATURAL; ++ + mpRT->SetTextRenderingParams(lclSetRenderingMode(mpDWriteFactory, eRenderingMode)); + mpRT->SetTextAntialiasMode(eTextAAMode); + } + +-HRESULT D2DWriteTextOutRenderer::CreateRenderTarget() ++HRESULT D2DWriteTextOutRenderer::CreateRenderTarget(bool bRenderingModeNatural) + { + if (mpRT) + { +@@ -173,19 +178,10 @@ HRESULT D2DWriteTextOutRenderer::CreateRenderTarget() + } + HRESULT hr = CHECKHR(mpD2DFactory->CreateDCRenderTarget(&mRTProps, &mpRT)); + if (SUCCEEDED(hr)) +- applyTextAntiAliasMode(); ++ applyTextAntiAliasMode(bRenderingModeNatural); + return hr; + } + +-void D2DWriteTextOutRenderer::changeTextAntiAliasMode(D2DTextAntiAliasMode eMode) +-{ +- if (meTextAntiAliasMode != eMode) +- { +- meTextAntiAliasMode = eMode; +- applyTextAntiAliasMode(); +- } +-} +- + bool D2DWriteTextOutRenderer::Ready() const + { + return mpGdiInterop && mpRT; +@@ -199,7 +195,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 D2DWriteTextOutRenderer::operator()(GenericSalLayout const & rLayout, SalGraphics& rGraphics, HDC hDC, bool bRenderingModeNatural) + { + bool bRetry = false; + bool bResult = false; +@@ -207,13 +203,13 @@ bool D2DWriteTextOutRenderer::operator ()(GenericSalLayout const & rLayout, SalG + do + { + bRetry = false; +- bResult = performRender(rLayout, rGraphics, hDC, bRetry); ++ bResult = performRender(rLayout, rGraphics, hDC, bRetry, bRenderingModeNatural); + nCount++; + } while (bRetry && nCount < 3); + return bResult; + } + +-bool D2DWriteTextOutRenderer::performRender(GenericSalLayout const & rLayout, SalGraphics& rGraphics, HDC hDC, bool& bRetry) ++bool D2DWriteTextOutRenderer::performRender(GenericSalLayout const & rLayout, SalGraphics& rGraphics, HDC hDC, bool& bRetry, bool bRenderingModeNatural) + { + if (!Ready()) + return false; +@@ -223,14 +219,14 @@ bool D2DWriteTextOutRenderer::performRender(GenericSalLayout const & rLayout, Sa + + if (hr == D2DERR_RECREATE_TARGET) + { +- CreateRenderTarget(); ++ CreateRenderTarget(bRenderingModeNatural); + bRetry = true; + return false; + } + if (FAILED(hr)) + { + // If for any reason we can't bind fallback to legacy APIs. +- return ExTextOutRenderer()(rLayout, rGraphics, hDC); ++ return ExTextOutRenderer()(rLayout, rGraphics, hDC, bRenderingModeNatural); + } + + mlfEmHeight = 0; +@@ -261,15 +257,15 @@ bool D2DWriteTextOutRenderer::performRender(GenericSalLayout const & rLayout, Sa + mpRT->BeginDraw(); + + int nStart = 0; +- Point aPos(0, 0); ++ DevicePoint aPos; + const GlyphItem* pGlyph; + while (rLayout.GetNextGlyph(&pGlyph, aPos, nStart)) + { + UINT16 glyphIndices[] = { pGlyph->glyphId() }; + FLOAT glyphAdvances[] = { static_cast(pGlyph->m_nNewWidth) / fHScale }; + DWRITE_GLYPH_OFFSET glyphOffsets[] = { { 0.0f, 0.0f }, }; +- D2D1_POINT_2F baseline = { static_cast(aPos.X() - bounds.Left()) / fHScale, +- static_cast(aPos.Y() - bounds.Top()) }; ++ D2D1_POINT_2F baseline = { static_cast(aPos.getX() - bounds.Left()) / fHScale, ++ static_cast(aPos.getY() - bounds.Top()) }; + WinFontTransformGuard aTransformGuard(mpRT, fHScale, rLayout, baseline, pGlyph->IsVertical()); + DWRITE_GLYPH_RUN glyphs = { + mpFontFace, +@@ -295,7 +291,7 @@ bool D2DWriteTextOutRenderer::performRender(GenericSalLayout const & rLayout, Sa + + if (hr == D2DERR_RECREATE_TARGET) + { +- CreateRenderTarget(); ++ CreateRenderTarget(bRenderingModeNatural); + bRetry = true; + } + +diff --git a/vcl/win/gdi/winlayout.cxx b/vcl/win/gdi/winlayout.cxx +index 83033e9f6be8..c55dbbf978e5 100644 +--- a/vcl/win/gdi/winlayout.cxx ++++ b/vcl/win/gdi/winlayout.cxx +@@ -52,7 +52,7 @@ + #include + #include + +-TextOutRenderer& TextOutRenderer::get(bool bUseDWrite) ++TextOutRenderer& TextOutRenderer::get(bool bUseDWrite, bool bRenderingModeNatural) + { + SalData* const pSalData = GetSalData(); + +@@ -64,9 +64,13 @@ TextOutRenderer& TextOutRenderer::get(bool bUseDWrite) + + if (bUseDWrite) + { +- if (!pSalData->m_pD2DWriteTextOutRenderer) ++ if (!pSalData->m_pD2DWriteTextOutRenderer ++ || static_cast(pSalData->m_pD2DWriteTextOutRenderer.get()) ++ ->GetRenderingModeNatural() ++ != bRenderingModeNatural) + { +- pSalData->m_pD2DWriteTextOutRenderer.reset(new D2DWriteTextOutRenderer()); ++ pSalData->m_pD2DWriteTextOutRenderer.reset( ++ new D2DWriteTextOutRenderer(bRenderingModeNatural)); + } + return *pSalData->m_pD2DWriteTextOutRenderer; + } +@@ -78,10 +82,10 @@ TextOutRenderer& TextOutRenderer::get(bool bUseDWrite) + } + + bool ExTextOutRenderer::operator()(GenericSalLayout const& rLayout, SalGraphics& /*rGraphics*/, +- HDC hDC) ++ HDC hDC, bool /*bRenderingModeNatural*/) + { + int nStart = 0; +- Point aPos(0, 0); ++ DevicePoint aPos; + const GlyphItem* pGlyph; + const WinFontInstance* pWinFont = static_cast(&rLayout.GetFont()); + UINT nTextAlign = GetTextAlign(hDC); +@@ -106,8 +110,8 @@ bool ExTextOutRenderer::operator()(GenericSalLayout const& rLayout, SalGraphics& + if (nCurTextAlign != nNewTextAlign) + SetTextAlign(hDC, nNewTextAlign); + +- ExtTextOutW(hDC, aPos.X(), aPos.Y() + nYOffset, ETO_GLYPH_INDEX, nullptr, &glyphWStr, 1, +- nullptr); ++ ExtTextOutW(hDC, aPos.getX(), aPos.getY() + nYOffset, ETO_GLYPH_INDEX, nullptr, &glyphWStr, ++ 1, nullptr); + + nCurTextAlign = nNewTextAlign; + } +@@ -294,10 +298,11 @@ void WinFontInstance::SetGraphics(WinSalGraphics* pGraphics) + SelectObject(hDC, hOrigFont); + } + +-void WinSalGraphics::DrawTextLayout(const GenericSalLayout& rLayout, HDC hDC, bool bUseDWrite) ++void WinSalGraphics::DrawTextLayout(const GenericSalLayout& rLayout, HDC hDC, bool bUseDWrite, ++ bool bRenderingModeNatural) + { +- TextOutRenderer& render = TextOutRenderer::get(bUseDWrite); +- render(rLayout, *this, hDC); ++ TextOutRenderer& render = TextOutRenderer::get(bUseDWrite, bRenderingModeNatural); ++ render(rLayout, *this, hDC, bRenderingModeNatural); + } + + void WinSalGraphics::DrawTextLayout(const GenericSalLayout& rLayout) +@@ -313,7 +318,11 @@ void WinSalGraphics::DrawTextLayout(const GenericSalLayout& rLayout) + const HFONT hOrigFont = ::SelectFont(hDC, hLayoutFont); + + // DWrite text renderer performs vertical writing better except printing. +- DrawTextLayout(rLayout, hDC, !mbPrinter && rLayout.GetFont().GetFontSelectPattern().mbVertical); ++ const bool bVerticalScreenText ++ = !mbPrinter && rLayout.GetFont().GetFontSelectPattern().mbVertical; ++ const bool bRenderingModeNatural = getTextRenderModeForResolutionIndependentLayoutEnabled(); ++ const bool bUseDWrite = bVerticalScreenText || bRenderingModeNatural; ++ DrawTextLayout(rLayout, hDC, bUseDWrite, bRenderingModeNatural); + + ::SelectFont(hDC, hOrigFont); + } +-- +2.35.1 + diff --git a/libreoffice.spec b/libreoffice.spec index b11c145..80e3bc2 100644 --- a/libreoffice.spec +++ b/libreoffice.spec @@ -62,7 +62,7 @@ Summary: Free Software Productivity Suite Name: libreoffice Epoch: 1 Version: %{libo_version}.3 -Release: 3%{?libo_prerelease}%{?dist} +Release: 4%{?libo_prerelease}%{?dist} License: (MPLv1.1 or LGPLv3+) and LGPLv3 and LGPLv2+ and BSD and (MPLv1.1 or GPLv2 or LGPLv2 or Netscape) and Public Domain and ASL 2.0 and MPLv2.0 and CC0 URL: http://www.libreoffice.org/ @@ -253,6 +253,7 @@ Patch3: 0001-Revert-tdf-101630-gdrive-support-w-oAuth-and-Drive-A.patch # ICE Patch4: 0001-workaround-x86-ICE-with-gcc-12.patch Patch5: 0001-s390x-canvas-test-fails.patch +Patch6: 0001-tdf-144862-use-resolution-independent-positions-for-.patch # not upstreamed Patch500: 0001-disable-libe-book-support.patch @@ -2181,6 +2182,9 @@ gtk-update-icon-cache -q %{_datadir}/icons/hicolor &>/dev/null || : %{_includedir}/LibreOfficeKit %changelog +* Mon Mar 14 2022 Caolán McNamara - 1:7.3.1.3-4 +- tdf#144862 use resolution independent text rendering + * Tue Mar 08 2022 Caolán McNamara - 1:7.3.1.3-3 - rhbz#2061598 No Icons Launcher of LibreOffice Apps in Raspberry Pi