summaryrefslogtreecommitdiff
path: root/writerfilter
diff options
context:
space:
mode:
authorRegina Henschel <rb.henschel@t-online.de>2021-04-11 13:03:27 +0200
committerXisco Fauli <xiscofauli@libreoffice.org>2021-05-07 00:07:54 +0200
commite72b36434dc299d0fb3ecc97045fbb9ca2b9abf8 (patch)
treebf5766adce19e7c1782aec5672d1220a7269f9b5 /writerfilter
parent6eac9d960f2bb5dae54ef6ca8e7c659e8c79d011 (diff)
tdf#141540 fix docx import of group or line with rotation
... and fix case wrap 'Square' and 'in Line' with them. Non-uniform scaling of a rotated shape might produce skew. Such had happened, when setting group or line to the size contained in GraphicImport. Avoid it. Writer has special rules for shape position and marging in case of wrap 'Square' and 'in Line', depending on rotation angle. The patch adds the needed margins. The patch changes some unit tests where we now get slightly different values. The patch fixes the wrong skew in sample document of tdf#73022. Change-Id: Ic743790c3fc8b8b10a4324d9e0184ad945cdceb6 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/114193 Tested-by: Jenkins Reviewed-by: Miklos Vajna <vmiklos@collabora.com> (cherry picked from commit 2a70cfb09c4d89154d229b6a95cf076e8bd76798) Reviewed-on: https://gerrit.libreoffice.org/c/core/+/115195 Reviewed-by: Xisco Fauli <xiscofauli@libreoffice.org>
Diffstat (limited to 'writerfilter')
-rw-r--r--writerfilter/qa/cppunittests/dmapper/GraphicImport.cxx65
-rw-r--r--writerfilter/qa/cppunittests/dmapper/data/tdf141540ChildRotation.docxbin0 -> 15385 bytes
-rw-r--r--writerfilter/qa/cppunittests/dmapper/data/tdf141540GroupLinePosSize.docxbin0 -> 19457 bytes
-rw-r--r--writerfilter/qa/cppunittests/dmapper/data/tdf141540GroupRotation.docxbin0 -> 18803 bytes
-rw-r--r--writerfilter/source/dmapper/GraphicImport.cxx120
5 files changed, 179 insertions, 6 deletions
diff --git a/writerfilter/qa/cppunittests/dmapper/GraphicImport.cxx b/writerfilter/qa/cppunittests/dmapper/GraphicImport.cxx
index b5cb8794a59c..456b7509e2c9 100644
--- a/writerfilter/qa/cppunittests/dmapper/GraphicImport.cxx
+++ b/writerfilter/qa/cppunittests/dmapper/GraphicImport.cxx
@@ -10,6 +10,8 @@
#include <test/bootstrapfixture.hxx>
#include <unotest/macros_test.hxx>
+#include <com/sun/star/awt/Point.hpp>
+#include <com/sun/star/awt/Size.hpp>
#include <com/sun/star/beans/XPropertySet.hpp>
#include <com/sun/star/drawing/XDrawPageSupplier.hpp>
#include <com/sun/star/frame/Desktop.hpp>
@@ -52,6 +54,69 @@ void Test::tearDown()
char const DATA_DIRECTORY[] = "/writerfilter/qa/cppunittests/dmapper/data/";
+CPPUNIT_TEST_FIXTURE(Test, testTdf141540ChildRotation)
+{
+ OUString aURL = m_directories.getURLFromSrc(DATA_DIRECTORY) + "tdf141540ChildRotation.docx";
+ getComponent() = loadFromDesktop(aURL);
+ uno::Reference<drawing::XDrawPageSupplier> xDrawPageSupplier(getComponent(), uno::UNO_QUERY);
+ uno::Reference<drawing::XDrawPage> xDrawPage = xDrawPageSupplier->getDrawPage();
+ uno::Reference<container::XIndexAccess> xGroup(xDrawPage->getByIndex(0), uno::UNO_QUERY_THROW);
+ uno::Reference<beans::XPropertySet> xRotatedShape(xGroup->getByIndex(1), uno::UNO_QUERY);
+ sal_Int32 nShearAngle = 9000; // initialize with invalid value
+ xRotatedShape->getPropertyValue("ShearAngle") >>= nShearAngle;
+ // Without fix in place, this test would have failed with:
+ // - Expected: 0
+ // - Actual : 2494
+ // i.e. the rotated rectangle in the group was sheared, although the group itself is not rotated
+ CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32>(0), nShearAngle);
+}
+
+CPPUNIT_TEST_FIXTURE(Test, testTdf141540GroupRotation)
+{
+ OUString aURL = m_directories.getURLFromSrc(DATA_DIRECTORY) + "tdf141540GroupRotation.docx";
+ getComponent() = loadFromDesktop(aURL);
+ uno::Reference<drawing::XDrawPageSupplier> xDrawPageSupplier(getComponent(), uno::UNO_QUERY);
+ uno::Reference<drawing::XDrawPage> xDrawPage = xDrawPageSupplier->getDrawPage();
+ uno::Reference<beans::XPropertySet> xShape(xDrawPage->getByIndex(0), uno::UNO_QUERY);
+ sal_Int32 nShearAngle = 9000; // init with invalid value
+ xShape->getPropertyValue("ShearAngle") >>= nShearAngle;
+ // Without fix in place, this test would have failed with:
+ // - Expected: 0
+ // - Actual : -3190
+ // i.e. the group has got a shearing although MSO does not know shearing at all.
+ CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32>(0), nShearAngle);
+}
+
+CPPUNIT_TEST_FIXTURE(Test, testTdf141540GroupLinePosSize)
+{
+ OUString aURL = m_directories.getURLFromSrc(DATA_DIRECTORY) + "tdf141540GroupLinePosSize.docx";
+ getComponent() = loadFromDesktop(aURL);
+ uno::Reference<drawing::XDrawPageSupplier> xDrawPageSupplier(getComponent(), uno::UNO_QUERY);
+ uno::Reference<drawing::XDrawPage> xDrawPage = xDrawPageSupplier->getDrawPage();
+
+ // Test line
+ uno::Reference<drawing::XShape> xLineShape(xDrawPage->getByIndex(0), uno::UNO_QUERY);
+ awt::Point aPosition = xLineShape->getPosition();
+ awt::Size aSize = xLineShape->getSize();
+ // Without fix in place, you had got Position = (19|6498), Size = 5001 x 2
+ // i.e. the line was nearly horizontal instead of vertical
+ CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32>(5022), aPosition.X);
+ CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32>(2963), aPosition.Y);
+ CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32>(0), aSize.Width);
+ CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32>(7073), aSize.Height);
+
+ // Test group
+ uno::Reference<drawing::XShape> xGroupShape(xDrawPage->getByIndex(1), uno::UNO_QUERY);
+ aPosition = xGroupShape->getPosition();
+ aSize = xGroupShape->getSize();
+ // Without fix in place, you had got Position = (11511|3480), Size = 4022 x 4022
+ // i.e. the group was erroneously downscaled to unrotated size
+ CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32>(10679), aPosition.X);
+ CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32>(2648), aPosition.Y);
+ CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32>(5687), aSize.Width);
+ CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32>(5687), aSize.Height);
+}
+
CPPUNIT_TEST_FIXTURE(Test, testGroupShapeRotation)
{
OUString aURL = m_directories.getURLFromSrc(DATA_DIRECTORY) + "group-shape-rotation.docx";
diff --git a/writerfilter/qa/cppunittests/dmapper/data/tdf141540ChildRotation.docx b/writerfilter/qa/cppunittests/dmapper/data/tdf141540ChildRotation.docx
new file mode 100644
index 000000000000..902bb6192afe
--- /dev/null
+++ b/writerfilter/qa/cppunittests/dmapper/data/tdf141540ChildRotation.docx
Binary files differ
diff --git a/writerfilter/qa/cppunittests/dmapper/data/tdf141540GroupLinePosSize.docx b/writerfilter/qa/cppunittests/dmapper/data/tdf141540GroupLinePosSize.docx
new file mode 100644
index 000000000000..d0ceff118eab
--- /dev/null
+++ b/writerfilter/qa/cppunittests/dmapper/data/tdf141540GroupLinePosSize.docx
Binary files differ
diff --git a/writerfilter/qa/cppunittests/dmapper/data/tdf141540GroupRotation.docx b/writerfilter/qa/cppunittests/dmapper/data/tdf141540GroupRotation.docx
new file mode 100644
index 000000000000..13e65c1d122a
--- /dev/null
+++ b/writerfilter/qa/cppunittests/dmapper/data/tdf141540GroupRotation.docx
Binary files differ
diff --git a/writerfilter/source/dmapper/GraphicImport.cxx b/writerfilter/source/dmapper/GraphicImport.cxx
index 5fb9a3499de3..6d0d4113e037 100644
--- a/writerfilter/source/dmapper/GraphicImport.cxx
+++ b/writerfilter/source/dmapper/GraphicImport.cxx
@@ -43,7 +43,9 @@
#include <com/sun/star/uno/XComponentContext.hpp>
#include <com/sun/star/table/ShadowFormat.hpp>
+#include <svx/svditer.hxx>
#include <svx/svdobj.hxx>
+#include <svx/svdtrans.hxx>
#include <svx/unoapi.hxx>
#include <cppuhelper/implbase.hxx>
#include <rtl/ustrbuf.hxx>
@@ -334,7 +336,7 @@ public:
return nYSize;
}
- bool isYSizeValis () const
+ bool isYSizeValid() const
{
return bYSizeValid;
}
@@ -508,6 +510,30 @@ void GraphicImport::putPropertyToFrameGrabBag( const OUString& sPropertyName, co
}
}
+static bool lcl_bHasGroupSlantedChild (const SdrObject* pObj)
+{
+ // Returns true, if a child object differs more then 0.02deg from horizontal or vertical.
+ // Because lines sometimes are imported as customshapes, a horizontal or vertical line
+ // might not have exactly 0, 90, 180, or 270 degree as rotate angle.
+ if (!pObj)
+ return false;
+ if (!pObj->IsGroupObject())
+ return false;
+ SdrObjList* pSubList = pObj->GetSubList();
+ if (!pSubList)
+ return false;
+ SdrObjListIter aIterator(pSubList, SdrIterMode::DeepNoGroups);
+ while (aIterator.IsMore())
+ {
+ const SdrObject* pSubObj = aIterator.Next();
+ const tools::Long nRot = NormAngle36000(pSubObj->GetRotateAngle());
+ if ((3 < nRot && nRot < 8997) || (9003 < nRot && nRot < 17997)
+ || (18003 < nRot && nRot < 26997) || (27003 < nRot && nRot < 35997))
+ return true;
+ }
+ return false;
+}
+
void GraphicImport::lcl_attribute(Id nName, Value& rValue)
{
sal_Int32 nIntValue = rValue.getInt();
@@ -787,10 +813,34 @@ void GraphicImport::lcl_attribute(Id nName, Value& rValue)
awt::Size aSize(m_xShape->getSize());
- if (m_pImpl->isXSizeValid())
- aSize.Width = m_pImpl->getXSize();
- if (m_pImpl->isYSizeValis())
- aSize.Height = m_pImpl->getYSize();
+ // One purpose of the next part is, to set the logic rectangle of the SdrObject
+ // to nXSize and nYSize from import. That doesn't work for groups or lines,
+ // because they do not have a logic rectangle and m_xShape->getSize and
+ // m_xShape->setSize would work on the snap rectangle. In case a shape is
+ // rotated, non-uniform scaling the snap rectangle will introduce shearing on
+ // the shape. In case group or line is rotated, nXSize and nYSize contain the
+ // unrotated size from oox. The rotation is already incorporated into group
+ // children and line points. We must not scale them to unrotated size. Exclude
+ // those shapes here.
+
+ // Get MSO rotation angle. GetRotateAngle from SdrObject is not suitable
+ // here, because it returns the rotate angle of the first child for groups
+ // and slope angle for lines, even if line or group had not been rotated.
+ // Import in oox has put the rotation from oox file into InteropGrabBag.
+ comphelper::SequenceAsHashMap aInteropGrabBag(xShapeProps->getPropertyValue("InteropGrabBag"));
+ sal_Int32 nOOXAngle(0);
+ aInteropGrabBag.getValue("mso-rotation-angle") >>= nOOXAngle; // 1/60000 deg
+ const bool bIsGroupOrLine = xServiceInfo->supportsService("com.sun.star.drawing.GroupShape")
+ || xServiceInfo->supportsService("com.sun.star.drawing.LineShape");
+ SdrObject* pShape = GetSdrObjectFromXShape(m_xShape);
+ if ((bIsGroupOrLine && !lcl_bHasGroupSlantedChild(pShape) && nOOXAngle == 0)
+ || !bIsGroupOrLine)
+ {
+ if (m_pImpl->isXSizeValid())
+ aSize.Width = m_pImpl->getXSize();
+ if (m_pImpl->isYSizeValid())
+ aSize.Height = m_pImpl->getYSize();
+ }
sal_Int32 nRotation = 0;
if (bKeepRotation)
@@ -798,7 +848,7 @@ void GraphicImport::lcl_attribute(Id nName, Value& rValue)
// Use internal API, getPropertyValue(RotateAngle)
// would use GetObjectRotation(), which is not what
// we want.
- if (SdrObject* pShape = GetSdrObjectFromXShape(m_xShape))
+ if (pShape)
nRotation = pShape->GetRotateAngle();
}
m_xShape->setSize(aSize);
@@ -865,6 +915,64 @@ void GraphicImport::lcl_attribute(Id nName, Value& rValue)
{
putPropertyToFrameGrabBag("AnchorId", uno::makeAny(m_pImpl->sAnchorId));
}
+
+ // Calculate mso unrotated rectangle and its center, needed below
+ awt::Size aImportSize(m_xShape->getSize()); // here only fallback
+ if (m_pImpl->isXSizeValid())
+ aImportSize.Width = m_pImpl->getXSize(); // Hmm
+ if (m_pImpl->isYSizeValid())
+ aImportSize.Height = m_pImpl->getYSize(); // Hmm
+ const awt::Point aImportPosition(GetGraphicObjectPosition()); // Hmm
+ const awt::Point aCentrum(aImportPosition.X + aImportSize.Width / 2,
+ aImportPosition.Y + aImportSize.Height / 2);
+
+ // In case of group and lines, rotations are incorported in the child shapes or
+ // points respectively in LO. MSO has rotation as separate property. The
+ // position refers to the unrotated rectangle of MSO. We need to adapt it to
+ // the left-top of the rotated shape.
+ if (bIsGroupOrLine)
+ {
+ // Get actual LO snap rectangle size of group or line.
+ awt::Size aLOSize(m_xShape->getSize()); //Hmm
+
+ // Set LO position. MSO rotation is done on shape center.
+ m_pImpl->nLeftPosition = aCentrum.X - aLOSize.Width / 2;
+ m_pImpl->nTopPosition = aCentrum.Y - aLOSize.Height / 2;
+ m_xShape->setPosition(GetGraphicObjectPosition());
+ }
+
+ // Margin correction
+ // In case of wrap "Square" or "in Line", MSO uses special rules to
+ // determine the rectangle into which the shape is placed, depending on
+ // rotation angle.
+ // If angle is smaller to horizontal than 45deg, the unrotated mso shape
+ // rectangle is used, whereby the height is expanded to the bounding
+ // rectangle height of the shape.
+ // If angle is larger to horizontal than 45deg, the 90deg rotated rectangle
+ // is used, whereby the width is expanded to the bounding width of the
+ // shape.
+ if (bIsGroupOrLine && (m_pImpl->eGraphicImportType == IMPORT_AS_DETECTED_INLINE
+ || (m_pImpl->nWrap == text::WrapTextMode_PARALLEL && !(m_pImpl->mpWrapPolygon))))
+ {
+
+ nOOXAngle = (nOOXAngle / 60000) % 180; // convert to degree in [0°,180°[
+
+ if (nOOXAngle >= 45 && nOOXAngle < 135)
+ {
+ const sal_Int32 nImportRot90Top(aCentrum.Y - aImportSize.Width / 2);
+ sal_Int32 nVertMarginOffset(m_pImpl->nTopPosition - nImportRot90Top);
+ nVertMarginOffset = std::max<sal_Int32>(nVertMarginOffset, 0);
+ m_pImpl->nTopMargin += nVertMarginOffset;
+ m_pImpl->nBottomMargin += nVertMarginOffset;
+ }
+ else
+ {
+ sal_Int32 nHoriMarginOffset(m_pImpl->nLeftPosition - aImportPosition.X);
+ nHoriMarginOffset = std::max<sal_Int32>(nHoriMarginOffset, 0);
+ m_pImpl->nLeftMargin += nHoriMarginOffset;
+ m_pImpl->nRightMargin += nHoriMarginOffset;
+ }
+ }
}
if (bUseShape && m_pImpl->eGraphicImportType == IMPORT_AS_DETECTED_ANCHOR)