sd/qa/unit/data/tdf98839_ShearVFlipH.odg |binary sd/qa/unit/misc-tests.cxx | 31 ++++++++++++- svx/qa/unit/customshapes.cxx | 40 +++++++++++++++++ svx/qa/unit/data/tdf129532_MatrixFlipV.odg |binary svx/source/svdraw/svdoashp.cxx | 65 ++++++++++++++++++++++------ xmloff/source/draw/ximpshap.cxx | 67 +++++++++++++++++------------ 6 files changed, 162 insertions(+), 41 deletions(-)
New commits: commit b4a6977e05d87fe0a79b266ec30e4f403404f1b4 Author: Regina Henschel <rb.hensc...@t-online.de> AuthorDate: Sat Dec 21 22:32:55 2019 +0100 Commit: Tomaž Vajngerl <qui...@gmail.com> CommitDate: Wed Feb 26 12:04:11 2020 +0100 tdf#129532 tdf#98839 fixes for mirror of custom shapes tdf#98839 In case a sheared custom shape was mirrored, the shear angle in draw:transform had a wrong sign in the saved file. tdf#129532 Mirroring given in draw:transform in file or via macro was wrongly applied. Errors: 1)Mirroring from draw:transform attribute had overwritten already existing mirroring in the enhanced-geometry. 2)Mirroring from draw:transform attribute was set in enhanced- geometry attributes but not really applied. Change-Id: Ifa52f3606b5a33e6492a02d6e19c883d28752da8 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/85670 Tested-by: Jenkins Reviewed-by: Tomaž Vajngerl <qui...@gmail.com> diff --git a/sd/qa/unit/data/tdf98839_ShearVFlipH.odg b/sd/qa/unit/data/tdf98839_ShearVFlipH.odg new file mode 100644 index 000000000000..88763f8d3ccf Binary files /dev/null and b/sd/qa/unit/data/tdf98839_ShearVFlipH.odg differ diff --git a/sd/qa/unit/misc-tests.cxx b/sd/qa/unit/misc-tests.cxx index 9bd6f8a64a55..852cdc03ece6 100644 --- a/sd/qa/unit/misc-tests.cxx +++ b/sd/qa/unit/misc-tests.cxx @@ -53,7 +53,8 @@ #include <vcl/window.hxx> #include <vcl/event.hxx> #include <vcl/keycodes.hxx> - +#include <svx/svdoashp.hxx> +#include <tools/gen.hxx> using namespace ::com::sun::star; @@ -76,6 +77,7 @@ public: void testTdf67248(); void testTdf119956(); void testTdf120527(); + void testTdf98839_ShearVFlipH(); CPPUNIT_TEST_SUITE(SdMiscTest); CPPUNIT_TEST(testTdf96206); @@ -93,6 +95,7 @@ public: CPPUNIT_TEST(testTdf67248); CPPUNIT_TEST(testTdf119956); CPPUNIT_TEST(testTdf120527); + CPPUNIT_TEST(testTdf98839_ShearVFlipH); CPPUNIT_TEST_SUITE_END(); virtual void registerNamespaces(xmlXPathContextPtr& pXmlXPathCtx) override @@ -773,6 +776,32 @@ void SdMiscTest::testTdf119956() xDocShRef->DoClose(); } +void SdMiscTest::testTdf98839_ShearVFlipH() +{ + // Loads a document with a sheared shape and mirrors it + const OUString sURL = "sd/qa/unit/data/tdf98839_ShearVFlipH.odg"; + sd::DrawDocShellRef xDocShRef = Load(m_directories.getURLFromSrc(sURL), ODG); + sd::GraphicViewShell* pViewShell = static_cast<sd::GraphicViewShell*>(xDocShRef->GetViewShell()); + SdPage* pPage = pViewShell->GetActualPage(); + SdrObjCustomShape* pShape = static_cast<SdrObjCustomShape*>(pPage->GetObj(0)); + pShape->Mirror(Point(4000, 2000), Point(4000, 10000)); + + // Save and examine attribute draw:transform + utl::TempFile aTempFile; + aTempFile.EnableKillingFile(); + save(xDocShRef.get(), getFormat(ODG), aTempFile); + xmlDocPtr pXmlDoc = parseExport(aTempFile, "content.xml"); + CPPUNIT_ASSERT_MESSAGE("Failed to get 'content.xml'", pXmlDoc); + const OString sPathStart("/office:document-content/office:body/office:drawing/draw:page"); + assertXPath(pXmlDoc, sPathStart); + const OUString sTransform = getXPath(pXmlDoc, sPathStart + "/draw:custom-shape","transform"); + + // Error was, that the shear angle had a wrong sign. + CPPUNIT_ASSERT_MESSAGE("expected: draw:transform='skewX (-0.64350...)", sTransform.startsWith("skewX (-")); + + xDocShRef->DoClose(); +} + CPPUNIT_TEST_SUITE_REGISTRATION(SdMiscTest); CPPUNIT_PLUGIN_IMPLEMENT(); diff --git a/svx/qa/unit/customshapes.cxx b/svx/qa/unit/customshapes.cxx index 796fa4824809..a21652113ef8 100644 --- a/svx/qa/unit/customshapes.cxx +++ b/svx/qa/unit/customshapes.cxx @@ -631,6 +631,46 @@ CPPUNIT_TEST_FIXTURE(CustomshapesTest, testTdf128413_tbrlOnOff) } CPPUNIT_ASSERT_EQUAL(OUString(), sError); } + +CPPUNIT_TEST_FIXTURE(CustomshapesTest, testTdf129532_MatrixFlipV) +{ + // The document contains two rotated shapes with the same geometry. For one of them + // "matrix(1 0 0 -1 0cm 0cm)" was manually added to the value of the draw:transform + // attribute. That should result in mirroring on the x-axis. Error was, that the lines + // which are drawn on the shape rectangle were mirrored, but not the rectangle itself. + // The rectangle was only shifted. + const OUString sFileName("tdf129532_MatrixFlipV.odg"); + OUString sURL = m_directories.getURLFromSrc(sDataDirectory) + sFileName; + mxComponent = loadFromDesktop(sURL, "com.sun.star.comp.drawing.DrawingDocument"); + CPPUNIT_ASSERT_MESSAGE("Could not load document", mxComponent.is()); + OUString sErrors; // sErrors collects the errors and should be empty in case all is OK. + + uno::Reference<drawing::XShape> xShape0(getShape(0)); + uno::Reference<beans::XPropertySet> xShape0Props(xShape0, uno::UNO_QUERY); + CPPUNIT_ASSERT_MESSAGE("Could not get the shape properties", xShape0Props.is()); + awt::Rectangle aBoundRect0; + xShape0Props->getPropertyValue(UNO_NAME_MISC_OBJ_BOUNDRECT) >>= aBoundRect0; + + uno::Reference<drawing::XShape> xShape1(getShape(1)); + uno::Reference<beans::XPropertySet> xShape1Props(xShape1, uno::UNO_QUERY); + CPPUNIT_ASSERT_MESSAGE("Could not get the shape properties", xShape1Props.is()); + awt::Rectangle aBoundRect1; + xShape1Props->getPropertyValue(UNO_NAME_MISC_OBJ_BOUNDRECT) >>= aBoundRect1; + + // The size of the two BoundRect rectangles are the same in case of correct + // vertical mirroring. + if (aBoundRect0.Width != aBoundRect1.Width) + { + sErrors += "\n Width expected: " + OUString::number(aBoundRect1.Width) + + " actual: " + OUString::number(aBoundRect0.Width); + } + if (aBoundRect0.Height != aBoundRect1.Height) + { + sErrors += "\n Height expected: " + OUString::number(aBoundRect1.Height) + + " actual: " + OUString::number(aBoundRect0.Height); + } + CPPUNIT_ASSERT_EQUAL(OUString(), sErrors); +} } /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svx/qa/unit/data/tdf129532_MatrixFlipV.odg b/svx/qa/unit/data/tdf129532_MatrixFlipV.odg new file mode 100644 index 000000000000..eb0c10b3d417 Binary files /dev/null and b/svx/qa/unit/data/tdf129532_MatrixFlipV.odg differ diff --git a/svx/source/svdraw/svdoashp.cxx b/svx/source/svdraw/svdoashp.cxx index 367e6a3ac24f..2412213f2992 100644 --- a/svx/source/svdraw/svdoashp.cxx +++ b/svx/source/svdraw/svdoashp.cxx @@ -2987,21 +2987,29 @@ void SdrObjCustomShape::AdjustToMaxRect(const tools::Rectangle& rMaxRect, bool b void SdrObjCustomShape::TRSetBaseGeometry(const basegfx::B2DHomMatrix& rMatrix, const basegfx::B2DPolyPolygon& /*rPolyPolygon*/) { + // The shape might have already flipping in its enhanced geometry. LibreOffice applies + // such after all transformations. We remove it, but remember it to apply them later. + bool bIsMirroredX = IsMirroredX(); + bool bIsMirroredY = IsMirroredY(); + if (bIsMirroredX || bIsMirroredY) + { + Point aCurrentCenter = GetSnapRect().Center(); + if (bIsMirroredX) // mirror on the y-axis + { + Mirror(aCurrentCenter, Point(aCurrentCenter.X(), aCurrentCenter.Y() + 1000)); + } + if (bIsMirroredY) // mirror on the x-axis + { + Mirror(aCurrentCenter, Point(aCurrentCenter.X() + 1000, aCurrentCenter.Y())); + } + } + // break up matrix basegfx::B2DTuple aScale; basegfx::B2DTuple aTranslate; double fRotate, fShearX; rMatrix.decompose(aScale, aTranslate, fRotate, fShearX); - // #i75086# Old DrawingLayer (GeoStat and geometry) does not support holding negative scalings - // in X and Y which equal a 180 degree rotation. Recognize it and react accordingly - if(basegfx::fTools::less(aScale.getX(), 0.0) && basegfx::fTools::less(aScale.getY(), 0.0)) - { - aScale.setX(fabs(aScale.getX())); - aScale.setY(fabs(aScale.getY())); - fRotate = fmod(fRotate + F_PI, F_2PI); - } - // reset object shear and rotations fObjectRotation = 0.0; aGeo.nRotationAngle = 0; @@ -3018,14 +3026,19 @@ void SdrObjCustomShape::TRSetBaseGeometry(const basegfx::B2DHomMatrix& rMatrix, } } - // build and set BaseRect (use scale) - Size aSize(FRound(aScale.getX()), FRound(aScale.getY())); + // scale + Size aSize(FRound(fabs(aScale.getX())), FRound(fabs(aScale.getY()))); // fdo#47434 We need a valid rectangle here if( !aSize.Height() ) aSize.setHeight( 1 ); if( !aSize.Width() ) aSize.setWidth( 1 ); - tools::Rectangle aBaseRect(Point(), aSize); - SetSnapRect(aBaseRect); + SetLogicRect(aBaseRect); + + // Apply flipping from Matrix, which is a transformation relative to origin + if (basegfx::fTools::less(aScale.getX(), 0.0)) + Mirror(Point(0, 0), Point(0, 1000)); // mirror on the y-axis + if (basegfx::fTools::less(aScale.getY(), 0.0)) + Mirror(Point(0, 0), Point(1000, 0)); // mirror on the x-axis // shear? if(!basegfx::fTools::equalZero(fShearX)) @@ -3057,6 +3070,30 @@ void SdrObjCustomShape::TRSetBaseGeometry(const basegfx::B2DHomMatrix& rMatrix, { Move(Size(FRound(aTranslate.getX()), FRound(aTranslate.getY()))); } + + // Apply flipping from enhanced geometry at center of the shape. + if (bIsMirroredX || bIsMirroredY) + { + // create mathematically matrix for the applied transformations + // aScale was in most cases built from a rectangle including edge + // and is therefore mathematically too large by 1 + if (aScale.getX() > 2.0 && aScale.getY() > 2.0) + aScale -= basegfx::B2DTuple(1.0, 1.0); + basegfx::B2DHomMatrix aMathMat = basegfx::utils::createScaleShearXRotateTranslateB2DHomMatrix( + aScale, -fShearX, basegfx::fTools::equalZero(fRotate) ? 0.0 : fRotate, + aTranslate); + // Use matrix to get current center + basegfx::B2DPoint aCenter(0.5,0.5); + aCenter = aMathMat * aCenter; + double fCenterX = aCenter.getX(); + double fCenterY = aCenter.getY(); + if (bIsMirroredX) // vertical axis + Mirror(Point(FRound(fCenterX),FRound(fCenterY)), + Point(FRound(fCenterX), FRound(fCenterY + 1000.0))); + if (bIsMirroredY) // horizontal axis + Mirror(Point(FRound(fCenterX),FRound(fCenterY)), + Point(FRound(fCenterX + 1000.0), FRound(fCenterY))); + } } // taking fObjectRotation instead of aGeo.nAngle @@ -3078,6 +3115,7 @@ bool SdrObjCustomShape::TRGetBaseGeometry(basegfx::B2DHomMatrix& rMatrix, basegf if ( bMirroredX ) { + fShearX = -fShearX; tools::Polygon aPol = Rect2Poly(maRect, aNewGeo); tools::Rectangle aBoundRect( aPol.GetBoundRect() ); @@ -3100,6 +3138,7 @@ bool SdrObjCustomShape::TRGetBaseGeometry(basegfx::B2DHomMatrix& rMatrix, basegf } if ( bMirroredY ) { + fShearX = -fShearX; tools::Polygon aPol( Rect2Poly( aRectangle, aNewGeo ) ); tools::Rectangle aBoundRect( aPol.GetBoundRect() ); diff --git a/xmloff/source/draw/ximpshap.cxx b/xmloff/source/draw/ximpshap.cxx index 419c0174ae81..f0a8a169842a 100644 --- a/xmloff/source/draw/ximpshap.cxx +++ b/xmloff/source/draw/ximpshap.cxx @@ -3755,11 +3755,9 @@ void SdXMLCustomShapeContext::StartElement( const uno::Reference< xml::sax::XAtt void SdXMLCustomShapeContext::EndElement() { - // for backward compatibility, the above SetTransformation() may already have - // applied a call to SetMirroredX/SetMirroredY. This is not yet added to the - // beans::PropertyValues in maCustomShapeGeometry. When applying these now, this - // would be lost again. - // TTTT: Remove again after aw080 + // Customshapes remember mirror state in its enhanced geometry. + // SetTransformation() in StartElement() may have applied mirroring, but that is not yet + // contained. Merge that information here before writing the property. if(!maUsedTransformation.isIdentity()) { basegfx::B2DVector aScale, aTranslate; @@ -3767,43 +3765,58 @@ void SdXMLCustomShapeContext::EndElement() maUsedTransformation.decompose(aScale, aTranslate, fRotate, fShearX); - bool bFlippedX(aScale.getX() < 0.0); - bool bFlippedY(aScale.getY() < 0.0); - - if(bFlippedX && bFlippedY) + if (aScale.getX() < 0.0) { - // when both are used it is the same as 180 degree rotation; reset - bFlippedX = bFlippedY = false; + const OUString sName("MirroredX"); + //fdo#84043 Merge, if property exists, otherwise append it + auto aI = std::find_if(maCustomShapeGeometry.begin(), maCustomShapeGeometry.end(), + [&sName](beans::PropertyValue& rValue) { return rValue.Name == sName; }); + if (aI != maCustomShapeGeometry.end()) + { + beans::PropertyValue& rItem = *aI; + bool bMirroredX; + rItem.Value >>= bMirroredX; + rItem.Value <<= !bMirroredX; + rItem.Handle = -1; + rItem.State = beans::PropertyState_DIRECT_VALUE; + } + else + { + beans::PropertyValue* pItem; + maCustomShapeGeometry.emplace_back(); + pItem = &maCustomShapeGeometry.back(); + pItem->Name = sName; + pItem->Handle = -1; + pItem->Value <<= true; + pItem->State = beans::PropertyState_DIRECT_VALUE; + } } - if(bFlippedX || bFlippedY) + if (aScale.getY() < 0.0) { - OUString sName; - - if(bFlippedX) - sName = "MirroredX"; - else - sName = "MirroredY"; - - //fdo#84043 overwrite the property if it already exists, otherwise append it - beans::PropertyValue* pItem; + const OUString sName("MirroredY"); + //fdo#84043 Merge, if property exists, otherwise append it auto aI = std::find_if(maCustomShapeGeometry.begin(), maCustomShapeGeometry.end(), [&sName](beans::PropertyValue& rValue) { return rValue.Name == sName; }); if (aI != maCustomShapeGeometry.end()) { beans::PropertyValue& rItem = *aI; - pItem = &rItem; + bool bMirroredY; + rItem.Value >>= bMirroredY; + rItem.Value <<= !bMirroredY; + rItem.Handle = -1; + rItem.State = beans::PropertyState_DIRECT_VALUE; } else { + beans::PropertyValue* pItem; maCustomShapeGeometry.emplace_back(); pItem = &maCustomShapeGeometry.back(); + pItem->Name = sName; + pItem->Handle = -1; + pItem->Value <<= true; + pItem->State = beans::PropertyState_DIRECT_VALUE; } - - pItem->Name = sName; - pItem->Handle = -1; - pItem->Value <<= true; - pItem->State = beans::PropertyState_DIRECT_VALUE; } } _______________________________________________ Libreoffice-commits mailing list libreoffice-comm...@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/libreoffice-commits