summaryrefslogtreecommitdiff
path: root/oox
diff options
context:
space:
mode:
authorRegina Henschel <rb.henschel@t-online.de>2021-07-11 15:31:58 +0200
committerXisco Fauli <xiscofauli@libreoffice.org>2021-07-13 15:17:38 +0200
commitd9cee5d4f71a6484dce650ea8dd8195debe7b343 (patch)
treea7d720cb19a8665249517ef1cdec96045d9985f4 /oox
parent1b4997d7a84d1ea7de6be6099bddcc8c31c0146a (diff)
tdf#141786 correct position of child elements in group
...in export to docx. Rotated child elements need a correction to the position values, because LO uses left/top of snap rectangle as position, but Word uses left/top of unrotated shape. For the group itself it is done in DocxSdrExport::startDMLAnchorInline. But child elements' position is exported in DrawingML::WriteShapeTransformation. And there this correction was missing. Position is relative to anchor in Writer and relative to group in Word. The adaption is contained in WriteShapeTransformation. But PolyPolygon and Connector have no explicit rotation and therefore they do not use DrawingML::WriteShapeTransformation but call directly DrawingML::WriteTransformation. So they missed this adaption. I have added the adapation directly to these shapes. Unittest testDmlTextshapeB in sw-ooxmlexport6: I have adapted the values. The position of the connectors relative to the group is better now. You see this, if you compare it with a screenshot of the original file in Word. The handles of the connectors are still wrong and the whole group is still shifted. The patch does not fix the wrong position of rotated legacy ovals, because that error exists independent of groups. Change-Id: Iaf843dcf04bac596427dd35ccfa6cd20e3a4cdc8 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/118748 Tested-by: Jenkins Reviewed-by: Regina Henschel <rb.henschel@t-online.de> Signed-off-by: Xisco Fauli <xiscofauli@libreoffice.org> Reviewed-on: https://gerrit.libreoffice.org/c/core/+/118826
Diffstat (limited to 'oox')
-rw-r--r--oox/qa/unit/data/tdf141786_PolylineConnectorInGroup.odtbin0 -> 10127 bytes
-rw-r--r--oox/qa/unit/data/tdf141786_RotatedShapeInGroup.odtbin0 -> 15507 bytes
-rw-r--r--oox/qa/unit/export.cxx55
-rw-r--r--oox/source/export/drawingml.cxx14
-rw-r--r--oox/source/export/shapes.cxx17
5 files changed, 85 insertions, 1 deletions
diff --git a/oox/qa/unit/data/tdf141786_PolylineConnectorInGroup.odt b/oox/qa/unit/data/tdf141786_PolylineConnectorInGroup.odt
new file mode 100644
index 000000000000..d40dccac8c5e
--- /dev/null
+++ b/oox/qa/unit/data/tdf141786_PolylineConnectorInGroup.odt
Binary files differ
diff --git a/oox/qa/unit/data/tdf141786_RotatedShapeInGroup.odt b/oox/qa/unit/data/tdf141786_RotatedShapeInGroup.odt
new file mode 100644
index 000000000000..e932cf47b8dc
--- /dev/null
+++ b/oox/qa/unit/data/tdf141786_RotatedShapeInGroup.odt
Binary files differ
diff --git a/oox/qa/unit/export.cxx b/oox/qa/unit/export.cxx
index 20ae35c6b727..0e597adb2854 100644
--- a/oox/qa/unit/export.cxx
+++ b/oox/qa/unit/export.cxx
@@ -77,6 +77,61 @@ void Test::loadAndSave(const OUString& rURL, const OUString& rFilterName)
constexpr OUStringLiteral DATA_DIRECTORY = u"/oox/qa/unit/data/";
+CPPUNIT_TEST_FIXTURE(Test, testPolylineConnectorPosition)
+{
+ // Given a document with a group shape and therein a polyline and a connector.
+ OUString aURL
+ = m_directories.getURLFromSrc(DATA_DIRECTORY) + "tdf141786_PolylineConnectorInGroup.odt";
+ // When saving that to DOCX:
+ loadAndSave(aURL, "Office Open XML Text");
+
+ // Then make sure polyline and connector have the correct position.
+ uno::Reference<packages::zip::XZipFileAccess2> xNameAccess
+ = packages::zip::ZipFileAccess::createWithURL(mxComponentContext, getTempFile().GetURL());
+ uno::Reference<io::XInputStream> xInputStream(xNameAccess->getByName("word/document.xml"),
+ uno::UNO_QUERY);
+ std::unique_ptr<SvStream> pStream(utl::UcbStreamHelper::CreateStream(xInputStream, true));
+ xmlDocUniquePtr pXmlDoc = parseXmlStream(pStream.get());
+
+ // For child elements of groups in Writer the position has to be adapted to be relative
+ // to group instead of being relative to anchor. That was missing for polyline and
+ // connector.
+ // Polyline: Without fix it would have failed with expected: 0, actual: 1800360
+ assertXPath(pXmlDoc, "//wpg:wgp/wps:wsp[1]/wps:spPr/a:xfrm/a:off", "x", "0");
+ // ... failed with expected: 509400, actual: 1229400
+ assertXPath(pXmlDoc, "//wpg:wgp/wps:wsp[1]/wps:spPr/a:xfrm/a:off", "y", "509400");
+
+ // Connector: Without fix it would have failed with expected: 763200, actual: 2563560
+ assertXPath(pXmlDoc, "//wpg:wgp/wps:wsp[3]/wps:spPr/a:xfrm/a:off", "x", "763200");
+ // ... failed with expected: 0, actual: 720000
+ assertXPath(pXmlDoc, "//wpg:wgp/wps:wsp[3]/wps:spPr/a:xfrm/a:off", "y", "0");
+ // Polyline and connector were shifted 1800360EMU right, 720000EMU down.
+}
+
+CPPUNIT_TEST_FIXTURE(Test, testRotatedShapePosition)
+{
+ // Given a document with a group shape and therein a rotated custom shape.
+ OUString aURL
+ = m_directories.getURLFromSrc(DATA_DIRECTORY) + "tdf141786_RotatedShapeInGroup.odt";
+ // When saving that to DOCX:
+ loadAndSave(aURL, "Office Open XML Text");
+
+ // Then make sure the rotated child shape has the correct position.
+ uno::Reference<packages::zip::XZipFileAccess2> xNameAccess
+ = packages::zip::ZipFileAccess::createWithURL(mxComponentContext, getTempFile().GetURL());
+ uno::Reference<io::XInputStream> xInputStream(xNameAccess->getByName("word/document.xml"),
+ uno::UNO_QUERY);
+ std::unique_ptr<SvStream> pStream(utl::UcbStreamHelper::CreateStream(xInputStream, true));
+ xmlDocUniquePtr pXmlDoc = parseXmlStream(pStream.get());
+
+ // For a group itself and for shapes outside of groups, the position calculation is done in
+ // DocxSdrExport. For child elements of groups it has to be done in
+ // DrawingML::WriteShapeTransformation(), but was missing.
+ // Without fix it would have failed with expected: 469440, actual: 92160
+ // The shape was about 1cm shifted up and partly outside its group.
+ assertXPath(pXmlDoc, "//wpg:wgp/wps:wsp[1]/wps:spPr/a:xfrm/a:off", "y", "469440");
+}
+
CPPUNIT_TEST_FIXTURE(Test, testDmlGroupshapePolygon)
{
// Given a document with a group shape, containing a single polygon child shape:
diff --git a/oox/source/export/drawingml.cxx b/oox/source/export/drawingml.cxx
index d27b28cb7d1f..1040f52fad1c 100644
--- a/oox/source/export/drawingml.cxx
+++ b/oox/source/export/drawingml.cxx
@@ -39,6 +39,8 @@
#include <tools/diagnose_ex.h>
#include <comphelper/processfactory.hxx>
#include <i18nlangtag/languagetag.hxx>
+#include <basegfx/matrix/b2dhommatrixtools.hxx>
+#include <basegfx/range/b2drange.hxx>
#include <numeric>
#include <string_view>
@@ -104,6 +106,7 @@
#include <comphelper/xmltools.hxx>
#include <o3tl/any.hxx>
#include <o3tl/safeint.hxx>
+#include <o3tl/unit_conversion.hxx>
#include <tools/stream.hxx>
#include <unotools/fontdefs.hxx>
#include <vcl/cvtgrf.hxx>
@@ -1801,6 +1804,17 @@ void DrawingML::WriteShapeTransformation( const Reference< XShape >& rXShape, sa
aPos.X-=(1-faccos*cos(nRotation.get()*F_PI18000))*aSize.Width/2-facsin*sin(nRotation.get()*F_PI18000)*aSize.Height/2;
aPos.Y-=(1-faccos*cos(nRotation.get()*F_PI18000))*aSize.Height/2+facsin*sin(nRotation.get()*F_PI18000)*aSize.Width/2;
}
+ else if (m_xParent.is() && nRotation != 0_deg100)
+ {
+ // Position for rotated shapes inside group is not set by DocxSdrExport.
+ basegfx::B2DRange aRect(-aSize.Width / 2.0, -aSize.Height / 2.0, aSize.Width / 2.0,
+ aSize.Height / 2.0);
+ basegfx::B2DHomMatrix aRotateMatrix =
+ basegfx::utils::createRotateB2DHomMatrix(toRadians(nRotation));
+ aRect.transform(aRotateMatrix);
+ aPos.X += -aSize.Width / 2.0 - aRect.getMinX();
+ aPos.Y += -aSize.Height / 2.0 - aRect.getMinY();
+ }
// The RotateAngle property's value is independent from any flipping, and that's exactly what we need here.
uno::Reference<beans::XPropertySet> xPropertySet(rXShape, uno::UNO_QUERY);
diff --git a/oox/source/export/shapes.cxx b/oox/source/export/shapes.cxx
index d52d6696be2a..23ea202db76b 100644
--- a/oox/source/export/shapes.cxx
+++ b/oox/source/export/shapes.cxx
@@ -402,6 +402,13 @@ ShapeExport& ShapeExport::WritePolyPolygonShape( const Reference< XShape >& xSha
tools::PolyPolygon aPolyPolygon = EscherPropertyContainer::GetPolyPolygon( xShape );
awt::Point aPos = xShape->getPosition();
+ // Position is relative to group for child elements in Word, but absolute in API.
+ if (GetDocumentType() == DOCUMENT_DOCX && m_xParent.is())
+ {
+ awt::Point aParentPos = m_xParent->getPosition();
+ aPos.X -= aParentPos.X;
+ aPos.Y -= aParentPos.Y;
+ }
awt::Size aSize = xShape->getSize();
tools::Rectangle aRect(Point(aPos.X, aPos.Y), Size(aSize.Width, aSize.Height));
@@ -1326,8 +1333,16 @@ ShapeExport& ShapeExport::WriteConnectorShape( const Reference< XShape >& xShape
GET( rXShapeA, EdgeStartConnection );
GET( rXShapeB, EdgeEndConnection );
}
+ // Position is relative to group in Word, but relative to anchor of group in API.
+ if (GetDocumentType() == DOCUMENT_DOCX && m_xParent.is())
+ {
+ awt::Point aParentPos = m_xParent->getPosition();
+ aStartPoint.X -= aParentPos.X;
+ aStartPoint.Y -= aParentPos.Y;
+ aEndPoint.X -= aParentPos.X;
+ aEndPoint.Y -= aParentPos.Y;
+ }
EscherConnectorListEntry aConnectorEntry( xShape, aStartPoint, rXShapeA, aEndPoint, rXShapeB );
-
tools::Rectangle aRect( Point( aStartPoint.X, aStartPoint.Y ), Point( aEndPoint.X, aEndPoint.Y ) );
if( aRect.getWidth() < 0 ) {
bFlipH = true;