summaryrefslogtreecommitdiff
path: root/sw
diff options
context:
space:
mode:
authorRegina Henschel <rb.henschel@t-online.de>2021-05-15 23:40:34 +0200
committerMiklos Vajna <vmiklos@collabora.com>2021-06-28 09:49:00 +0200
commit3262fc5ef3bde5b158909d11ccb008161ea95519 (patch)
treeaa2b4926146eaddaa60878b920da77bd5871f67d /sw
parent2ffdd37067926ddb841c6055205f267b96706945 (diff)
tdf#142304 a.o. Improve wrap margins in docx filters
LO uses the bounding box of the shape in case of type 'Parallel'. Word uses in the corresponding wrap 'square' a box based on the full size of the shape. That will be very different in some cases, e.g. for an arc. And Word exchanges width and height in case of rotation angle in [45°;135°[ and [225°; 315°[. To get the same appearance as in Word, the wrap margins are suitable expanded on import. Word puts the additional space needed for fat strokes into effectExtent in case of wrap 'inline', so there is no need to add the half strokes width in addition. Word determines the area for the shape depending on rotation angle. Both are now considered. Total same appearance is not possible because it would need negative vertical wrap margins, which are currently faulty in LO, see tdf#141880. Patch solves in addition tdf#142486, tdf#142305 The export to Word would require negative values in effectExtent in some cases. They are allowed in OOXML but not supported in Word. My idea is to switch to wrap mode 'Tight' if needed. But export of wrap has so many bad parts, that it needs separate work and is not included here. Handling of border width for export of own frames is missing. Unittest changes ---------------- testDmlTextshapeB and TestDmlTextshape in ooxmlexport6.cxx are set to current values. Import and Export still have large errors with these shapes and correct value from file is unknown. So an exact value is pointless. Only the original problem needs to be still fixed, which is the case. testWpsOnly in ooxmlexport10.cxx. I have removed the test for LeftMargin equals 0. The test makes no sense, because the original file has distL=114300. testTdf124600 in ooxmlimport2.cxx. I have added a tolerance. It would fail with Expected: 2029, Actual:2028, likely a rounding problem somewhere. testTdf124600 in ooxmlimport2.cxx Word refers to outer edge of the border for align='left', LO aligns at snap rectangle. The different intepretations become visible if a thick line is used. LO needs a margin to get the same rendering as in Word. So an expected value of 0 is wrong and I have disabled the test for now. ToDo: tdf#142798. Get the correct margin and activate the test then. testTextframeGradient in ooxmlexport2.cxx. I didn't find any reference for a default value. The test is not reliable, I get both 316 and 318 as actual value. Handling of shadow in VML shapes is buggy, the values for margin and shadow are wrong anyway. Reports are e.g. tdf#142486, tdf#142558. For now I have added a tolerance of 2. testDMLGroupShapeChildPosition in ooxmlexport6.cxx. The accuracy has become better. After reload we get the same values as before. testEffectExtent in ooxmlexport.cxx. tdf#142805. I have disabled the test, because the image is not loaded at all, and therefore it makes no sense to test a margin of it. And you can only test the sum of distL and effectExtent l, because LO has only one property 'LeftMargin' for it. testEffectExtentInline in ooxmlexport.cxx. To get the same vertical alignment as in Word, it would be necessary to have negative margins, but those are not implemented yet. tdf#141880. Currently the patch contains a heuristic to detect unchanged objects and write the grab-Bag values then. testEffectExtentLineWidth in ooxmlexport16.cxx. I have changed the expected value from 508 to 506. That is still away from the to be fixed faulty 561. It is a VML shape and import of connectors in VML shapes is faulty. testRelorientation in ooxmlexport4.cxx. Changed to 8662, the original problem is still fixed. I don't know the reason for the difference to the values from file. Change-Id: I28f156637f6ae64975cf2917f0e5cc89e689aff5 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/115668 Tested-by: Jenkins Reviewed-by: Miklos Vajna <vmiklos@collabora.com>
Diffstat (limited to 'sw')
-rw-r--r--sw/qa/extras/ooxmlexport/data/tdf142486_FrameShadow.odtbin0 -> 14129 bytes
-rw-r--r--sw/qa/extras/ooxmlexport/data/tdf142486_LeftMarginShadowLeft.docxbin0 -> 21616 bytes
-rw-r--r--sw/qa/extras/ooxmlexport/ooxmlexport.cxx4
-rw-r--r--sw/qa/extras/ooxmlexport/ooxmlexport10.cxx2
-rw-r--r--sw/qa/extras/ooxmlexport/ooxmlexport16.cxx31
-rw-r--r--sw/qa/extras/ooxmlexport/ooxmlexport2.cxx4
-rw-r--r--sw/qa/extras/ooxmlexport/ooxmlexport4.cxx7
-rw-r--r--sw/qa/extras/ooxmlexport/ooxmlexport6.cxx22
-rw-r--r--sw/qa/extras/ooxmlimport/ooxmlimport.cxx2
-rw-r--r--sw/qa/extras/ooxmlimport/ooxmlimport2.cxx14
-rw-r--r--sw/source/filter/ww8/docxsdrexport.cxx476
11 files changed, 413 insertions, 149 deletions
diff --git a/sw/qa/extras/ooxmlexport/data/tdf142486_FrameShadow.odt b/sw/qa/extras/ooxmlexport/data/tdf142486_FrameShadow.odt
new file mode 100644
index 000000000000..6556d695cb64
--- /dev/null
+++ b/sw/qa/extras/ooxmlexport/data/tdf142486_FrameShadow.odt
Binary files differ
diff --git a/sw/qa/extras/ooxmlexport/data/tdf142486_LeftMarginShadowLeft.docx b/sw/qa/extras/ooxmlexport/data/tdf142486_LeftMarginShadowLeft.docx
new file mode 100644
index 000000000000..07478e2b8be7
--- /dev/null
+++ b/sw/qa/extras/ooxmlexport/data/tdf142486_LeftMarginShadowLeft.docx
Binary files differ
diff --git a/sw/qa/extras/ooxmlexport/ooxmlexport.cxx b/sw/qa/extras/ooxmlexport/ooxmlexport.cxx
index a9e21389009f..46876dfc9b9b 100644
--- a/sw/qa/extras/ooxmlexport/ooxmlexport.cxx
+++ b/sw/qa/extras/ooxmlexport/ooxmlexport.cxx
@@ -578,7 +578,7 @@ DECLARE_OOXMLEXPORT_EXPORTONLY_TEST(testCropPixel, "crop-pixel.docx")
// become invisible), now is around 19072.
CPPUNIT_ASSERT(getXPath(pXmlDoc, "//a:srcRect", "l").toInt32() <= 22452);
}
-
+/* FixMe: tdf#142805 Test disabled, because the picture is not load at all.
DECLARE_OOXMLEXPORT_EXPORTONLY_TEST(testEffectExtent, "effect-extent.docx")
{
// The problem was that in case there were no shadows on the picture, we
@@ -587,7 +587,7 @@ DECLARE_OOXMLEXPORT_EXPORTONLY_TEST(testEffectExtent, "effect-extent.docx")
// E.g. this was 0.
assertXPath(pXmlDoc, "//wp:effectExtent", "l", "114300");
}
-
+*/
DECLARE_OOXMLEXPORT_EXPORTONLY_TEST(testEffectExtentInline, "effect-extent-inline.docx")
{
// The problem was that in case there was inline rotated picture, we
diff --git a/sw/qa/extras/ooxmlexport/ooxmlexport10.cxx b/sw/qa/extras/ooxmlexport/ooxmlexport10.cxx
index b2d81e45f9ae..9f26fd565035 100644
--- a/sw/qa/extras/ooxmlexport/ooxmlexport10.cxx
+++ b/sw/qa/extras/ooxmlexport/ooxmlexport10.cxx
@@ -152,8 +152,6 @@ DECLARE_OOXMLEXPORT_TEST(testWpsOnly, "wps-only.docx")
// Check position, it was 0. This is a shape, so use getPosition(), not a property.
CPPUNIT_ASSERT_EQUAL(oox::drawingml::convertEmuToHmm(671830), xShape->getPosition().X);
- // Left margin should be 0, as margins are not relevant for <wp:wrapNone/>.
- CPPUNIT_ASSERT_EQUAL(sal_Int32(0), getProperty<sal_Int32>(xShape, "LeftMargin"));
// Wrap type was PARALLEL.
CPPUNIT_ASSERT_EQUAL(text::WrapTextMode_THROUGH, getProperty<text::WrapTextMode>(xShape, "Surround"));
// Confirm that the deprecated (incorrectly spelled) _THROUGHT also matches
diff --git a/sw/qa/extras/ooxmlexport/ooxmlexport16.cxx b/sw/qa/extras/ooxmlexport/ooxmlexport16.cxx
index dcaa4e6e5e54..7f8ba137825b 100644
--- a/sw/qa/extras/ooxmlexport/ooxmlexport16.cxx
+++ b/sw/qa/extras/ooxmlexport/ooxmlexport16.cxx
@@ -27,7 +27,9 @@
#include <com/sun/star/text/XTextTable.hpp>
#include <com/sun/star/text/XTextTablesSupplier.hpp>
#include <com/sun/star/text/XTextFrame.hpp>
+#include <com/sun/star/text/XTextViewCursorSupplier.hpp>
#include <com/sun/star/packages/zip/ZipFileAccess.hpp>
+#include <com/sun/star/view/XViewCursor.hpp>
#include <comphelper/configuration.hxx>
#include <comphelper/propertysequence.hxx>
#include <editeng/escapementitem.hxx>
@@ -70,6 +72,33 @@ protected:
}
};
+DECLARE_OOXMLEXPORT_TEST(testTdf142486_LeftMarginShadowLeft, "tdf142486_LeftMarginShadowLeft.docx")
+{
+ uno::Reference<beans::XPropertySet> xFrame(getShape(1), uno::UNO_QUERY);
+ // Error was, that the shadow distance appeared as additional margin.
+ // Without fix this would have failed with expected 953, actual 2822
+ // Margin is 36px (= 952.5Hmm) in Word.
+ CPPUNIT_ASSERT_DOUBLES_EQUAL(sal_Int32(953), getProperty<sal_Int32>(xFrame, "LeftMargin"), 1);
+}
+
+DECLARE_OOXMLEXPORT_TEST(testTdf142486_FrameShadow, "tdf142486_FrameShadow.odt")
+{
+ uno::Reference<frame::XModel> xModel(mxComponent, uno::UNO_QUERY);
+ uno::Reference<text::XTextViewCursorSupplier> xTextViewCursorSupplier(
+ xModel->getCurrentController(), uno::UNO_QUERY_THROW);
+ uno::Reference<text::XTextViewCursor> xViewCursor(xTextViewCursorSupplier->getViewCursor());
+ xViewCursor->gotoStart(/*bExpand=*/false);
+ uno::Reference<view::XViewCursor> xCursor(xViewCursor, uno::UNO_QUERY);
+ xCursor->goDown(/*nCount=*/3, /*bExpand=*/false);
+ xViewCursor->goRight(/*nCount=*/1, /*bExpand=*/true);
+ OUString sText = xViewCursor->getString();
+ // Without fix in place, the frame size including shadow width was exported as object size. On
+ // import the shadow width was added as wrap "distance from text". That results in totally
+ // different wrapping of the surrouding text.
+ // Here line started with "x" instead of expected "e".
+ CPPUNIT_ASSERT(sText.startsWith("e"));
+}
+
DECLARE_OOXMLEXPORT_TEST(testTdf136059, "tdf136059.odt")
{
CPPUNIT_ASSERT_EQUAL_MESSAGE("Contour has not been exported!", true,
@@ -318,7 +347,7 @@ DECLARE_OOXMLEXPORT_EXPORTONLY_TEST(testFooterMarginLost, "footer-margin-lost.do
CPPUNIT_TEST_FIXTURE(Test, testEffectExtentLineWidth)
{
auto verify = [this]() {
- CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32>(508),
+ CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32>(506),
getProperty<sal_Int32>(getShape(1), "TopMargin"));
};
diff --git a/sw/qa/extras/ooxmlexport/ooxmlexport2.cxx b/sw/qa/extras/ooxmlexport/ooxmlexport2.cxx
index aaecebc5cf94..fb21f41284b4 100644
--- a/sw/qa/extras/ooxmlexport/ooxmlexport2.cxx
+++ b/sw/qa/extras/ooxmlexport/ooxmlexport2.cxx
@@ -596,8 +596,8 @@ DECLARE_OOXMLEXPORT_TEST(testTextframeGradient, "textframe-gradient.docx")
// Left / right margin was incorrect: the attribute was missing and we
// didn't have the right default (had 0 instead of the below one).
- CPPUNIT_ASSERT_EQUAL(sal_Int32(318), getProperty<sal_Int32>(xFrame, "LeftMargin"));
- CPPUNIT_ASSERT_EQUAL(sal_Int32(318), getProperty<sal_Int32>(xFrame, "RightMargin"));
+ CPPUNIT_ASSERT_DOUBLES_EQUAL(sal_Int32(318), getProperty<sal_Int32>(xFrame, "LeftMargin"), 2);
+ CPPUNIT_ASSERT_DOUBLES_EQUAL(sal_Int32(318), getProperty<sal_Int32>(xFrame, "RightMargin"), 2);
}
DECLARE_OOXMLEXPORT_EXPORTONLY_TEST(testCellBtlr, "cell-btlr.docx")
diff --git a/sw/qa/extras/ooxmlexport/ooxmlexport4.cxx b/sw/qa/extras/ooxmlexport/ooxmlexport4.cxx
index da74296687fd..23546453c7de 100644
--- a/sw/qa/extras/ooxmlexport/ooxmlexport4.cxx
+++ b/sw/qa/extras/ooxmlexport/ooxmlexport4.cxx
@@ -83,13 +83,14 @@ DECLARE_OOXMLEXPORT_TEST(testRelorientation, "relorientation.docx")
// 'actual child size' = 'group ext' * 'child ext' / 'chExt from group', see section 'chExt' in
// [MS-OI29500]. Here for width from file 3108960 * 4896 / 4911 = 3099464 EMU. That corresponds to
- // width 8.61cm and 325px in UI in Word and rounds down to 8609 Hmm. FIXME: Expected value is wrong.
- // Right after import we get a rounding error: 8662 vs 8664.
+ // width 8.61cm and 325px in UI in Word and rounds down to 8609 Hmm. Considering scaling of the
+ // parent group to the anchor extent (* 3118485 / 3108960) we get a display width of 3108960 EMU
+ // = 8636Hmm. FIXME: Expected value is as in LO 7.2. Reason for difference is yet unknown.
if (mbExported)
{
uno::Reference<drawing::XShape> xYear(xGroup->getByIndex(1), uno::UNO_QUERY);
// This was 2, due to incorrect handling of parent transformations inside DML groupshapes.
- CPPUNIT_ASSERT_EQUAL(sal_Int32(8664), xYear->getSize().Width);
+ CPPUNIT_ASSERT_EQUAL(sal_Int32(8662), xYear->getSize().Width);
}
}
diff --git a/sw/qa/extras/ooxmlexport/ooxmlexport6.cxx b/sw/qa/extras/ooxmlexport/ooxmlexport6.cxx
index b07cd328e342..4e2f62b0dcbc 100644
--- a/sw/qa/extras/ooxmlexport/ooxmlexport6.cxx
+++ b/sw/qa/extras/ooxmlexport/ooxmlexport6.cxx
@@ -108,12 +108,14 @@ DECLARE_OOXMLEXPORT_TEST(testDmlTextshape, "dml-textshape.docx")
OUString aType = comphelper::SequenceAsHashMap(getProperty<beans::PropertyValues>(xShape, "CustomShapeGeometry"))["Type"].get<OUString>();
CPPUNIT_ASSERT_EQUAL(OUString("ooxml-bentConnector3"), aType);
// Connector was incorrectly shifted towards the top left corner, X was 552, Y was 0.
- CPPUNIT_ASSERT_EQUAL(sal_Int32(4018), xShape->getPosition().X);
- CPPUNIT_ASSERT_EQUAL(sal_Int32(-4487), xShape->getPosition().Y);
+ // It is not a DML, but a VML shape. The whole group is shifted 3mm right and 6mm up.
+ // Values are as in LO7.2, original problem is still fixed.
+ CPPUNIT_ASSERT_EQUAL(sal_Int32(4016), xShape->getPosition().X);
+ CPPUNIT_ASSERT_EQUAL(sal_Int32(-4485), xShape->getPosition().Y);
xShape.set(xGroup->getByIndex(5), uno::UNO_QUERY);
// This was incorrectly shifted towards the top of the page, Y was 106.
- CPPUNIT_ASSERT_EQUAL(sal_Int32(-4727), xShape->getPosition().Y);
+ CPPUNIT_ASSERT_EQUAL(sal_Int32(-4725), xShape->getPosition().Y);
}
// testDmlTextshapeB was only made export-only because as an import-export test it failed for an unknown reason
@@ -123,13 +125,15 @@ DECLARE_OOXMLEXPORT_EXPORTONLY_TEST(testDmlTextshapeB, "dml-textshapeB.docx")
uno::Reference<drawing::XShape> xShape(xGroup->getByIndex(3), uno::UNO_QUERY);
// Connector was incorrectly shifted towards the top left corner, X was 192, Y was -5743.
CPPUNIT_ASSERT_EQUAL(sal_Int32(3776), xShape->getPosition().X);
- // Value as of LO7.2. Whole group is still shifted 3mm to right and 5mm down.
- CPPUNIT_ASSERT_EQUAL(sal_Int32(-5063), xShape->getPosition().Y);
+ // Values are as in LO7.2, the original problem is still fixed.
+ // FixMe: The shape is a VML group, not a DML. Export writes the connector shifted up, resulting
+ // in different routing. LO7.2 reads and writes the second connector wrongly. Whole group is
+ // still shifted.
+ CPPUNIT_ASSERT_EQUAL(sal_Int32(-5061), xShape->getPosition().Y);
xShape.set(xGroup->getByIndex(5), uno::UNO_QUERY);
// This was incorrectly shifted towards the top of the page, Y was -5011.
- // Value as of LO 7.2
- CPPUNIT_ASSERT_EQUAL(sal_Int32(-4712), xShape->getPosition().Y);
+ CPPUNIT_ASSERT_EQUAL(sal_Int32(-4710), xShape->getPosition().Y);
}
DECLARE_OOXMLEXPORT_TEST(testDMLSolidfillAlpha, "dml-solidfill-alpha.docx")
@@ -360,12 +364,12 @@ DECLARE_OOXMLEXPORT_TEST(testDMLGroupShapeChildPosition, "dml-groupshape-childpo
uno::Reference<drawing::XShapes> xGroup(getShape(1), uno::UNO_QUERY);
uno::Reference<drawing::XShape> xChildGroup(xGroup->getByIndex(1), uno::UNO_QUERY);
CPPUNIT_ASSERT_EQUAL(sal_Int32(-2123), xChildGroup->getPosition().X);
- CPPUNIT_ASSERT_EQUAL(sal_Int32(mbExported ? 11333 : 11331), xChildGroup->getPosition().Y);
+ CPPUNIT_ASSERT_EQUAL(sal_Int32(mbExported ? 11331 : 11331), xChildGroup->getPosition().Y);
xGroup.set(xChildGroup, uno::UNO_QUERY);
xChildGroup.set(xGroup->getByIndex(0), uno::UNO_QUERY);
CPPUNIT_ASSERT_EQUAL(sal_Int32(-1859), xChildGroup->getPosition().X);
- CPPUNIT_ASSERT_EQUAL(sal_Int32(mbExported ? 11333 : 11331), xChildGroup->getPosition().Y);
+ CPPUNIT_ASSERT_EQUAL(sal_Int32(mbExported ? 11331 : 11331), xChildGroup->getPosition().Y);
xChildGroup.set(xGroup->getByIndex(1), uno::UNO_QUERY);
CPPUNIT_ASSERT_EQUAL(sal_Int32(-2123), xChildGroup->getPosition().X);
diff --git a/sw/qa/extras/ooxmlimport/ooxmlimport.cxx b/sw/qa/extras/ooxmlimport/ooxmlimport.cxx
index deb17d50fb23..f6d986544993 100644
--- a/sw/qa/extras/ooxmlimport/ooxmlimport.cxx
+++ b/sw/qa/extras/ooxmlimport/ooxmlimport.cxx
@@ -1377,7 +1377,7 @@ CPPUNIT_TEST_FIXTURE(Test, testTdf85232)
CPPUNIT_ASSERT_EQUAL(OUString("com.sun.star.drawing.LineShape"), xShapeDescriptor->getShapeType());
// This was 2900: horizontal position of the line was incorrect, the 3 children were not connected visually.
- CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32>(2267), xShape->getPosition().X);
+ CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32>(2265), xShape->getPosition().X);
}
CPPUNIT_TEST_FIXTURE(Test, testTdf95755)
diff --git a/sw/qa/extras/ooxmlimport/ooxmlimport2.cxx b/sw/qa/extras/ooxmlimport/ooxmlimport2.cxx
index e01957f553ab..fe39e78a6423 100644
--- a/sw/qa/extras/ooxmlimport/ooxmlimport2.cxx
+++ b/sw/qa/extras/ooxmlimport/ooxmlimport2.cxx
@@ -266,13 +266,17 @@ CPPUNIT_TEST_FIXTURE(Test, testGroupShapeFontName)
CPPUNIT_TEST_FIXTURE(Test, testTdf124600)
{
load(mpTestDocumentPath, "tdf124600.docx");
- uno::Reference<drawing::XShape> xShape = getShape(1);
+ // uno::Reference<drawing::XShape> xShape = getShape(1);
// Without the accompanying fix in place, this test would have failed with:
// - Expected: 0
// - Actual : 318
// i.e. the shape had an unexpected left margin, but not in Word.
- CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32>(0),
- getProperty<sal_Int32>(xShape, "HoriOrientPosition"));
+ // Regina: LO needs a left margin to get the same rendering as Word, because Word aligns the
+ // shape with the outer edge of the border, but LibreOffice aligns with the snap rectangle.
+ // Expected: 0 is wrong. ToDo: The current margin is wrong and needs to be fixed. Then activate
+ // the test again with the correct margin.
+ // CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32>(0),
+ // getProperty<sal_Int32>(xShape, "HoriOrientPosition"));
// Make sure that "Shape 1 text" (anchored in the header) has the same left margin as the body
// text.
@@ -283,7 +287,7 @@ CPPUNIT_TEST_FIXTURE(Test, testTdf124600)
// - Actual : 1815
// i.e. there was a >0 left margin on the text of the shape, resulting in incorrect horizontal
// position.
- CPPUNIT_ASSERT_EQUAL(aBodyTextLeft, aShapeTextLeft);
+ CPPUNIT_ASSERT_DOUBLES_EQUAL(aBodyTextLeft.toDouble(), aShapeTextLeft.toDouble(), 1.0);
}
CPPUNIT_TEST_FIXTURE(Test, testTdf120548)
@@ -303,7 +307,7 @@ CPPUNIT_TEST_FIXTURE(Test, test120551)
// 'Expected: 430, Actual : -2542'.
// CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32>(430), nHoriOrientPosition);
// File 140335EMU = 389,8Hmm
- CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32>(392), nHoriOrientPosition);
+ CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32>(390), nHoriOrientPosition);
}
CPPUNIT_TEST_FIXTURE(Test, testTdf111550)
diff --git a/sw/source/filter/ww8/docxsdrexport.cxx b/sw/source/filter/ww8/docxsdrexport.cxx
index 9b734b9bbeb8..1416c4838904 100644
--- a/sw/source/filter/ww8/docxsdrexport.cxx
+++ b/sw/source/filter/ww8/docxsdrexport.cxx
@@ -17,6 +17,7 @@
#include <editeng/opaqitem.hxx>
#include <editeng/boxitem.hxx>
#include <svx/svdogrp.hxx>
+#include <svx/svdobjkind.hxx>
#include <oox/token/namespaces.hxx>
#include <textboxhelper.hxx>
#include <fmtanchr.hxx>
@@ -38,6 +39,7 @@
#include <tools/diagnose_ex.h>
#include <svx/xlnwtit.hxx>
+#include <svx/svdtrans.hxx>
using namespace com::sun::star;
using namespace oox;
@@ -87,29 +89,40 @@ void lclMovePositionWithRotation(awt::Point& aPos, const Size& rSize, Degree100
{
// code from ImplEESdrWriter::ImplFlipBoundingBox (filter/source/msfilter/eschesdo.cxx)
// TODO: refactor
-
+ // MSO uses left|top of the unrotated object rectangle as position. When you rotate that retangle
+ // around its center and build a snap rectangle S from it, then left|top of S has to be the
+ // position used in LO. This method converts LOs aPos to the position used by MSO.
+
+ // rSize has to be size of the logicRect of the object. For calculating the diff, we build a
+ // rectangle with left|top A = (-fWidthHalf | -fHeightHalf) and
+ // right|top B = (fWidthHalf | -fHeightHalf). The rotation matrix R is here
+ // fcos fsin
+ // -fsin fcos
+ // Left of rectangle S = X-coord of R * A, Top of rectangle S = Y-coord of R * B
+
+ // Use nRotation in [0;9000], for to have only one and not four cases.
if (nRotation100 == 0_deg100)
return;
sal_Int64 nRotation = nRotation100.get();
if (nRotation < 0)
nRotation = (36000 + nRotation) % 36000;
if (nRotation % 18000 == 0)
- nRotation = 0;
+ nRotation = 0; // prevents endless loop
while (nRotation > 9000)
nRotation = (18000 - (nRotation % 18000));
double fVal = static_cast<double>(nRotation) * F_PI18000;
- double fCos = cos(fVal);
+ double fCos = (nRotation == 9000) ? 0.0 : cos(fVal);
double fSin = sin(fVal);
- double nWidthHalf = static_cast<double>(rSize.Width()) / 2;
- double nHeightHalf = static_cast<double>(rSize.Height()) / 2;
+ double fWidthHalf = static_cast<double>(rSize.Width()) / 2.0;
+ double fHeightHalf = static_cast<double>(rSize.Height()) / 2.0;
- double nXDiff = fSin * nHeightHalf + fCos * nWidthHalf - nWidthHalf;
- double nYDiff = fSin * nWidthHalf + fCos * nHeightHalf - nHeightHalf;
+ double fXDiff = fSin * fHeightHalf + fCos * fWidthHalf - fWidthHalf;
+ double fYDiff = fSin * fWidthHalf + fCos * fHeightHalf - fHeightHalf;
- aPos.X += nXDiff;
- aPos.Y += nYDiff;
+ aPos.X += fXDiff + 0.5;
+ aPos.Y += fYDiff + 0.5;
}
/// Determines if the anchor is inside a paragraph.
@@ -118,8 +131,140 @@ bool IsAnchorTypeInsideParagraph(const ww8::Frame* pFrame)
const SwFormatAnchor& rAnchor = pFrame->GetFrameFormat().GetAttrSet().GetAnchor();
return rAnchor.GetAnchorId() != RndStdIds::FLY_AT_PAGE;
}
+
+bool lcl_IsRotateAngleValid(const SdrObject& rObj)
+{
+ // Some shape types report a rotation angle but are not actually rotated, because all rotation
+ // have been incorporated.
+ switch (rObj.GetObjIdentifier())
+ {
+ case OBJ_GRUP:
+ case OBJ_LINE:
+ case OBJ_PATHLINE:
+ case OBJ_PATHFILL:
+ return false;
+ default:
+ return true;
+ }
+}
+
+void lcl_calculateMSOBaseRectangle(const SdrObject& rObj, double& rfMSOLeft, double& rfMSORight,
+ double& rfMSOTop, double& rfMSOBottom)
+{
+ // Word rotates around shape center, LO around left/top. Thus logic rectangle of LO is not
+ // directly usable as 'base rectangle'.
+ double fCenterX = (rObj.GetSnapRect().Left() + rObj.GetSnapRect().Right()) / 2.0;
+ double fCenterY = (rObj.GetSnapRect().Top() + rObj.GetSnapRect().Bottom()) / 2.0;
+ double fHalfWidth = rObj.GetLogicRect().getWidth() / 2.0;
+ double fHalfHeight = rObj.GetLogicRect().getHeight() / 2.0;
+
+ // MSO swaps width and height depending on rotation angle.
+ double fRotation
+ = lcl_IsRotateAngleValid(rObj) ? toDegrees(NormAngle36000(rObj.GetRotateAngle())) : 0.0;
+ if ((fRotation > 45.0 && fRotation <= 135.0) || (fRotation > 225.0 && fRotation <= 315.0))
+ {
+ rfMSOLeft = fCenterX - fHalfHeight;
+ rfMSORight = fCenterX + fHalfHeight;
+ rfMSOTop = fCenterY - fHalfWidth;
+ rfMSOBottom = fCenterY + fHalfWidth;
+ }
+ else
+ {
+ rfMSOLeft = fCenterX - fHalfWidth;
+ rfMSORight = fCenterX + fHalfWidth;
+ rfMSOTop = fCenterY - fHalfHeight;
+ rfMSOBottom = fCenterY + fHalfHeight;
+ }
+}
+
+void lcl_calculateRawEffectExtent(sal_Int32& rLeft, sal_Int32& rRight, sal_Int32& rTop,
+ sal_Int32& rBottom, const SdrObject& rObj,
+ const bool bUseBoundRect)
+{
+ // This method calculates the extent needed, to let Word use the same outer area for the object
+ // as LO. Word uses as 'base rectangle' the unrotated shape rectangle, maybe having swapped width
+ // and height depending on rotation angle.
+ double fMSOLeft;
+ double fMSORight;
+ double fMSOTop;
+ double fMSOBottom;
+ lcl_calculateMSOBaseRectangle(rObj, fMSOLeft, fMSORight, fMSOTop, fMSOBottom);
+
+ tools::Rectangle aLORect = bUseBoundRect ? rObj.GetCurrentBoundRect() : rObj.GetSnapRect();
+ rLeft = fMSOLeft - aLORect.Left();
+ rRight = aLORect.Right() - fMSORight;
+ rTop = fMSOTop - aLORect.Top();
+ rBottom = aLORect.Bottom() - fMSOBottom;
+ // Result values might be negative, e.g for a custom shape 'Arc'.
+ return;
+}
+
+bool lcl_makeSingleDistAndEffectExtentNonNegative(sal_Int64& rDist, sal_Int32& rExt)
+{
+ // A negative effectExtent is allowed in OOXML, but Word cannot handle it (bug in Word). It
+ // might occur, if the BoundRect in LO is smaller than the base rect in Word.
+ // A negative wrap distance from text is allowed in ODF. LO can currently only handle left and
+ // right negative values, see bug tdf#141880. Dist must be non-negative in OOXML.
+ // We try to compensate Dist vs effectExtent to get similar visual appearance.
+ if (rExt >= 0 && rDist >= 0)
+ return true;
+ if (rExt < 0 && rDist < 0)
+ {
+ rExt = 0;
+ rDist = 0;
+ return false;
+ }
+ if (rDist + static_cast<sal_Int64>(rExt) < 0) // different sign, so no overflow
+ {
+ rExt = 0;
+ rDist = 0;
+ return false;
+ }
+ // rDist + rExt >= 0
+ rDist += rExt;
+ rExt = 0;
+ return true;
+}
+
+bool lcl_makeDistAndExtentNonNegative(sal_Int64& rDistT, sal_Int64& rDistB, sal_Int64& rDistL,
+ sal_Int64& rDistR, sal_Int32& rLeftExt, sal_Int32& rTopExt,
+ sal_Int32& rRightExt, sal_Int32& rBottomExt)
+{
+ bool bLeft = lcl_makeSingleDistAndEffectExtentNonNegative(rDistL, rLeftExt);
+ bool bTop = lcl_makeSingleDistAndEffectExtentNonNegative(rDistT, rTopExt);
+ bool bRight = lcl_makeSingleDistAndEffectExtentNonNegative(rDistR, rRightExt);
+ bool bBottom = lcl_makeSingleDistAndEffectExtentNonNegative(rDistB, rBottomExt);
+ return bLeft && bTop && bRight && bBottom;
}
+void lcl_makeSingleDistZeroAndExtentNonNegative(sal_Int64& rDist, sal_Int32& rExt)
+{
+ if (static_cast<double>(rDist) + static_cast<double>(rExt)
+ >= static_cast<double>(SAL_MAX_INT32))
+ rExt = SAL_MAX_INT32;
+ else if (static_cast<double>(rDist) + static_cast<double>(rExt) <= 0)
+ rExt = 0;
+ else // 0 < rDist + rExt < SAL_MAX_INT32
+ {
+ rExt = static_cast<sal_Int32>(rDist + rExt);
+ if (rExt < 0)
+ rExt = 0;
+ }
+ rDist = 0;
+}
+
+void lcl_makeDistZeroAndExtentNonNegative(sal_Int64& rDistT, sal_Int64& rDistB, sal_Int64& rDistL,
+ sal_Int64& rDistR, sal_Int32& rLeftExt,
+ sal_Int32& rTopExt, sal_Int32& rRightExt,
+ sal_Int32& rBottomExt)
+{
+ lcl_makeSingleDistZeroAndExtentNonNegative(rDistL, rLeftExt);
+ lcl_makeSingleDistZeroAndExtentNonNegative(rDistT, rTopExt);
+ lcl_makeSingleDistZeroAndExtentNonNegative(rDistR, rRightExt);
+ lcl_makeSingleDistZeroAndExtentNonNegative(rDistB, rBottomExt);
+}
+} // end anonymous namespace
+
ExportDataSaveRestore::ExportDataSaveRestore(DocxExport& rExport, sal_uLong nStt, sal_uLong nEnd,
ww8::Frame const* pParentFrame)
: m_rExport(rExport)
@@ -354,17 +499,20 @@ void DocxSdrExport::setFlyWrapAttrList(
void DocxSdrExport::startDMLAnchorInline(const SwFrameFormat* pFrameFormat, const Size& rSize)
{
+ // Word uses size excluding right edge. Caller writeDMLDrawing and writeDiagram are changed for
+ // now. ToDo: Look whether the other callers give the size this way.
m_pImpl->setDrawingOpen(true);
m_pImpl->setParagraphHasDrawing(true);
m_pImpl->getSerializer()->startElementNS(XML_w, XML_drawing);
+ const SdrObject* pObj = pFrameFormat->FindRealSdrObject();
- // tdf#135047: It must be allowed to find in parents too, but default value of bInP parameter
- // for GetLRSpace() and GetULSpace() is true, so no direct setting is required.
- const SvxLRSpaceItem& aLRSpaceItem = pFrameFormat->GetLRSpace();
- const SvxULSpaceItem& aULSpaceItem = pFrameFormat->GetULSpace();
-
- bool isAnchor;
+ // LO determines the place needed for the object from wrap type, wrap margin ('distance to text'),
+ // object type and anchor type. Word uses dist* for user set margins and effectExtent for place
+ // needed for effects like shadow and glow, for fat stroke and for rotation. We map the LO values
+ // to values needed by Word so that the appearance is the same as far as possible.
+ // All values in Twips, change to EMU is done immediately before writing out.
+ bool isAnchor; // true XML_anchor, false XML_inline
if (m_pImpl->getFlyFrameGraphic())
{
isAnchor = false; // make Graphic object inside DMLTextFrame & VMLTextFrame as Inline
@@ -374,55 +522,95 @@ void DocxSdrExport::startDMLAnchorInline(const SwFrameFormat* pFrameFormat, cons
isAnchor = pFrameFormat->GetAnchor().GetAnchorId() != RndStdIds::FLY_AS_CHAR;
}
- // Count effectExtent values, their value is needed before dist{T,B,L,R} is written.
- SvxShadowItem aShadowItem = pFrameFormat->GetShadow();
- sal_Int32 nLeftExt = 0;
- sal_Int32 nRightExt = 0;
- sal_Int32 nTopExt = 0;
- sal_Int32 nBottomExt = 0;
- if (aShadowItem.GetLocation() != SvxShadowLocation::NONE)
+ // tdf#135047: It must be allowed to find in parents too, but default value of bInP parameter
+ // for GetLRSpace() and GetULSpace() is true, so no direct setting is required.
+ const SvxLRSpaceItem& aLRSpaceItem = pFrameFormat->GetLRSpace();
+ const SvxULSpaceItem& aULSpaceItem = pFrameFormat->GetULSpace();
+ sal_Int64 nDistT = aULSpaceItem.GetUpper();
+ sal_Int64 nDistB = aULSpaceItem.GetLower();
+ sal_Int64 nDistL = aLRSpaceItem.GetLeft();
+ sal_Int64 nDistR = aLRSpaceItem.GetRight();
+
+ // LibreOffice behaves different for frames and drawing objects, but MS Office treats frames
+ // as drawing objects too. Therefore we transform the values from frame so as if they come
+ // from a drawing object.
+ sal_Int32 nWidthDiff(0);
+ sal_Int32 nHeightDiff(0);
+ sal_Int32 nPosXDiff(0);
+ sal_Int32 nPosYDiff(0);
+ sal_Int32 nLeftExt(0);
+ sal_Int32 nRightExt(0);
+ sal_Int32 nTopExt(0);
+ sal_Int32 nBottomExt(0);
+
+ if ((!pObj) || (pObj && (pObj->GetObjIdentifier() == SwFlyDrawObjIdentifier)))
{
- sal_Int32 nShadowWidth(TwipsToEMU(aShadowItem.GetWidth()));
- switch (aShadowItem.GetLocation())
+ // Frame objects have a restricted shadow and no further effects. They have border instead of
+ // stroke. LO includes shadow and border in the object size, but Word not.
+ SvxShadowItem aShadowItem = pFrameFormat->GetShadow();
+ if (aShadowItem.GetLocation() != SvxShadowLocation::NONE)
{
- case SvxShadowLocation::TopLeft:
- nTopExt = nLeftExt = nShadowWidth;
- break;
- case SvxShadowLocation::TopRight:
- nTopExt = nRightExt = nShadowWidth;
- break;
- case SvxShadowLocation::BottomLeft:
- nBottomExt = nLeftExt = nShadowWidth;
- break;
- case SvxShadowLocation::BottomRight:
- nBottomExt = nRightExt = nShadowWidth;
- break;
- case SvxShadowLocation::NONE:
- case SvxShadowLocation::End:
- break;
+ sal_Int32 nShadowWidth(aShadowItem.GetWidth());
+ switch (aShadowItem.GetLocation())
+ {
+ case SvxShadowLocation::TopLeft:
+ nTopExt = nLeftExt = nShadowWidth;
+ nPosXDiff = nLeftExt; // actual move is postponed
+ nPosYDiff = nTopExt;
+ nWidthDiff = -nLeftExt; // actual size extent is postponed
+ nHeightDiff = -nTopExt;
+ break;
+ case SvxShadowLocation::TopRight:
+ nTopExt = nRightExt = nShadowWidth;
+ nPosYDiff = nTopExt;
+ nWidthDiff = -nRightExt;
+ nHeightDiff = -nTopExt;
+ break;
+ case SvxShadowLocation::BottomLeft:
+ nBottomExt = nLeftExt = nShadowWidth;
+ nPosXDiff = nLeftExt;
+ nWidthDiff = -nLeftExt;
+ nHeightDiff = -nBottomExt;
+ break;
+ case SvxShadowLocation::BottomRight:
+ nBottomExt = nRightExt = nShadowWidth;
+ nWidthDiff = -nRightExt;
+ nHeightDiff = -nBottomExt;
+ break;
+ case SvxShadowLocation::NONE:
+ case SvxShadowLocation::End:
+ break;
+ }
}
+ // ToDo: Position refers to outer edge of border in LO, but to center of border in Word.
+ // Adaption is missing here. Frames in LO have no stroke but border. The current conversion
+ // from border to line treats borders like table borders. That might give wrong values
+ // for drawing frames.
}
- else if (const SdrObject* pObject = pFrameFormat->FindRealSdrObject())
+ else // other objects than frames. pObj exists.
{
- // No shadow, but we have an idea what was the original effectExtent.
- uno::Any aAny;
- pObject->GetGrabBagItem(aAny);
- comphelper::SequenceAsHashMap aGrabBag(aAny);
- auto it = aGrabBag.find("CT_EffectExtent");
- if (it != aGrabBag.end())
+ // Word cannot handle negative EffectExtent although allowed in OOXML, the 'dist' attributes
+ // may not be negative. Take care of that.
+ if (isAnchor)
{
- comphelper::SequenceAsHashMap aEffectExtent(it->second);
- for (const std::pair<const OUString, uno::Any>& rDirection : aEffectExtent)
- {
- if (rDirection.first == "l" && rDirection.second.has<sal_Int32>())
- nLeftExt = rDirection.second.get<sal_Int32>();
- else if (rDirection.first == "t" && rDirection.second.has<sal_Int32>())
- nTopExt = rDirection.second.get<sal_Int32>();
- else if (rDirection.first == "r" && rDirection.second.has<sal_Int32>())
- nRightExt = rDirection.second.get<sal_Int32>();
- else if (rDirection.first == "b" && rDirection.second.has<sal_Int32>())
- nBottomExt = rDirection.second.get<sal_Int32>();
- }
+ lcl_calculateRawEffectExtent(nLeftExt, nRightExt, nTopExt, nBottomExt, *pObj, true);
+ // We have calculated the effectExtent from boundRect, therefore half stroke width is
+ // already contained.
+ // ToDo: The other half of the strokeWidth needs to be subtracted from padding.
+ // Where is that?
+ // ToDo: bool bCompansated = ... to be later able to switch from wrapSquare to wrapTight,
+ // if wrapSquare would require negative effectExtent.
+ lcl_makeDistAndExtentNonNegative(nDistT, nDistB, nDistL, nDistR, nLeftExt, nTopExt,
+ nRightExt, nBottomExt);
+ }
+ else
+ {
+ lcl_calculateRawEffectExtent(nLeftExt, nRightExt, nTopExt, nBottomExt, *pObj, false);
+ // nDistT,... contain the needed distances from import or set by user. But Word
+ // ignores Dist attributes of inline shapes. So we move all needed distances to
+ // effectExtent and force effectExtent to non-negative.
+ lcl_makeDistZeroAndExtentNonNegative(nDistT, nDistB, nDistL, nDistR, nLeftExt, nTopExt,
+ nRightExt, nBottomExt);
}
}
@@ -430,12 +618,9 @@ void DocxSdrExport::startDMLAnchorInline(const SwFrameFormat* pFrameFormat, cons
{
rtl::Reference<sax_fastparser::FastAttributeList> attrList
= sax_fastparser::FastSerializerHelper::createAttrList();
+
bool bOpaque = pFrameFormat->GetOpaque().GetValue();
- awt::Point aPos(pFrameFormat->GetHoriOrient().GetPos(),
- pFrameFormat->GetVertOrient().GetPos());
- const SdrObject* pObj = pFrameFormat->FindRealSdrObject();
- Degree100 nRotation(0);
- if (pObj != nullptr)
+ if (pObj)
{
// SdrObjects know their layer, consider that instead of the frame format.
bOpaque = pObj->GetLayer()
@@ -444,46 +629,17 @@ void DocxSdrExport::startDMLAnchorInline(const SwFrameFormat* pFrameFormat, cons
!= pFrameFormat->GetDoc()
->getIDocumentDrawModelAccess()
.GetInvisibleHellId();
-
- // Do not do this with lines.
- if (pObj->GetObjIdentifier() != OBJ_LINE)
- {
- nRotation = pObj->GetRotateAngle();
- lclMovePositionWithRotation(aPos, rSize, nRotation);
- }
}
attrList->add(XML_behindDoc, bOpaque ? "0" : "1");
- sal_Int32 nLineWidth = 0;
- if (const SdrObject* pObject = pFrameFormat->FindRealSdrObject())
- {
- nLineWidth = pObject->GetMergedItem(XATTR_LINEWIDTH).GetValue();
- }
- // Extend distance with the effect extent if the shape is not rotated, which is the opposite
- // of the mapping done at import time.
- // The type of dist* attributes is unsigned, so make sure no negative value is written.
- sal_Int64 nTopExtDist = nRotation ? 0 : nTopExt;
- nTopExtDist -= TwipsToEMU(nLineWidth / 2);
- sal_Int64 nDistT = std::max(static_cast<sal_Int64>(0),
- TwipsToEMU(aULSpaceItem.GetUpper()) - nTopExtDist);
- attrList->add(XML_distT, OString::number(nDistT).getStr());
- sal_Int64 nBottomExtDist = nRotation ? 0 : nBottomExt;
- nBottomExtDist -= TwipsToEMU(nLineWidth / 2);
- sal_Int64 nDistB = std::max(static_cast<sal_Int64>(0),
- TwipsToEMU(aULSpaceItem.GetLower()) - nBottomExtDist);
- attrList->add(XML_distB, OString::number(nDistB).getStr());
- sal_Int64 nLeftExtDist = nRotation ? 0 : nLeftExt;
- nLeftExtDist -= TwipsToEMU(nLineWidth / 2);
- sal_Int64 nDistL = std::max(static_cast<sal_Int64>(0),
- TwipsToEMU(aLRSpaceItem.GetLeft()) - nLeftExtDist);
- attrList->add(XML_distL, OString::number(nDistL).getStr());
- sal_Int64 nRightExtDist = nRotation ? 0 : nRightExt;
- nRightExtDist -= TwipsToEMU(nLineWidth / 2);
- sal_Int64 nDistR = std::max(static_cast<sal_Int64>(0),
- TwipsToEMU(aLRSpaceItem.GetRight()) - nRightExtDist);
- attrList->add(XML_distR, OString::number(nDistR).getStr());
+ attrList->add(XML_distT, OString::number(TwipsToEMU(nDistT)).getStr());
+ attrList->add(XML_distB, OString::number(TwipsToEMU(nDistB)).getStr());
+ attrList->add(XML_distL, OString::number(TwipsToEMU(nDistL)).getStr());
+ attrList->add(XML_distR, OString::number(TwipsToEMU(nDistR)).getStr());
+
attrList->add(XML_simplePos, "0");
attrList->add(XML_locked, "0");
+
bool bLclInTabCell = true;
if (pObj)
{
@@ -497,24 +653,40 @@ void DocxSdrExport::startDMLAnchorInline(const SwFrameFormat* pFrameFormat, cons
attrList->add(XML_layoutInCell, "1");
else
attrList->add(XML_layoutInCell, "0");
+
bool bAllowOverlap = pFrameFormat->GetWrapInfluenceOnObjPos().GetAllowOverlap();
attrList->add(XML_allowOverlap, bAllowOverlap ? "1" : "0");
- if (pObj != nullptr)
+
+ if (pObj)
// It seems 0 and 1 have special meaning: just start counting from 2 to avoid issues with that.
attrList->add(XML_relativeHeight, OString::number(pObj->GetOrdNum() + 2));
else
// relativeHeight is mandatory attribute, if value is not present, we must write default value
attrList->add(XML_relativeHeight, "0");
- if (pObj != nullptr)
+
+ if (pObj)
{
OUString sAnchorId = lclGetAnchorIdFromGrabBag(pObj);
if (!sAnchorId.isEmpty())
attrList->addNS(XML_wp14, XML_anchorId,
OUStringToOString(sAnchorId, RTL_TEXTENCODING_UTF8));
}
+
m_pImpl->getSerializer()->startElementNS(XML_wp, XML_anchor, attrList);
+
m_pImpl->getSerializer()->singleElementNS(XML_wp, XML_simplePos, XML_x, "0", XML_y,
"0"); // required, unused
+
+ // Position is either determined by coordinates aPos or alignment keywords like 'center'.
+ // First prepare them.
+ awt::Point aPos(pFrameFormat->GetHoriOrient().GetPos(),
+ pFrameFormat->GetVertOrient().GetPos());
+
+ aPos.X += nPosXDiff; // Make the postponed position move of frames.
+ aPos.Y += nPosYDiff;
+ if (pObj && lcl_IsRotateAngleValid(*pObj))
+ lclMovePositionWithRotation(aPos, rSize, pObj->GetRotateAngle());
+
const char* relativeFromH;
const char* relativeFromV;
const char* alignH = nullptr;
@@ -611,8 +783,11 @@ void DocxSdrExport::startDMLAnchorInline(const SwFrameFormat* pFrameFormat, cons
default:
break;
}
+
+ // write out horizontal position
m_pImpl->getSerializer()->startElementNS(XML_wp, XML_positionH, XML_relativeFrom,
relativeFromH);
+
/**
* Sizes of integral types
* climits header defines constants with the limits of integral types for the specific system and compiler implementation used.
@@ -620,6 +795,7 @@ void DocxSdrExport::startDMLAnchorInline(const SwFrameFormat* pFrameFormat, cons
**/
const sal_Int64 MAX_INTEGER_VALUE = SAL_MAX_INT32;
const sal_Int64 MIN_INTEGER_VALUE = SAL_MIN_INT32;
+
if (alignH != nullptr)
{
m_pImpl->getSerializer()->startElementNS(XML_wp, XML_align);
@@ -654,9 +830,10 @@ void DocxSdrExport::startDMLAnchorInline(const SwFrameFormat* pFrameFormat, cons
m_pImpl->getSerializer()->endElementNS(XML_wp, XML_posOffset);
}
m_pImpl->getSerializer()->endElementNS(XML_wp, XML_positionH);
+
+ // write out vertical position
m_pImpl->getSerializer()->startElementNS(XML_wp, XML_positionV, XML_relativeFrom,
relativeFromV);
-
sal_Int64 nPosYEMU = TwipsToEMU(aPos.Y);
// tdf#93675, 0 below line/paragraph and/or top line/paragraph with
@@ -693,16 +870,16 @@ void DocxSdrExport::startDMLAnchorInline(const SwFrameFormat* pFrameFormat, cons
}
m_pImpl->getSerializer()->endElementNS(XML_wp, XML_positionV);
}
- else
+ else // inline
{
+ // nDist is forced to zero above and ignored by Word anyway, so write 0 directly.
rtl::Reference<sax_fastparser::FastAttributeList> aAttrList
= sax_fastparser::FastSerializerHelper::createAttrList();
- aAttrList->add(XML_distT, OString::number(TwipsToEMU(aULSpaceItem.GetUpper())).getStr());
- aAttrList->add(XML_distB, OString::number(TwipsToEMU(aULSpaceItem.GetLower())).getStr());
- aAttrList->add(XML_distL, OString::number(TwipsToEMU(aLRSpaceItem.GetLeft())).getStr());
- aAttrList->add(XML_distR, OString::number(TwipsToEMU(aLRSpaceItem.GetRight())).getStr());
- const SdrObject* pObj = pFrameFormat->FindRealSdrObject();
- if (pObj != nullptr)
+ aAttrList->add(XML_distT, OString::number(0).getStr());
+ aAttrList->add(XML_distB, OString::number(0).getStr());
+ aAttrList->add(XML_distL, OString::number(0).getStr());
+ aAttrList->add(XML_distR, OString::number(0).getStr());
+ if (pObj)
{
OUString sAnchorId = lclGetAnchorIdFromGrabBag(pObj);
if (!sAnchorId.isEmpty())
@@ -712,8 +889,7 @@ void DocxSdrExport::startDMLAnchorInline(const SwFrameFormat* pFrameFormat, cons
m_pImpl->getSerializer()->startElementNS(XML_wp, XML_inline, aAttrList);
}
- // now the common parts
- // extent of the image
+ // now the common parts 'extent' and 'effectExtent'
/**
* Extent width is of type long ( i.e cx & cy ) as
*
@@ -731,26 +907,78 @@ void DocxSdrExport::startDMLAnchorInline(const SwFrameFormat* pFrameFormat, cons
* 2147483647( MAX_INTEGER_VALUE ).
* Therefore changing the following accordingly so that LO sync's up with MSO.
**/
- sal_uInt64 cx
- = TwipsToEMU(std::clamp(rSize.Width(), tools::Long(0), tools::Long(SAL_MAX_INT32)));
+ sal_uInt64 cx = TwipsToEMU(
+ std::clamp(rSize.Width() + nWidthDiff, tools::Long(0), tools::Long(SAL_MAX_INT32)));
OString aWidth(OString::number(std::min(cx, sal_uInt64(SAL_MAX_INT32))));
- sal_uInt64 cy
- = TwipsToEMU(std::clamp(rSize.Height(), tools::Long(0), tools::Long(SAL_MAX_INT32)));
+ sal_uInt64 cy = TwipsToEMU(
+ std::clamp(rSize.Height() + nHeightDiff, tools::Long(0), tools::Long(SAL_MAX_INT32)));
OString aHeight(OString::number(std::min(cy, sal_uInt64(SAL_MAX_INT32))));
m_pImpl->getSerializer()->singleElementNS(XML_wp, XML_extent, XML_cx, aWidth, XML_cy, aHeight);
- // effectExtent, extent including the effect (shadow only for now)
+ // XML_effectExtent, includes effects, fat stroke and rotation
+ // FixMe: tdf141880. Because LibreOffice currently cannot handle negative vertical margins, they
+ // were forced to zero on import. Especially bottom margin of inline anchored rotated objects are
+ // affected. If the object was not changed, it would be better to export the original values
+ // from grab-Bag. Unfortulately there exists no marker for "not changed", so a heuristic is used
+ // here: If current left, top and right margins do not differ more than 1Hmm = 635EMU from the
+ // values in grab-Bag, it is likely, that the object was not changed and we restore the values
+ // from grab-Bag.
+ sal_Int64 nLeftExtEMU = TwipsToEMU(nLeftExt);
+ sal_Int64 nTopExtEMU = TwipsToEMU(nTopExt);
+ sal_Int64 nRightExtEMU = TwipsToEMU(nRightExt);
+ sal_Int64 nBottomExtEMU = TwipsToEMU(nBottomExt);
+ if (pObj)
+ {
+ uno::Any aAny;
+ pObj->GetGrabBagItem(aAny);
+ comphelper::SequenceAsHashMap aGrabBag(aAny);
+ auto it = aGrabBag.find("CT_EffectExtent");
+ if (it != aGrabBag.end())
+ {
+ comphelper::SequenceAsHashMap aEffectExtent(it->second);
+ sal_Int64 nLeftExtGrabBag(0);
+ sal_Int64 nTopExtGrabBag(0);
+ sal_Int64 nRightExtGrabBag(0);
+ sal_Int64 nBottomExtGrabBag(0);
+ for (const std::pair<const OUString, uno::Any>& rDirection : aEffectExtent)
+ {
+ if (rDirection.first == "l" && rDirection.second.has<sal_Int32>())
+ nLeftExtGrabBag = rDirection.second.get<sal_Int32>();
+ else if (rDirection.first == "t" && rDirection.second.has<sal_Int32>())
+ nTopExtGrabBag = rDirection.second.get<sal_Int32>();
+ else if (rDirection.first == "r" && rDirection.second.has<sal_Int32>())
+ nRightExtGrabBag = rDirection.second.get<sal_Int32>();
+ else if (rDirection.first == "b" && rDirection.second.has<sal_Int32>())
+ nBottomExtGrabBag = rDirection.second.get<sal_Int32>();
+ }
+ if (abs(nLeftExtEMU - nLeftExtGrabBag) <= 635 && abs(nTopExtEMU - nTopExtGrabBag) <= 635
+ && abs(nRightExtEMU - nRightExtGrabBag) <= 635)
+ {
+ nLeftExtEMU = nLeftExtGrabBag;
+ nTopExtEMU = nTopExtGrabBag;
+ nRightExtEMU = nRightExtGrabBag;
+ nBottomExtEMU = nBottomExtGrabBag;
+ }
+ }
+ }
m_pImpl->getSerializer()->singleElementNS(
- XML_wp, XML_effectExtent, XML_l, OString::number(nLeftExt), XML_t, OString::number(nTopExt),
- XML_r, OString::number(nRightExt), XML_b, OString::number(nBottomExt));
+ XML_wp, XML_effectExtent, XML_l, OString::number(nLeftExtEMU), XML_t,
+ OString::number(nTopExtEMU), XML_r, OString::number(nRightExtEMU), XML_b,
+ OString::number(nBottomExtEMU));
+
+ if (!isAnchor)
+ return; // OOXML 'inline' has not wrap type at all
+
+ // XML_anchor has exact one of types wrapNone, wrapSquare, wrapTight, wrapThrough and
+ // WrapTopAndBottom. Map our own types to them as far as possible.
+ sal_Int32 nWrapToken = 0; // 0 indicates that no wrap type is yet determined.
// See if we know the exact wrap type from grab-bag.
- sal_Int32 nWrapToken = 0;
- if (const SdrObject* pObject = pFrameFormat->FindRealSdrObject())
+ if (pObj)
{
uno::Any aAny;
- pObject->GetGrabBagItem(aAny);
+ pObj->GetGrabBagItem(aAny);
comphelper::SequenceAsHashMap aGrabBag(aAny);
auto it = aGrabBag.find("EG_WrapType");
if (it != aGrabBag.end())
@@ -891,10 +1119,10 @@ void DocxSdrExport::startDMLAnchorInline(const SwFrameFormat* pFrameFormat, cons
}
}
- // No? Then just approximate based on what we have.
- if (!isAnchor || nWrapToken)
+ if (nWrapToken)
return;
+ // No wrap type determined yet? Then just approximate based on what we have.
switch (pFrameFormat->GetSurround().GetValue())
{
case css::text::WrapTextMode_NONE:
@@ -973,7 +1201,7 @@ void DocxSdrExport::writeDMLDrawing(const SdrObject* pSdrObject, const SwFrameFo
m_pImpl->getExport().DocxAttrOutput().GetSdtEndBefore(pSdrObject);
sax_fastparser::FSHelperPtr pFS = m_pImpl->getSerializer();
- Size aSize(pSdrObject->GetLogicRect().GetWidth(), pSdrObject->GetLogicRect().GetHeight());
+ Size aSize(pSdrObject->GetLogicRect().getWidth(), pSdrObject->GetLogicRect().getHeight());
startDMLAnchorInline(pFrameFormat, aSize);
rtl::Reference<sax_fastparser::FastAttributeList> pDocPrAttrList
@@ -1242,7 +1470,7 @@ void DocxSdrExport::writeDiagram(const SdrObject* sdrObject, const SwFrameFormat
uno::UNO_QUERY);
// write necessary tags to document.xml
- Size aSize(sdrObject->GetSnapRect().GetWidth(), sdrObject->GetSnapRect().GetHeight());
+ Size aSize(sdrObject->GetSnapRect().getWidth(), sdrObject->GetSnapRect().getHeight());
startDMLAnchorInline(&rFrameFormat, aSize);
m_pImpl->getDrawingML()->WriteDiagram(xShape, nDiagramId);