sc/qa/unit/data/ods/tdf160369_groupshape.ods |binary
 sc/qa/unit/scshapetest.cxx                   |   68 +++++++++++++++++++++++++++
 sc/source/core/data/drwlayer.cxx             |    4 +
 sc/source/filter/xml/xmlexprt.cxx            |   41 +++++++++++-----
 4 files changed, 100 insertions(+), 13 deletions(-)

New commits:
commit 4e04b442e037a2f679b4cfe26c3e6b1c66ee8642
Author:     Regina Henschel <rb.hensc...@t-online.de>
AuthorDate: Sat Apr 6 14:59:38 2024 +0200
Commit:     Xisco Fauli <xiscofa...@libreoffice.org>
CommitDate: Mon Apr 8 17:31:42 2024 +0200

    tdf#160369 Do not broadcast temporarily group change
    
    The position and size of a group needs to be temporarily changed when
    saving because ODF does not treat hidden rows/cols as zero, but LO does.
    After saving, these changes have to be undone. The error was that the
    restore was performed with GetGeoDate/SetGeoData. But SetGeoData
    includes a broadcast that triggeres recalculations that should not be
    performed here. Now the change and restore are both done with NbcMove
    and NbcResize.
    
    The import had set a 'logical rectangle', but that is nonsense for a
    group, because a group does not have a 'logical rectangle'.
    For a group, none of the special corrections in
    ScDrawLayer::InitializeCellAnchoredObj are needed.
    
    Change-Id: I00adf39e83011112492822d2900e41242d188e84
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/165872
    Tested-by: Jenkins
    Reviewed-by: Regina Henschel <rb.hensc...@t-online.de>
    (cherry picked from commit 1e1b1d46155163380252093d9d2868351236ce0e)
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/165839
    Reviewed-by: Xisco Fauli <xiscofa...@libreoffice.org>

diff --git a/sc/qa/unit/data/ods/tdf160369_groupshape.ods 
b/sc/qa/unit/data/ods/tdf160369_groupshape.ods
new file mode 100644
index 000000000000..8c26fe8ce582
Binary files /dev/null and b/sc/qa/unit/data/ods/tdf160369_groupshape.ods differ
diff --git a/sc/qa/unit/scshapetest.cxx b/sc/qa/unit/scshapetest.cxx
index 5e4827005388..c5b4b098c80e 100644
--- a/sc/qa/unit/scshapetest.cxx
+++ b/sc/qa/unit/scshapetest.cxx
@@ -1231,6 +1231,74 @@ CPPUNIT_TEST_FIXTURE(ScShapeTest, 
testTdf160003_copy_page_anchored)
     CPPUNIT_ASSERT_EQUAL(size_t(1), pPage->GetObjCount());
 }
 
+CPPUNIT_TEST_FIXTURE(ScShapeTest, testTdf160369_groupshape)
+{
+    // The document contains a group spanning range C5:F12. It is currently 
anchored to page to
+    // make sure its position does not change. When the group was anchored 'To 
Cell' and rows or
+    // columns were hidden before the group, saving changed the anchor 
position and anchor
+    // offset. This happened both with using 'resize with cell' and not.
+    createScDoc("ods/tdf160369_groupshape.ods");
+
+    // Get document and group object
+    ScDocument* pDoc = getScDoc();
+    SdrObject* pObj = lcl_getSdrObjectWithAssert(*pDoc, 0);
+
+    // Anchor group 'To Cell (resize with cell)' to prepare the test.
+    ScDrawLayer::SetCellAnchoredFromPosition(*pObj, *pDoc, 0 /*SCTAB*/, true 
/*bResizeWithCell*/);
+
+    // Hide rows 3 and 4 (UI number), which are before the group
+    // Hide column D, which is inside the group
+    pDoc->SetRowHidden(2, 3, 0, true);
+    pDoc->SetDrawPageSize(0); // trigger recalcpos, otherwise shapes are not 
changed
+    pDoc->SetColHidden(3, 3, 0, true);
+    pDoc->SetDrawPageSize(0);
+
+    // Get geometry of the group
+    ScDrawObjData* pObjData = ScDrawLayer::GetObjData(pObj);
+    ScAddress aOrigStart = (*pObjData).maStart;
+    ScAddress aOrigEnd = (*pObjData).maEnd;
+    tools::Rectangle aOrigRect = pObj->GetSnapRect();
+
+    // Save document but do not reload. Saving alone had already caused the 
error.
+    save("calc8");
+
+    // Get geometry of the group again
+    ScDrawObjData* pAfterObjData = ScDrawLayer::GetObjData(pObj);
+    ScAddress aAfterStart = (*pAfterObjData).maStart;
+    ScAddress aAfterEnd = (*pAfterObjData).maEnd;
+    tools::Rectangle aAfterRect = pObj->GetSnapRect();
+
+    // verify Orig equals After
+    CPPUNIT_ASSERT_EQUAL(aOrigStart, aAfterStart);
+    CPPUNIT_ASSERT_EQUAL(aOrigEnd, aAfterEnd);
+    CPPUNIT_ASSERT_RECTANGLE_EQUAL_WITH_TOLERANCE(aOrigRect, aAfterRect, 1);
+
+    // The same but with saveAndReload.
+    createScDoc("ods/tdf160369_groupshape.ods");
+    pDoc = getScDoc();
+    pObj = lcl_getSdrObjectWithAssert(*pDoc, 0);
+    ScDrawLayer::SetCellAnchoredFromPosition(*pObj, *pDoc, 0 /*SCTAB*/, true 
/*bResizeWithCell*/);
+    pDoc->SetRowHidden(2, 3, 0, true);
+    pDoc->SetDrawPageSize(0); // trigger recalcpos, otherwise shapes are not 
changed
+    pDoc->SetColHidden(3, 3, 0, true);
+    pDoc->SetDrawPageSize(0);
+
+    saveAndReload("calc8");
+
+    // Verify geometry is same as before save
+    pDoc = getScDoc();
+    pObj = lcl_getSdrObjectWithAssert(*pDoc, 0);
+    pAfterObjData = ScDrawLayer::GetObjData(pObj);
+    aAfterStart = (*pAfterObjData).maStart;
+    aAfterEnd = (*pAfterObjData).maEnd;
+    aAfterRect = pObj->GetSnapRect();
+
+    // verify Orig equals After
+    CPPUNIT_ASSERT_EQUAL(aOrigStart, aAfterStart);
+    CPPUNIT_ASSERT_EQUAL(aOrigEnd, aAfterEnd);
+    CPPUNIT_ASSERT_RECTANGLE_EQUAL_WITH_TOLERANCE(aOrigRect, aAfterRect, 1);
+}
+
 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 d5f2cc09b979..d03f3572dc82 100644
--- a/sc/source/core/data/drwlayer.cxx
+++ b/sc/source/core/data/drwlayer.cxx
@@ -1055,6 +1055,10 @@ void ScDrawLayer::InitializeCellAnchoredObj(SdrObject* 
pObj, ScDrawObjData& rDat
             UpdateCellAnchorFromPositionEnd(*pObj, rNoRotatedAnchor, *pDoc, 
nTab1,
                                             true /*bUseLogicRect*/);
         }
+        else if (pObj->GetObjIdentifier() == SdrObjKind::Group)
+        {
+            // nothing to do.
+        }
         else
         {
             // In case there are hidden rows or cols, versions 7.0 and earlier 
have written width and
diff --git a/sc/source/filter/xml/xmlexprt.cxx 
b/sc/source/filter/xml/xmlexprt.cxx
index 9da5da65b7e6..74c9d093d546 100644
--- a/sc/source/filter/xml/xmlexprt.cxx
+++ b/sc/source/filter/xml/xmlexprt.cxx
@@ -3530,10 +3530,12 @@ void ScXMLExport::WriteShapes(const ScMyCell& rMyCell)
         // rectangle from the anchor as if all column/rows are shown. Then we 
move and resize
         // (in case of "resize with cell") the object to meet this snap 
rectangle. We need to
         // manipulate the object itself, because the used methods in xmloff do 
not evaluate the
-        // ObjData. This manipulation is only done temporarily for export. 
Thus we stash the geometry
-        // and restore it when export is done and we use NbcFoo methods.
-        bool bNeedsRestore = false;
-        std::unique_ptr<SdrObjGeoData> pGeoData = pObj->GetGeoData();
+        // ObjData. We remember the transformations and restore them later.
+        Point aMoveBy(0, 0);
+        bool bNeedsRestorePosition = false;
+        Fraction aScaleWidth(1, 1);
+        Fraction aScaleHeight(1, 1);
+        bool bNeedsRestoreSize = false;
 
         // Determine top point of fictive snap rectangle ('Full' rectangle).
         SCTAB aTab(aSnapStartAddress.Tab());
@@ -3554,8 +3556,8 @@ void ScXMLExport::WriteShapes(const ScMyCell& rMyCell)
         Point aActualTopPoint = bNegativePage ? aOrigSnapRect.TopRight() : 
aOrigSnapRect.TopLeft();
         if (aFullTopPoint != aActualTopPoint)
         {
-            bNeedsRestore = true;
-            Point aMoveBy = aFullTopPoint - aActualTopPoint;
+            bNeedsRestorePosition = true;
+            aMoveBy = aFullTopPoint - aActualTopPoint;
             pObj->NbcMove(Size(aMoveBy.X(), aMoveBy.Y()));
         }
 
@@ -3567,6 +3569,7 @@ void ScXMLExport::WriteShapes(const ScMyCell& rMyCell)
             // Object is anchored "To cell (resize with cell)". Compare size 
of actual snap rectangle
             // and fictive full one. Resize object accordingly.
             tools::Rectangle aActualSnapRect(pObj->GetSnapRect());
+
             Point aSnapEndOffset(pObjData->maEndOffset);
             aCol = aSnapEndAddress.Col();
             aRow = aSnapEndAddress.Row();
@@ -3583,12 +3586,13 @@ void ScXMLExport::WriteShapes(const ScMyCell& rMyCell)
 
             if (aFullSnapRect != aActualSnapRect)
             {
-                bNeedsRestore = true;
-                Fraction aScaleWidth(aFullSnapRect.getOpenWidth(), 
aActualSnapRect.getOpenWidth());
+                bNeedsRestoreSize = true;
+                aScaleWidth
+                    = Fraction(aFullSnapRect.getOpenWidth(), 
aActualSnapRect.getOpenWidth());
                 if (!aScaleWidth.IsValid())
                     aScaleWidth = Fraction(1, 1);
-                Fraction aScaleHeight(aFullSnapRect.getOpenHeight(),
-                                      aActualSnapRect.getOpenHeight());
+                aScaleHeight
+                    = Fraction(aFullSnapRect.getOpenHeight(), 
aActualSnapRect.getOpenHeight());
                 if (!aScaleHeight.IsValid())
                     aScaleHeight = Fraction(1, 1);
                 pObj->NbcResize(aFullTopPoint, aScaleWidth, aScaleHeight);
@@ -3636,9 +3640,20 @@ void ScXMLExport::WriteShapes(const ScMyCell& rMyCell)
 
         ExportShape(rShape.xShape, &aPoint);
 
-        // Restore object geometry
-        if (bNeedsRestore && pGeoData)
-            pObj->SetGeoData(*pGeoData);
+        if (bNeedsRestoreSize)
+        {
+            Fraction aScaleWidthInvers(aScaleWidth.GetDenominator(), 
aScaleWidth.GetNumerator());
+            if (!aScaleWidthInvers.IsValid())
+                aScaleWidthInvers = Fraction(1, 1);
+            Fraction aScaleHeightInvers(aScaleHeight.GetDenominator(), 
aScaleHeight.GetNumerator());
+            if (!aScaleHeightInvers.IsValid())
+                aScaleHeightInvers = Fraction(1, 1);
+            pObj->NbcResize(aFullTopPoint, aScaleWidthInvers, 
aScaleHeightInvers);
+        }
+        if (bNeedsRestorePosition)
+        {
+            pObj->NbcMove(Size(-aMoveBy.X(), -aMoveBy.Y()));
+        }
     }
 }
 

Reply via email to