[Libreoffice-commits] core.git: Branch 'libreoffice-6-4' - include/svx sc/CppunitTest_sc_shapetest.mk sc/Module_sc.mk sc/qa sc/source svx/source

Regina Henschel (via logerrit) logerrit at kemper.freedesktop.org
Fri Feb 7 17:41:26 UTC 2020


 include/svx/svdoashp.hxx                           |    1 
 sc/CppunitTest_sc_shapetest.mk                     |   53 +++++
 sc/Module_sc.mk                                    |    1 
 sc/qa/unit/data/ods/tdf119191_FitToCellSize.ods    |binary
 sc/qa/unit/data/ods/tdf119191_transformedShape.ods |binary
 sc/qa/unit/scshapetest.cxx                         |  189 +++++++++++++++++++++
 sc/source/core/data/drwlayer.cxx                   |    5 
 sc/source/ui/view/drawvie4.cxx                     |    6 
 svx/source/svdraw/svdoashp.cxx                     |   66 +++++++
 9 files changed, 318 insertions(+), 3 deletions(-)

New commits:
commit 7efd182997cb29ed4820145efc99a6c18e2c3303
Author:     Regina Henschel <rb.henschel at t-online.de>
AuthorDate: Thu Nov 28 19:28:29 2019 +0100
Commit:     Xisco Faulí <xiscofauli at libreoffice.org>
CommitDate: Fri Feb 7 18:40:31 2020 +0100

    tdf#119191 Implement SdrObjCustomShape::AdjustToMaxRect
    
    and use in ScDrawLayer::RecalcPos and in ScDrawView::FitToCellSize().
    Error was, that it was assumed, that SdObjCustomShape::SetSnapRect()
    changes the shape so, that it fits into the passed rectangle. That is
    true for other type of shapes, but not for custom shapes.
    
    Change-Id: Ib00d52087509f459165000abf43c7f244980a01b
    Reviewed-on: https://gerrit.libreoffice.org/84216
    Tested-by: Jenkins
    Reviewed-by: Regina Henschel <rb.henschel at t-online.de>
    (cherry picked from commit f44140bebb9c493d97ba5aef26c9692c53a6b93f)
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/85043
    Reviewed-by: Xisco Faulí <xiscofauli at libreoffice.org>

diff --git a/include/svx/svdoashp.hxx b/include/svx/svdoashp.hxx
index 02b957de95a0..ff51e20ec974 100644
--- a/include/svx/svdoashp.hxx
+++ b/include/svx/svdoashp.hxx
@@ -172,6 +172,7 @@ public:
     virtual void Shear(const Point& rRef, long nAngle, double tn, bool bVShear) override;
     virtual void SetSnapRect(const tools::Rectangle& rRect) override;
     virtual void SetLogicRect(const tools::Rectangle& rRect) override;
+    virtual void AdjustToMaxRect( const tools::Rectangle& rMaxRect, bool bShrinkOnly = false ) override;
 
     virtual void NbcMove(const Size& rSiz) override;
     virtual void NbcResize(const Point& rRef, const Fraction& xFact, const Fraction& yFact) override;
diff --git a/sc/CppunitTest_sc_shapetest.mk b/sc/CppunitTest_sc_shapetest.mk
new file mode 100644
index 000000000000..cf97cf99da13
--- /dev/null
+++ b/sc/CppunitTest_sc_shapetest.mk
@@ -0,0 +1,53 @@
+# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t -*-
+#*************************************************************************
+#
+# This file is part of the LibreOffice project.
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+#*************************************************************************
+
+$(eval $(call gb_CppunitTest_CppunitTest,sc_shapetest))
+
+$(eval $(call gb_CppunitTest_use_external,sc_shapetest,boost_headers))
+
+$(eval $(call gb_CppunitTest_use_common_precompiled_header,sc_shapetest))
+
+$(eval $(call gb_CppunitTest_add_exception_objects,sc_shapetest, \
+    sc/qa/unit/scshapetest \
+))
+
+$(eval $(call gb_CppunitTest_use_libraries,sc_shapetest, \
+    cppu \
+    sal \
+    sc \
+    sfx \
+    subsequenttest \
+    svx \
+    svxcore \
+    test \
+    tl \
+    unotest \
+    utl \
+))
+
+$(eval $(call gb_CppunitTest_set_include,sc_shapetest,\
+    -I$(SRCDIR)/sc/source/ui/inc \
+    -I$(SRCDIR)/sc/inc \
+    $$(INCLUDE) \
+))
+
+$(eval $(call gb_CppunitTest_use_sdk_api,sc_shapetest))
+
+$(eval $(call gb_CppunitTest_use_ure,sc_shapetest))
+$(eval $(call gb_CppunitTest_use_vcl,sc_shapetest))
+
+$(eval $(call gb_CppunitTest_use_components,sc_shapetest,\
+    $(sc_unoapi_common_components) \
+))
+
+$(eval $(call gb_CppunitTest_use_configuration,sc_shapetest))
+
+# vim: set noet sw=4 ts=4:
diff --git a/sc/Module_sc.mk b/sc/Module_sc.mk
index cbe32e32ebc2..d52537f1c908 100644
--- a/sc/Module_sc.mk
+++ b/sc/Module_sc.mk
@@ -48,6 +48,7 @@ $(eval $(call gb_Module_add_check_targets,sc,\
 	CppunitTest_sc_dataprovider \
 	CppunitTest_sc_datatransformation \
 	CppunitTest_sc_cache_test \
+    CppunitTest_sc_shapetest \
 ))
 
 ifneq ($(DISABLE_GUI),TRUE)
diff --git a/sc/qa/unit/data/ods/tdf119191_FitToCellSize.ods b/sc/qa/unit/data/ods/tdf119191_FitToCellSize.ods
new file mode 100644
index 000000000000..ff43af5f7589
Binary files /dev/null and b/sc/qa/unit/data/ods/tdf119191_FitToCellSize.ods differ
diff --git a/sc/qa/unit/data/ods/tdf119191_transformedShape.ods b/sc/qa/unit/data/ods/tdf119191_transformedShape.ods
new file mode 100644
index 000000000000..c3936a269627
Binary files /dev/null and b/sc/qa/unit/data/ods/tdf119191_transformedShape.ods differ
diff --git a/sc/qa/unit/scshapetest.cxx b/sc/qa/unit/scshapetest.cxx
new file mode 100644
index 000000000000..8e3cbe2c7802
--- /dev/null
+++ b/sc/qa/unit/scshapetest.cxx
@@ -0,0 +1,189 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#include <test/calc_unoapi_test.hxx>
+
+#include <sfx2/dispatch.hxx>
+#include <svx/svdoashp.hxx>
+#include <svx/svdpage.hxx>
+
+#include <docsh.hxx>
+#include <drwlayer.hxx>
+#include <tabvwsh.hxx>
+#include <userdat.hxx>
+
+#include <sc.hrc> // defines of slot-IDs
+
+using namespace css;
+
+namespace sc_apitest
+{
+class ScShapeTest : public CalcUnoApiTest
+{
+public:
+    ScShapeTest();
+
+    virtual void tearDown() override;
+
+    void testFitToCellSize();
+    void testCustomShapeCellAnchoredRotatedShape();
+
+    CPPUNIT_TEST_SUITE(ScShapeTest);
+    CPPUNIT_TEST(testFitToCellSize);
+    CPPUNIT_TEST(testCustomShapeCellAnchoredRotatedShape);
+    CPPUNIT_TEST_SUITE_END();
+
+private:
+    uno::Reference<lang::XComponent> mxComponent;
+};
+
+ScShapeTest::ScShapeTest()
+    : CalcUnoApiTest("sc/qa/unit/data/ods")
+{
+}
+
+static OUString lcl_compareRectWithTolerance(const tools::Rectangle& rExpected,
+                                             const tools::Rectangle& rActual,
+                                             const sal_Int32 nTolerance)
+{
+    OUString sErrors;
+    if (labs(rExpected.Left() - rActual.Left()) > nTolerance)
+        sErrors += "\nLeft expected " + OUString::number(rExpected.Left()) + " actual "
+                   + OUString::number(rActual.Left()) + " Tolerance "
+                   + OUString::number(nTolerance);
+    if (labs(rExpected.Top() - rActual.Top()) > nTolerance)
+        sErrors += "\nTop expected " + OUString::number(rExpected.Top()) + " actual "
+                   + OUString::number(rActual.Top()) + " Tolerance " + OUString::number(nTolerance);
+    if (labs(rExpected.GetWidth() - rActual.GetWidth()) > nTolerance)
+        sErrors += "\nWidth expected " + OUString::number(rExpected.GetWidth()) + " actual "
+                   + OUString::number(rActual.GetWidth()) + " Tolerance "
+                   + OUString::number(nTolerance);
+    if (labs(rExpected.GetHeight() - rActual.GetHeight()) > nTolerance)
+        sErrors += "\nHeight expected " + OUString::number(rExpected.GetHeight()) + " actual "
+                   + OUString::number(rActual.GetHeight()) + " Tolerance "
+                   + OUString::number(nTolerance);
+    return sErrors;
+}
+
+void ScShapeTest::testFitToCellSize()
+{
+    // The document has a cell anchored custom shape. Applying
+    // FitToCellSize should resize and position the shape so,
+    // that it fits into its anchor cell. That did not happened.
+    OUString aFileURL;
+    createFileURL("tdf119191_FitToCellSize.ods", aFileURL);
+    uno::Reference<css::lang::XComponent> xComponent = loadFromDesktop(aFileURL);
+    CPPUNIT_ASSERT(xComponent.is());
+
+    // Get the document model
+    SfxObjectShell* pFoundShell = SfxObjectShell::GetShellFromComponent(xComponent);
+    CPPUNIT_ASSERT_MESSAGE("Failed to access document shell", pFoundShell);
+
+    ScDocShell* pDocSh = dynamic_cast<ScDocShell*>(pFoundShell);
+    CPPUNIT_ASSERT(pDocSh);
+
+    // Get the shape
+    ScDocument& rDoc = pDocSh->GetDocument();
+    ScDrawLayer* pDrawLayer = rDoc.GetDrawLayer();
+    CPPUNIT_ASSERT(pDrawLayer);
+
+    const SdrPage* pPage = pDrawLayer->GetPage(0);
+    CPPUNIT_ASSERT(pPage);
+
+    SdrObjCustomShape* pObj = dynamic_cast<SdrObjCustomShape*>(pPage->GetObj(0));
+    CPPUNIT_ASSERT(pObj);
+
+    // Get the document controller
+    ScTabViewShell* pViewShell = pDocSh->GetBestViewShell(false);
+    CPPUNIT_ASSERT(pViewShell);
+
+    // Get the draw view of the document
+    ScDrawView* pDrawView = pViewShell->GetViewData().GetScDrawView();
+    CPPUNIT_ASSERT(pDrawView);
+
+    // Select the shape
+    pDrawView->MarkNextObj();
+    CPPUNIT_ASSERT(pDrawView->AreObjectsMarked());
+
+    // Fit selected shape into cell
+    pViewShell->GetViewData().GetDispatcher().Execute(SID_FITCELLSIZE);
+
+    const tools::Rectangle& rShapeRect(pObj->GetSnapRect());
+    const tools::Rectangle aCellRect = rDoc.GetMMRect(1, 1, 1, 1, 0);
+    const OUString sErrors(lcl_compareRectWithTolerance(aCellRect, rShapeRect, 1));
+    CPPUNIT_ASSERT_EQUAL(OUString(), sErrors);
+
+    pDocSh->DoClose();
+}
+
+void ScShapeTest::testCustomShapeCellAnchoredRotatedShape()
+{
+    // The example doc contains a cell anchored custom shape that is rotated
+    // and sheared. Error was, that the shape lost position and size on
+    // loading.
+    OUString aFileURL;
+    createFileURL("tdf119191_transformedShape.ods", aFileURL);
+    uno::Reference<css::lang::XComponent> xComponent = loadFromDesktop(aFileURL);
+    CPPUNIT_ASSERT(xComponent.is());
+
+    // Get the document model
+    SfxObjectShell* pFoundShell = SfxObjectShell::GetShellFromComponent(xComponent);
+    CPPUNIT_ASSERT_MESSAGE("Failed to access document shell", pFoundShell);
+
+    ScDocShell* pDocSh = dynamic_cast<ScDocShell*>(pFoundShell);
+    CPPUNIT_ASSERT(pDocSh);
+
+    // Get the shape
+    ScDocument& rDoc = pDocSh->GetDocument();
+    ScDrawLayer* pDrawLayer = rDoc.GetDrawLayer();
+    CPPUNIT_ASSERT(pDrawLayer);
+
+    const SdrPage* pPage = pDrawLayer->GetPage(0);
+    CPPUNIT_ASSERT(pPage);
+
+    SdrObjCustomShape* pObj = dynamic_cast<SdrObjCustomShape*>(pPage->GetObj(0));
+    CPPUNIT_ASSERT(pObj);
+
+    // Check Position and Size
+    tools::Rectangle aRect(2406, 754, 5774, 3692); // expected snap rect
+    rDoc.SetDrawPageSize(0); // trigger recalcpos
+    const tools::Rectangle& rShapeRect(pObj->GetSnapRect());
+    const OUString sPosSizeErrors(lcl_compareRectWithTolerance(aRect, rShapeRect, 1));
+    CPPUNIT_ASSERT_EQUAL(OUString(), sPosSizeErrors);
+
+    // Check anchor
+    ScDrawObjData* pData = ScDrawLayer::GetObjData(pObj);
+    CPPUNIT_ASSERT_MESSAGE("expected object meta data", pData);
+
+    const OUString sExpected("start col 1 row 1 end col 2 row 8");
+    const OUString sActual("start col " + OUString::number(pData->maStart.Col()) + " row "
+                           + OUString::number(pData->maStart.Row()) + " end col "
+                           + OUString::number(pData->maEnd.Col()) + " row "
+                           + OUString::number(pData->maEnd.Row()));
+    CPPUNIT_ASSERT_EQUAL(sExpected, sActual);
+
+    pDocSh->DoClose();
+}
+
+void ScShapeTest::tearDown()
+{
+    if (mxComponent.is())
+    {
+        closeDocument(mxComponent);
+    }
+
+    CalcUnoApiTest::tearDown();
+}
+
+CPPUNIT_TEST_SUITE_REGISTRATION(ScShapeTest);
+}
+
+CPPUNIT_PLUGIN_IMPLEMENT();
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/core/data/drwlayer.cxx b/sc/source/core/data/drwlayer.cxx
index 4dd80df9a85a..515118d84c23 100644
--- a/sc/source/core/data/drwlayer.cxx
+++ b/sc/source/core/data/drwlayer.cxx
@@ -1038,7 +1038,10 @@ void ScDrawLayer::RecalcPos( SdrObject* pObj, ScDrawObjData& rData, bool bNegati
                 // order of these lines is important, modify rData.maLastRect carefully it is used as both
                 // a value and a flag for initialisation
                 rData.setShapeRect(GetDocument(), lcl_makeSafeRectangle(rData.getShapeRect()), pObj->IsVisible());
-                pObj->SetSnapRect(rData.getShapeRect());
+                if (pObj->GetObjIdentifier() == OBJ_CUSTOMSHAPE)
+                    pObj->AdjustToMaxRect(rData.getShapeRect());
+                else
+                    pObj->SetSnapRect(rData.getShapeRect());
                 // update 'unrotated anchor' it's the anchor we persist, it must be kept in sync
                 // with the normal Anchor
                 ResizeLastRectFromAnchor( pObj, rNoRotatedAnchor, true, bNegativePage, bCanResize );
diff --git a/sc/source/ui/view/drawvie4.cxx b/sc/source/ui/view/drawvie4.cxx
index e1f0d04da7a4..25c64c2e5ef3 100644
--- a/sc/source/ui/view/drawvie4.cxx
+++ b/sc/source/ui/view/drawvie4.cxx
@@ -569,8 +569,10 @@ void ScDrawView::FitToCellSize()
     }
 
     pUndoGroup->AddAction( std::make_unique<SdrUndoGeoObj>( *pObj ) );
-
-    pObj->SetSnapRect(aCellRect);
+    if (pObj->GetObjIdentifier() == OBJ_CUSTOMSHAPE)
+        pObj->AdjustToMaxRect(aCellRect);
+    else
+        pObj->SetSnapRect(aCellRect);
 
     pUndoGroup->SetComment(ScResId( STR_UNDO_FITCELLSIZE ));
     ScDocShell* pDocSh = pViewData->GetDocShell();
diff --git a/svx/source/svdraw/svdoashp.cxx b/svx/source/svdraw/svdoashp.cxx
index 7afb195499cd..7d78963ea192 100644
--- a/svx/source/svdraw/svdoashp.cxx
+++ b/svx/source/svdraw/svdoashp.cxx
@@ -91,6 +91,9 @@
 #include <basegfx/polygon/b2dpolypolygontools.hxx>
 #include <basegfx/matrix/b2dhommatrix.hxx>
 #include <basegfx/matrix/b2dhommatrixtools.hxx>
+#include <basegfx/polygon/b2dpolygon.hxx>
+#include <basegfx/polygon/b2dpolygontools.hxx>
+#include <basegfx/range/b2drange.hxx>
 #include <svdobjplusdata.hxx>
 #include "presetooxhandleadjustmentrelations.hxx"
 
@@ -2920,6 +2923,69 @@ void SdrObjCustomShape::RestGeoData(const SdrObjGeoData& rGeo)
     InvalidateRenderGeometry();
 }
 
+void SdrObjCustomShape::AdjustToMaxRect(const tools::Rectangle& rMaxRect, bool bShrinkOnly /* = false */)
+{
+    SAL_INFO_IF(bShrinkOnly, "svx", "Case bShrinkOnly == true is not implemented yet.");
+
+    if (rMaxRect.IsEmpty() || rMaxRect == GetSnapRect())
+        return;
+
+    // Get a matrix, that would produce the existing shape, when applied to a unit square
+    basegfx::B2DPolyPolygon aPolyPolygon; //not used, but formal needed
+    basegfx::B2DHomMatrix aMatrix;
+    TRGetBaseGeometry(aMatrix, aPolyPolygon);
+    // Using TRSetBaseGeometry(aMatrix, aPolyPolygon) would regenerate the current shape. But
+    // applying aMatrix to a unit square will not generate the current shape. Scaling,
+    // rotation and translation are correct, but shear angle has wrong sign. So break up
+    // matrix and create a mathematically correct new one.
+    basegfx::B2DTuple aScale;
+    basegfx::B2DTuple aTranslate;
+    double fRotate, fShearX;
+    aMatrix.decompose(aScale, aTranslate, fRotate, fShearX);
+    basegfx::B2DHomMatrix aMathMatrix;
+    aMathMatrix = basegfx::utils::createScaleShearXRotateTranslateB2DHomMatrix(
+            aScale,
+            basegfx::fTools::equalZero(fShearX) ? 0.0 : -fShearX,
+            basegfx::fTools::equalZero(fRotate) ? 0.0 : fRotate,
+            aTranslate);
+
+    // Calculate scaling factors from size of the transformed unit polygon as ersatz for the not
+    // usable current snap rectangle.
+    basegfx::B2DPolygon aB2DPolygon(basegfx::utils::createUnitPolygon());
+    aB2DPolygon.transform(aMathMatrix);
+    basegfx::B2DRange aB2DRange(aB2DPolygon.getB2DRange());
+    double fPolygonWidth = aB2DRange.getWidth();
+    if (fPolygonWidth == 0)
+        fPolygonWidth = 1;
+    double fPolygonHeight = aB2DRange.getHeight();
+    if (fPolygonHeight == 0)
+        fPolygonHeight = 1;
+    const double aFactorX = static_cast<double>(rMaxRect.GetWidth()) / fPolygonWidth;
+    const double aFactorY = static_cast<double>(rMaxRect.GetHeight()) / fPolygonHeight;
+
+    // Generate matrix, that would produce the desired rMaxRect when applied to unit square
+    aMathMatrix.scale(aFactorX, aFactorY);
+    aB2DPolygon = basegfx::utils::createUnitPolygon();
+    aB2DPolygon.transform(aMathMatrix);
+    aB2DRange = aB2DPolygon.getB2DRange();
+    const double fPolygonLeft = aB2DRange.getMinX();
+    const double fPolygonTop = aB2DRange.getMinY();
+    aMathMatrix.translate(rMaxRect.getX() - fPolygonLeft, rMaxRect.getY() - fPolygonTop);
+
+    // Create a Matrix from aMathMatrix, which is usable with TRSetBaseGeometry
+    aMathMatrix.decompose(aScale, aTranslate, fRotate, fShearX);
+    aMatrix = basegfx::utils::createScaleShearXRotateTranslateB2DHomMatrix(
+            aScale,
+            basegfx::fTools::equalZero(fShearX) ? 0.0 : -fShearX,
+            basegfx::fTools::equalZero(fRotate) ? 0.0 : fRotate,
+            aTranslate);
+
+    // Now use TRSetBaseGeometry to actually perform scale, shear, rotate and translate
+    // on the shape. That considers gluepoints, interaction handles and text area, and includes
+    // setting rectangles dirty and broadcoast.
+    TRSetBaseGeometry(aMatrix, aPolyPolygon);
+}
+
 void SdrObjCustomShape::TRSetBaseGeometry(const basegfx::B2DHomMatrix& rMatrix, const basegfx::B2DPolyPolygon& /*rPolyPolygon*/)
 {
     // break up matrix


More information about the Libreoffice-commits mailing list