[Libreoffice-commits] core.git: sw/qa sw/source writerfilter/source
Regina Henschel (via logerrit)
logerrit at kemper.freedesktop.org
Wed Jul 14 09:35:20 UTC 2021
sw/qa/extras/ooxmlexport/data/tdf143219ContourWrap_glow_rotate.docx |binary
sw/qa/extras/ooxmlexport/data/tdf143219ContourWrap_stroke_shadow.docx |binary
sw/qa/extras/ooxmlexport/ooxmlexport16.cxx | 51 ++++
sw/qa/extras/ooxmlimport/data/tdf143219_ContourWrap_rotate.docx |binary
sw/qa/extras/ooxmlimport/ooxmlimport2.cxx | 27 ++
sw/source/filter/ww8/docxsdrexport.cxx | 36 ++-
writerfilter/source/dmapper/GraphicImport.cxx | 112 ++++++++--
7 files changed, 210 insertions(+), 16 deletions(-)
New commits:
commit af99c01570adc0c9205e1650d10866f80bb8118a
Author: Regina Henschel <rb.henschel at t-online.de>
AuthorDate: Mon Jul 5 22:21:23 2021 +0200
Commit: Miklos Vajna <vmiklos at collabora.com>
CommitDate: Wed Jul 14 11:34:43 2021 +0200
tdf#143219 improve docx import/export of contour wrap
The current solution has some problems:
(1)
Currently effectExtent is used on import to increase the wrap margins
for fat stroke and effects like shadow and glow in case of contour
wrap.
Problems:
Word writes these values, but actually the wrap polygon is authorative
and third party applications might not write these values at all.
The wrap margins were increased on import, but not decreased on export.
The effectExtent values by Word contain in addition the amount for
rotation as needed in wrapSquare. That part was not removed.
(2)
To avoid negative values in dist* values they were compensated with
effectExtent. For that, the complete effectExtent was shifted to dist*
and effectExtent was set to zero.
Problems:
Contour wrap does not use effectExtent but a wrap polygon. This is set
by Word in a way, that it includes the effect extents. LO uses the
original wrap polygon if available. So moving the effectExtent values
to dist* gives too large 'distance to text' in case of contour wrap,
because effects were considered twice.
The solution here replaces the way, how the margins are calculated in
case of contour wrap. Now the range used by the wrap polygon is
compared with the range used by the shape geometry. The difference is
added to 'distance to text'. To be able to remove these values on
export, they are put into the InteropGrabBag.
LO and Word use different concepts for contour wrap. Any solution gives
only an approximation. But adapting 'distance to text' brings
rendering in LO nearer to the way Word renders contour wrap.
Change-Id: Ic3c1c075fedfa7f79e4fe1f3c095da12cf274e36
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/118538
Tested-by: Jenkins
Reviewed-by: Miklos Vajna <vmiklos at collabora.com>
diff --git a/sw/qa/extras/ooxmlexport/data/tdf143219ContourWrap_glow_rotate.docx b/sw/qa/extras/ooxmlexport/data/tdf143219ContourWrap_glow_rotate.docx
new file mode 100644
index 000000000000..b83188073bea
Binary files /dev/null and b/sw/qa/extras/ooxmlexport/data/tdf143219ContourWrap_glow_rotate.docx differ
diff --git a/sw/qa/extras/ooxmlexport/data/tdf143219ContourWrap_stroke_shadow.docx b/sw/qa/extras/ooxmlexport/data/tdf143219ContourWrap_stroke_shadow.docx
new file mode 100644
index 000000000000..53dfb488a2ae
Binary files /dev/null and b/sw/qa/extras/ooxmlexport/data/tdf143219ContourWrap_stroke_shadow.docx differ
diff --git a/sw/qa/extras/ooxmlexport/ooxmlexport16.cxx b/sw/qa/extras/ooxmlexport/ooxmlexport16.cxx
index 504b2d3ea802..7a1486f6a46c 100644
--- a/sw/qa/extras/ooxmlexport/ooxmlexport16.cxx
+++ b/sw/qa/extras/ooxmlexport/ooxmlexport16.cxx
@@ -72,6 +72,57 @@ protected:
return OString(filename).endsWith(".docx");
}
};
+
+CPPUNIT_TEST_FIXTURE(Test, testTdf134219ContourWrap_glow_rotate)
+{
+ auto verify = [this]() {
+ CPPUNIT_ASSERT_DOUBLES_EQUAL(sal_Int32(1461),
+ getProperty<sal_Int32>(getShape(1), "LeftMargin"), 2);
+ CPPUNIT_ASSERT_DOUBLES_EQUAL(sal_Int32(1302),
+ getProperty<sal_Int32>(getShape(1), "RightMargin"), 1);
+ CPPUNIT_ASSERT_DOUBLES_EQUAL(sal_Int32(1522),
+ getProperty<sal_Int32>(getShape(1), "TopMargin"), 1);
+ CPPUNIT_ASSERT_DOUBLES_EQUAL(sal_Int32(1296),
+ getProperty<sal_Int32>(getShape(1), "BottomMargin"), 1);
+ };
+ // Given a document with a shape with contour wrap, that has glow effect and rotation.
+ load(mpTestDocumentPath, "tdf143219ContourWrap_glow_rotate.docx");
+
+ // Error was, that the margins, which were added on import to approximate Word's rendering of
+ // contour wrap, contained the effect extent for rotation. But LibreOffice extents the wrap
+ // distance automatically. The distance was too large on first load and because the extent was
+ // not removed on export, much larger on reload.
+ // Test fails on reload without fix with left: expected 1461 actual 2455; right: expected 1302
+ // actual 4177; top: expected 1522 actual 2457; bottom: expected 1296, actual 4179
+ verify();
+ reload(mpFilter, "tdf143219ContourWrap_glow_rotate.docx");
+ verify();
+}
+
+CPPUNIT_TEST_FIXTURE(Test, testTdf134219ContourWrap_stroke_shadow)
+{
+ auto verify = [this]() {
+ CPPUNIT_ASSERT_DOUBLES_EQUAL(sal_Int32(318),
+ getProperty<sal_Int32>(getShape(1), "LeftMargin"), 1);
+ CPPUNIT_ASSERT_DOUBLES_EQUAL(sal_Int32(1164),
+ getProperty<sal_Int32>(getShape(1), "RightMargin"), 1);
+ CPPUNIT_ASSERT_DOUBLES_EQUAL(sal_Int32(318),
+ getProperty<sal_Int32>(getShape(1), "TopMargin"), 1);
+ CPPUNIT_ASSERT_DOUBLES_EQUAL(sal_Int32(1164),
+ getProperty<sal_Int32>(getShape(1), "BottomMargin"), 1);
+ };
+ // Given a document with a shape with contour wrap, that has a fat stroke and large shadow.
+ load(mpTestDocumentPath, "tdf143219ContourWrap_stroke_shadow.docx");
+
+ // Error was, that the margins, which were added on import to approximate Word's rendering of
+ // contour wrap, were not removed on export and so used twice on reload.
+ // Test after reload would fail without fix with
+ // left, top: expected 318 actual 635; right, bottom: expected 1164 actual 2434
+ verify();
+ reload(mpFilter, "tdf143219ContourWrap_stroke_shadow.docx");
+ verify();
+}
+
DECLARE_OOXMLEXPORT_TEST(testTdf123569_rotWriterImage, "tdf123569_rotWriterImage_46deg.odt")
{
uno::Reference<beans::XPropertySet> xFrame(getShape(1), uno::UNO_QUERY);
diff --git a/sw/qa/extras/ooxmlimport/data/tdf143219_ContourWrap_rotate.docx b/sw/qa/extras/ooxmlimport/data/tdf143219_ContourWrap_rotate.docx
new file mode 100644
index 000000000000..8922882df50b
Binary files /dev/null and b/sw/qa/extras/ooxmlimport/data/tdf143219_ContourWrap_rotate.docx differ
diff --git a/sw/qa/extras/ooxmlimport/ooxmlimport2.cxx b/sw/qa/extras/ooxmlimport/ooxmlimport2.cxx
index fe39e78a6423..257ffb234b6e 100644
--- a/sw/qa/extras/ooxmlimport/ooxmlimport2.cxx
+++ b/sw/qa/extras/ooxmlimport/ooxmlimport2.cxx
@@ -48,6 +48,33 @@ public:
}
};
+CPPUNIT_TEST_FIXTURE(Test, testTdf143219ContourWrapRotate)
+{
+ load(mpTestDocumentPath, "tdf143219_ContourWrap_rotate.docx");
+ const uno::Reference<drawing::XShape> xShape = getShape(1);
+ const uno::Reference<beans::XPropertySet> xShapeProps(xShape, uno::UNO_QUERY_THROW);
+ sal_Int32 nWrapDistanceLeft = -1;
+ sal_Int32 nWrapDistanceRight = -1;
+ sal_Int32 nWrapDistanceTop = -1;
+ sal_Int32 nWrapDistanceBottom = -1;
+ xShapeProps->getPropertyValue("LeftMargin") >>= nWrapDistanceLeft;
+ xShapeProps->getPropertyValue("RightMargin") >>= nWrapDistanceRight;
+ xShapeProps->getPropertyValue("TopMargin") >>= nWrapDistanceTop;
+ xShapeProps->getPropertyValue("BottomMargin") >>= nWrapDistanceBottom;
+ // Word and Writer use different concepts for contour wrap. LO needs wrap margins to
+ // approximate Word's rendering.
+ // Without the fix in place left and right margin were too large, top and bottom margin too
+ // small. The test would have failed
+ // ... with expected 182 actual 1005.
+ CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("LeftMargin", 182, nWrapDistanceLeft, 1);
+ // ... with expected 183 actual 1005
+ CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("RightMargin", 183, nWrapDistanceRight, 1);
+ // ... with expected 42 actual 0
+ CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("TopMargin", 42, nWrapDistanceTop, 1);
+ // ... with expected 41 actual 0
+ CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("BottomMargin", 41, nWrapDistanceBottom, 1);
+}
+
CPPUNIT_TEST_FIXTURE(Test, testTdf108545_embeddedDocxIcon)
{
load(mpTestDocumentPath, "tdf108545_embeddedDocxIcon.docx");
diff --git a/sw/source/filter/ww8/docxsdrexport.cxx b/sw/source/filter/ww8/docxsdrexport.cxx
index 561bc475687b..80554455fa3b 100644
--- a/sw/source/filter/ww8/docxsdrexport.cxx
+++ b/sw/source/filter/ww8/docxsdrexport.cxx
@@ -221,8 +221,16 @@ bool lcl_makeSingleDistAndEffectExtentNonNegative(sal_Int64& rDist, sal_Int32& r
return false;
}
// rDist + rExt >= 0
- rDist += rExt;
- rExt = 0;
+ if (rDist < 0)
+ {
+ rExt += rDist;
+ rDist = 0;
+ }
+ else // rExt < 0
+ {
+ rDist += rExt;
+ rExt = 0;
+ }
return true;
}
@@ -616,8 +624,30 @@ void DocxSdrExport::startDMLAnchorInline(const SwFrameFormat* pFrameFormat, cons
lcl_calculateRawEffectExtent(nLeftExt, nTopExt, nRightExt, 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.
+ // ToDo: The other half of the stroke width needs to be subtracted from padding.
// Where is that?
+
+ // The import has added a difference to dist* in case of contour wrap for to give a
+ // rendering nearer to Word. In that case, we need to subtract it on export.
+ uno::Any aAny;
+ pObj->GetGrabBagItem(aAny);
+ comphelper::SequenceAsHashMap aGrabBag(aAny);
+ auto it = aGrabBag.find("AnchorDistDiff");
+ if (it != aGrabBag.end())
+ {
+ comphelper::SequenceAsHashMap aAnchorDistDiff(it->second);
+ for (const std::pair<const OUString, uno::Any>& rDiff : aAnchorDistDiff)
+ {
+ if (rDiff.first == "distTDiff" && rDiff.second.has<sal_Int32>())
+ nDistT -= round(rDiff.second.get<sal_Int32>());
+ else if (rDiff.first == "distBDiff" && rDiff.second.has<sal_Int32>())
+ nDistB -= round(rDiff.second.get<sal_Int32>());
+ else if (rDiff.first == "distLDiff" && rDiff.second.has<sal_Int32>())
+ nDistL -= rDiff.second.get<sal_Int32>();
+ else if (rDiff.first == "distRDiff" && rDiff.second.has<sal_Int32>())
+ nDistR -= rDiff.second.get<sal_Int32>();
+ }
+ }
// 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,
diff --git a/writerfilter/source/dmapper/GraphicImport.cxx b/writerfilter/source/dmapper/GraphicImport.cxx
index 0b0176516745..69bbc94f1225 100644
--- a/writerfilter/source/dmapper/GraphicImport.cxx
+++ b/writerfilter/source/dmapper/GraphicImport.cxx
@@ -71,7 +71,13 @@
#include "util.hxx"
#include <comphelper/propertysequence.hxx>
-#include <algorithm> // std::swap
+#include <algorithm>
+#include <basegfx/matrix/b2dhommatrixtools.hxx>
+#include <basegfx/matrix/b2dhommatrix.hxx>
+#include <basegfx/range/b2drange.hxx>
+#include <basegfx/numeric/ftools.hxx>
+#include <basegfx/polygon/b2dpolypolygontools.hxx>
+#include <basegfx/polygon/b2dpolypolygon.hxx>
using namespace css;
@@ -939,6 +945,9 @@ void GraphicImport::lcl_attribute(Id nName, Value& rValue)
}
m_xShape->setPosition(GetGraphicObjectPosition());
}
+ // ToDo: Rotated shapes with position type "Alignment" (UI of Word) have
+ // wrong position. Word aligns the unrotated logic rectangle, LO the rotated
+ // snap rectangle.
// Margin correction
if (m_pImpl->eGraphicImportType == IMPORT_AS_DETECTED_INLINE)
@@ -949,14 +958,14 @@ void GraphicImport::lcl_attribute(Id nName, Value& rValue)
// stroke and shadow. Simple add it to the margins.
sal_Int32 nEffectExtent = (m_pImpl->m_oEffectExtentLeft)
? oox::drawingml::convertEmuToHmm(*m_pImpl->m_oEffectExtentLeft)
- : 0;
- m_pImpl->nLeftMargin += nEffectExtent;
+ : 0;
+ m_pImpl->nLeftMargin += nEffectExtent;
nEffectExtent = (m_pImpl->m_oEffectExtentRight)
? oox::drawingml::convertEmuToHmm(*m_pImpl->m_oEffectExtentRight) : 0;
m_pImpl->nRightMargin += nEffectExtent;
nEffectExtent = (m_pImpl->m_oEffectExtentTop)
? oox::drawingml::convertEmuToHmm(*m_pImpl->m_oEffectExtentTop) : 0;
- m_pImpl->nTopMargin += nEffectExtent;
+ m_pImpl->nTopMargin += nEffectExtent;
nEffectExtent = (m_pImpl->m_oEffectExtentBottom)
? oox::drawingml::convertEmuToHmm(*m_pImpl->m_oEffectExtentBottom) : 0;
m_pImpl->nBottomMargin += nEffectExtent;
@@ -1029,21 +1038,98 @@ void GraphicImport::lcl_attribute(Id nName, Value& rValue)
aLOBoundRect.Height = ConversionHelper::convertTwipToMM100(aBoundRect.getHeight());
}
- m_pImpl->nLeftMargin += aLOBoundRect.X - aMSOBaseLeftTop.X;
+ m_pImpl->nLeftMargin += aLOBoundRect.X - aMSOBaseLeftTop.X;
m_pImpl->nRightMargin += aMSOBaseLeftTop.X + aMSOBaseSize.Width
- (aLOBoundRect.X + aLOBoundRect.Width);
- m_pImpl->nTopMargin += aLOBoundRect.Y - aMSOBaseLeftTop.Y;
+ m_pImpl->nTopMargin += aLOBoundRect.Y - aMSOBaseLeftTop.Y;
m_pImpl->nBottomMargin += aMSOBaseLeftTop.Y + aMSOBaseSize.Height
- (aLOBoundRect.Y + aLOBoundRect.Height);
}
- else // WrapTextMode_THROUGH(T) or mpWrapPolygon exists
+ else if (m_pImpl->mpWrapPolygon) // with contour
{
- // Word uses a wrap polygon in case of wrapTight or wrapThrough, which the
- // user can manipulate. Word writes values to effectExtent which would
- // be uses in case of wrapSquare, but ignores them for wrapTight or
- // wrapThrough. But they contain the information about the needed extent
- // for effects, rotation and fat stroke. So use them in that cases too
- // although LibreOffice has its own methods for contour.
+ // Word uses a wrap polygon, LibreOffice has no explicit wrap polygon
+ // but creates the wrap contour based on the shape geometry, without
+ // stroke width and shadow, but with rotation and flip. The concepts
+ // are not compatible. We approximate Word's rendering by setting
+ // wrap margins.
+
+ // Build a range from the wrap polygon from Word.
+ const drawing::PointSequenceSequence aWrapPolygon
+ = m_pImpl->mpWrapPolygon->getPointSequenceSequence();
+ basegfx::B2DPolyPolygon aB2DWrapPolyPolygon
+ = basegfx::utils::UnoPointSequenceSequenceToB2DPolyPolygon(
+ aWrapPolygon);
+ // Wrap polygon values are relative to 0..21600|0..21600.
+ // Scale to shape size (in Hmm).
+ basegfx::B2DHomMatrix aMatrix = basegfx::utils::createScaleB2DHomMatrix(
+ aImportSize.Width / 21600.0, aImportSize.Height / 21600.0);
+ aB2DWrapPolyPolygon.transform(aMatrix);
+
+ // Shape geometry will be rotated, rotate wrap polygon too.
+ if (nOOXAngle != 0)
+ {
+ aMatrix = basegfx::utils::createRotateAroundPoint(
+ aImportSize.Width / 2.0, aImportSize.Height / 2.0,
+ basegfx::deg2rad( nOOXAngle / 60000.0));
+ aB2DWrapPolyPolygon.transform(aMatrix);
+ }
+ basegfx::B2DRange aB2DWrapRange = aB2DWrapPolyPolygon.getB2DRange();
+
+ // Build a range from shape geometry
+ basegfx::B2DRange aShapeRange;
+ if (pShape)
+ {
+ basegfx::B2DPolyPolygon aShapePolygon = pShape->TakeXorPoly(); // Twips
+ constexpr double fTwips2Hmm = 127.0 / 72.0;
+ aMatrix = basegfx::utils::createScaleB2DHomMatrix(fTwips2Hmm, fTwips2Hmm);
+ aShapePolygon.transform(aMatrix);
+ // Wrap polygon treats left/top of shape as origin, shift shape polygon accordingly
+ aMatrix = basegfx::utils::createTranslateB2DHomMatrix(
+ -aImportPosition.X, -aImportPosition.Y);
+ aShapePolygon.transform(aMatrix);
+ aShapeRange = aShapePolygon.getB2DRange();
+ }
+ else // can this happen?
+ {
+ aShapeRange
+ = basegfx::B2DRange(0, 0, aImportSize.Width, aImportSize.Height);
+ if (nOOXAngle != 0)
+ {
+ aMatrix = basegfx::utils::createRotateB2DHomMatrix(
+ basegfx::deg2rad(nOOXAngle / 60000.0));
+ aShapeRange.transform(aMatrix);
+ }
+ }
+
+ // Add difference between shape and wrap range to margin and remember
+ // difference in Twips for export.
+ comphelper::SequenceAsHashMap aAnchorDistDiff;
+ constexpr double fHmm2Twips = 72.0 / 127.0;
+ const double fTopDiff = aShapeRange.getMinY() - aB2DWrapRange.getMinY();
+ m_pImpl->nTopMargin += basegfx::fround(fTopDiff);
+ aAnchorDistDiff["distTDiff"] <<= basegfx::fround(fTopDiff * fHmm2Twips);
+ const double fBottomDiff = aB2DWrapRange.getMaxY() - aShapeRange.getMaxY();
+ m_pImpl->nBottomMargin += basegfx::fround(fBottomDiff);
+ aAnchorDistDiff["distBDiff"] <<= basegfx::fround(fBottomDiff * fHmm2Twips);
+ const double fLeftDiff = aShapeRange.getMinX() - aB2DWrapRange.getMinX();
+ m_pImpl->nLeftMargin += basegfx::fround(fLeftDiff);
+ aAnchorDistDiff["distLDiff"] <<= basegfx::fround(fLeftDiff * fHmm2Twips);
+ const double fRightDiff = aB2DWrapRange.getMaxX() - aShapeRange.getMaxX();
+ m_pImpl->nRightMargin += basegfx::fround(fRightDiff);
+ aAnchorDistDiff["distRDiff"] <<= basegfx::fround(fRightDiff * fHmm2Twips);
+ m_pImpl->m_aInteropGrabBag["AnchorDistDiff"] <<= aAnchorDistDiff.getAsConstPropertyValueList();
+
+ // FixMe: tdf#141880. LibreOffice cannot handle negative horizontal margin in contour wrap
+ if (m_pImpl->nLeftMargin < 0)
+ m_pImpl->nLeftMargin = 0;
+ if (m_pImpl->nRightMargin < 0)
+ m_pImpl->nRightMargin = 0;
+ }
+ else // text::WrapTextMode_THROUGH
+ {
+ // Word writes and evaluates the effectExtent in case of position
+ // type 'Alignment' (UI). We move these values to margin to approximate
+ // Word's rendering.
if (m_pImpl->m_oEffectExtentLeft)
{
m_pImpl->nLeftMargin
More information about the Libreoffice-commits
mailing list