basegfx/source/curve/b2dcubicbezier.cxx | 24 +-- canvas/source/directx/dx_textlayout_drawhelper.cxx | 4 canvas/source/opengl/ogl_canvashelper.cxx | 2 canvas/source/tools/surface.cxx | 9 - canvas/source/vcl/canvasfont.cxx | 2 canvas/source/vcl/canvashelper.cxx | 4 canvas/source/vcl/canvashelper_texturefill.cxx | 36 ++--- canvas/source/vcl/impltools.cxx | 8 - chart2/source/controller/main/SelectionHelper.cxx | 2 cppcanvas/source/mtfrenderer/transparencygroupaction.cxx | 4 cui/source/dialogs/cuigrfflt.cxx | 8 - cui/source/tabpages/transfrm.cxx | 4 drawinglayer/source/drawinglayeruno/xprimitive2drenderer.cxx | 2 drawinglayer/source/primitive2d/textlayoutdevice.cxx | 67 +++++++-- drawinglayer/source/processor2d/helperwrongspellrenderer.cxx | 6 drawinglayer/source/processor2d/vclmetafileprocessor2d.cxx | 16 +- drawinglayer/source/processor2d/vclprocessor2d.cxx | 76 +++++++---- drawinglayer/source/tools/converters.cxx | 2 drawinglayer/source/tools/emfphelperdata.cxx | 34 ++-- drawinglayer/source/tools/emfphelperdata.hxx | 10 + editeng/source/editeng/impedit3.cxx | 6 editeng/source/items/svxfont.cxx | 2 editeng/source/outliner/outliner.cxx | 4 emfio/qa/cppunit/emf/EmfImportTest.cxx | 4 emfio/source/reader/mtftools.cxx | 6 filter/source/msfilter/escherex.cxx | 2 filter/source/msfilter/eschesdo.cxx | 15 +- filter/source/svg/svgfilter.cxx | 2 filter/source/svg/svgwriter.cxx | 24 +-- hwpfilter/source/drawing.h | 1 include/basegfx/numeric/ftools.hxx | 20 +- include/drawinglayer/primitive2d/textlayoutdevice.hxx | 2 include/svx/svdtrans.hxx | 12 - include/svx/transfrmhelper.hxx | 8 - include/tools/helpers.hxx | 8 - include/vcl/bitmap.hxx | 8 + include/vcl/outdev.hxx | 21 ++- include/vcl/vcllayout.hxx | 11 + sc/qa/uitest/chart/chartLegend.py | 4 sc/source/core/data/drwlayer.cxx | 12 - sc/source/ui/miscdlgs/autofmt.cxx | 8 - sc/source/ui/view/drawview.cxx | 4 sd/qa/unit/layout-tests.cxx | 4 sd/qa/unit/tiledrendering/LOKitSearchTest.cxx | 6 sd/source/core/sdpage.cxx | 16 +- sd/source/filter/html/htmlex.cxx | 3 sd/source/ui/app/optsitem.cxx | 4 sd/source/ui/dlg/vectdlg.cxx | 6 sd/source/ui/func/fuoaprms.cxx | 3 sd/source/ui/view/drviews8.cxx | 4 sfx2/source/doc/objcont.cxx | 4 slideshow/source/engine/shapes/drawinglayeranimation.cxx | 6 slideshow/source/engine/slideview.cxx | 4 svgio/inc/SvgNumber.hxx | 8 - svgio/inc/svgnode.hxx | 7 - svgio/inc/svgtspannode.hxx | 2 svgio/qa/cppunit/SvgImportTest.cxx | 61 ++++++++ svgio/qa/cppunit/SvgNumberTest.cxx | 4 svgio/qa/cppunit/data/dy_in_ems.svg | 7 + svgio/qa/cppunit/data/dy_in_exs.svg | 7 + svgio/source/svgreader/SvgNumber.cxx | 5 svgio/source/svgreader/svgcharacternode.cxx | 18 +- svgio/source/svgreader/svgnode.cxx | 36 +---- svgio/source/svgreader/svgstyleattributes.cxx | 8 - svgio/source/svgreader/svgtspannode.cxx | 5 svx/qa/unit/customshapes.cxx | 10 - svx/source/customshapes/EnhancedCustomShape2d.cxx | 4 svx/source/customshapes/EnhancedCustomShape3d.cxx | 2 svx/source/dialog/_contdlg.cxx | 4 svx/source/dialog/frmsel.cxx | 12 - svx/source/engine3d/obj3d.cxx | 2 svx/source/engine3d/view3d.cxx | 6 svx/source/gallery2/galobj.cxx | 4 svx/source/svdraw/gradtrns.cxx | 24 +-- svx/source/svdraw/sdrpagewindow.cxx | 7 - svx/source/svdraw/svdcrtv.cxx | 4 svx/source/svdraw/svddrgmt.cxx | 16 +- svx/source/svdraw/svddrgv.cxx | 16 +- svx/source/svdraw/svdedtv1.cxx | 4 svx/source/svdraw/svdedxv.cxx | 8 - svx/source/svdraw/svdfmtf.cxx | 15 +- svx/source/svdraw/svdhdl.cxx | 12 + svx/source/svdraw/svdmrkv.cxx | 12 - svx/source/svdraw/svdoashp.cxx | 18 +- svx/source/svdraw/svdobj.cxx | 6 svx/source/svdraw/svdocapt.cxx | 6 svx/source/svdraw/svdocirc.cxx | 11 + svx/source/svdraw/svdoedge.cxx | 16 +- svx/source/svdraw/svdograf.cxx | 18 +- svx/source/svdraw/svdomeas.cxx | 22 +-- svx/source/svdraw/svdopath.cxx | 37 +++-- svx/source/svdraw/svdorect.cxx | 2 svx/source/svdraw/svdotext.cxx | 10 - svx/source/svdraw/svdotextdecomposition.cxx | 12 - svx/source/svdraw/svdpdf.cxx | 20 +- svx/source/svdraw/svdpoev.cxx | 9 - svx/source/svdraw/svdtrans.cxx | 24 +-- svx/source/unodraw/UnoGraphicExporter.cxx | 8 - svx/source/unodraw/unoshape.cxx | 8 - svx/source/xoutdev/_xpoly.cxx | 7 - sw/qa/extras/layout/layout.cxx | 6 sw/qa/extras/ww8export/data/empty_group.docx |binary sw/qa/extras/ww8export/ww8export4.cxx | 24 +++ sw/source/core/draw/dflyobj.cxx | 32 ++-- sw/source/core/graphic/ndgrf.cxx | 3 sw/source/core/layout/fly.cxx | 2 sw/source/core/layout/wsfrm.cxx | 28 ++-- sw/source/ui/table/autoformatpreview.cxx | 15 +- toolkit/source/awt/vclxfont.cxx | 2 tools/CppunitTest_tools_test.mk | 1 tools/qa/cppunit/test_fround.cxx | 62 -------- tools/source/generic/color.cxx | 7 - tools/source/generic/line.cxx | 7 - tools/source/generic/poly.cxx | 37 +++-- vcl/inc/font/LogicalFontInstance.hxx | 5 vcl/inc/impfontcache.hxx | 8 - vcl/inc/impglyphitem.hxx | 6 vcl/inc/pdf/pdfbuildin_fonts.hxx | 2 vcl/inc/qt5/QtFont.hxx | 1 vcl/inc/quartz/salgdi.h | 2 vcl/inc/unx/freetype_glyphcache.hxx | 2 vcl/inc/unx/glyphcache.hxx | 1 vcl/inc/win/DWriteTextRenderer.hxx | 15 -- vcl/inc/win/winlayout.hxx | 4 vcl/osx/salframeview.mm | 3 vcl/qa/cppunit/cjktext.cxx | 2 vcl/qa/cppunit/complextext.cxx | 2 vcl/qa/cppunit/fontmocks.hxx | 3 vcl/qa/cppunit/logicalfontinstance.cxx | 67 ++++++++- vcl/qt5/QtFont.cxx | 7 - vcl/quartz/ctfonts.cxx | 21 --- vcl/skia/gdiimpl.cxx | 17 +- vcl/skia/salbmp.cxx | 3 vcl/skia/win/gdiimpl.cxx | 33 ++-- vcl/skia/x11/textrender.cxx | 10 - vcl/source/animate/AnimationRenderer.cxx | 8 - vcl/source/bitmap/BitmapEmbossGreyFilter.cxx | 6 vcl/source/bitmap/BitmapEx.cxx | 2 vcl/source/bitmap/BitmapFastScaleFilter.cxx | 4 vcl/source/bitmap/BitmapInterpolateScaleFilter.cxx | 4 vcl/source/bitmap/BitmapScaleConvolutionFilter.cxx | 8 - vcl/source/bitmap/BitmapScaleSuperFilter.cxx | 4 vcl/source/bitmap/BitmapTools.cxx | 16 +- vcl/source/bitmap/bitmap.cxx | 52 +++---- vcl/source/bitmap/impvect.cxx | 6 vcl/source/filter/eps/eps.cxx | 4 vcl/source/filter/wmf/emfwr.cxx | 8 - vcl/source/filter/wmf/wmfwr.cxx | 6 vcl/source/font/LogicalFontInstance.cxx | 36 ++++- vcl/source/font/fontcache.cxx | 4 vcl/source/gdi/CommonSalLayout.cxx | 6 vcl/source/gdi/gdimetafiletools.cxx | 8 - vcl/source/gdi/gdimtf.cxx | 28 ++-- vcl/source/gdi/impgraph.cxx | 6 vcl/source/gdi/metaact.cxx | 30 ++-- vcl/source/gdi/pdfbuildin_fonts.cxx | 5 vcl/source/gdi/pdfwriter_impl.cxx | 37 ++++- vcl/source/gdi/pdfwriter_impl2.cxx | 8 - vcl/source/gdi/print.cxx | 10 - vcl/source/gdi/regband.cxx | 7 - vcl/source/gdi/region.cxx | 4 vcl/source/gdi/regionband.cxx | 4 vcl/source/gdi/sallayout.cxx | 40 +++-- vcl/source/gdi/textlayout.cxx | 2 vcl/source/graphic/GraphicObject.cxx | 24 +-- vcl/source/helper/canvastools.cxx | 4 vcl/source/outdev/bitmapex.cxx | 18 +- vcl/source/outdev/font.cxx | 6 vcl/source/outdev/hatch.cxx | 19 +- vcl/source/outdev/map.cxx | 37 +++++ vcl/source/outdev/rect.cxx | 16 +- vcl/source/outdev/text.cxx | 73 +++++++--- vcl/source/outdev/textline.cxx | 4 vcl/source/outdev/transparent.cxx | 2 vcl/source/window/printdlg.cxx | 4 vcl/source/window/window2.cxx | 6 vcl/unx/generic/glyphs/freetype_glyphcache.cxx | 38 ----- vcl/unx/generic/glyphs/glyphcache.cxx | 8 - vcl/unx/gtk3/gtkinst.cxx | 3 vcl/win/gdi/DWriteTextRenderer.cxx | 72 +++++++--- vcl/win/gdi/gdiimpl.cxx | 4 vcl/win/gdi/salfont.cxx | 53 ------- vcl/win/gdi/winlayout.cxx | 18 -- vcl/workben/listglyphs.cxx | 2 xmloff/source/draw/shapeexport.cxx | 17 +- 185 files changed, 1274 insertions(+), 1085 deletions(-)
New commits: commit cba231b431274e9709c6afc2cec1354265e182a5 Author: Mike Kaganski <[email protected]> AuthorDate: Fri May 17 20:40:37 2024 +0500 Commit: Mike Kaganski <[email protected]> CommitDate: Sat Jun 1 14:56:20 2024 +0500 tdf#161154: pass "scaling is done externally" information down the stack VclProcessor2D::RenderTextSimpleOrDecoratedPortionPrimitive2D does the scaling, taking into account the font scaling. Before commit 8557ea84c9336ba8061246f1f46ddb6e02f413a1, D2DWriteTextOutRenderer was doing own scaling in addition, but it seems that it somehow didn't affect the result much. The said commit removed the scalng from D2DWriteTextOutRenderer. As tdf#160901 demonstrated, the scaling is necessary in different code paths - and it turns out, that we need to know, if the caller does its own scaling or not, to make a decision, if the scaling should be fone in D2DWriteTextOutRenderer. This hack passes this from VclProcessor2D to D2DWriteTextOutRenderer through OutputDevice. Thanks to Miklos for the isea. I still don't understand, why all this seemingly doesn't affect other renderers. Change-Id: I001036f4574898b8e7606652525638df43c35240 diff --git a/drawinglayer/source/processor2d/vclprocessor2d.cxx b/drawinglayer/source/processor2d/vclprocessor2d.cxx index 109be5cd40f9..f401c220f170 100644 --- a/drawinglayer/source/processor2d/vclprocessor2d.cxx +++ b/drawinglayer/source/processor2d/vclprocessor2d.cxx @@ -428,17 +428,21 @@ void VclProcessor2D::RenderTextSimpleOrDecoratedPortionPrimitive2D( mpOutputDevice->SetFont(aFont); mpOutputDevice->SetTextColor(Color(aRGBFontColor)); - if (!aDXArray.empty()) { - const SalLayoutGlyphs* pGlyphs = SalLayoutGlyphsCache::self()->GetLayoutGlyphs( - mpOutputDevice, aText, nPos, nLen); - mpOutputDevice->DrawTextArray(aStartPoint, aText, aDXArray, - rTextCandidate.getKashidaArray(), nPos, nLen, - SalLayoutFlags::NONE, pGlyphs); - } - else - { - mpOutputDevice->DrawText(aStartPoint, aText, nPos, nLen); + // For D2DWriteTextOutRenderer, we must pass a flag to not use font scaling + auto guard = mpOutputDevice->ScopedNoFontScaling(); + if (!aDXArray.empty()) + { + const SalLayoutGlyphs* pGlyphs = SalLayoutGlyphsCache::self()->GetLayoutGlyphs( + mpOutputDevice, aText, nPos, nLen); + mpOutputDevice->DrawTextArray(aStartPoint, aText, aDXArray, + rTextCandidate.getKashidaArray(), nPos, nLen, + SalLayoutFlags::NONE, pGlyphs); + } + else + { + mpOutputDevice->DrawText(aStartPoint, aText, nPos, nLen); + } } if (rTextCandidate.getFontAttribute().getRTL()) diff --git a/include/vcl/outdev.hxx b/include/vcl/outdev.hxx index 098a5ccc5ef0..7142fee006df 100644 --- a/include/vcl/outdev.hxx +++ b/include/vcl/outdev.hxx @@ -21,6 +21,7 @@ #include <sal/config.h> +#include <comphelper/flagguard.hxx> #include <o3tl/span.hxx> #include <tools/gen.hxx> #include <tools/ref.hxx> @@ -258,6 +259,8 @@ private: mutable bool mbRefPoint : 1; mutable bool mbEnableRTL : 1; + bool mbNoFontScaling = false; // Used only by D2DWriteTextOutRenderer + protected: mutable std::shared_ptr<vcl::font::PhysicalFontCollection> mxFontCollection; mutable std::shared_ptr<ImplFontCache> mxFontCache; @@ -340,6 +343,8 @@ public: /// request XSpriteCanvas render interface css::uno::Reference< css::rendering::XSpriteCanvas > GetSpriteCanvas() const; + auto ScopedNoFontScaling() { return comphelper::FlagRestorationGuard(mbNoFontScaling, true); } + protected: /** Acquire a graphics device that the output device uses to draw on. diff --git a/include/vcl/vcllayout.hxx b/include/vcl/vcllayout.hxx index 887480ce3ee9..a70fdaaddc1c 100644 --- a/include/vcl/vcllayout.hxx +++ b/include/vcl/vcllayout.hxx @@ -21,6 +21,7 @@ #include <basegfx/polygon/b2dpolypolygon.hxx> #include <basegfx/range/b2drectangle.hxx> +#include <comphelper/flagguard.hxx> #include <i18nlangtag/languagetag.hxx> #include <tools/gen.hxx> #include <tools/degree.hxx> @@ -110,6 +111,9 @@ public: virtual SalLayoutGlyphs GetGlyphs() const; + auto ScopedFontScaling(bool v) { return comphelper::FlagRestorationGuard(mbScaleFont, v); } + bool ScaleFont() const { return mbScaleFont; } + protected: // used by layout engines SalLayout(); @@ -118,6 +122,8 @@ private: SalLayout(const SalLayout&) = delete; SalLayout& operator=(const SalLayout&) = delete; + bool mbScaleFont = true; // Used only by D2DWriteTextOutRenderer + protected: int mnMinCharPos; int mnEndCharPos; diff --git a/vcl/source/outdev/text.cxx b/vcl/source/outdev/text.cxx index 0ff3ac91f177..ce652f4506dd 100644 --- a/vcl/source/outdev/text.cxx +++ b/vcl/source/outdev/text.cxx @@ -468,7 +468,7 @@ void OutputDevice::ImplDrawSpecialText( SalLayout& rSalLayout ) void OutputDevice::ImplDrawText( SalLayout& rSalLayout ) { - + auto guard = rSalLayout.ScopedFontScaling(!mbNoFontScaling); if( mbInitClipRegion ) InitClipRegion(); if( mbOutputClipped ) diff --git a/vcl/win/gdi/DWriteTextRenderer.cxx b/vcl/win/gdi/DWriteTextRenderer.cxx index f8c8c93958d7..57bd0ff62b83 100644 --- a/vcl/win/gdi/DWriteTextRenderer.cxx +++ b/vcl/win/gdi/DWriteTextRenderer.cxx @@ -332,12 +332,14 @@ WinFontTransformGuard::WinFontTransformGuard(ID2D1RenderTarget* pRenderTarget, bool bIsVertical) : mpRenderTarget(pRenderTarget) { - const float hscale = [&font = rLayout.GetFont()] + const float hscale = [&rLayout] { - const auto& rPattern = font.GetFontSelectPattern(); + if (!rLayout.ScaleFont()) + return 1.0; + const auto& rPattern = rLayout.GetFont().GetFontSelectPattern(); if (!rPattern.mnHeight || !rPattern.mnWidth) return 1.0; - return rPattern.mnWidth * font.GetAverageWidthFactor() / rPattern.mnHeight; + return rPattern.mnWidth * rLayout.GetFont().GetAverageWidthFactor() / rPattern.mnHeight; }(); Degree10 angle = rLayout.GetOrientation(); commit dbc4b74a175d80e2ee006ee92fb5c0feecdd9a1d Author: Mike Kaganski <[email protected]> AuthorDate: Thu May 2 14:23:53 2024 +0500 Commit: Mike Kaganski <[email protected]> CommitDate: Sat Jun 1 14:56:19 2024 +0500 tdf#160901: partially revert 8557ea84c9336ba8061246f1f46ddb6e02f413a1 It turns out, that the horizontal scale is needed in the applied transform. Change-Id: Ic07e015f0ddeee3d66653b683d83435d04b9ab81 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/167004 Tested-by: Jenkins Reviewed-by: Mike Kaganski <[email protected]> diff --git a/vcl/win/gdi/DWriteTextRenderer.cxx b/vcl/win/gdi/DWriteTextRenderer.cxx index a962cbcbb801..f8c8c93958d7 100644 --- a/vcl/win/gdi/DWriteTextRenderer.cxx +++ b/vcl/win/gdi/DWriteTextRenderer.cxx @@ -332,19 +332,32 @@ WinFontTransformGuard::WinFontTransformGuard(ID2D1RenderTarget* pRenderTarget, bool bIsVertical) : mpRenderTarget(pRenderTarget) { + const float hscale = [&font = rLayout.GetFont()] + { + const auto& rPattern = font.GetFontSelectPattern(); + if (!rPattern.mnHeight || !rPattern.mnWidth) + return 1.0; + return rPattern.mnWidth * font.GetAverageWidthFactor() / rPattern.mnHeight; + }(); + Degree10 angle = rLayout.GetOrientation(); if (bIsVertical) angle += 900_deg10; - if (angle) + if (hscale != 1.0f || angle) { D2D1::Matrix3x2F aTransform; pRenderTarget->GetTransform(&aTransform); moTransform = aTransform; + if (hscale != 1.0f) // basegfx::fTools::equal is useless with float + aTransform = aTransform * D2D1::Matrix3x2F::Scale(hscale, 1.0f, { 0, 0 }); + // DWrite angle is in clockwise degrees, our orientation is in counter-clockwise 10th // degrees. - aTransform = aTransform * D2D1::Matrix3x2F::Rotation(-toDegrees(angle), rBaseline); + if (angle) + aTransform = aTransform * D2D1::Matrix3x2F::Rotation(-toDegrees(angle), rBaseline); + mpRenderTarget->SetTransform(aTransform); } } commit ed7409a32faeea0d54d2a07db8dd24c53a25b421 Author: Mike Kaganski <[email protected]> AuthorDate: Wed Apr 10 22:45:41 2024 +0500 Commit: Mike Kaganski <[email protected]> CommitDate: Sat Jun 1 14:56:19 2024 +0500 Only call ID2D1RenderTarget::Get/SetTransform when needed Change-Id: Ifde2c24c222e3c316f5a4f47e86c2c08f676639a Reviewed-on: https://gerrit.libreoffice.org/c/core/+/165983 Tested-by: Jenkins Reviewed-by: Mike Kaganski <[email protected]> diff --git a/vcl/win/gdi/DWriteTextRenderer.cxx b/vcl/win/gdi/DWriteTextRenderer.cxx index fb96cfa68659..a962cbcbb801 100644 --- a/vcl/win/gdi/DWriteTextRenderer.cxx +++ b/vcl/win/gdi/DWriteTextRenderer.cxx @@ -108,7 +108,7 @@ public: private: ID2D1RenderTarget* mpRenderTarget; - D2D1::Matrix3x2F maTransform; + std::optional<D2D1::Matrix3x2F> moTransform; }; } // end anonymous namespace @@ -332,25 +332,27 @@ WinFontTransformGuard::WinFontTransformGuard(ID2D1RenderTarget* pRenderTarget, bool bIsVertical) : mpRenderTarget(pRenderTarget) { - pRenderTarget->GetTransform(&maTransform); - D2D1::Matrix3x2F aTransform = maTransform; - Degree10 angle = rLayout.GetOrientation(); - if (bIsVertical) angle += 900_deg10; if (angle) { + D2D1::Matrix3x2F aTransform; + pRenderTarget->GetTransform(&aTransform); + moTransform = aTransform; + // DWrite angle is in clockwise degrees, our orientation is in counter-clockwise 10th // degrees. - aTransform = aTransform - * D2D1::Matrix3x2F::Rotation( - -toDegrees(angle), rBaseline); + aTransform = aTransform * D2D1::Matrix3x2F::Rotation(-toDegrees(angle), rBaseline); + mpRenderTarget->SetTransform(aTransform); } - mpRenderTarget->SetTransform(aTransform); } -WinFontTransformGuard::~WinFontTransformGuard() { mpRenderTarget->SetTransform(maTransform); } +WinFontTransformGuard::~WinFontTransformGuard() +{ + if (moTransform) + mpRenderTarget->SetTransform(*moTransform); +} /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ commit c9db962242abcaf7025865d9444e59c3e89a2bec Author: Mike Kaganski <[email protected]> AuthorDate: Fri Apr 26 01:00:21 2024 +0500 Commit: Mike Kaganski <[email protected]> CommitDate: Sat Jun 1 14:56:19 2024 +0500 tdf#157240: also set font color together with font Commit b008831a5545e5a777d77547ef96b9798d795f30 (tdf#152990 set the font after the MapMode is (potentially) set, 2023-01-14) had moved setting of font forward, but left setting the font color behind. This fixed that. Change-Id: I73fadc7c139da3b6f84b7fc875c585d02fc1c719 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/166654 Tested-by: Jenkins Reviewed-by: Mike Kaganski <[email protected]> diff --git a/drawinglayer/source/processor2d/vclprocessor2d.cxx b/drawinglayer/source/processor2d/vclprocessor2d.cxx index f8e5d0457583..109be5cd40f9 100644 --- a/drawinglayer/source/processor2d/vclprocessor2d.cxx +++ b/drawinglayer/source/processor2d/vclprocessor2d.cxx @@ -329,8 +329,6 @@ void VclProcessor2D::RenderTextSimpleOrDecoratedPortionPrimitive2D( mpOutputDevice->SetLayoutMode(nRTLLayoutMode); } - mpOutputDevice->SetTextColor(Color(aRGBFontColor)); - OUString aText(rTextCandidate.getText()); sal_Int32 nPos = rTextCandidate.getTextPosition(); sal_Int32 nLen = rTextCandidate.getTextLength(); @@ -428,6 +426,7 @@ void VclProcessor2D::RenderTextSimpleOrDecoratedPortionPrimitive2D( // tdf#152990 set the font after the MapMode is (potentially) set so canvas uses the desired // font size mpOutputDevice->SetFont(aFont); + mpOutputDevice->SetTextColor(Color(aRGBFontColor)); if (!aDXArray.empty()) { commit 474ef30244dcbcba075cb7201e60bbf5e3ecd7a2 Author: Mike Kaganski <[email protected]> AuthorDate: Tue Apr 23 05:25:34 2024 +0500 Commit: Mike Kaganski <[email protected]> CommitDate: Sat Jun 1 14:56:19 2024 +0500 Fix a thinko in 8a97f1ba8d9ccb65b2c89106de20666311d90c30 Change-Id: I07bb67e9384470269d1fd3df087acee377cce729 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/166490 Tested-by: Jenkins Reviewed-by: Mike Kaganski <[email protected]> diff --git a/drawinglayer/source/primitive2d/textlayoutdevice.cxx b/drawinglayer/source/primitive2d/textlayoutdevice.cxx index 31120279a732..a9b78c599042 100644 --- a/drawinglayer/source/primitive2d/textlayoutdevice.cxx +++ b/drawinglayer/source/primitive2d/textlayoutdevice.cxx @@ -188,7 +188,7 @@ void TextLayouterDevice::setFontAttribute(const attribute::FontAttribute& rFontA } else { - mnFontScalingFixX = mnFontScalingFixY = 0; + mnFontScalingFixX = mnFontScalingFixY = 1.0; } } commit 6f434f2d8a05cf85fa8d55a6d5be882c425ba5d5 Author: Mike Kaganski <[email protected]> AuthorDate: Mon Apr 22 14:45:37 2024 +0500 Commit: Mike Kaganski <[email protected]> CommitDate: Sat Jun 1 14:56:19 2024 +0500 tdf#160773: handle 0-size font height Regression from commit cc3663bbaed4f65d64154e5f9abb51a5f622f710 (tdf#160702: improve text positioning, 2024-04-20), where I didn't know yet that this was possible :-) Change-Id: I1316db48b8acbacd077eeb4a8989c3dbf7e4a7b5 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/166425 Tested-by: Jenkins Reviewed-by: Mike Kaganski <[email protected]> diff --git a/drawinglayer/source/primitive2d/textlayoutdevice.cxx b/drawinglayer/source/primitive2d/textlayoutdevice.cxx index 5bc82301b794..31120279a732 100644 --- a/drawinglayer/source/primitive2d/textlayoutdevice.cxx +++ b/drawinglayer/source/primitive2d/textlayoutdevice.cxx @@ -179,8 +179,17 @@ void TextLayouterDevice::setFontAttribute(const attribute::FontAttribute& rFontA = getVclFontFromFontAttribute(rFontAttribute, fFontScaleX, fFontScaleY, 0.0, rLocale); setFont(aFont); Size aFontSize = aFont.GetFontSize(); - mnFontScalingFixY = fFontScaleY / aFontSize.Height(); - mnFontScalingFixX = fFontScaleX / (aFontSize.Width() ? aFontSize.Width() : aFontSize.Height()); + if (aFontSize.Height()) + { + mnFontScalingFixY = fFontScaleY / aFontSize.Height(); + // aFontSize.Width() is 0 for uninformly scaled fonts: see getVclFontFromFontAttribute + mnFontScalingFixX + = fFontScaleX / (aFontSize.Width() ? aFontSize.Width() : aFontSize.Height()); + } + else + { + mnFontScalingFixX = mnFontScalingFixY = 0; + } } double TextLayouterDevice::getOverlineOffset() const commit bec812bb7c15a62dbc620e6d94fd3ee0bd56c9a8 Author: Mike Kaganski <[email protected]> AuthorDate: Tue Apr 16 10:48:06 2024 +0500 Commit: Mike Kaganski <[email protected]> CommitDate: Sat Jun 1 14:56:19 2024 +0500 tdf#160702: improve text positioning Opening an SVG with text in different elements (e.g., tspans) in the same text element performs calculations to position the parts properly (i.e., the next part will start where the previous part ended, unless the position in overridden explicitly). These calculations require to know the text widths. The first problem leas here: the text width was calculated for a typically small text size (numerically equal to the pixel size defined in the SVG), but these calculations aren't truly linear, because font rendering may change depending on font height. Additionally, the rounding gives much higher error in smaller sizes than in larger. There was already a workaround for a similar problem in ViewRedirector::createRedirectedPrimitive2DSequence, where a large font (with 100 times greater height) was used to increase correctness. This was also used here, with the same large height (50000) used as a reference. Then, at the time of wrawing the text at different zoom levels, the code in VclProcessor2D::RenderTextSimpleOrDecoratedPortionPrimitive2D creates a font of a calculated size, and uses it to output the text. But the font is always created with an integral height, which means, that for a wanted height of 2.5 (in a zoomed out view), the really used height will be 3, which is 20% larger; or for wanted height of 2.4, the actual height will be 2 (20% smaller). This resulted in odd jumps of the text widths, when the text may overlap the following part, or conversely, create a big gap before the next gap. To try to mitigate that, the function now takes the difference between the wanted and the actual font size into account, and adjusts the MapMode accordingly. This doesn't fix the jumping completely (e.g., because of the mentioned special handling of small font sizes in the fonts thenselves, like hinting), but still makes the calculations much more stable, decreasing the amount of jumping. Similar changes are made in TextLayouterDevice. Use of the functions that return text size as a double, not rounded value, should additionally help improving stability. Change-Id: I455845d8ca43ee9c06a0fc980947f35d8a25797a Reviewed-on: https://gerrit.libreoffice.org/c/core/+/166238 Tested-by: Jenkins Reviewed-by: Mike Kaganski <[email protected]> diff --git a/drawinglayer/source/primitive2d/textlayoutdevice.cxx b/drawinglayer/source/primitive2d/textlayoutdevice.cxx index e36abffe2e64..5bc82301b794 100644 --- a/drawinglayer/source/primitive2d/textlayoutdevice.cxx +++ b/drawinglayer/source/primitive2d/textlayoutdevice.cxx @@ -22,6 +22,8 @@ #include <algorithm> #include <com/sun/star/uno/XComponentContext.hpp> + +#include <basegfx/matrix/b2dhommatrixtools.hxx> #include <drawinglayer/attribute/fontattribute.hxx> #include <drawinglayer/primitive2d/textlayoutdevice.hxx> #include <comphelper/processfactory.hxx> @@ -162,56 +164,69 @@ TextLayouterDevice::TextLayouterDevice() TextLayouterDevice::~TextLayouterDevice() COVERITY_NOEXCEPT_FALSE { releaseGlobalVirtualDevice(); } -void TextLayouterDevice::setFont(const vcl::Font& rFont) { mrDevice.SetFont(rFont); } +void TextLayouterDevice::setFont(const vcl::Font& rFont) +{ + mrDevice.SetFont(rFont); + mnFontScalingFixX = 1.0; + mnFontScalingFixY = 1.0; +} void TextLayouterDevice::setFontAttribute(const attribute::FontAttribute& rFontAttribute, double fFontScaleX, double fFontScaleY, const css::lang::Locale& rLocale) { - setFont(getVclFontFromFontAttribute(rFontAttribute, fFontScaleX, fFontScaleY, 0.0, rLocale)); + vcl::Font aFont + = getVclFontFromFontAttribute(rFontAttribute, fFontScaleX, fFontScaleY, 0.0, rLocale); + setFont(aFont); + Size aFontSize = aFont.GetFontSize(); + mnFontScalingFixY = fFontScaleY / aFontSize.Height(); + mnFontScalingFixX = fFontScaleX / (aFontSize.Width() ? aFontSize.Width() : aFontSize.Height()); } double TextLayouterDevice::getOverlineOffset() const { const ::FontMetric& rMetric = mrDevice.GetFontMetric(); double fRet = (rMetric.GetInternalLeading() / 2.0) - rMetric.GetAscent(); - return fRet; + return fRet * mnFontScalingFixY; } double TextLayouterDevice::getUnderlineOffset() const { const ::FontMetric& rMetric = mrDevice.GetFontMetric(); double fRet = rMetric.GetDescent() / 2.0; - return fRet; + return fRet * mnFontScalingFixY; } double TextLayouterDevice::getStrikeoutOffset() const { const ::FontMetric& rMetric = mrDevice.GetFontMetric(); double fRet = (rMetric.GetAscent() - rMetric.GetInternalLeading()) / 3.0; - return fRet; + return fRet * mnFontScalingFixY; } double TextLayouterDevice::getOverlineHeight() const { const ::FontMetric& rMetric = mrDevice.GetFontMetric(); double fRet = rMetric.GetInternalLeading() / 2.5; - return fRet; + return fRet * mnFontScalingFixY; } double TextLayouterDevice::getUnderlineHeight() const { const ::FontMetric& rMetric = mrDevice.GetFontMetric(); double fRet = rMetric.GetDescent() / 4.0; - return fRet; + return fRet * mnFontScalingFixY; } -double TextLayouterDevice::getTextHeight() const { return mrDevice.GetTextHeight(); } +double TextLayouterDevice::getTextHeight() const +{ + return mrDevice.GetTextHeightDouble() * mnFontScalingFixY; +} double TextLayouterDevice::getTextWidth(const OUString& rText, sal_uInt32 nIndex, sal_uInt32 nLength) const { - return mrDevice.GetTextWidth(rText, nIndex, nLength); + return mrDevice.GetTextWidthDouble(rText, nIndex, nLength) * mnFontScalingFixX; } void TextLayouterDevice::getTextOutlines(basegfx::B2DPolyPolygonVector& rB2DPolyPolyVector, @@ -245,6 +260,13 @@ void TextLayouterDevice::getTextOutlines(basegfx::B2DPolyPolygonVector& rB2DPoly { mrDevice.GetTextOutlines(rB2DPolyPolyVector, rText, nIndex, nIndex, nLength); } + if (!rtl_math_approxEqual(mnFontScalingFixY, 1.0) + || !rtl_math_approxEqual(mnFontScalingFixX, 1.0)) + { + auto scale = basegfx::utils::createScaleB2DHomMatrix(mnFontScalingFixX, mnFontScalingFixY); + for (auto& poly : rB2DPolyPolyVector) + poly.transform(scale); + } } basegfx::B2DRange TextLayouterDevice::getTextBoundRect(const OUString& rText, sal_uInt32 nIndex, @@ -262,6 +284,12 @@ basegfx::B2DRange TextLayouterDevice::getTextBoundRect(const OUString& rText, sa { basegfx::B2DRange aRect; mrDevice.GetTextBoundRect(aRect, rText, nIndex, nIndex, nLength); + if (!rtl_math_approxEqual(mnFontScalingFixY, 1.0) + || !rtl_math_approxEqual(mnFontScalingFixX, 1.0)) + { + aRect.transform( + basegfx::utils::createScaleB2DHomMatrix(mnFontScalingFixX, mnFontScalingFixY)); + } return aRect; } @@ -271,13 +299,13 @@ basegfx::B2DRange TextLayouterDevice::getTextBoundRect(const OUString& rText, sa double TextLayouterDevice::getFontAscent() const { const ::FontMetric& rMetric = mrDevice.GetFontMetric(); - return rMetric.GetAscent(); + return rMetric.GetAscent() * mnFontScalingFixY; } double TextLayouterDevice::getFontDescent() const { const ::FontMetric& rMetric = mrDevice.GetFontMetric(); - return rMetric.GetDescent(); + return rMetric.GetDescent() * mnFontScalingFixY; } void TextLayouterDevice::addTextRectActions(const ::tools::Rectangle& rRectangle, @@ -305,7 +333,7 @@ std::vector<double> TextLayouterDevice::getTextArray(const OUString& rText, sal_ mrDevice.GetTextArray(rText, &aArray, nIndex, nTextLength); aRetval.reserve(aArray.size()); for (size_t i = 0, nEnd = aArray.size(); i < nEnd; ++i) - aRetval.push_back(aArray[i]); + aRetval.push_back(aArray[i] * mnFontScalingFixX); } return aRetval; diff --git a/drawinglayer/source/processor2d/vclprocessor2d.cxx b/drawinglayer/source/processor2d/vclprocessor2d.cxx index 6a1073fa9d5b..f8e5d0457583 100644 --- a/drawinglayer/source/processor2d/vclprocessor2d.cxx +++ b/drawinglayer/source/processor2d/vclprocessor2d.cxx @@ -180,7 +180,8 @@ void VclProcessor2D::RenderTextSimpleOrDecoratedPortionPrimitive2D( } // Don't draw fonts without height - if (aFont.GetFontHeight() <= 0) + Size aResultFontSize = aFont.GetFontSize(); + if (aResultFontSize.Height() <= 0) return; // set FillColor Attribute @@ -397,8 +398,31 @@ void VclProcessor2D::RenderTextSimpleOrDecoratedPortionPrimitive2D( else { const basegfx::B2DPoint aPoint(aLocalTransform * basegfx::B2DPoint(0.0, 0.0)); - aStartPoint = Point(basegfx::fround<tools::Long>(aPoint.getX()), - basegfx::fround<tools::Long>(aPoint.getY())); + double aPointX = aPoint.getX(), aPointY = aPoint.getY(); + + // aFont has an integer size; we must scale a bit for precision + double nFontScalingFixY = aFontScaling.getY() / aResultFontSize.Height(); + double nFontScalingFixX = aFontScaling.getX() + / (aResultFontSize.Width() ? aResultFontSize.Width() + : aResultFontSize.Height()); + + if (!rtl_math_approxEqual(nFontScalingFixY, 1.0) + || !rtl_math_approxEqual(nFontScalingFixX, 1.0)) + { + MapMode aMapMode = mpOutputDevice->GetMapMode(); + aMapMode.SetScaleX(aMapMode.GetScaleX() * nFontScalingFixX); + aMapMode.SetScaleY(aMapMode.GetScaleY() * nFontScalingFixY); + + mpOutputDevice->Push(vcl::PushFlags::MAPMODE); + mpOutputDevice->SetRelativeMapMode(aMapMode); + bChangeMapMode = true; + + aPointX /= nFontScalingFixX; + aPointY /= nFontScalingFixY; + } + + aStartPoint = Point(basegfx::fround<tools::Long>(aPointX), + basegfx::fround<tools::Long>(aPointY)); } // tdf#152990 set the font after the MapMode is (potentially) set so canvas uses the desired diff --git a/include/drawinglayer/primitive2d/textlayoutdevice.hxx b/include/drawinglayer/primitive2d/textlayoutdevice.hxx index 6348de0ddd25..a2e45f1bd8b5 100644 --- a/include/drawinglayer/primitive2d/textlayoutdevice.hxx +++ b/include/drawinglayer/primitive2d/textlayoutdevice.hxx @@ -63,6 +63,8 @@ class DRAWINGLAYER_DLLPUBLIC TextLayouterDevice /// internally used VirtualDevice SolarMutexGuard maSolarGuard; VirtualDevice& mrDevice; + double mnFontScalingFixX = 1.0; + double mnFontScalingFixY = 1.0; public: /// constructor/destructor diff --git a/svgio/source/svgreader/svgcharacternode.cxx b/svgio/source/svgreader/svgcharacternode.cxx index 4ffc46a483db..d83c8b645202 100644 --- a/svgio/source/svgreader/svgcharacternode.cxx +++ b/svgio/source/svgreader/svgcharacternode.cxx @@ -257,9 +257,12 @@ namespace svgio::svgreader // prepare locale css::lang::Locale aLocale; - // prepare TextLayouterDevice + // prepare TextLayouterDevice; use a larger font size for more linear size + // calculations. Similar to nTextSizeFactor in sd/source/ui/view/sdview.cxx + // (ViewRedirector::createRedirectedPrimitive2DSequence). + const double sizeFactor = fFontHeight < 50000 ? 50000 / fFontHeight : 1.0; TextLayouterDevice aTextLayouterDevice; - aTextLayouterDevice.setFontAttribute(aFontAttribute, fFontWidth, fFontHeight, aLocale); + aTextLayouterDevice.setFontAttribute(aFontAttribute, fFontWidth * sizeFactor, fFontHeight * sizeFactor, aLocale); // prepare TextArray ::std::vector< double > aTextArray(rSvgTextPosition.getX()); @@ -293,7 +296,7 @@ namespace svgio::svgreader // get current TextPosition and TextWidth in units basegfx::B2DPoint aPosition(rSvgTextPosition.getPosition()); - double fTextWidth(aTextLayouterDevice.getTextWidth(getText(), nIndex, nLength)); + double fTextWidth(aTextLayouterDevice.getTextWidth(getText(), nIndex, nLength) / sizeFactor); // check for user-given TextLength if(0.0 != rSvgTextPosition.getTextLength() @@ -306,7 +309,10 @@ namespace svgio::svgreader // spacing, need to create and expand TextArray if(aTextArray.empty()) { - aTextArray = aTextLayouterDevice.getTextArray(getText(), nIndex, nLength); + auto aExtendArray(aTextLayouterDevice.getTextArray(getText(), nIndex, nLength)); + aTextArray.reserve(aExtendArray.size()); + for (auto n : aExtendArray) + aTextArray.push_back(n / sizeFactor); } for(auto &a : aTextArray) @@ -384,12 +390,12 @@ namespace svgio::svgreader { case BaselineShift::Sub: { - aPosition.setY(aPosition.getY() + aTextLayouterDevice.getUnderlineOffset()); + aPosition.setY(aPosition.getY() + aTextLayouterDevice.getUnderlineOffset() / sizeFactor); break; } case BaselineShift::Super: { - aPosition.setY(aPosition.getY() + aTextLayouterDevice.getOverlineOffset()); + aPosition.setY(aPosition.getY() + aTextLayouterDevice.getOverlineOffset() / sizeFactor); break; } case BaselineShift::Percentage: commit b46ff244140dc5f4b3609174cacdec1a4300bf04 Author: Mike Kaganski <[email protected]> AuthorDate: Thu Apr 18 13:18:37 2024 +0500 Commit: Mike Kaganski <[email protected]> CommitDate: Sat Jun 1 14:56:19 2024 +0500 Return double from OutputDevice::GetTextArray And introduce GetTextWidth / GetTextHeight variants returning double. It allows to avoid premature rounding. At least in one case - testTdf145111_anchor_in_Fontwork - it allowed to make the test DPI-independent (at least in my testing on Windows, using 125, 150, and 175% UI scaling). Change-Id: I973d2c729ec6bb7114b4f99b9027f1ead7c1d061 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/166237 Tested-by: Jenkins Reviewed-by: Mike Kaganski <[email protected]> diff --git a/drawinglayer/source/processor2d/vclprocessor2d.cxx b/drawinglayer/source/processor2d/vclprocessor2d.cxx index 13025b27d8a2..6a1073fa9d5b 100644 --- a/drawinglayer/source/processor2d/vclprocessor2d.cxx +++ b/drawinglayer/source/processor2d/vclprocessor2d.cxx @@ -340,8 +340,8 @@ void VclProcessor2D::RenderTextSimpleOrDecoratedPortionPrimitive2D( { tools::Long nWidthToFill = rTextCandidate.getWidthToFill(); - tools::Long nWidth - = mpOutputDevice->GetTextArray(rTextCandidate.getText(), &aDXArray, 0, 1); + tools::Long nWidth = basegfx::fround<tools::Long>( + mpOutputDevice->GetTextArray(rTextCandidate.getText(), &aDXArray, 0, 1)); sal_Int32 nChars = 2; if (nWidth) nChars = nWidthToFill / nWidth; diff --git a/editeng/source/items/svxfont.cxx b/editeng/source/items/svxfont.cxx index b484a645a7f6..306b378fc133 100644 --- a/editeng/source/items/svxfont.cxx +++ b/editeng/source/items/svxfont.cxx @@ -37,7 +37,7 @@ static tools::Long GetTextArray( const OutputDevice* pOut, const OUString& rStr, { const SalLayoutGlyphs* layoutGlyphs = SalLayoutGlyphsCache::self()->GetLayoutGlyphs(pOut, rStr, nIndex, nLen); - return pOut->GetTextArray( rStr, pDXAry, nIndex, nLen, true, nullptr, layoutGlyphs); + return basegfx::fround<tools::Long>(pOut->GetTextArray( rStr, pDXAry, nIndex, nLen, true, nullptr, layoutGlyphs)); } SvxFont::SvxFont() diff --git a/emfio/qa/cppunit/emf/EmfImportTest.cxx b/emfio/qa/cppunit/emf/EmfImportTest.cxx index 1e03cc15dc20..627423117b37 100644 --- a/emfio/qa/cppunit/emf/EmfImportTest.cxx +++ b/emfio/qa/cppunit/emf/EmfImportTest.cxx @@ -1343,7 +1343,7 @@ void Test::TestExtTextOutOpaqueAndClipWMF() assertXPath(pDocument, aXPathPrefix + "mask/group[3]/mask/group/polypolygoncolor", "color", "#ff8000"); assertXPath(pDocument, aXPathPrefix + "mask/group[3]/mask/group/polypolygoncolor/polypolygon", - "path", "m1067 1067h1270v473h-1270z"); + "path", "m1067 1067h1317v473h-1317z"); assertXPath(pDocument, aXPathPrefix + "mask/group[3]/mask/group/textsimpleportion", "text", "OOOO"); assertXPath(pDocument, aXPathPrefix + "mask/group[3]/mask/group/textsimpleportion", "fontcolor", diff --git a/filter/source/svg/svgwriter.cxx b/filter/source/svg/svgwriter.cxx index ff444a6ce284..aa83250f53b6 100644 --- a/filter/source/svg/svgwriter.cxx +++ b/filter/source/svg/svgwriter.cxx @@ -2690,7 +2690,7 @@ void SVGActionWriter::ImplWriteText( const Point& rPos, const OUString& rText, } else { - aNormSize = Size( mpVDev->GetTextArray( rText, &aTmpArray ), 0 ); + aNormSize = Size(basegfx::fround<tools::Long>(mpVDev->GetTextArray(rText, &aTmpArray)), 0); } // if text is rotated, set transform matrix at new g element diff --git a/include/vcl/outdev.hxx b/include/vcl/outdev.hxx index 8bcf883c8fc9..098a5ccc5ef0 100644 --- a/include/vcl/outdev.hxx +++ b/include/vcl/outdev.hxx @@ -1033,12 +1033,16 @@ public: tools::Long GetTextWidth( const OUString& rStr, sal_Int32 nIndex = 0, sal_Int32 nLen = -1, vcl::text::TextLayoutCache const* = nullptr, SalLayoutGlyphs const*const pLayoutCache = nullptr) const; + double GetTextWidthDouble(const OUString& rStr, sal_Int32 nIndex = 0, sal_Int32 nLen = -1, + vcl::text::TextLayoutCache const* = nullptr, + SalLayoutGlyphs const* const pLayoutCache = nullptr) const; /** Height where any character of the current font fits; in logic coordinates. See also GetTextBoundRect() for more explanation + code examples. */ tools::Long GetTextHeight() const; + double GetTextHeightDouble() const; float approximate_digit_width() const; void DrawTextArray( const Point& rStartPt, const OUString& rStr, @@ -1048,7 +1052,7 @@ public: sal_Int32 nLen, SalLayoutFlags flags = SalLayoutFlags::NONE, const SalLayoutGlyphs* pLayoutCache = nullptr); - tools::Long GetTextArray( const OUString& rStr, KernArray* pDXAry, + double GetTextArray( const OUString& rStr, KernArray* pDXAry, sal_Int32 nIndex = 0, sal_Int32 nLen = -1, bool bCaret = false, vcl::text::TextLayoutCache const* = nullptr, SalLayoutGlyphs const*const pLayoutCache = nullptr) const; @@ -1841,6 +1845,9 @@ private: */ SAL_DLLPRIVATE tools::Long ImplLogicYToDevicePixel( tools::Long nY ) const; + SAL_DLLPRIVATE double ImplDevicePixelToLogicWidthDouble(double nWidth) const; + SAL_DLLPRIVATE double ImplDevicePixelToLogicHeightDouble(double nHeight) const; + /** @name Native Widget Rendering functions These all just call through to the private mpGraphics functions of the same name. diff --git a/sc/qa/uitest/chart/chartLegend.py b/sc/qa/uitest/chart/chartLegend.py index 45938e23017d..fe0525a0746d 100644 --- a/sc/qa/uitest/chart/chartLegend.py +++ b/sc/qa/uitest/chart/chartLegend.py @@ -88,7 +88,7 @@ class chartLegend(UITestCase): xLegends.executeAction("SELECT", tuple()) with self.ui_test.execute_dialog_through_action(xLegends, "COMMAND", mkPropertyValues({"COMMAND": "TransformDialog"})) as xDialog: - self.assertEqual("4.61", get_state_as_dict(xDialog.getChild("MTR_FLD_POS_X"))['Value']) + self.assertEqual("4.59", get_state_as_dict(xDialog.getChild("MTR_FLD_POS_X"))['Value']) self.assertEqual("1.54", get_state_as_dict(xDialog.getChild("MTR_FLD_POS_Y"))['Value']) xChartMain.executeAction("TYPE", mkPropertyValues({"KEYCODE": "UP"})) @@ -96,7 +96,7 @@ class chartLegend(UITestCase): # Check the position has changed after moving the label using the arrows keys with self.ui_test.execute_dialog_through_action(xLegends, "COMMAND", mkPropertyValues({"COMMAND": "TransformDialog"})) as xDialog: - self.assertEqual("4.51", get_state_as_dict(xDialog.getChild("MTR_FLD_POS_X"))['Value']) + self.assertEqual("4.49", get_state_as_dict(xDialog.getChild("MTR_FLD_POS_X"))['Value']) self.assertEqual("1.44", get_state_as_dict(xDialog.getChild("MTR_FLD_POS_Y"))['Value']) def test_Tdf147394(self): diff --git a/sd/qa/unit/layout-tests.cxx b/sd/qa/unit/layout-tests.cxx index 1c4429f62aec..681f137f4dbb 100644 --- a/sd/qa/unit/layout-tests.cxx +++ b/sd/qa/unit/layout-tests.cxx @@ -103,7 +103,7 @@ CPPUNIT_TEST_FIXTURE(SdLayoutTest, testTdf128212) xmlDocUniquePtr pXmlDoc = load("pptx/tdf128212.pptx"); // Without the fix in place, this test would have failed with - // - Expected: 7793 + // - Expected: 7795 // - Actual : 12068 assertXPath(pXmlDoc, "/metafile/push[1]/push[1]/textarray", "x", "4525"); assertXPath(pXmlDoc, "/metafile/push[1]/push[1]/textarray", "y", "7797"); @@ -310,7 +310,7 @@ CPPUNIT_TEST_FIXTURE(SdLayoutTest, testFitToFrameTextFitting) assertXPath(pXmlDoc, "/metafile/push[1]/push[1]/textarray[1]", "y", "406"); assertXPath(pXmlDoc, "/metafile/push[1]/push[1]/textarray[1]/dxarray", "first", "114"); #ifndef _WIN32 // Windows seems to differ in text layouting, so ignore for now - assertXPath(pXmlDoc, "/metafile/push[1]/push[1]/textarray[1]/dxarray", "last", "7010"); + assertXPath(pXmlDoc, "/metafile/push[1]/push[1]/textarray[1]/dxarray", "last", "6984"); #endif } diff --git a/sd/qa/unit/tiledrendering/LOKitSearchTest.cxx b/sd/qa/unit/tiledrendering/LOKitSearchTest.cxx index f3beb12b3ce1..1468f130cf11 100644 --- a/sd/qa/unit/tiledrendering/LOKitSearchTest.cxx +++ b/sd/qa/unit/tiledrendering/LOKitSearchTest.cxx @@ -655,8 +655,7 @@ void LOKitSearchTest::testSearchIn2MixedObjects() CPPUNIT_ASSERT_EQUAL(size_t(1), mpCallbackRecorder->m_aSearchResultSelection.size()); CPPUNIT_ASSERT_EQUAL(size_t(1), mpCallbackRecorder->m_aSearchResultPart.size()); - CPPUNIT_ASSERT_EQUAL(OString("3546, 3174, 738, 402"), - mpCallbackRecorder->m_aSearchResultSelection[0]); + CPPUNIT_ASSERT_EQUAL("3545, 3174, 740, 402", mpCallbackRecorder->m_aSearchResultSelection[0]); // Search next @@ -681,8 +680,7 @@ void LOKitSearchTest::testSearchIn2MixedObjects() CPPUNIT_ASSERT_EQUAL(size_t(1), mpCallbackRecorder->m_aSearchResultSelection.size()); CPPUNIT_ASSERT_EQUAL(size_t(1), mpCallbackRecorder->m_aSearchResultPart.size()); - CPPUNIT_ASSERT_EQUAL(OString("3546, 3174, 738, 402"), - mpCallbackRecorder->m_aSearchResultSelection[0]); + CPPUNIT_ASSERT_EQUAL("3545, 3174, 740, 402", mpCallbackRecorder->m_aSearchResultSelection[0]); } // Test searching in document with mixed objects. We have 6 objects. diff --git a/svx/qa/unit/customshapes.cxx b/svx/qa/unit/customshapes.cxx index efe322d54e76..86bae7d50447 100644 --- a/svx/qa/unit/customshapes.cxx +++ b/svx/qa/unit/customshapes.cxx @@ -406,10 +406,6 @@ CPPUNIT_TEST_FIXTURE(CustomshapesTest, testTdf145111_Fontwork_rendering_font_siz CPPUNIT_TEST_FIXTURE(CustomshapesTest, testTdf145111_anchor_in_Fontwork) { - // The tested positions depend on dpi. - if (!IsDefaultDPI()) - return; - // tdf#145004 In case ScaleX is true in property TextPath, SDRTEXTVERTADJUST is // evaluated and should shift the Fontwork text. That did not work for // 'Top-Left' and 'Bottom-Left'. @@ -423,8 +419,7 @@ CPPUNIT_TEST_FIXTURE(CustomshapesTest, testTdf145111_anchor_in_Fontwork) SdrObjCustomShape& rSdrCustomShape( static_cast<SdrObjCustomShape&>(*SdrObject::getSdrObjectFromXShape(xShape))); - // Without the fix in place top was 2295, but should be 2916 for 96dpi. - // Was 2184, should be 2886 for 120dpi. + // Without the fix in place top was 2295, but should be 2900. tools::Rectangle aBoundRect(rSdrCustomShape.GetCurrentBoundRect()); CPPUNIT_ASSERT_DOUBLES_EQUAL(tools::Long(2916), aBoundRect.Top(), 5); } @@ -434,8 +429,7 @@ CPPUNIT_TEST_FIXTURE(CustomshapesTest, testTdf145111_anchor_in_Fontwork) SdrObjCustomShape& rSdrCustomShape( static_cast<SdrObjCustomShape&>(*SdrObject::getSdrObjectFromXShape(xShape))); - // Without the fix in place top was 10294, but should be 9519 for 96dpi. - // Was 10184, should be 9481 for 120dpi. + // Without the fix in place top was 10294, but should be 9500. tools::Rectangle aBoundRect(rSdrCustomShape.GetCurrentBoundRect()); CPPUNIT_ASSERT_DOUBLES_EQUAL(tools::Long(9519), aBoundRect.Top(), 5); } diff --git a/sw/qa/extras/layout/layout.cxx b/sw/qa/extras/layout/layout.cxx index 0ffdb543fbd9..65b31c5909c6 100644 --- a/sw/qa/extras/layout/layout.cxx +++ b/sw/qa/extras/layout/layout.cxx @@ -3673,7 +3673,7 @@ CPPUNIT_TEST_FIXTURE(SwLayoutWriter, testTdf106234) "type", "PortionType::Margin"); // This was a justified line, without width assertXPath(pXmlDoc, "/root/page/body/txt[2]/SwParaPortion/SwLineLayout[1]/SwGluePortion", - "width", "7881"); + "width", "7882"); } CPPUNIT_TEST_FIXTURE(SwLayoutWriter, testTdf120287b) @@ -3686,7 +3686,7 @@ CPPUNIT_TEST_FIXTURE(SwLayoutWriter, testTdf120287b) assertXPath( pXmlDoc, "/root/page/body/txt[1]/SwParaPortion/SwLineLayout/child::*[@type='PortionType::TabRight']", - "width", "17"); + "width", "19"); } CPPUNIT_TEST_FIXTURE(SwLayoutWriter, testTdf120287c) @@ -3819,7 +3819,7 @@ CPPUNIT_TEST_FIXTURE(SwLayoutWriter, testTdf122607) assertXPath(pXmlDoc, "/root/page[1]/anchored/fly/txt[1]/anchored/fly/tab/row[2]/cell/txt[7]/anchored/" "fly/txt/SwParaPortion/SwLineLayout/child::*[1]", - "width", "428"); + "width", "427"); assertXPath(pXmlDoc, "/root/page[1]/anchored/fly/txt[1]/anchored/fly/tab/row[2]/cell/txt[7]/anchored/" "fly/txt/SwParaPortion/SwLineLayout/child::*[1]", diff --git a/toolkit/source/awt/vclxfont.cxx b/toolkit/source/awt/vclxfont.cxx index 866a362e710f..f7338e79cfd0 100644 --- a/toolkit/source/awt/vclxfont.cxx +++ b/toolkit/source/awt/vclxfont.cxx @@ -156,7 +156,7 @@ sal_Int32 VCLXFont::getStringWidthArray( const OUString& str, css::uno::Sequence vcl::Font aOldFont = pOutDev->GetFont(); pOutDev->SetFont( maFont ); KernArray aDXA; - nRet = pOutDev->GetTextArray( str, &aDXA ); + nRet = basegfx::fround(pOutDev->GetTextArray(str, &aDXA)); rDXArray.realloc(aDXA.size()); sal_Int32* pArray = rDXArray.getArray(); for (size_t i = 0, nLen = aDXA.size(); i < nLen; ++i) diff --git a/vcl/qa/cppunit/complextext.cxx b/vcl/qa/cppunit/complextext.cxx index daf70a1c589d..cfe5d2213d7d 100644 --- a/vcl/qa/cppunit/complextext.cxx +++ b/vcl/qa/cppunit/complextext.cxx @@ -79,7 +79,7 @@ void VclComplexTextTest::testArabic() std::vector<sal_Int32> aRefCharWidths {6, 9, 16, 16, 22, 22, 26, 29, 32, 32, 36, 40, 49, 53, 56, 63, 63, 66, 72, 72}; KernArray aCharWidths; - tools::Long nTextWidth = pOutDev->GetTextArray(aOneTwoThree, &aCharWidths); + tools::Long nTextWidth = basegfx::fround<tools::Long>(pOutDev->GetTextArray(aOneTwoThree, &aCharWidths)); CPPUNIT_ASSERT_EQUAL(aRefCharWidths, aCharWidths.get_subunit_array()); // this sporadically returns 75 or 74 on some of the windows tinderboxes eg. tb73 diff --git a/vcl/source/filter/wmf/emfwr.cxx b/vcl/source/filter/wmf/emfwr.cxx index 45a1a7ce4e27..dfdc3156f838 100644 --- a/vcl/source/filter/wmf/emfwr.cxx +++ b/vcl/source/filter/wmf/emfwr.cxx @@ -880,7 +880,7 @@ void EMFWriter::ImplWriteTextRecord( const Point& rPos, const OUString& rText, K } else { - nNormWidth = maVDev->GetTextArray( rText, &aOwnArray ); + nNormWidth = basegfx::fround<sal_uInt32>(maVDev->GetTextArray(rText, &aOwnArray)); pDX = aOwnArray; } diff --git a/vcl/source/filter/wmf/wmfwr.cxx b/vcl/source/filter/wmf/wmfwr.cxx index 335773728b60..c35d7236c9d9 100644 --- a/vcl/source/filter/wmf/wmfwr.cxx +++ b/vcl/source/filter/wmf/wmfwr.cxx @@ -1198,7 +1198,7 @@ void WMFWriter::WriteRecords( const GDIMetaFile & rMTF ) pVirDev->SetFont( aSrcFont ); const sal_Int32 nLen = aTemp.getLength(); KernArray aDXAry; - const sal_Int32 nNormSize = pVirDev->GetTextArray( aTemp, nLen ? &aDXAry : nullptr ); + const sal_Int32 nNormSize = basegfx::fround(pVirDev->GetTextArray( aTemp, nLen ? &aDXAry : nullptr )); if (nLen && nNormSize == 0) { OSL_FAIL("Impossible div by 0 action: MetaStretchTextAction!"); diff --git a/vcl/source/gdi/textlayout.cxx b/vcl/source/gdi/textlayout.cxx index 0ee1c9ba5faf..ce681a2e9eea 100644 --- a/vcl/source/gdi/textlayout.cxx +++ b/vcl/source/gdi/textlayout.cxx @@ -166,7 +166,7 @@ namespace vcl return 0; // retrieve the character widths from the reference device - tools::Long nTextWidth = m_rReferenceDevice.GetTextArray( _rText, _pDXAry, _nStartIndex, _nLength ); + tools::Long nTextWidth = basegfx::fround<tools::Long>(m_rReferenceDevice.GetTextArray( _rText, _pDXAry, _nStartIndex, _nLength )); #if OSL_DEBUG_LEVEL > 1 if ( _pDXAry ) { diff --git a/vcl/source/outdev/map.cxx b/vcl/source/outdev/map.cxx index d7ddaf3ccd86..3359cc70c210 100644 --- a/vcl/source/outdev/map.cxx +++ b/vcl/source/outdev/map.cxx @@ -272,6 +272,19 @@ static tools::Long ImplPixelToLogic(tools::Long n, tools::Long nDPI, tools::Long return n; } +static double ImplPixelToLogicDouble(double n, tools::Long nDPI, tools::Long nMapNum, + tools::Long nMapDenom) +{ + assert(nDPI > 0); + if (nMapNum == 0) + return 0; + + n *= nMapDenom; + n /= nDPI; + n /= nMapNum; + return n; +} + tools::Long OutputDevice::ImplLogicXToDevicePixel( tools::Long nX ) const { if ( !mbMap ) @@ -314,6 +327,14 @@ tools::Long OutputDevice::ImplDevicePixelToLogicWidth( tools::Long nWidth ) cons return ImplPixelToLogic(nWidth, mnDPIX, maMapRes.mnMapScNumX, maMapRes.mnMapScDenomX); } +SAL_DLLPRIVATE double OutputDevice::ImplDevicePixelToLogicWidthDouble(double nWidth) const +{ + if (!mbMap) + return nWidth; + + return ImplPixelToLogicDouble(nWidth, mnDPIX, maMapRes.mnMapScNumX, maMapRes.mnMapScDenomX); +} + tools::Long OutputDevice::ImplDevicePixelToLogicHeight( tools::Long nHeight ) const { if ( !mbMap ) @@ -322,6 +343,14 @@ tools::Long OutputDevice::ImplDevicePixelToLogicHeight( tools::Long nHeight ) co return ImplPixelToLogic(nHeight, mnDPIY, maMapRes.mnMapScNumY, maMapRes.mnMapScDenomY); } +double OutputDevice::ImplDevicePixelToLogicHeightDouble(double nHeight) const +{ + if (!mbMap) + return nHeight; + + return ImplPixelToLogicDouble(nHeight, mnDPIY, maMapRes.mnMapScNumY, maMapRes.mnMapScDenomY); +} + Point OutputDevice::ImplLogicToDevicePixel( const Point& rLogicPt ) const { if ( !mbMap ) diff --git a/vcl/source/outdev/text.cxx b/vcl/source/outdev/text.cxx index bbbb63f2326d..0ff3ac91f177 100644 --- a/vcl/source/outdev/text.cxx +++ b/vcl/source/outdev/text.cxx @@ -892,11 +892,15 @@ tools::Long OutputDevice::GetTextWidth( const OUString& rStr, sal_Int32 nIndex, vcl::text::TextLayoutCache const*const pLayoutCache, SalLayoutGlyphs const*const pSalLayoutCache) const { + double nWidth = GetTextWidthDouble(rStr, nIndex, nLen, pLayoutCache, pSalLayoutCache); + return basegfx::fround<tools::Long>(nWidth); +} - tools::Long nWidth = GetTextArray( rStr, nullptr, nIndex, - nLen, false, pLayoutCache, pSalLayoutCache ); - - return nWidth; +double OutputDevice::GetTextWidthDouble(const OUString& rStr, sal_Int32 nIndex, sal_Int32 nLen, + vcl::text::TextLayoutCache const* const pLayoutCache, + SalLayoutGlyphs const* const pSalLayoutCache) const +{ + return GetTextArray(rStr, nullptr, nIndex, nLen, false, pLayoutCache, pSalLayoutCache); } tools::Long OutputDevice::GetTextHeight() const @@ -912,6 +916,16 @@ tools::Long OutputDevice::GetTextHeight() const return nHeight; } +double OutputDevice::GetTextHeightDouble() const +{ + if (!InitFont()) + return 0; + + tools::Long nHeight = mpFontInstance->mnLineHeight + mnEmphasisAscent + mnEmphasisDescent; + + return ImplDevicePixelToLogicHeightDouble(nHeight); +} + float OutputDevice::approximate_char_width() const { //note pango uses "The quick brown fox jumps over the lazy dog." for english @@ -960,7 +974,7 @@ void OutputDevice::DrawTextArray( const Point& rStartPt, const OUString& rStr, mpAlphaVDev->DrawTextArray( rStartPt, rStr, pDXAry, pKashidaAry, nIndex, nLen, flags ); } -tools::Long OutputDevice::GetTextArray( const OUString& rStr, KernArray* pKernArray, +double OutputDevice::GetTextArray( const OUString& rStr, KernArray* pKernArray, sal_Int32 nIndex, sal_Int32 nLen, bool bCaret, vcl::text::TextLayoutCache const*const pLayoutCache, SalLayoutGlyphs const*const pSalLayoutCache) const @@ -1048,7 +1062,7 @@ tools::Long OutputDevice::GetTextArray( const OUString& rStr, KernArray* pKernAr if (mbMap) { for (int i = 0; i < nLen; ++i) - (*pDXAry)[i] = ImplDevicePixelToLogicWidth( (*pDXAry)[i] * nSubPixelFactor ); + (*pDXAry)[i] = ImplDevicePixelToLogicWidthDouble((*pDXAry)[i] * nSubPixelFactor); } else if (nSubPixelFactor) { @@ -1057,10 +1071,7 @@ tools::Long OutputDevice::GetTextArray( const OUString& rStr, KernArray* pKernAr } } - if (mbMap) - nWidth = ImplDevicePixelToLogicWidth( nWidth ); - - return basegfx::fround<tools::Long>(nWidth); + return ImplDevicePixelToLogicWidthDouble(nWidth); #endif /* VCL_FLOAT_DEVICE_PIXEL */ } commit 15863b7d2a28e67f3c3adbafda77d4d864be05cf Author: Mike Kaganski <[email protected]> AuthorDate: Wed Apr 10 12:15:55 2024 +0500 Commit: Mike Kaganski <[email protected]> CommitDate: Sat Jun 1 14:56:19 2024 +0500 tdf#160622: Let SalLayout::GetBoundRect return basegfx::B2DRectangle This avoids premature rounding in TextLayouterDevice::getTextBoundRect. The box in D2DWriteTextOutRenderer::performRender needs to be expanded to allow room for the line width (which now will be guaranteed on all sides; previously, the rounding could happen to give no room on some side, even prior to commit 8962141a12c966b2d891829925e6203bf8d51852). Fixes some lines partially cut off in smaller text (or zoomed out). Change-Id: I07335136021f894cf045363b4d736bfab06c64d4 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/166236 Tested-by: Jenkins Reviewed-by: Mike Kaganski <[email protected]> diff --git a/drawinglayer/source/primitive2d/textlayoutdevice.cxx b/drawinglayer/source/primitive2d/textlayoutdevice.cxx index ea9fed02a20f..e36abffe2e64 100644 --- a/drawinglayer/source/primitive2d/textlayoutdevice.cxx +++ b/drawinglayer/source/primitive2d/textlayoutdevice.cxx @@ -260,15 +260,9 @@ basegfx::B2DRange TextLayouterDevice::getTextBoundRect(const OUString& rText, sa if (nTextLength) { - ::tools::Rectangle aRect; - + basegfx::B2DRange aRect; mrDevice.GetTextBoundRect(aRect, rText, nIndex, nIndex, nLength); - - // #i104432#, #i102556# take empty results into account - if (!aRect.IsEmpty()) - { - return vcl::unotools::b2DRectangleFromRectangle(aRect); - } + return aRect; } return basegfx::B2DRange(); diff --git a/include/vcl/outdev.hxx b/include/vcl/outdev.hxx index 6b864213a66d..8bcf883c8fc9 100644 --- a/include/vcl/outdev.hxx +++ b/include/vcl/outdev.hxx @@ -54,6 +54,7 @@ #include <vcl/settings.hxx> #include <vcl/vclreferencebase.hxx> +#include <basegfx/range/b2drectangle.hxx> #include <basegfx/numeric/ftools.hxx> #include <basegfx/vector/b2enums.hxx> #include <basegfx/polygon/b2dpolypolygon.hxx> @@ -960,6 +961,11 @@ public: sal_uLong nLayoutWidth = 0, KernArraySpan aDXArray = KernArraySpan(), o3tl::span<const sal_Bool> pKashidaArray = {}, const SalLayoutGlyphs* pGlyphs = nullptr ) const; + bool GetTextBoundRect( basegfx::B2DRectangle& rRect, + const OUString& rStr, sal_Int32 nBase = 0, sal_Int32 nIndex = 0, sal_Int32 nLen = -1, + sal_uLong nLayoutWidth = 0, KernArraySpan aDXArray = KernArraySpan(), + o3tl::span<const sal_Bool> pKashidaArray = {}, + const SalLayoutGlyphs* pGlyphs = nullptr ) const; tools::Rectangle ImplGetTextBoundRect( const SalLayout& ) const; @@ -1627,6 +1633,7 @@ public: SAL_WARN_UNUSED_RESULT tools::Polygon PixelToLogic(const tools::Polygon& rDevicePoly) const; SAL_WARN_UNUSED_RESULT tools::PolyPolygon PixelToLogic(const tools::PolyPolygon& rDevicePolyPoly) const; SAL_WARN_UNUSED_RESULT basegfx::B2DPolyPolygon PixelToLogic(const basegfx::B2DPolyPolygon& rDevicePolyPoly) const; + SAL_WARN_UNUSED_RESULT basegfx::B2DRectangle PixelToLogic(const basegfx::B2DRectangle& rDeviceRect) const; SAL_WARN_UNUSED_RESULT vcl::Region PixelToLogic(const vcl::Region& rDeviceRegion) const; SAL_WARN_UNUSED_RESULT Point PixelToLogic(const Point& rDevicePt, const MapMode& rMapMode) const; SAL_WARN_UNUSED_RESULT Size PixelToLogic(const Size& rDeviceSize, const MapMode& rMapMode) const; diff --git a/include/vcl/vcllayout.hxx b/include/vcl/vcllayout.hxx index 4705a44d9b92..887480ce3ee9 100644 --- a/include/vcl/vcllayout.hxx +++ b/include/vcl/vcllayout.hxx @@ -20,6 +20,7 @@ #pragma once #include <basegfx/polygon/b2dpolypolygon.hxx> +#include <basegfx/range/b2drectangle.hxx> #include <i18nlangtag/languagetag.hxx> #include <tools/gen.hxx> #include <tools/degree.hxx> @@ -103,7 +104,9 @@ public: virtual bool GetNextGlyph(const GlyphItem** pGlyph, DevicePoint& rPos, int& nStart, const LogicalFontInstance** ppGlyphFont = nullptr) const = 0; virtual bool GetOutline(basegfx::B2DPolyPolygonVector&) const; - bool GetBoundRect(tools::Rectangle&) const; + bool GetBoundRect(basegfx::B2DRectangle&) const; + + static tools::Rectangle BoundRect2Rectangle(basegfx::B2DRectangle&); virtual SalLayoutGlyphs GetGlyphs() const; diff --git a/svgio/qa/cppunit/SvgImportTest.cxx b/svgio/qa/cppunit/SvgImportTest.cxx index d61c5c761f79..7a8706556129 100644 --- a/svgio/qa/cppunit/SvgImportTest.cxx +++ b/svgio/qa/cppunit/SvgImportTest.cxx @@ -41,6 +41,8 @@ protected: void checkRectPrimitive(Primitive2DSequence const & rPrimitive); Primitive2DSequence parseSvg(std::u16string_view aSource); + void assertXPathDouble(const xmlDocUniquePtr& pXmlDoc, const OString& rXPath, + const OString& rAttribute, double nExpectedValue, double delta); }; Primitive2DSequence Test::parseSvg(std::u16string_view aSource) @@ -82,6 +84,13 @@ void Test::checkRectPrimitive(Primitive2DSequence const & rPrimitive) } +void Test::assertXPathDouble(const xmlDocUniquePtr& pXmlDoc, const OString& rXPath, + const OString& rAttribute, double nExpectedValue, double delta) +{ + auto sVal = getXPath(pXmlDoc, rXPath, rAttribute); + CPPUNIT_ASSERT_DOUBLES_EQUAL(nExpectedValue, sVal.toDouble(), delta); +} + bool arePrimitive2DSequencesEqual(const Primitive2DSequence& rA, const Primitive2DSequence& rB) { return std::equal(rA.begin(), rA.end(), rB.begin(), rB.end(), diff --git a/vcl/skia/gdiimpl.cxx b/vcl/skia/gdiimpl.cxx index 84b625115648..c40a010173a7 100644 --- a/vcl/skia/gdiimpl.cxx +++ b/vcl/skia/gdiimpl.cxx @@ -2131,7 +2131,7 @@ void SkiaSalGraphicsImpl::drawGenericLayout(const GenericSalLayout& layout, Colo preDraw(); auto getBoundRect = [&layout]() { - tools::Rectangle rect; + basegfx::B2DRectangle rect; layout.GetBoundRect(rect); return rect; }; diff --git a/vcl/source/gdi/sallayout.cxx b/vcl/source/gdi/sallayout.cxx index c8c2d6ca1093..e801ac3057ca 100644 --- a/vcl/source/gdi/sallayout.cxx +++ b/vcl/source/gdi/sallayout.cxx @@ -221,11 +221,10 @@ static double trimInsignificant(double n) return std::abs(n) >= 0x1p53 ? n : std::round(n * 1e5) / 1e5; } -bool SalLayout::GetBoundRect(tools::Rectangle& rRect) const +bool SalLayout::GetBoundRect(basegfx::B2DRectangle& rRect) const { bool bRet = false; - - basegfx::B2DRectangle aUnion; + rRect.reset(); basegfx::B2DRectangle aRectangle; DevicePoint aPos; @@ -241,28 +240,28 @@ bool SalLayout::GetBoundRect(tools::Rectangle& rRect) const { aRectangle.transform(basegfx::utils::createTranslateB2DHomMatrix(aPos)); // merge rectangle - aUnion.expand(aRectangle); + rRect.expand(aRectangle); } bRet = true; } } - if (aUnion.isEmpty()) - { - rRect = {}; - } - else - { - double l = rtl::math::approxFloor(trimInsignificant(aUnion.getMinX())), - t = rtl::math::approxFloor(trimInsignificant(aUnion.getMinY())), - r = rtl::math::approxCeil(trimInsignificant(aUnion.getMaxX())), - b = rtl::math::approxCeil(trimInsignificant(aUnion.getMaxY())); - assert(std::isfinite(l) && std::isfinite(t) && std::isfinite(r) && std::isfinite(b)); - rRect = tools::Rectangle(l, t, r, b); - } return bRet; } +tools::Rectangle SalLayout::BoundRect2Rectangle(basegfx::B2DRectangle& rRect) +{ + if (rRect.isEmpty()) + return {}; + + double l = rtl::math::approxFloor(trimInsignificant(rRect.getMinX())), + t = rtl::math::approxFloor(trimInsignificant(rRect.getMinY())), + r = rtl::math::approxCeil(trimInsignificant(rRect.getMaxX())), + b = rtl::math::approxCeil(trimInsignificant(rRect.getMaxY())); + assert(std::isfinite(l) && std::isfinite(t) && std::isfinite(r) && std::isfinite(b)); + return tools::Rectangle(l, t, r, b); +} + SalLayoutGlyphs SalLayout::GetGlyphs() const { return SalLayoutGlyphs(); // invalid diff --git a/vcl/source/outdev/map.cxx b/vcl/source/outdev/map.cxx index 67414cb2976c..d7ddaf3ccd86 100644 --- a/vcl/source/outdev/map.cxx +++ b/vcl/source/outdev/map.cxx @@ -1230,6 +1230,14 @@ basegfx::B2DPolyPolygon OutputDevice::PixelToLogic( const basegfx::B2DPolyPolygo return aTransformedPoly; } +basegfx::B2DRectangle OutputDevice::PixelToLogic(const basegfx::B2DRectangle& rDeviceRect) const +{ + basegfx::B2DRectangle aTransformedRect = rDeviceRect; + const basegfx::B2DHomMatrix& rTransformationMatrix = GetInverseViewTransformation(); + aTransformedRect.transform(rTransformationMatrix); + return aTransformedRect; +} + vcl::Region OutputDevice::PixelToLogic( const vcl::Region& rDeviceRegion ) const { diff --git a/vcl/source/outdev/text.cxx b/vcl/source/outdev/text.cxx index 9e93a5775857..bbbb63f2326d 100644 --- a/vcl/source/outdev/text.cxx +++ b/vcl/source/outdev/text.cxx @@ -23,6 +23,7 @@ #include <rtl/ustrbuf.hxx> #include <sal/log.hxx> #include <basegfx/matrix/b2dhommatrix.hxx> +#include <basegfx/matrix/b2dhommatrixtools.hxx> #include <comphelper/processfactory.hxx> #include <tools/lineend.hxx> #include <tools/debug.hxx> @@ -233,7 +234,11 @@ bool OutputDevice::ImplDrawRotateText( SalLayout& rSalLayout ) tools::Rectangle aBoundRect; rSalLayout.DrawBase() = DevicePoint( 0, 0 ); rSalLayout.DrawOffset() = Point( 0, 0 ); - if (!rSalLayout.GetBoundRect(aBoundRect)) + if (basegfx::B2DRectangle r; rSalLayout.GetBoundRect(r)) + { + aBoundRect = SalLayout::BoundRect2Rectangle(r); + } + else { // guess vertical text extents if GetBoundRect failed tools::Long nRight = rSalLayout.GetTextWidth(); @@ -2326,9 +2331,22 @@ bool OutputDevice::GetTextBoundRect( tools::Rectangle& rRect, sal_uLong nLayoutWidth, KernArraySpan pDXAry, o3tl::span<const sal_Bool> pKashidaAry, const SalLayoutGlyphs* pGlyphs ) const +{ + basegfx::B2DRectangle aRect; + bool bRet = GetTextBoundRect(aRect, rStr, nBase, nIndex, nLen, nLayoutWidth, pDXAry, + pKashidaAry, pGlyphs); + rRect = SalLayout::BoundRect2Rectangle(aRect); + return bRet; +} + +bool OutputDevice::GetTextBoundRect(basegfx::B2DRectangle& rRect, const OUString& rStr, + sal_Int32 nBase, sal_Int32 nIndex, sal_Int32 nLen, + sal_uLong nLayoutWidth, KernArraySpan pDXAry, + o3tl::span<const sal_Bool> pKashidaAry, + const SalLayoutGlyphs* pGlyphs) const { bool bRet = false; - rRect.SetEmpty(); + rRect.reset(); std::unique_ptr<SalLayout> pSalLayout; const Point aPoint; @@ -2352,18 +2370,22 @@ bool OutputDevice::GetTextBoundRect( tools::Rectangle& rRect, nullptr, pGlyphs); if( pSalLayout ) { - tools::Rectangle aPixelRect; + basegfx::B2DRectangle aPixelRect; bRet = pSalLayout->GetBoundRect(aPixelRect); if( bRet ) { - Point aRotatedOfs( mnTextOffX, mnTextOffY ); - DevicePoint aPos = pSalLayout->GetDrawPosition(DevicePoint(nXOffset, 0)); - aRotatedOfs -= Point(aPos.getX(), aPos.getY()); - aPixelRect += aRotatedOfs; + basegfx::B2DPoint aPos = pSalLayout->GetDrawPosition(basegfx::B2DPoint(nXOffset, 0)); + auto m = basegfx::utils::createTranslateB2DHomMatrix(mnTextOffX - aPos.getX(), + mnTextOffY - aPos.getY()); + aPixelRect.transform(m); rRect = PixelToLogic( aPixelRect ); - if( mbMap ) - rRect += Point( maMapRes.mnMapOfsX, maMapRes.mnMapOfsY ); + if (mbMap) + { + m = basegfx::utils::createTranslateB2DHomMatrix(maMapRes.mnMapOfsX, + maMapRes.mnMapOfsY); + rRect.transform(m); + } } } diff --git a/vcl/win/gdi/DWriteTextRenderer.cxx b/vcl/win/gdi/DWriteTextRenderer.cxx index 321587db8137..fb96cfa68659 100644 --- a/vcl/win/gdi/DWriteTextRenderer.cxx +++ b/vcl/win/gdi/DWriteTextRenderer.cxx @@ -238,8 +238,15 @@ bool D2DWriteTextOutRenderer::performRender(GenericSalLayout const & rLayout, Sa if (!pFontFace) return false; - tools::Rectangle bounds; - bool succeeded = rLayout.GetBoundRect(bounds); + auto [succeeded, bounds] = [&rLayout]() + { + basegfx::B2DRectangle r; + bool result = rLayout.GetBoundRect(r); + if (result) + r.grow(1); // plus 1 pixel to the tight range + return std::make_pair(result, SalLayout::BoundRect2Rectangle(r)); + }(); + if (succeeded) { hr = BindDC(hDC, bounds); // Update the bounding rect. commit a54c14ba6cee50e118578997ca3b03be07ea1206 Author: Mike Kaganski <[email protected]> AuthorDate: Fri Apr 19 10:31:39 2024 +0500 Commit: Mike Kaganski <[email protected]> CommitDate: Sat Jun 1 14:56:18 2024 +0500 Add a unit test for commit 3f0220f18a66630e06e3c128980f21a5722f49ca (Handle empty range properly, 2024-04-18) Change-Id: Ifea60ace8ff864ef092e6a62177d5926ceb60de3 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/166278 Tested-by: Jenkins Reviewed-by: Mike Kaganski <[email protected]> diff --git a/sw/qa/extras/ww8export/data/empty_group.docx b/sw/qa/extras/ww8export/data/empty_group.docx new file mode 100644 index 000000000000..a3454746c5e0 Binary files /dev/null and b/sw/qa/extras/ww8export/data/empty_group.docx differ diff --git a/sw/qa/extras/ww8export/ww8export4.cxx b/sw/qa/extras/ww8export/ww8export4.cxx index efb198ebae00..b4116ff2bbac 100644 --- a/sw/qa/extras/ww8export/ww8export4.cxx +++ b/sw/qa/extras/ww8export/ww8export4.cxx @@ -19,8 +19,11 @@ #include <comphelper/sequenceashashmap.hxx> #include <o3tl/string_view.hxx> +#include <svx/svdpage.hxx> #include <docsh.hxx> +#include <drawdoc.hxx> +#include <IDocumentDrawModelAccess.hxx> #include <IDocumentSettingAccess.hxx> #include <unotxdoc.hxx> @@ -78,6 +81,27 @@ DECLARE_WW8EXPORT_TEST(testTdf104704_mangledFooter, "tdf104704_mangledFooter.odt CPPUNIT_ASSERT_EQUAL(2, getPages()); } +CPPUNIT_TEST_FIXTURE(Test, testEmptyGroup) +{ + // Given a document with an empty group + createSwDoc("empty_group.docx"); + + CPPUNIT_ASSERT_EQUAL(1, getPages()); + CPPUNIT_ASSERT_EQUAL(1, getShapes()); + SwXTextDocument* pTextDoc = dynamic_cast<SwXTextDocument*>(mxComponent.get()); + CPPUNIT_ASSERT(pTextDoc); + SwDoc* pDoc = pTextDoc->GetDocShell()->GetDoc(); + SdrPage* pPage = pDoc->getIDocumentDrawModelAccess().GetDrawModel()->GetPage(0); + SdrObject* pObject = pPage->GetObj(0); + + CPPUNIT_ASSERT_EQUAL(OUString("Empty group"), pObject->GetName()); + CPPUNIT_ASSERT(pObject->IsGroupObject()); + CPPUNIT_ASSERT_EQUAL(size_t(0), pObject->GetSubList()->GetObjCount()); + + // it must not assert/crash on save + saveAndReload(OUString::createFromAscii(mpFilter)); +} + CPPUNIT_PLUGIN_IMPLEMENT(); /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ commit d67d69b422837bd14b2d63d888ba3768969ea6af Author: Mike Kaganski <[email protected]> AuthorDate: Wed Apr 17 21:16:23 2024 +0100 Commit: Mike Kaganski <[email protected]> CommitDate: Sat Jun 1 14:56:18 2024 +0500 Handle empty range properly Since commit 690526f95e3ee4fd25bb2c987e093543e4bc435b (Generalize basegfx::fround for templated return type, 2024-04-14), an assertion could fail for certain case, like include/o3tl/unit_conversion.hxx:75: sal_Int64 o3tl::detail::MulDiv(I, sal_Int64, sal_Int64) [I = long]: Assertion `isBetween(n, (SAL_MIN_INT64 + d / 2) / m, (SAL_MAX_INT64 - d / 2) / m)' The problem was unchecked case of empty B2DRange. Change-Id: Ice9125ea557b73a7fabf64bc1ad9368f503ad525 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/166101 Tested-by: Jenkins Reviewed-by: Caolán McNamara <[email protected]> diff --git a/filter/source/msfilter/eschesdo.cxx b/filter/source/msfilter/eschesdo.cxx index ca0dc8a4a14d..2f8f84bb9dc4 100644 --- a/filter/source/msfilter/eschesdo.cxx +++ b/filter/source/msfilter/eschesdo.cxx @@ -1151,10 +1151,19 @@ void ImplEESdrObject::Init() { // if it's a group, the unrotated range is needed for that group const basegfx::B2DRange aUnrotatedRange(getUnrotatedGroupBoundRange(mXShape)); - const Point aNewP(basegfx::fround<tools::Long>(aUnrotatedRange.getMinX()), basegfx::fround<tools::Long>(aUnrotatedRange.getMinY())); - const Size aNewS(basegfx::fround<tools::Long>(aUnrotatedRange.getWidth()), basegfx::fround<tools::Long>(aUnrotatedRange.getHeight())); + if (aUnrotatedRange.isEmpty()) + { + SetRect(tools::Rectangle()); + } + else + { + const Point aNewP(basegfx::fround<tools::Long>(aUnrotatedRange.getMinX()), + basegfx::fround<tools::Long>(aUnrotatedRange.getMinY())); + const Size aNewS(basegfx::fround<tools::Long>(aUnrotatedRange.getWidth()), + basegfx::fround<tools::Long>(aUnrotatedRange.getHeight())); - SetRect(ImplEESdrWriter::ImplMapPoint(aNewP), ImplEESdrWriter::ImplMapSize(aNewS)); + SetRect(ImplEESdrWriter::ImplMapPoint(aNewP), ImplEESdrWriter::ImplMapSize(aNewS)); + } } else { commit 294483b2eee20b30053428110833aa0d1844bc53 Author: Mike Kaganski <[email protected]> AuthorDate: Tue Apr 16 23:10:45 2024 +0500 Commit: Mike Kaganski <[email protected]> CommitDate: Sat Jun 1 14:56:18 2024 +0500 Drop FRound, and use generalized basegfx::fround Change-Id: I7447e649dc3ef4e51242f69c7486a3e84e103d2e Reviewed-on: https://gerrit.libreoffice.org/c/core/+/166159 Tested-by: Jenkins Reviewed-by: Mike Kaganski <[email protected]> diff --git a/cui/source/dialogs/cuigrfflt.cxx b/cui/source/dialogs/cuigrfflt.cxx index c67ee92d141c..0ffb50342936 100644 --- a/cui/source/dialogs/cuigrfflt.cxx +++ b/cui/source/dialogs/cuigrfflt.cxx @@ -192,8 +192,8 @@ Graphic GraphicFilterMosaic::GetFilteredGraphic( const Graphic& rGraphic, Graphic aRet; tools::Long nTileWidth = static_cast<tools::Long>(mxMtrWidth->get_value(FieldUnit::PIXEL)); tools::Long nTileHeight = static_cast<tools::Long>(mxMtrHeight->get_value(FieldUnit::PIXEL)); - const Size aSize( std::max( FRound( nTileWidth * fScaleX ), tools::Long(1) ), - std::max( FRound( nTileHeight * fScaleY ), tools::Long(1) ) ); + const Size aSize( std::max( basegfx::fround<tools::Long>( nTileWidth * fScaleX ), tools::Long(1) ), + std::max( basegfx::fround<tools::Long>( nTileHeight * fScaleY ), tools::Long(1) ) ); if( rGraphic.IsAnimated() ) { @@ -270,7 +270,7 @@ GraphicFilterSolarize::GraphicFilterSolarize(weld::Window* pParent, const Graphi , mxMtrThreshold(m_xBuilder->weld_metric_spin_button("value", FieldUnit::PERCENT)) , mxCbxInvert(m_xBuilder->weld_check_button("invert")) { - mxMtrThreshold->set_value(FRound(cGreyThreshold / 2.55), FieldUnit::PERCENT); + mxMtrThreshold->set_value(basegfx::fround(cGreyThreshold / 2.55), FieldUnit::PERCENT); mxMtrThreshold->connect_value_changed(LINK(this, GraphicFilterSolarize, EditModifyHdl)); mxCbxInvert->set_active(bInvert); @@ -290,7 +290,7 @@ IMPL_LINK_NOARG(GraphicFilterSolarize, EditModifyHdl, weld::MetricSpinButton&, v Graphic GraphicFilterSolarize::GetFilteredGraphic( const Graphic& rGraphic, double, double ) { Graphic aRet; - sal_uInt8 nGreyThreshold = static_cast<sal_uInt8>(FRound(mxMtrThreshold->get_value(FieldUnit::PERCENT) * 2.55)); + sal_uInt8 nGreyThreshold = basegfx::fround<sal_uInt8>(mxMtrThreshold->get_value(FieldUnit::PERCENT) * 2.55); if( rGraphic.IsAnimated() ) { diff --git a/filter/source/svg/svgwriter.cxx b/filter/source/svg/svgwriter.cxx index e4420dc27e71..ff444a6ce284 100644 --- a/filter/source/svg/svgwriter.cxx +++ b/filter/source/svg/svgwriter.cxx @@ -2184,7 +2184,7 @@ void SVGActionWriter::ImplWriteShape( const SVGShapeDescriptor& rShape ) for( size_t k = 0; k < rShape.maDashArray.size(); ++k ) { - const sal_Int32 nDash = ImplMap( FRound( rShape.maDashArray[ k ] ) ); + const sal_Int32 nDash = ImplMap(basegfx::fround(rShape.maDashArray[k])); if( k ) aDashArrayStr.append(","); @@ -2546,8 +2546,8 @@ void SVGActionWriter::ImplWriteMask(GDIMetaFile& rMtf, const Point& rDestPt, con if (fScaleX != 1.0 || fScaleY != 1.0) { rMtf.Scale(fScaleX, fScaleY); - aSrcPt.setX(FRound(aSrcPt.X() * fScaleX)); - aSrcPt.setY(FRound(aSrcPt.Y() * fScaleY)); + aSrcPt.setX(basegfx::fround<tools::Long>(aSrcPt.X() * fScaleX)); + aSrcPt.setY(basegfx::fround<tools::Long>(aSrcPt.Y() * fScaleY)); } nMoveX = rDestPt.X() - aSrcPt.X(); @@ -2747,7 +2747,7 @@ void SVGActionWriter::ImplWriteText( const Point& rPos, const OUString& rText, const double fFactor = static_cast<double>(nWidth) / aNormSize.Width(); for( i = 0; i < ( nLen - 1 ); i++ ) - aTmpArray.set(i, FRound(aTmpArray[i] * fFactor)); + aTmpArray.set(i, basegfx::fround(aTmpArray[i] * fFactor)); } else { @@ -2811,11 +2811,11 @@ void SVGActionWriter::ImplWriteText( const Point& rPos, const OUString& rText, return; tools::Polygon aPoly( 4 ); - const tools::Long nLineHeight = std::max<tools::Long>( FRound( aMetric.GetLineHeight() * 0.05 ), 1 ); + const tools::Long nLineHeight = std::max<tools::Long>( basegfx::fround<tools::Long>( aMetric.GetLineHeight() * 0.05 ), 1 ); if( rFont.GetStrikeout() ) { - const tools::Long nYLinePos = aBaseLinePos.Y() - FRound( aMetric.GetAscent() * 0.26 ); + const tools::Long nYLinePos = aBaseLinePos.Y() - basegfx::fround<tools::Long>( aMetric.GetAscent() * 0.26 ); aPoly[ 0 ].setX( aBaseLinePos.X() ); aPoly[ 0 ].setY( nYLinePos - ( nLineHeight >> 1 ) ); aPoly[ 1 ].setX( aBaseLinePos.X() + aNormSize.Width() - 1 ); aPoly[ 1 ].setY( aPoly[ 0 ].Y() ); @@ -3323,8 +3323,8 @@ void SVGActionWriter::ImplWriteActions( const GDIMetaFile& rMtf, // i.e. invisible line, because it makes it visible, // resulting an extra line behind the normal shape line if ( aNewLineColor.GetAlpha() > 0 ) - aNewLineColor.SetAlpha( 255 - sal::static_int_cast<sal_uInt8>( FRound( pA->GetTransparence() * 2.55 ) ) ); - aNewFillColor.SetAlpha( 255 - sal::static_int_cast<sal_uInt8>( FRound( pA->GetTransparence() * 2.55 ) ) ); + aNewLineColor.SetAlpha( 255 - basegfx::fround<sal_uInt8>( pA->GetTransparence() * 2.55 ) ); + aNewFillColor.SetAlpha( 255 - basegfx::fround<sal_uInt8>( pA->GetTransparence() * 2.55 ) ); maAttributeWriter.AddPaintAttr( aNewLineColor, aNewFillColor ); ImplWritePolyPolygon( rPolyPoly, false ); @@ -3435,7 +3435,7 @@ void SVGActionWriter::ImplWriteActions( const GDIMetaFile& rMtf, mapCurShape->maShapePolyPoly = aShapePolyPoly; mapCurShape->maShapeFillColor = aFill.getFillColor(); - mapCurShape->maShapeFillColor.SetAlpha( 255 - static_cast<sal_uInt8>(FRound( 255.0 * aFill.getTransparency() )) ); + mapCurShape->maShapeFillColor.SetAlpha( 255 - basegfx::fround<sal_uInt8>( 255.0 * aFill.getTransparency() ) ); if( bGradient ) { @@ -3525,8 +3525,8 @@ void SVGActionWriter::ImplWriteActions( const GDIMetaFile& rMtf, } mapCurShape->maShapeLineColor = mpVDev->GetLineColor(); - mapCurShape->maShapeLineColor.SetAlpha( 255 - static_cast<sal_uInt8>(FRound( aStroke.getTransparency() * 255.0 )) ); - mapCurShape->mnStrokeWidth = FRound( aStroke.getStrokeWidth() ); + mapCurShape->maShapeLineColor.SetAlpha( 255 - basegfx::fround<sal_uInt8>( aStroke.getTransparency() * 255.0 ) ); + mapCurShape->mnStrokeWidth = basegfx::fround(aStroke.getStrokeWidth()); aStroke.getDashArray( mapCurShape->maDashArray ); // added support for LineJoin diff --git a/include/basegfx/numeric/ftools.hxx b/include/basegfx/numeric/ftools.hxx index 28efe2770b74..b286c847968c 100644 --- a/include/basegfx/numeric/ftools.hxx +++ b/include/basegfx/numeric/ftools.hxx @@ -35,41 +35,25 @@ namespace basegfx @return the nearest integer */ - inline sal_Int32 fround32( double fVal ) + template <class Int = sal_Int32> + inline std::enable_if_t<std::is_integral_v<Int>, Int> fround(double fVal) { if (fVal >= 0.0) { - if (fVal >= std::numeric_limits<sal_Int32>::max() - .5) - return std::numeric_limits<sal_Int32>::max(); - return static_cast<sal_Int32>(fVal + .5); + if (fVal >= static_cast<double>(std::numeric_limits<Int>::max()) - 0.5) + return std::numeric_limits<Int>::max(); + return static_cast<Int>(fVal + .5); } - if (fVal <= std::numeric_limits<sal_Int32>::min() + .5) - return std::numeric_limits<sal_Int32>::min(); - return static_cast<sal_Int32>(fVal - .5); + if (fVal <= static_cast<double>(std::numeric_limits<Int>::min()) + 0.5) + return std::numeric_limits<Int>::min(); + return static_cast<Int>(fVal - .5); } /** Round double to nearest integer @return the nearest 64 bit integer */ - inline sal_Int64 fround64( double fVal ) - { - return fVal > 0.0 ? static_cast<sal_Int64>( fVal + .5 ) : -static_cast<sal_Int64>( -fVal + .5 ); - } - - /** Round double to nearest integer - - @return the nearest integer - */ - template <class Int = sal_Int32> - inline std::enable_if_t<std::is_integral_v<Int> && std::is_signed_v<Int>, Int> - fround(double fVal) - { - if constexpr (sizeof(Int) == sizeof(sal_Int64)) - return fround64(fVal); - else - return fround32(fVal); - } + inline sal_Int64 fround64(double fVal) { return fround<sal_Int64>(fVal); } /** Prune a small epsilon range around zero. diff --git a/include/svx/svdtrans.hxx b/include/svx/svdtrans.hxx index 0087f5407f81..88bbfc6fa1ed 100644 --- a/include/svx/svdtrans.hxx +++ b/include/svx/svdtrans.hxx @@ -94,27 +94,27 @@ inline void ResizePoint(Point& rPnt, const Point& rRef, const Fraction& xFract, { double nxFract = xFract.IsValid() ? static_cast<double>(xFract) : 1.0; double nyFract = yFract.IsValid() ? static_cast<double>(yFract) : 1.0; - rPnt.setX(rRef.X() + FRound( (rPnt.X() - rRef.X()) * nxFract )); - rPnt.setY(rRef.Y() + FRound( (rPnt.Y() - rRef.Y()) * nyFract )); + rPnt.setX(rRef.X() + basegfx::fround<tools::Long>((rPnt.X() - rRef.X()) * nxFract)); + rPnt.setY(rRef.Y() + basegfx::fround<tools::Long>((rPnt.Y() - rRef.Y()) * nyFract)); } inline void RotatePoint(Point& rPnt, const Point& rRef, double sn, double cs) { tools::Long dx=rPnt.X()-rRef.X(); tools::Long dy=rPnt.Y()-rRef.Y(); - rPnt.setX(FRound(rRef.X()+dx*cs+dy*sn)); - rPnt.setY(FRound(rRef.Y()+dy*cs-dx*sn)); + rPnt.setX(basegfx::fround<tools::Long>(rRef.X() + dx * cs + dy * sn)); + rPnt.setY(basegfx::fround<tools::Long>(rRef.Y() + dy * cs - dx * sn)); } inline void ShearPoint(Point& rPnt, const Point& rRef, double tn, bool bVShear) { if (!bVShear) { // Horizontal if (rPnt.Y()!=rRef.Y()) { // else not needed - rPnt.AdjustX(-FRound((rPnt.Y()-rRef.Y())*tn)); + rPnt.AdjustX(basegfx::fround<tools::Long>((rRef.Y() - rPnt.Y()) * tn)); } } else { // or else vertical if (rPnt.X()!=rRef.X()) { // else not needed - rPnt.AdjustY(-FRound((rPnt.X()-rRef.X())*tn)); + rPnt.AdjustY(basegfx::fround<tools::Long>((rRef.X() - rPnt.X()) * tn)); } } } diff --git a/include/tools/helpers.hxx b/include/tools/helpers.hxx index 5f61ba80836c..d0fb08f70e5b 100644 --- a/include/tools/helpers.hxx +++ b/include/tools/helpers.hxx @@ -71,14 +71,6 @@ inline sal_uInt32 AlignedWidth4Bytes(sal_uInt32 nWidthBits) return (nWidthBits >> 5) << 2; } -inline tools::Long FRound( double fVal ) -{ - return fVal > 0.0 - ? fVal == double(std::numeric_limits<tools::Long>::max()) - ? std::numeric_limits<tools::Long>::max() : static_cast<tools::Long>( fVal + 0.5 ) - : static_cast<tools::Long>( fVal - 0.5 ); -} - //valid range: (-180,180] template <typename T> [[nodiscard]] inline typename std::enable_if<std::is_signed<T>::value, T>::type diff --git a/include/vcl/bitmap.hxx b/include/vcl/bitmap.hxx index 123b48fcd752..19dc78bc5d76 100644 --- a/include/vcl/bitmap.hxx +++ b/include/vcl/bitmap.hxx @@ -20,6 +20,9 @@ #ifndef INCLUDED_VCL_BITMAP_HXX #define INCLUDED_VCL_BITMAP_HXX +#include <sal/config.h> + +#include <basegfx/numeric/ftools.hxx> #include <tools/degree.hxx> #include <vcl/checksum.hxx> #include <vcl/dllapi.h> @@ -32,7 +35,10 @@ #include <memory> -#define GAMMA( _def_cVal, _def_InvGamma ) (static_cast<sal_uInt8>(MinMax(FRound(pow( _def_cVal/255.0,_def_InvGamma)*255.0),0,255))) +inline sal_uInt8 GAMMA(double _def_cVal, double _def_InvGamma) +{ + return basegfx::fround<sal_uInt8>(pow(_def_cVal / 255.0, _def_InvGamma) * 255.0); +} class Color; diff --git a/sc/source/core/data/drwlayer.cxx b/sc/source/core/data/drwlayer.cxx index 20da0fcac2bf..c4bd1b2f90b1 100644 --- a/sc/source/core/data/drwlayer.cxx +++ b/sc/source/core/data/drwlayer.cxx @@ -963,10 +963,10 @@ void ScDrawLayer::InitializeCellAnchoredObj(SdrObject* pObj, ScDrawObjData& rDat static_cast<SdrPathObj*>(pObj)->GetPathPoly().getB2DPolygon(0)); const basegfx::B2DPoint aB2DPoint0(aPoly.getB2DPoint(0)); const basegfx::B2DPoint aB2DPoint1(aPoly.getB2DPoint(1)); - const Point aPointLT(FRound(std::min(aB2DPoint0.getX(), aB2DPoint1.getX())), - FRound(std::min(aB2DPoint0.getY(), aB2DPoint1.getY()))); - const Point aPointRB(FRound(std::max(aB2DPoint0.getX(), aB2DPoint1.getX())), - FRound(std::max(aB2DPoint0.getY(), aB2DPoint1.getY()))); + const Point aPointLT(basegfx::fround<tools::Long>(std::min(aB2DPoint0.getX(), aB2DPoint1.getX())), + basegfx::fround<tools::Long>(std::min(aB2DPoint0.getY(), aB2DPoint1.getY()))); + const Point aPointRB(basegfx::fround<tools::Long>(std::max(aB2DPoint0.getX(), aB2DPoint1.getX())), + basegfx::fround<tools::Long>(std::max(aB2DPoint0.getY(), aB2DPoint1.getY()))); const tools::Rectangle aObjRect(aPointLT, aPointRB); GetCellAnchorFromPosition(aObjRect, rNoRotatedAnchor, *pDoc, nTab1, false /*bHiddenAsZero*/); diff --git a/sd/source/filter/html/htmlex.cxx b/sd/source/filter/html/htmlex.cxx index aed0312bc812..7f41e2966a4d 100644 --- a/sd/source/filter/html/htmlex.cxx +++ b/sd/source/filter/html/htmlex.cxx @@ -2774,7 +2774,8 @@ OUString HtmlExport::CreateHTMLPolygonArea( const ::basegfx::B2DPolyPolygon& rPo for ( sal_uInt32 nPoint = 0; nPoint < nNoOfPoints; nPoint++ ) { const ::basegfx::B2DPoint aB2DPoint(aPolygon.getB2DPoint(nPoint)); - Point aPnt(FRound(aB2DPoint.getX()), FRound(aB2DPoint.getY())); + Point aPnt(basegfx::fround<tools::Long>(aB2DPoint.getX()), + basegfx::fround<tools::Long>(aB2DPoint.getY())); // coordinates are relative to the physical page origin, not the // origin of ordinates aPnt.Move(aShift.Width(), aShift.Height()); diff --git a/sd/source/ui/app/optsitem.cxx b/sd/source/ui/app/optsitem.cxx index fb9aac86d491..1736a20e0c6c 100644 --- a/sd/source/ui/app/optsitem.cxx +++ b/sd/source/ui/app/optsitem.cxx @@ -1042,13 +1042,13 @@ bool SdOptionsGrid::ReadData( const Any* pValues ) if( pValues[2].hasValue() ) { - const sal_uInt32 nDivX = FRound( *o3tl::doAccess<double>(pValues[ 2 ]) ); + const sal_uInt32 nDivX = basegfx::fround<sal_uInt32>(*o3tl::doAccess<double>(pValues[2])); SetFieldDivisionX( SvxOptionsGrid::GetFieldDrawX() / ( nDivX + 1 ) ); } if( pValues[3].hasValue() ) { - const sal_uInt32 nDivY = FRound( *o3tl::doAccess<double>(pValues[ 3 ]) ); + const sal_uInt32 nDivY = basegfx::fround<sal_uInt32>(*o3tl::doAccess<double>(pValues[3])); SetFieldDivisionY( SvxOptionsGrid::GetFieldDrawY() / ( nDivY + 1 ) ); } diff --git a/sd/source/ui/dlg/vectdlg.cxx b/sd/source/ui/dlg/vectdlg.cxx index 4069aa225f44..ee407b7eb8ca 100644 --- a/sd/source/ui/dlg/vectdlg.cxx +++ b/sd/source/ui/dlg/vectdlg.cxx @@ -221,9 +221,9 @@ void SdVectorizeDlg::AddTile( BitmapReadAccess const * pRAcc, GDIMetaFile& rMtf, } } - const Color aColor( static_cast<sal_uInt8>(FRound( nSumR * fMult )), - static_cast<sal_uInt8>(FRound( nSumG * fMult )), - static_cast<sal_uInt8>(FRound( nSumB * fMult )) ); + const Color aColor( basegfx::fround<sal_uInt8>( nSumR * fMult ), + basegfx::fround<sal_uInt8>( nSumG * fMult ), + basegfx::fround<sal_uInt8>( nSumB * fMult ) ); ::tools::Rectangle aRect( Point( nPosX, nPosY ), Size( nWidth + 1, nHeight + 1 ) ); const Size& rMaxSize = rMtf.GetPrefSize(); diff --git a/sd/source/ui/func/fuoaprms.cxx b/sd/source/ui/func/fuoaprms.cxx index 0feaabfb4736..893f833fcdf1 100644 --- a/sd/source/ui/func/fuoaprms.cxx +++ b/sd/source/ui/func/fuoaprms.cxx @@ -662,7 +662,8 @@ void FuObjectAnimationParameters::DoExecute( SfxRequest& rReq ) const ::basegfx::B2DPolygon& aPolygon(rPolyPolygon.getB2DPolygon(nNoOfPolygons - 1)); sal_uInt32 nPoints(aPolygon.count()); const ::basegfx::B2DPoint aNewB2DCenter(aPolygon.getB2DPoint(nPoints - 1)); - const Point aNewCenter(FRound(aNewB2DCenter.getX()), FRound(aNewB2DCenter.getY())); + const Point aNewCenter(basegfx::fround<::tools::Long>(aNewB2DCenter.getX()), + basegfx::fround<::tools::Long>(aNewB2DCenter.getY())); Size aDistance(aNewCenter.X() - aCurCenter.X(), aNewCenter.Y() - aCurCenter.Y()); pRunningObj->Move(aDistance); diff --git a/sd/source/ui/view/drviews8.cxx b/sd/source/ui/view/drviews8.cxx index 0f2907d424ba..15be1d1c95d0 100644 --- a/sd/source/ui/view/drviews8.cxx +++ b/sd/source/ui/view/drviews8.cxx @@ -74,13 +74,13 @@ void DrawViewShell::ScannerEvent() if( fGrfWH < fWinWH ) { - aBmpSize.setWidth( FRound( aPageSize.Height() * fGrfWH ) ); + aBmpSize.setWidth( basegfx::fround<::tools::Long>( aPageSize.Height() * fGrfWH ) ); aBmpSize.setHeight( aPageSize.Height() ); } else if( fGrfWH > 0.F ) { aBmpSize.setWidth( aPageSize.Width() ); - aBmpSize.setHeight( FRound( aPageSize.Width() / fGrfWH ) ); + aBmpSize.setHeight( basegfx::fround<::tools::Long>( aPageSize.Width() / fGrfWH ) ); } } diff --git a/sfx2/source/doc/objcont.cxx b/sfx2/source/doc/objcont.cxx index a3b5226917d3..847d44d00820 100644 --- a/sfx2/source/doc/objcont.cxx +++ b/sfx2/source/doc/objcont.cxx @@ -171,13 +171,13 @@ bool SfxObjectShell::CreatePreview_Impl( bool bFullContent, VirtualDevice* pDevi double fWH = static_cast< double >( aSizePix.Width() ) / aSizePix.Height(); if ( fWH <= 1.0 ) { - aSizePix.setWidth( FRound( nMaximumExtent * fWH ) ); + aSizePix.setWidth(basegfx::fround<tools::Long>(nMaximumExtent * fWH)); aSizePix.setHeight( nMaximumExtent ); } else { aSizePix.setWidth( nMaximumExtent ); - aSizePix.setHeight( FRound( nMaximumExtent / fWH ) ); + aSizePix.setHeight(basegfx::fround<tools::Long>(nMaximumExtent / fWH)); } } // do it 4x larger to be able to scale it down & get beautiful antialias diff --git a/slideshow/source/engine/shapes/drawinglayeranimation.cxx b/slideshow/source/engine/shapes/drawinglayeranimation.cxx index d0d3acb66369..ea765eea9649 100644 --- a/slideshow/source/engine/shapes/drawinglayeranimation.cxx +++ b/slideshow/source/engine/shapes/drawinglayeranimation.cxx @@ -422,7 +422,7 @@ void ActivityImpl::ImpForceScrollTextAnimNodes() const double fNumberSteps = (fRelativeDistance * fDistanceLogic) / GetStepWidthLogic(); - nLoopTime = FRound(fNumberSteps * mnFrequency); + nLoopTime = basegfx::fround<sal_uInt32>(fNumberSteps * mnFrequency); // init loop ScrollTextAnimNode aInitNode( @@ -451,7 +451,7 @@ void ActivityImpl::ImpForceScrollTextAnimNodes() const double fNumberSteps = (fRelativeDistance * fDistanceLogic) / GetStepWidthLogic(); - nLoopTime = FRound(fNumberSteps * mnFrequency); + nLoopTime = basegfx::fround<sal_uInt32>(fNumberSteps * mnFrequency); if(0 == mnRepeat) { @@ -501,7 +501,7 @@ void ActivityImpl::ImpForceScrollTextAnimNodes() const double fNumberSteps = (fRelativeDistance * fDistanceLogic) / GetStepWidthLogic(); - nLoopTime = FRound(fNumberSteps * mnFrequency); + nLoopTime = basegfx::fround<sal_uInt32>(fNumberSteps * mnFrequency); // exit loop ScrollTextAnimNode aExitNode( diff --git a/svx/source/customshapes/EnhancedCustomShape2d.cxx b/svx/source/customshapes/EnhancedCustomShape2d.cxx index d93dbee142a3..4c5214bf8674 100644 --- a/svx/source/customshapes/EnhancedCustomShape2d.cxx +++ b/svx/source/customshapes/EnhancedCustomShape2d.cxx @@ -1171,9 +1171,9 @@ bool EnhancedCustomShape2d::GetHandlePosition( const sal_uInt32 nIndex, Point& r double fY =-dx * sin( a ); rReturnPosition = Point( - FRound( fX + aReferencePoint.X() ), + basegfx::fround<tools::Long>( fX + aReferencePoint.X() ), basegfx::fTools::equalZero(fXScale) ? aReferencePoint.Y() : - FRound( ( fY * fYScale ) / fXScale + aReferencePoint.Y() ) ); + basegfx::fround<tools::Long>( ( fY * fYScale ) / fXScale + aReferencePoint.Y() ) ); } else { diff --git a/svx/source/dialog/_contdlg.cxx b/svx/source/dialog/_contdlg.cxx index 3ae8cba852e0..02608859b380 100644 --- a/svx/source/dialog/_contdlg.cxx +++ b/svx/source/dialog/_contdlg.cxx @@ -135,12 +135,12 @@ tools::PolyPolygon SvxContourDlg::CreateAutoContour( const Graphic& rGraphic, if( fWH <= 1.0 ) { aSizePix.setHeight(512); - aSizePix.setWidth( FRound( ( aSizePix.Height() ) * fWH ) ); + aSizePix.setWidth(basegfx::fround<tools::Long>(aSizePix.Height() * fWH)); } else { aSizePix.setWidth(512); - aSizePix.setHeight( FRound( ( aSizePix.Width() ) / fWH ) ); + aSizePix.setHeight(basegfx::fround<tools::Long>(aSizePix.Width() / fWH)); } } diff --git a/svx/source/gallery2/galobj.cxx b/svx/source/gallery2/galobj.cxx index 44362b9a78ec..80d49228e750 100644 --- a/svx/source/gallery2/galobj.cxx +++ b/svx/source/gallery2/galobj.cxx @@ -118,9 +118,9 @@ bool SgaObject::CreateThumb( const Graphic& rGraphic ) double fFactorPix = static_cast< double >( aBmpSize.Width() ) / aBmpSize.Height(); if( fFactorPix > fFactorLog ) - aBmpSize.setWidth( FRound( aBmpSize.Height() * fFactorLog ) ); + aBmpSize.setWidth( basegfx::fround<tools::Long>( aBmpSize.Height() * fFactorLog ) ); else - aBmpSize.setHeight( FRound( aBmpSize.Width() / fFactorLog ) ); + aBmpSize.setHeight( basegfx::fround<tools::Long>( aBmpSize.Width() / fFactorLog ) ); aBmpEx.Scale(aBmpSize, BmpScaleFlag::BestQuality); } diff --git a/svx/source/svdraw/gradtrns.cxx b/svx/source/svdraw/gradtrns.cxx index d665cd48ce16..34c547bba8ed 100644 --- a/svx/source/svdraw/gradtrns.cxx +++ b/svx/source/svdraw/gradtrns.cxx @@ -235,7 +235,7 @@ void GradTransformer::VecToGrad(GradTransVector const & rV, GradTransGradient& r } // to int and set - Degree10 nNewAngle( FRound(fNewFullAngle)); + Degree10 nNewAngle(basegfx::fround<sal_Int16>(fNewFullAngle)); if(nNewAngle != rGOld.aGradient.GetAngle()) { @@ -252,7 +252,7 @@ void GradTransformer::VecToGrad(GradTransVector const & rV, GradTransGradient& r const double fFullLen(aFullVec.getLength()); const double fOldLen(aOldVec.getLength()); const double fNewBorder((fFullLen * 100.0) / fOldLen); - sal_Int32 nNewBorder(100 - FRound(fNewBorder)); + sal_Int32 nNewBorder(100 - basegfx::fround(fNewBorder)); // clip if(nNewBorder < 0) @@ -283,7 +283,7 @@ void GradTransformer::VecToGrad(GradTransVector const & rV, GradTransGradient& r const double fFullLen(aFullVec.getLength()); const double fOldLen(aOldVec.getLength()); const double fNewBorder((fFullLen * 100.0) / fOldLen); - sal_Int32 nNewBorder = 100 - FRound(fNewBorder); + sal_Int32 nNewBorder = 100 - basegfx::fround(fNewBorder); // clip if(nNewBorder < 0) @@ -319,7 +319,7 @@ void GradTransformer::VecToGrad(GradTransVector const & rV, GradTransGradient& r } // to int and set - const Degree10 nNewAngle(FRound(fNewFullAngle)); + const Degree10 nNewAngle(basegfx::fround<sal_Int16>(fNewFullAngle)); if(nNewAngle != rGOld.aGradient.GetAngle()) { @@ -336,8 +336,8 @@ void GradTransformer::VecToGrad(GradTransVector const & rV, GradTransGradient& r { const basegfx::B2DPoint aTopLeft(aRange.getMinX(), aRange.getMinY()); const basegfx::B2DPoint aOffset(aEndPos - aTopLeft); - sal_Int32 nNewXOffset(FRound((aOffset.getX() * 100.0) / aRange.getWidth())); - sal_Int32 nNewYOffset(FRound((aOffset.getY() * 100.0) / aRange.getHeight())); + sal_Int32 nNewXOffset(basegfx::fround(aOffset.getX() * 100.0 / aRange.getWidth())); + sal_Int32 nNewYOffset(basegfx::fround(aOffset.getY() * 100.0 / aRange.getHeight())); // clip if(nNewXOffset < 0) @@ -376,7 +376,7 @@ void GradTransformer::VecToGrad(GradTransVector const & rV, GradTransGradient& r const double fFullLen(aFullVec.getLength()); const double fOldLen(aOldVec.getLength()); const double fNewBorder((fFullLen * 100.0) / fOldLen); - sal_Int32 nNewBorder(100 - FRound(fNewBorder)); + sal_Int32 nNewBorder(100 - basegfx::fround(fNewBorder)); // clip if(nNewBorder < 0) @@ -414,7 +414,7 @@ void GradTransformer::VecToGrad(GradTransVector const & rV, GradTransGradient& r } // to int and set - const Degree10 nNewAngle(FRound(fNewFullAngle)); + const Degree10 nNewAngle(basegfx::fround<sal_Int16>(fNewFullAngle)); if(nNewAngle != rGOld.aGradient.GetAngle()) { @@ -431,8 +431,8 @@ void GradTransformer::VecToGrad(GradTransVector const & rV, GradTransGradient& r { const basegfx::B2DPoint aTopLeft(aRange.getMinX(), aRange.getMinY()); const basegfx::B2DPoint aOffset(aEndPos - aTopLeft); - sal_Int32 nNewXOffset(FRound((aOffset.getX() * 100.0) / aRange.getWidth())); - sal_Int32 nNewYOffset(FRound((aOffset.getY() * 100.0) / aRange.getHeight())); + sal_Int32 nNewXOffset(basegfx::fround(aOffset.getX() * 100.0 / aRange.getWidth())); + sal_Int32 nNewYOffset(basegfx::fround(aOffset.getY() * 100.0 / aRange.getHeight())); // clip if(nNewXOffset < 0) @@ -471,7 +471,7 @@ void GradTransformer::VecToGrad(GradTransVector const & rV, GradTransGradient& r const double fFullLen(aFullVec.getLength()); const double fOldLen(aOldVec.getLength()); const double fNewBorder((fFullLen * 100.0) / fOldLen); - sal_Int32 nNewBorder(100 - FRound(fNewBorder)); + sal_Int32 nNewBorder(100 - basegfx::fround(fNewBorder)); // clip if(nNewBorder < 0) @@ -509,7 +509,7 @@ void GradTransformer::VecToGrad(GradTransVector const & rV, GradTransGradient& r } // to int and set - const Degree10 nNewAngle(FRound(fNewFullAngle)); + const Degree10 nNewAngle(basegfx::fround<sal_Int16>(fNewFullAngle)); if(nNewAngle != rGOld.aGradient.GetAngle()) { diff --git a/svx/source/svdraw/sdrpagewindow.cxx b/svx/source/svdraw/sdrpagewindow.cxx index 01be77135674..86fbe967ccbc 100644 --- a/svx/source/svdraw/sdrpagewindow.cxx +++ b/svx/source/svdraw/sdrpagewindow.cxx @@ -231,6 +231,7 @@ void SdrPageWindow::PrepareRedraw(const vcl::Region& rReg) // clip test #ifdef CLIPPER_TEST #include <svx/svdopath.hxx> +#include <basegfx/numeric/ftools.hxx> #include <basegfx/polygon/b2dpolygon.hxx> #include <tools/helpers.hxx> #include <basegfx/polygon/b2dpolygoncutandtouch.hxx> @@ -262,8 +263,10 @@ namespace { const basegfx::B2DPoint aBStart(aCandidate.getB2DPoint(a)); -e ... etc. - the rest is truncated
