[Libreoffice-commits] core.git: sw/qa sw/source

Regina Henschel (via logerrit) logerrit at kemper.freedesktop.org
Fri Aug 13 14:01:10 UTC 2021


 sw/qa/extras/ooxmlexport/data/tdf136386_WrapPolygonCurve.odt       |binary
 sw/qa/extras/ooxmlexport/data/tdf136386_WrapPolygonLineShape.odt   |binary
 sw/qa/extras/ooxmlexport/data/tdf142433_WrapPolygonCustomShape.odt |binary
 sw/qa/extras/ooxmlexport/data/tdf143432_Frame_WrapTextMode.odt     |binary
 sw/qa/extras/ooxmlexport/ooxmlexport16.cxx                         |   57 +
 sw/qa/extras/uiwriter/data/tdf143760_ContourToWrapOff.docx         |binary
 sw/qa/extras/uiwriter/uiwriter4.cxx                                |   27 
 sw/source/filter/ww8/docxsdrexport.cxx                             |  435 ++++++----
 8 files changed, 372 insertions(+), 147 deletions(-)

New commits:
commit 61ef73bb9e06ac7325abc26698314d7e35c164cc
Author:     Regina Henschel <rb.henschel at t-online.de>
AuthorDate: Sun Aug 8 01:05:09 2021 +0200
Commit:     Miklos Vajna <vmiklos at collabora.com>
CommitDate: Fri Aug 13 16:00:35 2021 +0200

    tdf#143432 and more, improve wrap contour export
    
    tdf#143432 The example document triggers the case, that there is no
    own, no from docx imported, and no from customShape generated wrap
    polygon. In that case WrapTextMode_LEFT and WrapTextMode_RIGHT were not
    evaluated and default bothSides was written. The patch moves
    evaluation of WrapTextMode up and out of individual cases, so that
    result can be used in all cases.
    
    tdf#142433 The previous solution has written the enhanced-path of a
    customShape literally as wrap polygon. But it might contain references
    to equation or modifier. The patch uses the polyPolygon, which you
    can get from the customShape.
    
    tdf#143760 The previous solution took a wrap polygon, which was
    imported from docx unconditionally. So it misses the case that the user
    has changed the wrap type. The patch reorders it so, that first the
    current wrap type is evaluated and then a wrap polygon is determined
    if the wrap type needs it.
    
    tdf#136386 The previous solution wrote for shapes not imported from
    docx a wrap polygon only in case of customShapes. So for other kind
    of shapes contour wrap was lost. The patch adds wrap polygons for
    other kind of shapes too. Default is a rectangular wrap polygon. That
    is used too, if a shape type would need an individual wrap polygon,
    but that is not yet implemented.
    
    The wrap polygon for Bezier curves is correct, but the curve itself
    is wrongly exported. That is bug tdf#142605.
    
    The previous solution misses to normalize the wrap polygon to range
    [0..21600; 0..21600.] In case there was no contour wrap in the
    imported docx and the user sets a contour wrap, the wrap polygon
    had wrong range, because the enhanced-path of an ooxml-foo shape
    is not in range [0..21600; 0..21600].
    
    Change-Id: Idc678fd385b705e66b7d5cb02a7dea5925f2ada1
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/120169
    Tested-by: Jenkins
    Reviewed-by: Miklos Vajna <vmiklos at collabora.com>

diff --git a/sw/qa/extras/ooxmlexport/data/tdf136386_WrapPolygonCurve.odt b/sw/qa/extras/ooxmlexport/data/tdf136386_WrapPolygonCurve.odt
new file mode 100644
index 000000000000..70a8b7a5b4b1
Binary files /dev/null and b/sw/qa/extras/ooxmlexport/data/tdf136386_WrapPolygonCurve.odt differ
diff --git a/sw/qa/extras/ooxmlexport/data/tdf136386_WrapPolygonLineShape.odt b/sw/qa/extras/ooxmlexport/data/tdf136386_WrapPolygonLineShape.odt
new file mode 100644
index 000000000000..5d193f56ed18
Binary files /dev/null and b/sw/qa/extras/ooxmlexport/data/tdf136386_WrapPolygonLineShape.odt differ
diff --git a/sw/qa/extras/ooxmlexport/data/tdf142433_WrapPolygonCustomShape.odt b/sw/qa/extras/ooxmlexport/data/tdf142433_WrapPolygonCustomShape.odt
new file mode 100644
index 000000000000..fbe0b97d6da8
Binary files /dev/null and b/sw/qa/extras/ooxmlexport/data/tdf142433_WrapPolygonCustomShape.odt differ
diff --git a/sw/qa/extras/ooxmlexport/data/tdf143432_Frame_WrapTextMode.odt b/sw/qa/extras/ooxmlexport/data/tdf143432_Frame_WrapTextMode.odt
new file mode 100644
index 000000000000..6390c12db0b7
Binary files /dev/null and b/sw/qa/extras/ooxmlexport/data/tdf143432_Frame_WrapTextMode.odt differ
diff --git a/sw/qa/extras/ooxmlexport/ooxmlexport16.cxx b/sw/qa/extras/ooxmlexport/ooxmlexport16.cxx
index 9533db4d369e..17d6204f97fd 100644
--- a/sw/qa/extras/ooxmlexport/ooxmlexport16.cxx
+++ b/sw/qa/extras/ooxmlexport/ooxmlexport16.cxx
@@ -73,6 +73,63 @@ protected:
     }
 };
 
+DECLARE_OOXMLEXPORT_EXPORTONLY_TEST(testWrapPolygonCurve, "tdf136386_WrapPolygonCurve.odt")
+{
+    // Document has a curve with contour wrap and 'outside only'. Error was, that type 'square' was
+    // written and no wrap polygon. Make sure we write wrapTight and a wrapPolygon.
+    xmlDocUniquePtr pXmlDocument = parseExport("word/document.xml");
+    CPPUNIT_ASSERT(pXmlDocument);
+    assertXPath(pXmlDocument, "//wp:wrapTight", 1);
+    assertXPath(pXmlDocument, "//wp:wrapPolygon", 1);
+    assertXPath(pXmlDocument, "//wp:start", 1);
+    xmlXPathObjectPtr pXmlObj = getXPathNode(pXmlDocument, "//wp:lineTo");
+    CPPUNIT_ASSERT_GREATER(sal_Int32(2),
+                           static_cast<sal_Int32>(xmlXPathNodeSetGetLength(pXmlObj->nodesetval)));
+    xmlXPathFreeObject(pXmlObj);
+}
+
+DECLARE_OOXMLEXPORT_EXPORTONLY_TEST(testWrapPolygonLineShape, "tdf136386_WrapPolygonLineShape.odt")
+{
+    // Document has a sloping line with contour wrap. Error was, that type 'square' was written and
+    // no wrap polygon. Now we write 'through' and use wrap polygon 0|0, 21600|21600, 0|0.
+    xmlDocUniquePtr pXmlDocument = parseExport("word/document.xml");
+    CPPUNIT_ASSERT(pXmlDocument);
+    assertXPath(pXmlDocument, "//wp:wrapThrough", 1);
+    assertXPath(pXmlDocument, "//wp:lineTo", 2);
+    sal_Int32 nYCoord = getXPath(pXmlDocument, "(//wp:lineTo)[1]", "y").toInt32();
+    CPPUNIT_ASSERT_EQUAL(sal_Int32(21600), nYCoord);
+    sal_Int32 nXCoord = getXPath(pXmlDocument, "(//wp:lineTo)[2]", "x").toInt32();
+    CPPUNIT_ASSERT_EQUAL(sal_Int32(0), nXCoord);
+}
+
+DECLARE_OOXMLEXPORT_EXPORTONLY_TEST(testWrapPolygonCustomShape,
+                                    "tdf142433_WrapPolygonCustomShape.odt")
+{
+    // Document has 4-point star with contour wrap. Error was, that the enhanced path was written
+    // literally as wrap polygon. But that does not work, because path might have links to equations
+    // and handles and not only numbers.
+    xmlDocUniquePtr pXmlDocument = parseExport("word/document.xml");
+    CPPUNIT_ASSERT(pXmlDocument);
+    // Expected coordinates are 0|10800, 8936|8936, 10800|0, 12664|8936, 21600|10800, 12664|12664,
+    // 10800|21600, 8936|12664, 0|10800. Assert forth point, which comes from equations. Allow some
+    // tolerance.
+    sal_Int32 nXCoord = getXPath(pXmlDocument, "(//wp:lineTo)[3]", "x").toInt32();
+    // Without fix it would fail with expected 12664, actual 3
+    CPPUNIT_ASSERT_DOUBLES_EQUAL(12664, nXCoord, 10);
+    // Without fix it would fail with expected 8936, actual 4
+    sal_Int32 nYCoord = getXPath(pXmlDocument, "(//wp:lineTo)[3]", "y").toInt32();
+    CPPUNIT_ASSERT_DOUBLES_EQUAL(8936, nYCoord, 10);
+}
+
+DECLARE_OOXMLEXPORT_EXPORTONLY_TEST(testFrameWrapTextMode, "tdf143432_Frame_WrapTextMode.odt")
+{
+    xmlDocUniquePtr pXmlDocument = parseExport("word/document.xml");
+    CPPUNIT_ASSERT(pXmlDocument);
+    // Without the fix the value "largest" was written to file in both cases.
+    assertXPath(pXmlDocument, "(//wp:wrapSquare)[1]", "wrapText", "right");
+    assertXPath(pXmlDocument, "(//wp:wrapSquare)[2]", "wrapText", "left");
+}
+
 CPPUNIT_TEST_FIXTURE(Test, testTdf134219ContourWrap_glow_rotate)
 {
     auto verify = [this]() {
diff --git a/sw/qa/extras/uiwriter/data/tdf143760_ContourToWrapOff.docx b/sw/qa/extras/uiwriter/data/tdf143760_ContourToWrapOff.docx
new file mode 100644
index 000000000000..a604513c6b77
Binary files /dev/null and b/sw/qa/extras/uiwriter/data/tdf143760_ContourToWrapOff.docx differ
diff --git a/sw/qa/extras/uiwriter/uiwriter4.cxx b/sw/qa/extras/uiwriter/uiwriter4.cxx
index e079ff006aea..154b46704510 100644
--- a/sw/qa/extras/uiwriter/uiwriter4.cxx
+++ b/sw/qa/extras/uiwriter/uiwriter4.cxx
@@ -280,6 +280,7 @@ public:
     void testEmojiAutoCorrect();
     void testTdf129270();
     void testInsertPdf();
+    void testTdf143760WrapContourToOff();
 
     CPPUNIT_TEST_SUITE(SwUiWriterTest4);
     CPPUNIT_TEST(testTdf96515);
@@ -396,6 +397,7 @@ public:
     CPPUNIT_TEST(testEmojiAutoCorrect);
     CPPUNIT_TEST(testTdf129270);
     CPPUNIT_TEST(testInsertPdf);
+    CPPUNIT_TEST(testTdf143760WrapContourToOff);
     CPPUNIT_TEST_SUITE_END();
 };
 
@@ -3659,6 +3661,31 @@ void SwUiWriterTest4::testInsertPdf()
     CPPUNIT_ASSERT_EQUAL(OUString("application/pdf"), getProperty<OUString>(xGraphic, "MimeType"));
 }
 
+void SwUiWriterTest4::testTdf143760WrapContourToOff()
+{
+    // Actually, this is an ooxmlexport test. It is here because here is a ready environment
+    // to change a shape by dispatchCommand.
+    SwDoc* pDoc = createSwDoc(DATA_DIRECTORY, "tdf143760_ContourToWrapOff.docx");
+    CPPUNIT_ASSERT(pDoc);
+    CPPUNIT_ASSERT_EQUAL(true, getProperty<bool>(getShape(1), "SurroundContour"));
+
+    // Mark the object
+    SwWrtShell* pWrtShell = pDoc->GetDocShell()->GetWrtShell();
+    SdrPage* pPage = pDoc->getIDocumentDrawModelAccess().GetDrawModel()->GetPage(0);
+    SdrObject* pObject = pPage->GetObj(0);
+    CPPUNIT_ASSERT(pObject);
+    SdrView* pView = pWrtShell->GetDrawView();
+    pView->MarkObj(pObject, pView->GetSdrPageView());
+
+    // Set "wrap off"
+    dispatchCommand(mxComponent, ".uno:WrapOff", {});
+    CPPUNIT_ASSERT_EQUAL(false, getProperty<bool>(getShape(1), "SurroundContour"));
+
+    // Without fix this had failed, because the shape was written to file with contour.
+    reload("Office Open XML Text", "tdf143760_ContourToWrapOff.docx");
+    CPPUNIT_ASSERT_EQUAL(false, getProperty<bool>(getShape(1), "SurroundContour"));
+}
+
 CPPUNIT_TEST_SUITE_REGISTRATION(SwUiWriterTest4);
 CPPUNIT_PLUGIN_IMPLEMENT();
 
diff --git a/sw/source/filter/ww8/docxsdrexport.cxx b/sw/source/filter/ww8/docxsdrexport.cxx
index 821cda8d053b..d4e616d1ff65 100644
--- a/sw/source/filter/ww8/docxsdrexport.cxx
+++ b/sw/source/filter/ww8/docxsdrexport.cxx
@@ -17,8 +17,12 @@
 #include <editeng/shaditem.hxx>
 #include <editeng/opaqitem.hxx>
 #include <editeng/boxitem.hxx>
+#include <svx/svdoashp.hxx>
 #include <svx/svdogrp.hxx>
+#include <svx/svdopath.hxx>
 #include <svx/svdobjkind.hxx>
+#include <svx/svditer.hxx>
+#include <svx/EnhancedCustomShape2d.hxx>
 #include <oox/token/namespaces.hxx>
 #include <oox/token/relationship.hxx>
 #include <textboxhelper.hxx>
@@ -42,6 +46,7 @@
 #include <tools/diagnose_ex.h>
 #include <svx/xlnwtit.hxx>
 #include <svx/svdtrans.hxx>
+#include <basegfx/matrix/b2dhommatrixtools.hxx>
 
 using namespace com::sun::star;
 using namespace oox;
@@ -277,6 +282,180 @@ void lcl_makeDistZeroAndExtentNonNegative(sal_Int64& rDistT, sal_Int64& rDistB,
     lcl_makeSingleDistZeroAndExtentNonNegative(rDistR, rRightExt);
     lcl_makeSingleDistZeroAndExtentNonNegative(rDistB, rBottomExt);
 }
+
+tools::Polygon lcl_CreateContourPolygon(SdrObject* pSdrObj)
+{
+    tools::Polygon aContour;
+    if (!pSdrObj)
+    {
+        // use rectangular default
+        aContour.Insert(0, Point(0, 0));
+        aContour.Insert(1, Point(21600, 0));
+        aContour.Insert(2, Point(21600, 21600));
+        aContour.Insert(3, Point(0, 21600));
+        aContour.Insert(4, Point(0, 0));
+        return aContour;
+    }
+
+    // Simple version for now: Use ready PolygonFromPolyPolygon().
+    // For that we first create a B2DPolyPolygon from the shape, that ideally contains
+    // the outline of the shape.
+    basegfx::B2DPolyPolygon aPolyPolygon;
+    switch (pSdrObj->GetObjIdentifier())
+    {
+        case OBJ_CUSTOMSHAPE:
+        {
+            // EnhancedCustomShapeEngine::GetLineGeometry() is not directly usable, because the wrap
+            // polygon acts on the untransformed shape in Word. We do here similar as in
+            // GetLineGreometry(), but without transformations.
+            EnhancedCustomShape2d aCustomShape2d(*static_cast<SdrObjCustomShape*>(pSdrObj));
+            SdrObjectUniquePtr pLineGeometryObj = aCustomShape2d.CreateLineGeometry();
+            if (!pLineGeometryObj)
+                break;
+
+            // We might have got other object kinds than SdrPathObj, even groups.
+            SdrObjListIter aIter(*pLineGeometryObj, SdrIterMode::DeepWithGroups);
+            while (aIter.IsMore())
+            {
+                basegfx::B2DPolyPolygon aPP;
+                const SdrObject* pNext = aIter.Next();
+                if (auto pPathObj = dynamic_cast<const SdrPathObj*>(pNext))
+                    aPP = pPathObj->GetPathPoly();
+                else
+                {
+                    SdrObjectUniquePtr pNewObj = pLineGeometryObj->ConvertToPolyObj(false, false);
+                    SdrPathObj* pPath = dynamic_cast<SdrPathObj*>(pNewObj.get());
+                    if (pPath)
+                        aPP = pPath->GetPathPoly();
+                }
+                if (aPP.count())
+                    aPolyPolygon.append(aPP);
+            }
+
+            if (!aPolyPolygon.count())
+                break;
+
+            // Make relative to range 0..21600, 0..21600
+            Point aCenter(pSdrObj->GetSnapRect().Center());
+            basegfx::B2DHomMatrix aTranslateToOrigin(
+                basegfx::utils::createTranslateB2DHomMatrix(-aCenter.X(), -aCenter.Y()));
+            aPolyPolygon.transform(aTranslateToOrigin);
+            const double fWidth(pSdrObj->GetLogicRect().getWidth());
+            double fScaleX = fWidth == 0.0 ? 1.0 : 21600.0 / fWidth;
+            const double fHeight(pSdrObj->GetLogicRect().getHeight());
+            double fScaleY = fHeight == 0.0 ? 1.0 : 21600.0 / fHeight;
+            basegfx::B2DHomMatrix aScale(basegfx::utils::createScaleB2DHomMatrix(fScaleX, fScaleY));
+            aPolyPolygon.transform(aScale);
+
+            // ToDo: In some cases (see ShapeExport::WriteCustomShape()) flip is suppressed when
+            // calling WriteShapeTransformation(), because the path in custGeom contains already
+            // flipped coordinates. In such cases the wrap polygon needs to contain flipped
+            // coordinates too. That is missing here.
+
+            // "moon" and "msp-spt89" (up-right-arrow) are currently mirrowed horizontal. But
+            // that is removed on export in shapes.cxx. So need to remove it in wrap polygon too.
+            uno::Reference<drawing::XShape> xShape(pSdrObj->getUnoShape(), uno::UNO_QUERY);
+            uno::Reference<beans::XPropertySet> xProps(xShape, uno::UNO_QUERY);
+            comphelper::SequenceAsHashMap aCustomShapeGeometry(
+                xProps->getPropertyValue("CustomShapeGeometry"));
+            auto it = aCustomShapeGeometry.find("Type");
+            if (it != aCustomShapeGeometry.end()
+                && (aCustomShapeGeometry["Type"].get<OUString>() == "moon"
+                    || aCustomShapeGeometry["Type"].get<OUString>() == "mso-spt89"))
+            {
+                basegfx::B2DHomMatrix aFlipH(basegfx::utils::createScaleB2DHomMatrix(-1.0, 1.0));
+                aPolyPolygon.transform(aFlipH);
+            }
+
+            basegfx::B2DHomMatrix aTranslateToCenter(
+                basegfx::utils::createTranslateB2DHomMatrix(10800.0, 10800.0));
+            aPolyPolygon.transform(aTranslateToCenter);
+            break;
+        } // end case OBJ_CUSTOMSHAPE
+        case OBJ_LINE:
+        {
+            aContour.Insert(0, Point(0, 0));
+            aContour.Insert(1, Point(21600, 21600));
+            aContour.Insert(2, Point(0, 0));
+            return aContour;
+        }
+        case OBJ_PATHFILL:
+        case OBJ_PATHLINE:
+        case OBJ_FREEFILL:
+        case OBJ_FREELINE:
+        case OBJ_PATHPOLY:
+        case OBJ_PATHPLIN:
+            // case OBJ_POLY: FixMe: Creating wrap polygon would work, but export to DML is currently
+            // case OBJ_PLIN: disabled for unknown reason; related bug 75254.
+            {
+                // Includes removing any control points
+                SdrObject* pConverted = pSdrObj->ConvertToPolyObj(false, false).release();
+                if (!pConverted)
+                    break;
+                aPolyPolygon = static_cast<SdrPathObj*>(pConverted)->GetPathPoly();
+                SdrObject::Free(pConverted);
+
+                // Word adds a line from last to first point. That will cut of indentations from being
+                // filled. To prevent this, the wrap polygon is lead along the path back to the first
+                // point and so indentation is kept.
+                if (!aPolyPolygon.isClosed())
+                {
+                    basegfx::B2DPolyPolygon aReverse(aPolyPolygon);
+                    aReverse.flip();
+                    aPolyPolygon.append(aReverse);
+                }
+
+                // Make relative to range 0..21600, 0..21600
+                Point aCenter(pSdrObj->GetSnapRect().Center());
+                basegfx::B2DHomMatrix aTranslateToOrigin(
+                    basegfx::utils::createTranslateB2DHomMatrix(-aCenter.X(), -aCenter.Y()));
+                aPolyPolygon.transform(aTranslateToOrigin);
+
+                const double fWidth(pSdrObj->GetLogicRect().getWidth());
+                double fScaleX = fWidth == 0.0 ? 1.0 : 21600.0 / fWidth;
+                const double fHeight(pSdrObj->GetLogicRect().getHeight());
+                double fScaleY = fHeight == 0.0 ? 1.0 : 21600.0 / fHeight;
+                basegfx::B2DHomMatrix aScale(
+                    basegfx::utils::createScaleB2DHomMatrix(fScaleX, fScaleY));
+                aPolyPolygon.transform(aScale);
+
+                basegfx::B2DHomMatrix aTranslateToCenter(
+                    basegfx::utils::createTranslateB2DHomMatrix(10800.0, 10800.0));
+                aPolyPolygon.transform(aTranslateToCenter);
+                break;
+            }
+        case OBJ_NONE:
+        default:
+            break;
+    }
+
+    // Simple version for now: Use ready PolygonFromPolyPolygon()
+    const tools::PolyPolygon aToolsPolyPoly(aPolyPolygon);
+    aContour = sw::util::PolygonFromPolyPolygon(aToolsPolyPoly);
+
+    // The wrap polygon needs at least two points in OOXML and three points in Word.
+    switch (aContour.GetSize())
+    {
+        case 0:
+            // use rectangular default
+            aContour.Insert(0, Point(0, 0));
+            aContour.Insert(1, Point(21600, 0));
+            aContour.Insert(2, Point(21600, 21600));
+            aContour.Insert(3, Point(0, 21600));
+            aContour.Insert(4, Point(0, 0));
+            break;
+        case 1:
+            aContour.Insert(1, aContour.GetPoint(0));
+            aContour.Insert(2, aContour.GetPoint(0));
+            break;
+        case 2:
+            aContour.Insert(2, aContour.GetPoint(0));
+            break;
+        default:
+            break;
+    }
+    return aContour;
+}
 } // end anonymous namespace
 
 ExportDataSaveRestore::ExportDataSaveRestore(DocxExport& rExport, sal_uLong nStt, sal_uLong nEnd,
@@ -1035,175 +1214,137 @@ void DocxSdrExport::startDMLAnchorInline(const SwFrameFormat* pFrameFormat, cons
 
     // 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.
-    if (pObj)
+    if (pFrameFormat->GetSurround().GetValue() == css::text::WrapTextMode_THROUGH
+        || pFrameFormat->GetSurround().GetValue() == css::text::WrapTextMode_THROUGHT)
     {
-        uno::Any aAny;
-        pObj->GetGrabBagItem(aAny);
-        comphelper::SequenceAsHashMap aGrabBag(aAny);
-        auto it = aGrabBag.find("EG_WrapType");
-        if (it != aGrabBag.end())
-        {
-            auto sType = it->second.get<OUString>();
-            if (sType == "wrapTight")
-                nWrapToken = XML_wrapTight;
-            else if (sType == "wrapThrough")
-                nWrapToken = XML_wrapThrough;
-            else
-                SAL_WARN("sw.ww8",
-                         "DocxSdrExport::startDMLAnchorInline: unexpected EG_WrapType value");
-
-            m_pImpl->getSerializer()->startElementNS(XML_wp, nWrapToken, XML_wrapText, "bothSides");
-
-            it = aGrabBag.find("CT_WrapPath");
-            if (it != aGrabBag.end())
-            {
-                m_pImpl->getSerializer()->startElementNS(XML_wp, XML_wrapPolygon, XML_edited, "0");
-                auto aSeqSeq = it->second.get<drawing::PointSequenceSequence>();
-                auto aPoints(comphelper::sequenceToContainer<std::vector<awt::Point>>(aSeqSeq[0]));
-                for (auto i = aPoints.begin(); i != aPoints.end(); ++i)
-                {
-                    awt::Point& rPoint = *i;
-                    m_pImpl->getSerializer()->singleElementNS(
-                        XML_wp, (i == aPoints.begin() ? XML_start : XML_lineTo), XML_x,
-                        OString::number(rPoint.X), XML_y, OString::number(rPoint.Y));
-                }
-                m_pImpl->getSerializer()->endElementNS(XML_wp, XML_wrapPolygon);
-            }
+        m_pImpl->getSerializer()->singleElementNS(XML_wp, XML_wrapNone);
+        return;
+    }
 
-            m_pImpl->getSerializer()->endElementNS(XML_wp, nWrapToken);
-        }
+    if (pFrameFormat->GetSurround().GetValue() == css::text::WrapTextMode_NONE)
+    {
+        m_pImpl->getSerializer()->singleElementNS(XML_wp, XML_wrapTopAndBottom);
+        return;
     }
 
-    // Or if we have a contour.
-    if (!nWrapToken && pFrameFormat->GetSurround().IsContour())
+    // All remaining cases need attribute XML_wrapText
+    OUString sWrapType;
+    switch (pFrameFormat->GetSurround().GetSurround())
     {
-        if (const SwNoTextNode* pNd = sw::util::GetNoTextNodeFromSwFrameFormat(*pFrameFormat))
+        case text::WrapTextMode_DYNAMIC:
+            sWrapType = OUString("largest");
+            break;
+        case text::WrapTextMode_LEFT:
+            sWrapType = OUString("left");
+            break;
+        case text::WrapTextMode_RIGHT:
+            sWrapType = OUString("right");
+            break;
+        case text::WrapTextMode_PARALLEL:
+        default:
+            sWrapType = OUString("bothSides");
+            break;
+    }
+
+    // ToDo: Exclude cases where LibreOffice wrap without contour is different
+    // from Word XML_wrapSquare or where direct use of distances not possible and workaround
+    // will be done using wrapPolygon.
+    // ToDo: handle case Writer frame, where contour can be set in LibreOffice but is not rendered.
+
+    // This case needs no wrapPolygon
+    if (!pFrameFormat->GetSurround().IsContour())
+    {
+        m_pImpl->getSerializer()->singleElementNS(XML_wp, XML_wrapSquare, XML_wrapText, sWrapType);
+        return;
+    }
+
+    // Contour wrap.
+    sal_Int32 nWrapToken
+        = pFrameFormat->GetSurround().IsOutside() ? XML_wrapTight : XML_wrapThrough;
+
+    // ToDo: cases where wrapPolygon is used as workaround.
+
+    // Own wrap polygon exists only for TextGraphicObject and TextEmbeddedObject. It might be edited
+    // by user. If such exists, we use it and we are done.
+    if (const SwNoTextNode* pNd = sw::util::GetNoTextNodeFromSwFrameFormat(*pFrameFormat))
+    {
+        const tools::PolyPolygon* pPolyPoly = pNd->HasContour();
+        if (pPolyPoly && pPolyPoly->Count())
         {
-            const tools::PolyPolygon* pPolyPoly = pNd->HasContour();
-            if (pPolyPoly && pPolyPoly->Count())
+            tools::Polygon aPoly
+                = sw::util::CorrectWordWrapPolygonForExport(*pPolyPoly, pNd, /*bCorrectCrop=*/true);
+            if (aPoly.GetSize() >= 3)
             {
-                nWrapToken = XML_wrapTight;
                 m_pImpl->getSerializer()->startElementNS(XML_wp, nWrapToken, XML_wrapText,
-                                                         "bothSides");
-
+                                                         sWrapType);
+                // ToDo: Test whether XML_edited true or false gives better results.
                 m_pImpl->getSerializer()->startElementNS(XML_wp, XML_wrapPolygon, XML_edited, "0");
-                tools::Polygon aPoly = sw::util::CorrectWordWrapPolygonForExport(
-                    *pPolyPoly, pNd, /*bCorrectCrop=*/true);
-                for (sal_uInt16 i = 0; i < aPoly.GetSize(); ++i)
-                    m_pImpl->getSerializer()->singleElementNS(
-                        XML_wp, (i == 0 ? XML_start : XML_lineTo), XML_x,
-                        OString::number(aPoly[i].X()), XML_y, OString::number(aPoly[i].Y()));
+                m_pImpl->getSerializer()->singleElementNS(XML_wp, XML_start, XML_x,
+                                                          OString::number(aPoly[0].X()), XML_y,
+                                                          OString::number(aPoly[0].Y()));
+                for (sal_uInt16 i = 1; i < aPoly.GetSize(); ++i)
+                    m_pImpl->getSerializer()->singleElementNS(XML_wp, XML_lineTo, XML_x,
+                                                              OString::number(aPoly[i].X()), XML_y,
+                                                              OString::number(aPoly[i].Y()));
                 m_pImpl->getSerializer()->endElementNS(XML_wp, XML_wrapPolygon);
 
                 m_pImpl->getSerializer()->endElementNS(XML_wp, nWrapToken);
+                return;
             }
         }
-        else if (SdrObject* pSdrObj = const_cast<SdrObject*>(pFrameFormat->FindRealSdrObject()))
+    }
+
+    // If this shape comes from ooxml import, there might be a wrap polygon in InteropGrabBag.
+    // Wrap polygons can be edited by users in Word. They are independent from changing shape size or
+    // rotation. So it is likely, that it is still usable.
+    if (pObj)
+    {
+        uno::Any aAny;
+        pObj->GetGrabBagItem(aAny);
+        comphelper::SequenceAsHashMap aGrabBag(aAny);
+        auto it = aGrabBag.find("CT_WrapPath");
+        if (it != aGrabBag.end())
         {
-            // In this case we likely had an odt document to be exported to docx.
-            // There is no grab-bag or something else so for a workaround,
-            // let's export the geometry of the shape...
-            // First get the UNO-shape
-            uno::Reference<drawing::XShape> xShape(pSdrObj->getUnoShape(), uno::UNO_QUERY);
+            m_pImpl->getSerializer()->startElementNS(XML_wp, nWrapToken, XML_wrapText, sWrapType);
 
-            if (xShape && xShape->getShapeType() == u"com.sun.star.drawing.CustomShape")
+            m_pImpl->getSerializer()->startElementNS(XML_wp, XML_wrapPolygon, XML_edited, "0");
+            auto aSeqSeq = it->second.get<drawing::PointSequenceSequence>();
+            auto aPoints(comphelper::sequenceToContainer<std::vector<awt::Point>>(aSeqSeq[0]));
+            for (auto i = aPoints.begin(); i != aPoints.end(); ++i)
             {
-                try
-                {
-                    // Get the properties of the Xshape
-                    uno::Reference<beans::XPropertySet> XProps(xShape, uno::UNO_QUERY);
-                    // Get the "CustomShapeGeometry" property and from its Any() make a hashMap
-                    comphelper::SequenceAsHashMap aCustomShapeGeometry(
-                        XProps->getPropertyValue("CustomShapeGeometry"));
-                    // Get the "Path" property and from its Any() make a hashMap
-                    comphelper::SequenceAsHashMap aPath(aCustomShapeGeometry.getValue("Path"));
-                    // From the Any() of the "Coordinates" property get the points
-                    uno::Sequence<css::drawing::EnhancedCustomShapeParameterPair> aCoords
-                        = aPath.getValue("Coordinates")
-                              .get<uno::Sequence<css::drawing::EnhancedCustomShapeParameterPair>>();
-
-                    // Check if only one side wrap allowed
-                    OUString sWrapType;
-                    switch (pFrameFormat->GetSurround().GetSurround())
-                    {
-                        case text::WrapTextMode_DYNAMIC:
-                            sWrapType = OUString("largest");
-                            break;
-                        case text::WrapTextMode_LEFT:
-                            sWrapType = OUString("left");
-                            break;
-                        case text::WrapTextMode_RIGHT:
-                            sWrapType = OUString("right");
-                            break;
-                        case text::WrapTextMode_PARALLEL:
-                        default:
-                            sWrapType = OUString("bothSides");
-                            break;
-                    }
-
-                    // And export:
-                    nWrapToken = XML_wrapTight;
-                    m_pImpl->getSerializer()->startElementNS(XML_wp, nWrapToken, XML_wrapText,
-                                                             sWrapType);
-
-                    m_pImpl->getSerializer()->startElementNS(XML_wp, XML_wrapPolygon, XML_edited,
-                                                             "0");
-
-                    try
-                    {
-                        // There are the coordinates
-                        for (sal_Int32 i = 0; i < aCoords.getLength(); i++)
-                            m_pImpl->getSerializer()->singleElementNS(
-                                XML_wp, (i == 0 ? XML_start : XML_lineTo), XML_x,
-                                OString::number(aCoords[i].First.Value.get<double>()), XML_y,
-                                OString::number(aCoords[i].Second.Value.get<double>()));
-                    }
-                    catch (const uno::Exception& e)
-                    {
-                        // e.g. on exporting first attachment of tdf#94591 to docx
-                        TOOLS_WARN_EXCEPTION(
-                            "sw.ww8",
-                            "DocxSdrExport::startDMLAnchorInline: bad coordinate: " << e.Message);
-                    }
-
-                    m_pImpl->getSerializer()->endElementNS(XML_wp, XML_wrapPolygon);
-
-                    m_pImpl->getSerializer()->endElementNS(XML_wp, nWrapToken);
-                }
-                catch (uno::Exception& e)
-                {
-                    TOOLS_WARN_EXCEPTION(
-                        "sw.ww8", "DocxSdrExport::startDMLAnchorInline: exception: " << e.Message);
-                }
+                awt::Point& rPoint = *i;
+                m_pImpl->getSerializer()->singleElementNS(
+                    XML_wp, (i == aPoints.begin() ? XML_start : XML_lineTo), XML_x,
+                    OString::number(rPoint.X), XML_y, OString::number(rPoint.Y));
             }
+            m_pImpl->getSerializer()->endElementNS(XML_wp, XML_wrapPolygon);
+
+            m_pImpl->getSerializer()->endElementNS(XML_wp, nWrapToken);
+            return;
         }
     }
 
-    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:
-            m_pImpl->getSerializer()->singleElementNS(XML_wp, XML_wrapTopAndBottom);
-            break;
-        case css::text::WrapTextMode_THROUGH:
-            m_pImpl->getSerializer()->singleElementNS(XML_wp, XML_wrapNone);
-            break;
-        case css::text::WrapTextMode_PARALLEL:
-            m_pImpl->getSerializer()->singleElementNS(XML_wp, XML_wrapSquare, XML_wrapText,
-                                                      "bothSides");
-            break;
-        case css::text::WrapTextMode_DYNAMIC:
-        default:
-            m_pImpl->getSerializer()->singleElementNS(XML_wp, XML_wrapSquare, XML_wrapText,
-                                                      "largest");
-            break;
-    }
+    // In this case we likely had an odt document to be exported to docx. ODF does not know the
+    // concept of a wrap polygon and LibreOffice has no one internally. So as a workaround, we
+    // generate a wrap polygon from the shape geometry.
+    tools::Polygon aContour = lcl_CreateContourPolygon(const_cast<SdrObject*>(pObj));
+
+    // lcl_CreateContourPolygon() ensures at least three points
+    m_pImpl->getSerializer()->startElementNS(XML_wp, nWrapToken, XML_wrapText, sWrapType);
+
+    // ToDo: Test whether XML_edited true or false gives better results.
+    m_pImpl->getSerializer()->startElementNS(XML_wp, XML_wrapPolygon, XML_edited, "0");
+    m_pImpl->getSerializer()->singleElementNS(XML_wp, XML_start, XML_x,
+                                              OString::number(aContour.GetPoint(0).getX()), XML_y,
+                                              OString::number(aContour.GetPoint(0).getY()));
+    for (sal_uInt32 i = 1; i < aContour.GetSize(); i++)
+        m_pImpl->getSerializer()->singleElementNS(
+            XML_wp, XML_lineTo, XML_x, OString::number(aContour.GetPoint(i).getX()), XML_y,
+            OString::number(aContour.GetPoint(i).getY()));
+    m_pImpl->getSerializer()->endElementNS(XML_wp, XML_wrapPolygon);
+
+    m_pImpl->getSerializer()->endElementNS(XML_wp, nWrapToken);
 }
 
 void DocxSdrExport::endDMLAnchorInline(const SwFrameFormat* pFrameFormat)


More information about the Libreoffice-commits mailing list