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())); + } } }