diff options
-rw-r--r-- | svx/CppunitTest_svx_unit.mk | 1 | ||||
-rwxr-xr-x | svx/qa/unit/classicshapes.cxx | 189 | ||||
-rw-r--r-- | svx/qa/unit/data/tdf98583_ShearHorizontal.odp | bin | 0 -> 14328 bytes | |||
-rw-r--r-- | svx/qa/unit/data/tdf98584_ShearVertical.odg | bin | 0 -> 9109 bytes | |||
-rw-r--r-- | svx/source/svdraw/svdopath.cxx | 28 | ||||
-rw-r--r-- | xmloff/source/draw/xexptran.cxx | 10 | ||||
-rw-r--r-- | xmloff/source/draw/ximpshap.cxx | 53 |
7 files changed, 260 insertions, 21 deletions
diff --git a/svx/CppunitTest_svx_unit.mk b/svx/CppunitTest_svx_unit.mk index 8514f438c8f0..92feb45d6578 100644 --- a/svx/CppunitTest_svx_unit.mk +++ b/svx/CppunitTest_svx_unit.mk @@ -25,6 +25,7 @@ $(eval $(call gb_CppunitTest_set_include,svx_unit,\ $(eval $(call gb_CppunitTest_add_exception_objects,svx_unit, \ svx/qa/unit/svdraw/test_SdrTextObject \ svx/qa/unit/customshapes \ + svx/qa/unit/classicshapes \ svx/qa/unit/svdraw \ svx/qa/unit/unodraw \ svx/qa/unit/xoutdev \ diff --git a/svx/qa/unit/classicshapes.cxx b/svx/qa/unit/classicshapes.cxx new file mode 100755 index 000000000000..b03210d12c20 --- /dev/null +++ b/svx/qa/unit/classicshapes.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/bootstrapfixture.hxx> +#include <unotest/macros_test.hxx> +#include <rtl/ustring.hxx> +#include <editeng/unoprnms.hxx> + +#include <cppunit/TestAssert.h> + +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/drawing/XDrawPagesSupplier.hpp> +#include <com/sun/star/drawing/XDrawPage.hpp> +#include <com/sun/star/awt/Rectangle.hpp> +#include <com/sun/star/frame/Desktop.hpp> + +using namespace ::com::sun::star; + +namespace +{ +const OUString sDataDirectory("svx/qa/unit/data/"); + +/// Tests not about special features of custom shapes, but about shapes in general. +class ClassicshapesTest : public test::BootstrapFixture, public unotest::MacrosTest +{ +protected: + uno::Reference<lang::XComponent> mxComponent; + uno::Reference<drawing::XShape> getShape(sal_uInt8 nShapeIndex, sal_uInt8 nPageIndex); + +public: + virtual void setUp() override + { + test::BootstrapFixture::setUp(); + mxDesktop.set(frame::Desktop::create(m_xContext)); + } + + virtual void tearDown() override + { + if (mxComponent.is()) + { + mxComponent->dispose(); + } + test::BootstrapFixture::tearDown(); + } +}; + +uno::Reference<drawing::XShape> ClassicshapesTest::getShape(sal_uInt8 nShapeIndex, + sal_uInt8 nPageIndex) +{ + uno::Reference<drawing::XDrawPagesSupplier> xDrawPagesSupplier(mxComponent, + uno::UNO_QUERY_THROW); + CPPUNIT_ASSERT_MESSAGE("Could not get XDrawPagesSupplier", xDrawPagesSupplier.is()); + uno::Reference<drawing::XDrawPages> xDrawPages(xDrawPagesSupplier->getDrawPages()); + uno::Reference<drawing::XDrawPage> xDrawPage(xDrawPages->getByIndex(nPageIndex), + uno::UNO_QUERY_THROW); + CPPUNIT_ASSERT_MESSAGE("Could not get xDrawPage", xDrawPage.is()); + uno::Reference<drawing::XShape> xShape(xDrawPage->getByIndex(nShapeIndex), uno::UNO_QUERY); + CPPUNIT_ASSERT_MESSAGE("Could not get xShape", xShape.is()); + return xShape; +} + +CPPUNIT_TEST_FIXTURE(ClassicshapesTest, testTdf98584ShearVertical) +{ + // The document contains draw:rect, draw:polygon and draw:path objects. + // They are vertical sheared by skewY(-0.927295218002) or by matrix(1 2 0 1 1cm 1cm). + // Notice, skewY and matrix are interpreted on file open, but not written on file save. + // They are converted to rotate * shear horizontal * scale. + // Besides using a wrong sign in shear angle, error was, that TRSetGeometry of SdrPathObj did + // not consider the additional scaling (tdf#98565). + const OUString sFileName("tdf98584_ShearVertical.odg"); + const 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. + // All tests have a small tolerance for to avoid failing because of rounding errors. + + // Tests skewY + sal_Int32 nShearE = -5313; // expected angle for horizontal shear + sal_Int32 nRotE = 30687; // = -5313 expected angle for generated rotation + // expected width of shape, should not change on vertical shearing + sal_Int32 nWidthE = 5001; + + for (sal_uInt8 nPageIndex = 0; nPageIndex < 3; ++nPageIndex) + { + awt::Rectangle aFrameRect; + uno::Reference<drawing::XShape> xShape(getShape(0, nPageIndex)); + uno::Reference<beans::XPropertySet> xShapeProps(xShape, uno::UNO_QUERY); + CPPUNIT_ASSERT_MESSAGE("Could not get the shape properties", xShapeProps.is()); + xShapeProps->getPropertyValue(UNO_NAME_MISC_OBJ_FRAMERECT) >>= aFrameRect; + const sal_Int32 nWidthA(aFrameRect.Width); + if (abs(nWidthE - nWidthA) > 2) + sErrors += "skewY page " + OUString::number(nPageIndex) + " width expected " + + OUString::number(nWidthE) + ", actual " + OUString::number(nWidthA) + "\n"; + sal_Int32 nShearA(0); + xShapeProps->getPropertyValue(UNO_NAME_MISC_OBJ_SHEARANGLE) >>= nShearA; + if (abs(nShearE - nShearA) > 2) + sErrors += "skewY page" + OUString::number(nPageIndex) + " shear angle expected " + + OUString::number(nShearE) + ", actual " + OUString::number(nShearA) + "\n"; + sal_Int32 nRotA(0); + xShapeProps->getPropertyValue(UNO_NAME_MISC_OBJ_ROTATEANGLE) >>= nRotA; + if (abs(nRotE - nRotA) > 2) + sErrors += "skewY page" + OUString::number(nPageIndex) + " rotate angle expected " + + OUString::number(nRotE) + ", actual " + OUString::number(nRotA) + "\n"; + } + + // Tests matrix + nShearE = -6343; + nRotE = 29657; + nWidthE = 5001; + + for (sal_uInt8 nPageIndex = 3; nPageIndex < 6; ++nPageIndex) + { + awt::Rectangle aFrameRect; + uno::Reference<drawing::XShape> xShape(getShape(0, nPageIndex)); + uno::Reference<beans::XPropertySet> xShapeProps(xShape, uno::UNO_QUERY); + CPPUNIT_ASSERT_MESSAGE("Could not get the shape properties", xShapeProps.is()); + xShapeProps->getPropertyValue(UNO_NAME_MISC_OBJ_FRAMERECT) >>= aFrameRect; + const sal_Int32 nWidthA(aFrameRect.Width); + if (abs(nWidthE - nWidthA) > 2) + sErrors += "matrix page " + OUString::number(nPageIndex) + " width expected " + + OUString::number(nWidthE) + ", actual " + OUString::number(nWidthA) + "\n"; + sal_Int32 nShearA(0); + xShapeProps->getPropertyValue(UNO_NAME_MISC_OBJ_SHEARANGLE) >>= nShearA; + if (abs(nShearE - nShearA) > 2) + sErrors += "matrix page" + OUString::number(nPageIndex) + " shear angle expected " + + OUString::number(nShearE) + ", actual " + OUString::number(nShearA) + "\n"; + sal_Int32 nRotA(0); + xShapeProps->getPropertyValue(UNO_NAME_MISC_OBJ_ROTATEANGLE) >>= nRotA; + if (abs(nRotE - nRotA) > 2) + sErrors += "matrix page" + OUString::number(nPageIndex) + " rotate angle expected " + + OUString::number(nRotE) + ", actual " + OUString::number(nRotA) + "\n"; + } + + CPPUNIT_ASSERT_EQUAL(OUString(), sErrors); +} + +CPPUNIT_TEST_FIXTURE(ClassicshapesTest, testTdf98583ShearHorizontal) +{ + // The document contains rectangles with LT 3000,5000 and RB 5000,9000. + // skewX (-0.78539816339744830961) = skewX(-45deg) is applied on the first page + // matrix(1 0 1 1 0cm 0cm) on the second page. Both should result in a parallelogram with + // LT 8000,5000 and RB 14000, 9000, which means width 6001, height 4001. + // Error was, that not the mathematical matrix was used, but the API matrix, which has + // wrong sign in shear angle. + const OUString sFileName("tdf98583_ShearHorizontal.odp"); + const OUString sURL(m_directories.getURLFromSrc(sDataDirectory) + sFileName); + mxComponent = loadFromDesktop(sURL, "com.sun.star.comp.presentation.PresentationDocument"); + CPPUNIT_ASSERT_MESSAGE("Could not load document", mxComponent.is()); + + OUString sErrors; // sErrors collects the errors and should be empty in case all is OK. + // All tests have a small tolerance for to avoid failing because of rounding errors. + const sal_Int32 nLeftE(8000); // expected values + const sal_Int32 nTopE(5000); + const sal_Int32 nWidthE(6001); + const sal_Int32 nHeightE(4001); + for (sal_uInt8 nPageIndex = 0; nPageIndex < 2; ++nPageIndex) + { + awt::Rectangle aFrameRect; + uno::Reference<drawing::XShape> xShape(getShape(0, nPageIndex)); + uno::Reference<beans::XPropertySet> xShapeProps(xShape, uno::UNO_QUERY); + CPPUNIT_ASSERT_MESSAGE("Could not get the shape properties", xShapeProps.is()); + xShapeProps->getPropertyValue(UNO_NAME_MISC_OBJ_FRAMERECT) >>= aFrameRect; + const sal_Int32 nLeftA(aFrameRect.X); + const sal_Int32 nTopA(aFrameRect.Y); + const sal_Int32 nWidthA(aFrameRect.Width); + const sal_Int32 nHeightA(aFrameRect.Height); + if (abs(nLeftE - nLeftA) > 2 || abs(nTopE - nTopA) > 2) + sErrors += "page " + OUString::number(nPageIndex) + " LT expected " + + OUString::number(nLeftE) + " | " + OUString::number(nTopE) + ", actual " + + OUString::number(nLeftA) + " | " + OUString::number(nTopA) + "\n"; + if (abs(nWidthE - nWidthA) > 2 || abs(nHeightE - nHeightA) > 2) + sErrors += "page " + OUString::number(nPageIndex) + " width x height expected " + + OUString::number(nWidthE) + " x " + OUString::number(nHeightE) + + ", actual " + OUString::number(nWidthA) + " x " + + OUString::number(nHeightA) + "\n"; + } + + CPPUNIT_ASSERT_EQUAL(OUString(), sErrors); +} +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svx/qa/unit/data/tdf98583_ShearHorizontal.odp b/svx/qa/unit/data/tdf98583_ShearHorizontal.odp Binary files differnew file mode 100644 index 000000000000..37719e825c82 --- /dev/null +++ b/svx/qa/unit/data/tdf98583_ShearHorizontal.odp diff --git a/svx/qa/unit/data/tdf98584_ShearVertical.odg b/svx/qa/unit/data/tdf98584_ShearVertical.odg Binary files differnew file mode 100644 index 000000000000..457521d50394 --- /dev/null +++ b/svx/qa/unit/data/tdf98584_ShearVertical.odg diff --git a/svx/source/svdraw/svdopath.cxx b/svx/source/svdraw/svdopath.cxx index b586d83b3079..a897887ec708 100644 --- a/svx/source/svdraw/svdopath.cxx +++ b/svx/source/svdraw/svdopath.cxx @@ -2929,13 +2929,31 @@ void SdrPathObj::TRSetBaseGeometry(const basegfx::B2DHomMatrix& rMatrix, const b // #i75086# // Given polygon is already scaled (for historical reasons), but not mirrored yet. // Thus, when scale is negative in X or Y, apply the needed mirroring accordingly. - if(basegfx::fTools::less(aScale.getX(), 0.0) || basegfx::fTools::less(aScale.getY(), 0.0)) - { - aTransform.scale( - basegfx::fTools::less(aScale.getX(), 0.0) ? -1.0 : 1.0, - basegfx::fTools::less(aScale.getY(), 0.0) ? -1.0 : 1.0); + double fScaleX(basegfx::fTools::less(aScale.getX(), 0.0) ? -1.0 : 1.0); + double fScaleY(basegfx::fTools::less(aScale.getY(), 0.0) ? -1.0 : 1.0); + + // tdf#98565, tdf#98584. While loading a shape, svg:width and svg:height is used to scale + // the polygon. But draw:transform might introduce additional scaling factors, which need to + // be applied to the polygon too, so aScale cannot be ignored while loading. + // I use "maSnapRect.IsEmpty() && GetPathPoly().count()" to detect this case. Any better + // idea? The behavior in other cases is the same as it was before this fix. + if (maSnapRect.IsEmpty() && GetPathPoly().count()) + { + // In case of a Writer document, the scaling factors were converted to twips. That is not + // correct here, because width and height are already in the points coordinates and aScale + // is no length but only a factor here. Convert back. + if (getSdrModelFromSdrObject().IsWriter()) + { + aScale.setX(aScale.getX() * 127.0 / 72.0); + aScale.setY(aScale.getY() * 127.0 / 72.0); + } + fScaleX *= fabs(aScale.getX()); + fScaleY *= fabs(aScale.getY()); } + if (fScaleX != 1.0 || fScaleY != 1.0) + aTransform.scale(fScaleX, fScaleY); + if(!basegfx::fTools::equalZero(fShearX)) { aTransform.shearX(tan(-atan(fShearX))); diff --git a/xmloff/source/draw/xexptran.cxx b/xmloff/source/draw/xexptran.cxx index eb7cb4a634d5..0137166871bc 100644 --- a/xmloff/source/draw/xexptran.cxx +++ b/xmloff/source/draw/xexptran.cxx @@ -510,12 +510,18 @@ void SdXMLImExTransform2D::GetFullTransform(::basegfx::B2DHomMatrix& rFullTrans) } case IMP_SDXMLEXP_TRANSOBJ2D_SKEWX : { - rFullTrans.shearX(tan(static_cast<ImpSdXMLExpTransObj2DSkewX*>(pObj)->mfSkewX)); + // For to get a mathematical correct matrix from already existing documents, + // mirror the value here. ODF spec is unclear about direction. + rFullTrans.shearX(-tan(static_cast<ImpSdXMLExpTransObj2DSkewX*>(pObj)->mfSkewX)); break; } case IMP_SDXMLEXP_TRANSOBJ2D_SKEWY : { - rFullTrans.shearY(tan(static_cast<ImpSdXMLExpTransObj2DSkewY*>(pObj)->mfSkewY)); + // LibreOffice does not write skewY, OOo neither. Such files are foreign documents + // or manually set transformations. OOo had used the value as -tan(value) before + // errors were introduced, Scribus 1.5.4 uses it as -tan(value) too, MS Office does + // not shear at all. ODF spec is unclear about direction. + rFullTrans.shearY(-tan(static_cast<ImpSdXMLExpTransObj2DSkewY*>(pObj)->mfSkewY)); break; } case IMP_SDXMLEXP_TRANSOBJ2D_MATRIX : diff --git a/xmloff/source/draw/ximpshap.cxx b/xmloff/source/draw/ximpshap.cxx index 8bee4b376ba1..3594d1605363 100644 --- a/xmloff/source/draw/ximpshap.cxx +++ b/xmloff/source/draw/ximpshap.cxx @@ -575,21 +575,38 @@ void SdXMLShapeContext::SetTransformation() } // now set transformation for this object - drawing::HomogenMatrix3 aMatrix; - aMatrix.Line1.Column1 = maUsedTransformation.get(0, 0); - aMatrix.Line1.Column2 = maUsedTransformation.get(0, 1); - aMatrix.Line1.Column3 = maUsedTransformation.get(0, 2); + // maUsedTransformtion contains the mathematical correct matrix, which if + // applied to a unit square would generate the transformed shape. But the property + // "Transformation" contains a matrix, which can be used in TRSetBaseGeometry + // and would be created by TRGetBaseGeometry. And those use a mathematically wrong + // sign for the shearing angle. So we need to adapt the matrix here. + basegfx::B2DTuple aScale; + basegfx::B2DTuple aTranslate; + double fRotate; + double fShearX; + maUsedTransformation.decompose(aScale, aTranslate, fRotate, fShearX); + basegfx::B2DHomMatrix aB2DHomMatrix; + aB2DHomMatrix = basegfx::utils::createScaleShearXRotateTranslateB2DHomMatrix( + aScale, + basegfx::fTools::equalZero(fShearX) ? 0.0 : -fShearX, + basegfx::fTools::equalZero(fRotate) ? 0.0 : fRotate, + aTranslate); + drawing::HomogenMatrix3 aUnoMatrix; - aMatrix.Line2.Column1 = maUsedTransformation.get(1, 0); - aMatrix.Line2.Column2 = maUsedTransformation.get(1, 1); - aMatrix.Line2.Column3 = maUsedTransformation.get(1, 2); + aUnoMatrix.Line1.Column1 = aB2DHomMatrix.get(0, 0); + aUnoMatrix.Line1.Column2 = aB2DHomMatrix.get(0, 1); + aUnoMatrix.Line1.Column3 = aB2DHomMatrix.get(0, 2); - aMatrix.Line3.Column1 = maUsedTransformation.get(2, 0); - aMatrix.Line3.Column2 = maUsedTransformation.get(2, 1); - aMatrix.Line3.Column3 = maUsedTransformation.get(2, 2); + aUnoMatrix.Line2.Column1 = aB2DHomMatrix.get(1, 0); + aUnoMatrix.Line2.Column2 = aB2DHomMatrix.get(1, 1); + aUnoMatrix.Line2.Column3 = aB2DHomMatrix.get(1, 2); - xPropSet->setPropertyValue("Transformation", Any(aMatrix)); + aUnoMatrix.Line3.Column1 = aB2DHomMatrix.get(2, 0); + aUnoMatrix.Line3.Column2 = aB2DHomMatrix.get(2, 1); + aUnoMatrix.Line3.Column3 = aB2DHomMatrix.get(2, 2); + + xPropSet->setPropertyValue("Transformation", Any(aUnoMatrix)); } } } @@ -1093,9 +1110,9 @@ void SdXMLLineShapeContext::StartElement(const uno::Reference< xml::sax::XAttrib xPropSet->setPropertyValue("Geometry", Any(aPolyPoly)); } - // set sizes for transformation - maSize.Width = o3tl::saturating_sub(aBottomRight.X, aTopLeft.X); - maSize.Height = o3tl::saturating_sub(aBottomRight.Y, aTopLeft.Y); + // Size is included in point coordinates + maSize.Width = 1; + maSize.Height = 1; maPosition.X = aTopLeft.X; maPosition.Y = aTopLeft.Y; @@ -1322,6 +1339,10 @@ void SdXMLPolygonShapeContext::StartElement(const uno::Reference< xml::sax::XAtt css::drawing::PointSequenceSequence aPointSequenceSequence; basegfx::utils::B2DPolyPolygonToUnoPointSequenceSequence(basegfx::B2DPolyPolygon(aPolygon), aPointSequenceSequence); xPropSet->setPropertyValue("Geometry", Any(aPointSequenceSequence)); + // Size is now contained in the point coordinates, adapt maSize for + // to use the correct transformation matrix in SetTransformation() + maSize.Width = 1; + maSize.Height = 1; } } } @@ -1471,6 +1492,10 @@ void SdXMLPathShapeContext::StartElement(const uno::Reference< xml::sax::XAttrib } xPropSet->setPropertyValue("Geometry", aAny); + // Size is now contained in the point coordinates, adapt maSize for + // to use the correct transformation matrix in SetTransformation() + maSize.Width = 1; + maSize.Height = 1; } // set pos, size, shear and rotate |