include/oox/export/drawingml.hxx                                     |   13 
 oox/qa/unit/data/tdf147978_endsubpath.odp                            |binary
 oox/qa/unit/data/tdf147978_enhancedPath_commandA.odp                 |binary
 oox/qa/unit/data/tdf147978_enhancedPath_commandHIJK.odp              |binary
 oox/qa/unit/data/tdf147978_enhancedPath_commandT.odp                 |binary
 oox/qa/unit/data/tdf147978_enhancedPath_commandXY.odp                |binary
 oox/qa/unit/data/tdf147978_enhancedPath_subpath.pptx                 |binary
 oox/qa/unit/export.cxx                                               |  155 +
 oox/source/export/drawingml.cxx                                      |  919 
++++++----
 oox/source/export/shapes.cxx                                         |   31 
 sd/qa/unit/data/odp/tdf147978_enhancedPath_viewBox.odp               |binary
 sd/qa/unit/data/xml/tdf92001_0.xml                                   |   42 
 sd/qa/unit/export-tests-ooxml2.cxx                                   |   32 
 sd/qa/unit/export-tests-ooxml3.cxx                                   |   16 
 sw/qa/extras/ooxmlexport/data/tdf147978_enhancedPath_commandABVW.odt |binary
 sw/qa/extras/ooxmlexport/ooxmlexport17.cxx                           |   16 
 sw/qa/extras/ooxmlexport/ooxmlexport7.cxx                            |   24 
 sw/source/filter/ww8/docxsdrexport.cxx                               |   20 
 18 files changed, 861 insertions(+), 407 deletions(-)

New commits:
commit 6bd85136efe3d3668b59a596d692f65bf0a4982c
Author:     Regina Henschel <rb.hensc...@t-online.de>
AuthorDate: Fri Mar 18 18:31:05 2022 +0100
Commit:     Miklos Vajna <vmik...@collabora.com>
CommitDate: Mon Mar 28 11:38:26 2022 +0200

    tdf#147978 export subpaths individually in custGeom
    
    ...and implement export of all missing commands,
       use existing viewBox if suitable,
       use one EnhancedCustomShape2d
       move WriteCustomGeometryPoint to protected,
       make GetCustomGeometryPointValue local
    
    The fix solves tdf#100390 too.
    
    Without the fix the entire enhanced-path was exported as one element
    <a:path>. The command F was applied to the whole drawing but should
    affect only the subpath. The implementation is changed so that each
    subpath gets its own element <a:path> and command F acts only on its
    subpath.
    
    Support for export of handles and equations is still a long way off.
    Thus there is no reason to tread shapes with and without handles
    different. Shapes with handles had used method WritePolyPolygon, but
    that is not able to handle subpaths. So have desided to use method
    WriteCustomGeometry for all cases.
    
    To get shapes exported regardless of path commands I have added the
    export for the missing commands.
    
    I have removed the no longer used method WritePolyPolygon.
    
    The special treatment of shapes "moon" and "mso-spt89" (right up arrow)
    in export is no longer needed. Related code parts are removed. The
    unit test testFlipAndRotateCustomShape is adapted.
    
    In case the method WriteCustomGeometry fails, the enhanced-path is
    invalid. In that case an minimal custGeom is written in case of docx.
    
    Shapes whose drawing does not touch all edges of the snap rectangle
    were exported with wrong position and size of the drawing inside the
    snap rectangle. That is fixed by using an existing ViewBox for the
    OOXML path size. The old way of creating a path size from point
    coordinates is only used if the shape has no suitable ViewBox.
    
    The point values in unit test SdOOXMLExportTest2::testTdf111798 are
    adapted to path size 21600 x 21600 and traverse direction of the points
    is corrected. The resulting shape outline is still the same as before.
    
    The expected xml is updated for file tdf92001.odp in
    SdImportTest::testDocumentLayout. The resulting shape outline is the
    same, because the shape touches the edges of the snap rectangle.
    
    The case, that the shape outline does not touch a edge of the snap
    rectangle is tested in SdOOXMLExportTest3::testEnhancedPathViewBox.
    
    Still missing is the case, that ViewBox has other left,top than 0,0.
    In that case all coordinates would have to be shifted because the path
    size in OOXML has only width and height but not left,top. That will
    not be included in this patch.
    
    Change-Id: Ib1736d6a08371f4d98411d2769275f0580cd0030
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/131837
    Tested-by: Jenkins
    Reviewed-by: Miklos Vajna <vmik...@collabora.com>
    (cherry picked from commit 2029b2f6dd0109c5892e5ac5640022b31fe42fd2)
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/132048
    Reviewed-by: Bartosz Kosiorek <gan...@poczta.onet.pl>

diff --git a/include/oox/export/drawingml.hxx b/include/oox/export/drawingml.hxx
index 63a32225172b..7fbb015b4ce4 100644
--- a/include/oox/export/drawingml.hxx
+++ b/include/oox/export/drawingml.hxx
@@ -43,6 +43,7 @@
 #include <vcl/checksum.hxx>
 #include <tools/gen.hxx>
 #include <vcl/mapmod.hxx>
+#include <svx/EnhancedCustomShape2d.hxx>
 
 class Graphic;
 class SdrObjCustomShape;
@@ -198,7 +199,8 @@ protected:
 
     void WriteGlowEffect(const css::uno::Reference<css::beans::XPropertySet>& 
rXPropSet);
     void WriteSoftEdgeEffect(const 
css::uno::Reference<css::beans::XPropertySet>& rXPropSet);
-    bool HasEnhancedCustomShapeSegmentCommand(const 
css::uno::Reference<css::drawing::XShape>& rXShape, const sal_Int16 nCommand);
+    void WriteCustomGeometryPoint(const 
css::drawing::EnhancedCustomShapeParameterPair& rParamPair,
+                                  const EnhancedCustomShape2d& rCustomShape2d);
 
 public:
     DrawingML( ::sax_fastparser::FSHelperPtr pFS, ::oox::core::XmlFilterBase* 
pFB, DocumentType eDocumentType = DOCUMENT_PPTX, DMLTextExport* pTextExport = 
nullptr )
@@ -306,14 +308,7 @@ public:
     bool WriteCustomGeometry(
         const css::uno::Reference<css::drawing::XShape>& rXShape,
         const SdrObjCustomShape& rSdrObjCustomShape);
-    void WriteCustomGeometryPoint(
-        const css::drawing::EnhancedCustomShapeParameterPair& rParamPair,
-        const SdrObjCustomShape& rSdrObjCustomShape);
-    static sal_Int32 GetCustomGeometryPointValue(
-        const css::drawing::EnhancedCustomShapeParameter& rParam,
-        const SdrObjCustomShape& rSdrObjCustomShape);
-    void WritePolyPolygon(const css::uno::Reference<css::drawing::XShape>& 
rXShape,
-                          const tools::PolyPolygon& rPolyPolygon, const bool 
bClosed);
+    void WriteEmptyCustomGeometry();
     void WritePolyPolygon(const css::uno::Reference<css::drawing::XShape>& 
rXShape,
                           const bool bClosed);
     void WriteFill( const css::uno::Reference< css::beans::XPropertySet >& 
xPropSet );
diff --git a/oox/qa/unit/data/tdf147978_endsubpath.odp 
b/oox/qa/unit/data/tdf147978_endsubpath.odp
new file mode 100644
index 000000000000..2dfd55de1be3
Binary files /dev/null and b/oox/qa/unit/data/tdf147978_endsubpath.odp differ
diff --git a/oox/qa/unit/data/tdf147978_enhancedPath_commandA.odp 
b/oox/qa/unit/data/tdf147978_enhancedPath_commandA.odp
new file mode 100644
index 000000000000..99ddda7c132e
Binary files /dev/null and 
b/oox/qa/unit/data/tdf147978_enhancedPath_commandA.odp differ
diff --git a/oox/qa/unit/data/tdf147978_enhancedPath_commandHIJK.odp 
b/oox/qa/unit/data/tdf147978_enhancedPath_commandHIJK.odp
new file mode 100644
index 000000000000..49e01bc0933a
Binary files /dev/null and 
b/oox/qa/unit/data/tdf147978_enhancedPath_commandHIJK.odp differ
diff --git a/oox/qa/unit/data/tdf147978_enhancedPath_commandT.odp 
b/oox/qa/unit/data/tdf147978_enhancedPath_commandT.odp
new file mode 100644
index 000000000000..3dcd0d567545
Binary files /dev/null and 
b/oox/qa/unit/data/tdf147978_enhancedPath_commandT.odp differ
diff --git a/oox/qa/unit/data/tdf147978_enhancedPath_commandXY.odp 
b/oox/qa/unit/data/tdf147978_enhancedPath_commandXY.odp
new file mode 100644
index 000000000000..6112251783e1
Binary files /dev/null and 
b/oox/qa/unit/data/tdf147978_enhancedPath_commandXY.odp differ
diff --git a/oox/qa/unit/data/tdf147978_enhancedPath_subpath.pptx 
b/oox/qa/unit/data/tdf147978_enhancedPath_subpath.pptx
new file mode 100644
index 000000000000..bbedc7ab98f5
Binary files /dev/null and 
b/oox/qa/unit/data/tdf147978_enhancedPath_subpath.pptx differ
diff --git a/oox/qa/unit/export.cxx b/oox/qa/unit/export.cxx
index dd722cd04e79..e104b4effdd7 100644
--- a/oox/qa/unit/export.cxx
+++ b/oox/qa/unit/export.cxx
@@ -374,6 +374,161 @@ CPPUNIT_TEST_FIXTURE(Test, 
testTdf146690_endParagraphRunPropertiesNewLinesTextSi
     assertXPath(pXmlDoc, "//p:sp[1]/p:txBody/a:p[2]/a:endParaRPr", "sz", 
"500");
     assertXPath(pXmlDoc, "//p:sp[1]/p:txBody/a:p[3]/a:endParaRPr", "sz", 
"500");
 }
+
+CPPUNIT_TEST_FIXTURE(Test, testTdf147978_endsubpath)
+{
+    // Given an odp file that contains a non-primitive custom shape with 
command N
+    OUString aURL = m_directories.getURLFromSrc(DATA_DIRECTORY) + 
"tdf147978_endsubpath.odp";
+
+    // When saving that document:
+    loadAndSave(aURL, "Impress Office Open XML");
+
+    std::unique_ptr<SvStream> pStream = parseExportStream(getTempFile(), 
"ppt/slides/slide1.xml");
+    xmlDocUniquePtr pXmlDoc = parseXmlStream(pStream.get());
+    // Then make sure the pathLst has two child elements,
+    // Without the accompanying fix in place, only one element a:path was 
exported.
+    assertXPathChildren(pXmlDoc, "//a:pathLst", 2);
+    // and make sure first path with no stroke, second with no fill
+    assertXPath(pXmlDoc, "//a:pathLst/a:path[1]", "stroke", "0");
+    assertXPath(pXmlDoc, "//a:pathLst/a:path[2]", "fill", "none");
+}
+
+CPPUNIT_TEST_FIXTURE(Test, testTdf147978_commandA)
+{
+    // Given an odp file that contains a non-primitive custom shape with 
command N
+    OUString aURL
+        = m_directories.getURLFromSrc(DATA_DIRECTORY) + 
"tdf147978_enhancedPath_commandA.odp";
+
+    // When saving that document:
+    loadAndSave(aURL, "Impress Office Open XML");
+
+    std::unique_ptr<SvStream> pStream = parseExportStream(getTempFile(), 
"ppt/slides/slide1.xml");
+    xmlDocUniquePtr pXmlDoc = parseXmlStream(pStream.get());
+    // Then make sure the path has a child element arcTo. Prior to the fix 
that part of the curve was
+    // not exported at all. In odp it is a command A. Such does not exist in 
OOXML and is therefore
+    // exported as a:lnTo followed by a:arcTo
+    assertXPath(pXmlDoc, "//a:pathLst/a:path/a:lnTo", 2);
+    assertXPath(pXmlDoc, "//a:pathLst/a:path/a:arcTo", 1);
+    // And assert its attribute values
+    assertXPath(pXmlDoc, "//a:pathLst/a:path/a:arcTo", "wR", "7200");
+    assertXPath(pXmlDoc, "//a:pathLst/a:path/a:arcTo", "hR", "5400");
+    assertXPath(pXmlDoc, "//a:pathLst/a:path/a:arcTo", "stAng", "7719588");
+    assertXPath(pXmlDoc, "//a:pathLst/a:path/a:arcTo", "swAng", "-5799266");
+}
+
+CPPUNIT_TEST_FIXTURE(Test, testTdf147978_commandT)
+{
+    // The odp file contains a non-primitive custom shape with commands MTZ
+    OUString aURL
+        = m_directories.getURLFromSrc(DATA_DIRECTORY) + 
"tdf147978_enhancedPath_commandT.odp";
+
+    // Export to pptx had only exported the command M and has used a wrong 
path size
+    loadAndSave(aURL, "Impress Office Open XML");
+
+    // Verify the markup:
+    std::unique_ptr<SvStream> pStream = parseExportStream(getTempFile(), 
"ppt/slides/slide1.xml");
+    xmlDocUniquePtr pXmlDoc = parseXmlStream(pStream.get());
+    // File has draw:viewBox="0 0 216 216"
+    assertXPath(pXmlDoc, "//a:pathLst/a:path", "w", "216");
+    assertXPath(pXmlDoc, "//a:pathLst/a:path", "h", "216");
+    // Command T is exported as lnTo followed by arcTo.
+    assertXPath(pXmlDoc, "//a:pathLst/a:path/a:moveTo", 1);
+    assertXPath(pXmlDoc, "//a:pathLst/a:path/a:lnTo", 1);
+    assertXPath(pXmlDoc, "//a:pathLst/a:path/a:arcTo", 1);
+    assertXPath(pXmlDoc, "//a:pathLst/a:path/a:close", 1);
+    // And assert its values
+    assertXPath(pXmlDoc, "//a:pathLst/a:path/a:moveTo/a:pt", "x", "108");
+    assertXPath(pXmlDoc, "//a:pathLst/a:path/a:moveTo/a:pt", "y", "162");
+    assertXPath(pXmlDoc, "//a:pathLst/a:path/a:lnTo/a:pt", "x", "138");
+    assertXPath(pXmlDoc, "//a:pathLst/a:path/a:lnTo/a:pt", "y", "110");
+    assertXPath(pXmlDoc, "//a:pathLst/a:path/a:arcTo", "wR", "108");
+    assertXPath(pXmlDoc, "//a:pathLst/a:path/a:arcTo", "hR", "54");
+    assertXPath(pXmlDoc, "//a:pathLst/a:path/a:arcTo", "stAng", "18000000");
+    assertXPath(pXmlDoc, "//a:pathLst/a:path/a:arcTo", "swAng", "18000000");
+}
+
+CPPUNIT_TEST_FIXTURE(Test, testTdf147978_commandXY)
+{
+    // The odp file contains a non-primitive custom shapes with commands XY
+    OUString aURL
+        = m_directories.getURLFromSrc(DATA_DIRECTORY) + 
"tdf147978_enhancedPath_commandXY.odp";
+
+    // Export to pptx had dropped commands X and Y.
+    loadAndSave(aURL, "Impress Office Open XML");
+
+    // Verify the markup:
+    std::unique_ptr<SvStream> pStream = parseExportStream(getTempFile(), 
"ppt/slides/slide1.xml");
+    xmlDocUniquePtr pXmlDoc = parseXmlStream(pStream.get());
+    // File has draw:viewBox="0 0 10 10"
+    assertXPath(pXmlDoc, "//a:pathLst/a:path[1]", "w", "10");
+    assertXPath(pXmlDoc, "//a:pathLst/a:path[1]", "h", "10");
+    // Shape has M 0 5 Y 5 0 10 5 5 10 F Y 0 5 N M 10 10 X 0 0
+    assertXPath(pXmlDoc, "//a:pathLst/a:path[1]/a:moveTo/a:pt", "x", "0");
+    assertXPath(pXmlDoc, "//a:pathLst/a:path[1]/a:moveTo/a:pt", "y", "5");
+    assertXPath(pXmlDoc, "//a:pathLst/a:path[1]/a:arcTo[1]", "wR", "5");
+    assertXPath(pXmlDoc, "//a:pathLst/a:path[1]/a:arcTo[1]", "hR", "5");
+    assertXPath(pXmlDoc, "//a:pathLst/a:path[1]/a:arcTo[1]", "stAng", 
"10800000");
+    assertXPath(pXmlDoc, "//a:pathLst/a:path[1]/a:arcTo[1]", "swAng", 
"5400000");
+    assertXPath(pXmlDoc, "//a:pathLst/a:path[1]/a:arcTo[2]", "stAng", 
"16200000");
+    assertXPath(pXmlDoc, "//a:pathLst/a:path[1]/a:arcTo[2]", "swAng", 
"5400000");
+    assertXPath(pXmlDoc, "//a:pathLst/a:path[1]/a:arcTo[3]", "stAng", "0");
+    assertXPath(pXmlDoc, "//a:pathLst/a:path[1]/a:arcTo[3]", "swAng", 
"5400000");
+    assertXPath(pXmlDoc, "//a:pathLst/a:path[1]/a:arcTo[4]", "stAng", "0");
+    assertXPath(pXmlDoc, "//a:pathLst/a:path[1]/a:arcTo[4]", "swAng", 
"-5400000");
+    assertXPath(pXmlDoc, "//a:pathLst/a:path[2]/a:moveTo/a:pt", "x", "10");
+    assertXPath(pXmlDoc, "//a:pathLst/a:path[2]/a:moveTo/a:pt", "y", "10");
+    assertXPath(pXmlDoc, "//a:pathLst/a:path[2]/a:arcTo", "wR", "10");
+    assertXPath(pXmlDoc, "//a:pathLst/a:path[2]/a:arcTo", "hR", "10");
+    assertXPath(pXmlDoc, "//a:pathLst/a:path[2]/a:arcTo", "stAng", "5400000");
+    assertXPath(pXmlDoc, "//a:pathLst/a:path[2]/a:arcTo", "swAng", "5400000");
+}
+
+CPPUNIT_TEST_FIXTURE(Test, testTdf147978_commandHIJK)
+{
+    // The odp file contains a non-primitive custom shapes with commands 
H,I,J,K
+    OUString aURL
+        = m_directories.getURLFromSrc(DATA_DIRECTORY) + 
"tdf147978_enhancedPath_commandHIJK.odp";
+
+    // Export to pptx had dropped commands X and Y.
+    loadAndSave(aURL, "Impress Office Open XML");
+
+    // Verify the markup:
+    std::unique_ptr<SvStream> pStream = parseExportStream(getTempFile(), 
"ppt/slides/slide1.xml");
+    xmlDocUniquePtr pXmlDoc = parseXmlStream(pStream.get());
+    // File has draw:viewBox="0 0 80 80"
+    assertXPath(pXmlDoc, "//a:pathLst/a:path[1]", "w", "80");
+    assertXPath(pXmlDoc, "//a:pathLst/a:path[1]", "h", "80");
+    // File uses from back to front J (lighten), I (lightenLess), normal fill, 
K (darkenLess),
+    // H (darken). New feature, old versions did not export these at all.
+    assertXPath(pXmlDoc, "//a:pathLst/a:path[1]", "fill", "lighten");
+    assertXPath(pXmlDoc, "//a:pathLst/a:path[2]", "fill", "lightenLess");
+    assertXPathNoAttribute(pXmlDoc, "//a:pathLst/a:path[3]", "fill");
+    assertXPath(pXmlDoc, "//a:pathLst/a:path[4]", "fill", "darkenLess");
+    assertXPath(pXmlDoc, "//a:pathLst/a:path[5]", "fill", "darken");
+}
+
+CPPUNIT_TEST_FIXTURE(Test, testTdf147978_subpath)
+{
+    // The odp file contains a non-primitive custom shapes with commands 
H,I,J,K
+    OUString aURL
+        = m_directories.getURLFromSrc(DATA_DIRECTORY) + 
"tdf147978_enhancedPath_subpath.pptx";
+
+    // Export to pptx had dropped the subpaths.
+    loadAndSave(aURL, "Impress Office Open XML");
+
+    // Verify the markup:
+    std::unique_ptr<SvStream> pStream = parseExportStream(getTempFile(), 
"ppt/slides/slide1.xml");
+    xmlDocUniquePtr pXmlDoc = parseXmlStream(pStream.get());
+    // File should have four subpaths with increasing path size
+    assertXPath(pXmlDoc, "//a:pathLst/a:path[1]", "w", "10");
+    assertXPath(pXmlDoc, "//a:pathLst/a:path[1]", "h", "10");
+    assertXPath(pXmlDoc, "//a:pathLst/a:path[2]", "w", "20");
+    assertXPath(pXmlDoc, "//a:pathLst/a:path[2]", "h", "20");
+    assertXPath(pXmlDoc, "//a:pathLst/a:path[3]", "w", "40");
+    assertXPath(pXmlDoc, "//a:pathLst/a:path[3]", "h", "40");
+    assertXPath(pXmlDoc, "//a:pathLst/a:path[4]", "w", "80");
+    assertXPath(pXmlDoc, "//a:pathLst/a:path[4]", "h", "80");
+}
 }
 
 CPPUNIT_PLUGIN_IMPLEMENT();
diff --git a/oox/source/export/drawingml.cxx b/oox/source/export/drawingml.cxx
index c457e65ac0c8..708aea6fb29a 100644
--- a/oox/source/export/drawingml.cxx
+++ b/oox/source/export/drawingml.cxx
@@ -62,6 +62,7 @@
 #include <com/sun/star/drawing/ColorMode.hpp>
 #include <com/sun/star/drawing/EnhancedCustomShapeAdjustmentValue.hpp>
 #include <com/sun/star/drawing/EnhancedCustomShapeParameterPair.hpp>
+#include <com/sun/star/drawing/EnhancedCustomShapeParameterType.hpp>
 #include <com/sun/star/drawing/EnhancedCustomShapeSegment.hpp>
 #include <com/sun/star/drawing/EnhancedCustomShapeSegmentCommand.hpp>
 #include <com/sun/star/drawing/Hatch.hpp>
@@ -137,6 +138,7 @@ using namespace ::css::style;
 using namespace ::css::text;
 using namespace ::css::uno;
 using namespace ::css::container;
+using namespace ::com::sun::star::drawing::EnhancedCustomShapeSegmentCommand;
 
 using ::css::io::XOutputStream;
 using ::sax_fastparser::FSHelperPtr;
@@ -3658,6 +3660,86 @@ void DrawingML::WritePresetShape( const char* pShape, 
MSO_SPT eShapeType, bool b
     mpFS->endElementNS(  XML_a, XML_prstGeom );
 }
 
+namespace // helpers for DrawingML::WriteCustomGeometry
+{
+sal_Int32
+FindNextCommandEndSubpath(const sal_Int32 nStart,
+                          const 
uno::Sequence<drawing::EnhancedCustomShapeSegment>& rSegments)
+{
+    sal_Int32 i = nStart < 0 ? 0 : nStart;
+    while (i < rSegments.getLength() && rSegments[i].Command != ENDSUBPATH)
+        i++;
+    return i;
+}
+
+bool HasCommandInSubPath(const sal_Int16 nCommand, const sal_Int32 nFirst, 
const sal_Int32 nLast,
+                         const 
uno::Sequence<drawing::EnhancedCustomShapeSegment>& rSegments)
+{
+    for (sal_Int32 i = nFirst < 0 ? 0 : nFirst; i <= nLast && i < 
rSegments.getLength(); i++)
+    {
+        if (rSegments[i].Command == nCommand)
+            return true;
+    }
+    return false;
+}
+
+// Ellipse is given by radii fwR and fhR and center (fCx|fCy). The ray from 
center through point RayP
+// intersects the ellipse in point S and this point S has angle fAngleDeg in 
degrees.
+void getEllipsePointAndAngleFromRayPoint(double& rfAngleDeg, double& rfSx, 
double& rfSy,
+                                         const double fWR, const double fHR, 
const double fCx,
+                                         const double fCy, const double 
fRayPx, const double fRayPy)
+{
+    if (basegfx::fTools::equalZero(fWR) || basegfx::fTools::equalZero(fHR))
+    {
+        rfSx = fCx; // needed for getting new 'current point'
+        rfSy = fCy;
+    }
+    else
+    {
+        // center ellipse at origin, stretch in y-direction to circle, flip to 
Math orientation
+        // and get angle
+        double fCircleMathAngle = atan2(-fWR / fHR * (fRayPy - fCy), fRayPx - 
fCx);
+        // use angle for intersection point on circle and stretch back to 
ellipse
+        double fPointMathEllipse_x = fWR * cos(fCircleMathAngle);
+        double fPointMathEllipse_y = fHR * sin(fCircleMathAngle);
+        // get angle of intersection point on ellipse
+        double fEllipseMathAngle = atan2(fPointMathEllipse_y, 
fPointMathEllipse_x);
+        // convert from Math to View orientation and shift ellipse back from 
origin
+        rfAngleDeg = -basegfx::rad2deg(fEllipseMathAngle);
+        rfSx = fPointMathEllipse_x + fCx;
+        rfSy = -fPointMathEllipse_y + fCy;
+    }
+}
+
+void getEllipsePointFromViewAngle(double& rfSx, double& rfSy, const double 
fWR, const double fHR,
+                                  const double fCx, const double fCy, const 
double fViewAngleDeg)
+{
+    if (basegfx::fTools::equalZero(fWR) || basegfx::fTools::equalZero(fHR))
+    {
+        rfSx = fCx; // needed for getting new 'current point'
+        rfSy = fCy;
+    }
+    else
+    {
+        double fX = cos(basegfx::deg2rad(fViewAngleDeg)) / fWR;
+        double fY = sin(basegfx::deg2rad(fViewAngleDeg)) / fHR;
+        double fRadius = 1.0 / std::hypot(fX, fY);
+        rfSx = fCx + fRadius * cos(basegfx::deg2rad(fViewAngleDeg));
+        rfSy = fCy + fRadius * sin(basegfx::deg2rad(fViewAngleDeg));
+    }
+}
+
+sal_Int32 GetCustomGeometryPointValue(const 
css::drawing::EnhancedCustomShapeParameter& rParam,
+                                      const EnhancedCustomShape2d& 
rCustomShape2d)
+{
+    double fValue = 0.0;
+    rCustomShape2d.GetParameter(fValue, rParam, false, false);
+    sal_Int32 nValue(std::lround(fValue));
+
+    return nValue;
+}
+}
+
 bool DrawingML::WriteCustomGeometry(
     const Reference< XShape >& rXShape,
     const SdrObjCustomShape& rSdrObjCustomShape)
@@ -3679,349 +3761,574 @@ bool DrawingML::WriteCustomGeometry(
         return false;
     }
 
-
     auto pGeometrySeq = 
o3tl::tryAccess<uno::Sequence<beans::PropertyValue>>(aAny);
+    if (!pGeometrySeq)
+        return false;
+
+    auto pPathProp = std::find_if(std::cbegin(*pGeometrySeq), 
std::cend(*pGeometrySeq),
+                                  [](const PropertyValue& rProp) { return 
rProp.Name == "Path"; });
+    if (pPathProp == std::cend(*pGeometrySeq))
+        return false;
 
-    if ( pGeometrySeq )
+    uno::Sequence<beans::PropertyValue> aPathProp;
+    pPathProp->Value >>= aPathProp;
+
+    uno::Sequence<drawing::EnhancedCustomShapeParameterPair> aPairs;
+    uno::Sequence<drawing::EnhancedCustomShapeSegment> aSegments;
+    uno::Sequence<awt::Size> aPathSize;
+    for (const beans::PropertyValue& rPathProp : std::as_const(aPathProp))
     {
-        for( const beans::PropertyValue& rProp : *pGeometrySeq )
-        {
-            if ( rProp.Name == "Path" )
-            {
-                uno::Sequence<beans::PropertyValue> aPathProp;
-                rProp.Value >>= aPathProp;
+        if (rPathProp.Name == "Coordinates")
+            rPathProp.Value >>= aPairs;
+        else if (rPathProp.Name == "Segments")
+            rPathProp.Value >>= aSegments;
+        else if (rPathProp.Name == "SubViewSize")
+            rPathProp.Value >>= aPathSize;
+    }
 
-                uno::Sequence<drawing::EnhancedCustomShapeParameterPair> 
aPairs;
-                uno::Sequence<drawing::EnhancedCustomShapeSegment> aSegments;
-                uno::Sequence<awt::Size> aPathSize;
-                for (const beans::PropertyValue& rPathProp : 
std::as_const(aPathProp))
-                {
-                    if (rPathProp.Name == "Coordinates")
-                        rPathProp.Value >>= aPairs;
-                    else if (rPathProp.Name == "Segments")
-                        rPathProp.Value >>= aSegments;
-                    else if (rPathProp.Name == "SubViewSize")
-                        rPathProp.Value >>= aPathSize;
-                }
+    if ( !aPairs.hasElements() )
+        return false;
 
-                if ( !aPairs.hasElements() )
-                    return false;
+    if ( !aSegments.hasElements() )
+    {
+        aSegments = uno::Sequence<drawing::EnhancedCustomShapeSegment>
+            {
+                { MOVETO, 1 },
+                { LINETO,
+                  static_cast<sal_Int16>(std::min( aPairs.getLength() - 1, 
sal_Int32(32767) )) },
+                { CLOSESUBPATH, 0 },
+                { ENDSUBPATH, 0 }
+            };
+    };
 
-                if ( !aSegments.hasElements() )
-                {
-                    aSegments = 
uno::Sequence<drawing::EnhancedCustomShapeSegment>
-                    {
-                        { drawing::EnhancedCustomShapeSegmentCommand::MOVETO, 
1 },
-                        { drawing::EnhancedCustomShapeSegmentCommand::LINETO,
-                          static_cast<sal_Int16>(std::min( aPairs.getLength() 
- 1, sal_Int32(32767) )) },
-                        { 
drawing::EnhancedCustomShapeSegmentCommand::CLOSESUBPATH, 0 },
-                        { 
drawing::EnhancedCustomShapeSegmentCommand::ENDSUBPATH, 0 }
-                    };
-                };
+    int nExpectedPairCount = std::accumulate(std::cbegin(aSegments), 
std::cend(aSegments), 0,
+        [](const int nSum, const drawing::EnhancedCustomShapeSegment& 
rSegment) { return nSum + rSegment.Count; });
 
-                int nExpectedPairCount = 
std::accumulate(std::cbegin(aSegments), std::cend(aSegments), 0,
-                    [](const int nSum, const 
drawing::EnhancedCustomShapeSegment& rSegment) { return nSum + rSegment.Count; 
});
+    if ( nExpectedPairCount > aPairs.getLength() )
+    {
+        SAL_WARN("oox.shape", "Segments need " << nExpectedPairCount << " 
coordinates, but Coordinates have only " << aPairs.getLength() << " pairs.");
+        return false;
+    }
 
-                if ( nExpectedPairCount > aPairs.getLength() )
-                {
-                    SAL_WARN("oox.shape", "Segments need " << 
nExpectedPairCount << " coordinates, but Coordinates have only " << 
aPairs.getLength() << " pairs.");
-                    return false;
-                }
+    // A EnhancedCustomShape2d caches the equation results. Therefor we use 
only one of it for the
+    // entire method.
+    const EnhancedCustomShape2d 
aCustomShape2d(const_cast<SdrObjCustomShape&>(rSdrObjCustomShape));
 
-                mpFS->startElementNS(XML_a, XML_custGeom);
-                mpFS->singleElementNS(XML_a, XML_avLst);
-                mpFS->singleElementNS(XML_a, XML_gdLst);
-                mpFS->singleElementNS(XML_a, XML_ahLst);
-                mpFS->singleElementNS(XML_a, XML_rect, XML_l, "l", XML_t, "t",
-                                      XML_r, "r", XML_b, "b");
-                mpFS->startElementNS(XML_a, XML_pathLst);
+    mpFS->startElementNS(XML_a, XML_custGeom);
+    mpFS->singleElementNS(XML_a, XML_avLst);
+    mpFS->singleElementNS(XML_a, XML_gdLst);
+    mpFS->singleElementNS(XML_a, XML_ahLst);
+    // ToDO: use draw:TextAreas for <a:rect>
+    mpFS->singleElementNS(XML_a, XML_rect, XML_l, "l", XML_t, "t", XML_r, "r", 
XML_b, "b");
+    mpFS->startElementNS(XML_a, XML_pathLst);
 
-                std::optional<OString> sFill;
-                if (HasEnhancedCustomShapeSegmentCommand(rXShape, 
css::drawing::EnhancedCustomShapeSegmentCommand::NOFILL))
-                    sFill = "none"; // for possible values see ST_PathFillMode 
in OOXML standard
+    // Prepare width and height for <a:path>
+    bool bUseGlobalViewBox(false);
+
+    // nViewBoxWidth must be integer otherwise ReplaceGeoWidth in 
aCustomShape2d.GetParameter() is not
+    // triggered; same for height.
+    sal_Int32 nViewBoxWidth(0);
+    sal_Int32 nViewBoxHeight(0);
+    if (!aPathSize.hasElements())
+    {
+        bUseGlobalViewBox = true;
+        // If draw:viewBox is missing in draw:enhancedGeometry, then import 
sets
+        // viewBox="0 0 21600 21600". Missing ViewBox can only occur, if user 
has manipulated
+        // current file via macro. Author of macro has to fix it.
+        auto pProp = std::find_if(
+            std::cbegin(*pGeometrySeq), std::cend(*pGeometrySeq),
+            [](const beans::PropertyValue& rGeomProp) { return rGeomProp.Name 
== "ViewBox"; });
+        if (pProp != std::cend(*pGeometrySeq))
+        {
+            css::awt::Rectangle aViewBox;
+            if (pProp->Value >>= aViewBox)
+            {
+                nViewBoxWidth = aViewBox.Width;
+                nViewBoxHeight = aViewBox.Height;
+                css::drawing::EnhancedCustomShapeParameter aECSP;
+                aECSP.Type = 
css::drawing::EnhancedCustomShapeParameterType::NORMAL;
+                aECSP.Value <<= nViewBoxWidth;
+                double fRetValue;
+                aCustomShape2d.GetParameter(fRetValue, aECSP, true, false);
+                nViewBoxWidth = basegfx::fround(fRetValue);
+                aECSP.Value <<= nViewBoxHeight;
+                aCustomShape2d.GetParameter(fRetValue, aECSP, false, true);
+                nViewBoxHeight = basegfx::fround(fRetValue);
+            }
+        }
+        // Import from oox or documents, which are imported from oox and saved 
to strict ODF, might
+        // have no subViewSize but viewBox="0 0 0 0". We need to generate 
width and height in those
+        // cases. Even if that is fixed, we need the substitute for old 
documents.
+        if ((nViewBoxWidth == 0 && nViewBoxHeight == 0) || pProp == 
std::cend(*pGeometrySeq))
+        {
+            // Generate a substitute based on point coordinates
+            sal_Int32 nXMin(0);
+            aPairs[0].First.Value >>= nXMin;
+            sal_Int32 nXMax = nXMin;
+            sal_Int32 nYMin(0);
+            aPairs[0].Second.Value >>= nYMin;
+            sal_Int32 nYMax = nYMin;
 
-                if ( aPathSize.hasElements() )
-                {
-                    mpFS->startElementNS( XML_a, XML_path,
-                          XML_fill, sFill,
-                          XML_w, OString::number(aPathSize[0].Width),
-                          XML_h, OString::number(aPathSize[0].Height) );
-                }
-                else
+            for (const auto& rPair : std::as_const(aPairs))
+            {
+                sal_Int32 nX = GetCustomGeometryPointValue(rPair.First, 
aCustomShape2d);
+                sal_Int32 nY = GetCustomGeometryPointValue(rPair.Second, 
aCustomShape2d);
+                if (nX < nXMin)
+                    nXMin = nX;
+                if (nY < nYMin)
+                    nYMin = nY;
+                if (nX > nXMax)
+                    nXMax = nX;
+                if (nY > nYMax)
+                    nYMax = nY;
+            }
+            nViewBoxWidth = std::max(nXMax, nXMax - nXMin);
+            nViewBoxHeight = std::max(nYMax, nYMax - nYMin);
+        }
+        // ToDo: Other values of left,top than 0,0 are not considered yet. 
Such would require a
+        // shift of the resulting path coordinates.
+    }
+
+    // Iterate over subpaths
+    sal_Int32 nPairIndex = 0; // index over "Coordinates"
+    sal_Int32 nPathSizeIndex = 0; // index over "SubViewSize"
+    sal_Int32 nSubpathStartIndex(0); // index over "Segments"
+    do
+    {
+        bool bOK(true); // catch faulty paths were commands do not correspond 
to points
+        // get index of next command ENDSUBPATH; if such doesn't exist use 
index behind last segment
+        sal_Int32 nNextNcommandIndex = 
FindNextCommandEndSubpath(nSubpathStartIndex, aSegments);
+
+        // Prepare attributes for a:path start element
+        // NOFILL or one of the LIGHTEN commands
+        std::optional<OString> sFill;
+        if (HasCommandInSubPath(NOFILL, nSubpathStartIndex, nNextNcommandIndex 
- 1, aSegments))
+            sFill = "none";
+        else if (HasCommandInSubPath(DARKEN, nSubpathStartIndex, 
nNextNcommandIndex - 1, aSegments))
+            sFill = "darken";
+        else if (HasCommandInSubPath(DARKENLESS, nSubpathStartIndex, 
nNextNcommandIndex - 1,
+                                     aSegments))
+            sFill = "darkenLess";
+        else if (HasCommandInSubPath(LIGHTEN, nSubpathStartIndex, 
nNextNcommandIndex - 1,
+                                     aSegments))
+            sFill = "lighten";
+        else if (HasCommandInSubPath(LIGHTENLESS, nSubpathStartIndex, 
nNextNcommandIndex - 1,
+                                     aSegments))
+            sFill = "lightenLess";
+        // NOSTROKE
+        std::optional<OString> sStroke;
+        if (HasCommandInSubPath(NOSTROKE, nSubpathStartIndex, 
nNextNcommandIndex - 1, aSegments))
+            sStroke = "0";
+
+        // Write a:path start element
+        mpFS->startElementNS(
+            XML_a, XML_path, XML_fill, sFill, XML_stroke, sStroke, XML_w,
+            OString::number(bUseGlobalViewBox ? nViewBoxWidth : 
aPathSize[nPathSizeIndex].Width),
+            XML_h,
+            OString::number(bUseGlobalViewBox ? nViewBoxHeight : 
aPathSize[nPathSizeIndex].Height));
+
+        // Arcs drawn by commands ELLIPTICALQUADRANTX and ELLIPTICALQUADRANTY 
depend on the position
+        // of the target point in regard to the current point. Therefore we 
need to track the
+        // current point. A current point is not defined in the beginning.
+        double fCurrentX(0.0);
+        double fCurrentY(0.0);
+        bool bCurrentValid(false);
+        // Actually write the subpath
+        for (sal_Int32 nSegmentIndex = nSubpathStartIndex; nSegmentIndex < 
nNextNcommandIndex;
+             ++nSegmentIndex)
+        {
+            const auto& rSegment(aSegments[nSegmentIndex]);
+            if (rSegment.Command == CLOSESUBPATH)
+            {
+                mpFS->singleElementNS(XML_a, XML_close); // command Z has no 
parameter
+                // ODF 1.4 specifies, that the start of the subpath becomes 
the current point.
+                // But that is not implemented yet. Currently LO keeps the 
last current point.
+            }
+            for (sal_Int32 k = 0; k < rSegment.Count && bOK; ++k)
+            {
+                switch (rSegment.Command)
                 {
-                    sal_Int32 nXMin(0);
-                    aPairs[0].First.Value >>= nXMin;
-                    sal_Int32 nXMax = nXMin;
-                    sal_Int32 nYMin(0);
-                    aPairs[0].Second.Value >>= nYMin;
-                    sal_Int32 nYMax = nYMin;
-
-                    for ( const auto& rPair : std::as_const(aPairs) )
+                    case MOVETO:
                     {
-                        sal_Int32 nX = 
GetCustomGeometryPointValue(rPair.First, rSdrObjCustomShape);
-                        sal_Int32 nY = 
GetCustomGeometryPointValue(rPair.Second, rSdrObjCustomShape);
-                        if (nX < nXMin)
-                            nXMin = nX;
-                        if (nY < nYMin)
-                            nYMin = nY;
-                        if (nX > nXMax)
-                            nXMax = nX;
-                        if (nY > nYMax)
-                            nYMax = nY;
+                        if (nPairIndex >= aPairs.getLength())
+                            bOK = false;
+                        else
+                        {
+                            mpFS->startElementNS(XML_a, XML_moveTo);
+                            WriteCustomGeometryPoint(aPairs[nPairIndex], 
aCustomShape2d);
+                            mpFS->endElementNS(XML_a, XML_moveTo);
+                            aCustomShape2d.GetParameter(fCurrentX, 
aPairs[nPairIndex].First, false,
+                                                        false);
+                            aCustomShape2d.GetParameter(fCurrentY, 
aPairs[nPairIndex].Second, false,
+                                                        false);
+                            bCurrentValid = true;
+                            nPairIndex++;
+                        }
+                        break;
                     }
-                    mpFS->startElementNS( XML_a, XML_path,
-                          XML_fill, sFill,
-                          XML_w, OString::number(nXMax - nXMin),
-                          XML_h, OString::number(nYMax - nYMin) );
-                }
-
-
-                int nPairIndex = 0;
-                bool bOK = true;
-                for (const auto& rSegment : std::as_const(aSegments))
-                {
-                    if ( rSegment.Command == 
drawing::EnhancedCustomShapeSegmentCommand::CLOSESUBPATH )
+                    case LINETO:
                     {
-                        mpFS->singleElementNS(XML_a, XML_close);
+                        if (nPairIndex >= aPairs.getLength())
+                            bOK = false;
+                        else
+                        {
+                            mpFS->startElementNS(XML_a, XML_lnTo);
+                            WriteCustomGeometryPoint(aPairs[nPairIndex], 
aCustomShape2d);
+                            mpFS->endElementNS(XML_a, XML_lnTo);
+                            aCustomShape2d.GetParameter(fCurrentX, 
aPairs[nPairIndex].First, false,
+                                                        false);
+                            aCustomShape2d.GetParameter(fCurrentY, 
aPairs[nPairIndex].Second, false,
+                                                        false);
+                            bCurrentValid = true;
+                            nPairIndex++;
+                        }
+                        break;
                     }
-                    for (int k = 0; k < rSegment.Count && bOK; ++k)
+                    case CURVETO:
                     {
-                        switch( rSegment.Command )
+                        if (nPairIndex + 2 >= aPairs.getLength())
+                            bOK = false;
+                        else
                         {
-                            case 
drawing::EnhancedCustomShapeSegmentCommand::MOVETO :
+                            mpFS->startElementNS(XML_a, XML_cubicBezTo);
+                            for (sal_uInt8 l = 0; l <= 2; ++l)
                             {
-                                if (nPairIndex >= aPairs.getLength())
-                                    bOK = false;
-                                else
-                                {
-                                    mpFS->startElementNS(XML_a, XML_moveTo);
-                                    
WriteCustomGeometryPoint(aPairs[nPairIndex], rSdrObjCustomShape);
-                                    mpFS->endElementNS( XML_a, XML_moveTo );
-                                    nPairIndex++;
-                                }
-                                break;
+                                WriteCustomGeometryPoint(aPairs[nPairIndex + 
l], aCustomShape2d);
                             }
-                            case 
drawing::EnhancedCustomShapeSegmentCommand::LINETO :
+                            mpFS->endElementNS(XML_a, XML_cubicBezTo);
+                            aCustomShape2d.GetParameter(fCurrentX, 
aPairs[nPairIndex + 2].First,
+                                                        false, false);
+                            aCustomShape2d.GetParameter(fCurrentY, 
aPairs[nPairIndex + 2].Second,
+                                                        false, false);
+                            bCurrentValid = true;
+                            nPairIndex += 3;
+                        }
+                        break;
+                    }
+                    case ANGLEELLIPSETO:
+                    case ANGLEELLIPSE:
+                    {
+                        if (nPairIndex + 2 >= aPairs.getLength())
+                            bOK = false;
+                        else
+                        {
+                            // Read parameters
+                            double fCx = 0.0;
+                            aCustomShape2d.GetParameter(fCx, 
aPairs[nPairIndex].First, false,
+                                                        false);
+                            double fCy = 0.0;
+                            aCustomShape2d.GetParameter(fCy, 
aPairs[nPairIndex].Second, false,
+                                                        false);
+                            double fWR = 0.0;
+                            aCustomShape2d.GetParameter(fWR, aPairs[nPairIndex 
+ 1].First, false,
+                                                        false);
+                            double fHR = 0.0;
+                            aCustomShape2d.GetParameter(fHR, aPairs[nPairIndex 
+ 1].Second, false,
+                                                        false);
+                            double fStartAngle = 0.0;
+                            aCustomShape2d.GetParameter(fStartAngle, 
aPairs[nPairIndex + 2].First,
+                                                        false, false);
+                            double fEndAngle = 0.0;
+                            aCustomShape2d.GetParameter(fEndAngle, 
aPairs[nPairIndex + 2].Second,
+                                                        false, false);
+
+                            // Prepare start and swing angle
+                            sal_Int32 nStartAng(std::lround(fStartAngle * 
60000));
+                            sal_Int32 nSwingAng = 0;
+                            if (basegfx::fTools::equalZero(fStartAngle)
+                                && basegfx::fTools::equalZero(fEndAngle - 
360.0))
+                                nSwingAng = 360 * 60000; // special case full 
circle
+                            else
                             {
-                                if (nPairIndex >= aPairs.getLength())
-                                    bOK = false;
-                                else
-                                {
-                                    mpFS->startElementNS(XML_a, XML_lnTo);
-                                    
WriteCustomGeometryPoint(aPairs[nPairIndex], rSdrObjCustomShape);
-                                    mpFS->endElementNS( XML_a, XML_lnTo );
-                                    nPairIndex++;
-                                }
-                                break;
+                                nSwingAng = std::lround((fEndAngle - 
fStartAngle) * 60000);
+                                if (nSwingAng < 0)
+                                    nSwingAng += 360 * 60000;
                             }
-                            case 
drawing::EnhancedCustomShapeSegmentCommand::CURVETO :
+
+                            // calculate start point on ellipse
+                            double fSx = 0.0;
+                            double fSy = 0.0;
+                            getEllipsePointFromViewAngle(fSx, fSy, fWR, fHR, 
fCx, fCy, fStartAngle);
+
+                            // write markup for going to start point
+                            if (rSegment.Command == ANGLEELLIPSETO)
                             {
-                                if (nPairIndex + 2 >= aPairs.getLength())
-                                    bOK = false;
-                                else
-                                {
-                                    mpFS->startElementNS(XML_a, 
XML_cubicBezTo);
-                                    for( sal_uInt8 l = 0; l <= 2; ++l )
-                                    {
-                                        
WriteCustomGeometryPoint(aPairs[nPairIndex+l], rSdrObjCustomShape);
-                                    }
-                                    mpFS->endElementNS( XML_a, XML_cubicBezTo 
);
-                                    nPairIndex += 3;
-                                }
-                                break;
+                                mpFS->startElementNS(XML_a, XML_lnTo);
+                                mpFS->singleElementNS(XML_a, XML_pt, XML_x,
+                                                      
OString::number(std::lround(fSx)), XML_y,
+                                                      
OString::number(std::lround(fSy)));
+                                mpFS->endElementNS(XML_a, XML_lnTo);
                             }
-                            case 
drawing::EnhancedCustomShapeSegmentCommand::ANGLEELLIPSETO :
-                            case 
drawing::EnhancedCustomShapeSegmentCommand::ANGLEELLIPSE :
+                            else
                             {
-                                nPairIndex += 3;
-                                break;
+                                mpFS->startElementNS(XML_a, XML_moveTo);
+                                mpFS->singleElementNS(XML_a, XML_pt, XML_x,
+                                                      
OString::number(std::lround(fSx)), XML_y,
+                                                      
OString::number(std::lround(fSy)));
+                                mpFS->endElementNS(XML_a, XML_moveTo);
                             }
-                            case 
drawing::EnhancedCustomShapeSegmentCommand::ARCTO :
-                            case 
drawing::EnhancedCustomShapeSegmentCommand::ARC :
-                            case 
drawing::EnhancedCustomShapeSegmentCommand::CLOCKWISEARCTO :
-                            case 
drawing::EnhancedCustomShapeSegmentCommand::CLOCKWISEARC :
+                            // write markup for arcTo
+                            if (!basegfx::fTools::equalZero(fWR)
+                                && !basegfx::fTools::equalZero(fHR))
+                                mpFS->singleElement(FSNS(XML_a, XML_arcTo), 
XML_wR,
+                                                    
OString::number(std::lround(fWR)), XML_hR,
+                                                    
OString::number(std::lround(fHR)), XML_stAng,
+                                                    
OString::number(nStartAng), XML_swAng,
+                                                    
OString::number(nSwingAng));
+
+                            getEllipsePointFromViewAngle(fCurrentX, fCurrentY, 
fWR, fHR, fCx, fCy,
+                                                         fEndAngle);
+                            bCurrentValid = true;
+                            nPairIndex += 3;
+                        }
+                        break;
+                    }
+                    case ARCTO:
+                    case ARC:
+                    case CLOCKWISEARCTO:
+                    case CLOCKWISEARC:
+                    {
+                        if (nPairIndex + 3 >= aPairs.getLength())
+                            bOK = false;
+                        else
+                        {
+                            // read parameters
+                            double fX1 = 0.0;
+                            aCustomShape2d.GetParameter(fX1, 
aPairs[nPairIndex].First, false,
+                                                        false);
+                            double fY1 = 0.0;
+                            aCustomShape2d.GetParameter(fY1, 
aPairs[nPairIndex].Second, false,
+                                                        false);
+                            double fX2 = 0.0;
+                            aCustomShape2d.GetParameter(fX2, aPairs[nPairIndex 
+ 1].First, false,
+                                                        false);
+                            double fY2 = 0.0;
+                            aCustomShape2d.GetParameter(fY2, aPairs[nPairIndex 
+ 1].Second, false,
+                                                        false);
+                            double fX3 = 0.0;
+                            aCustomShape2d.GetParameter(fX3, aPairs[nPairIndex 
+ 2].First, false,
+                                                        false);
+                            double fY3 = 0.0;
+                            aCustomShape2d.GetParameter(fY3, aPairs[nPairIndex 
+ 2].Second, false,
+                                                        false);
+                            double fX4 = 0.0;
+                            aCustomShape2d.GetParameter(fX4, aPairs[nPairIndex 
+ 3].First, false,
+                                                        false);
+                            double fY4 = 0.0;
+                            aCustomShape2d.GetParameter(fY4, aPairs[nPairIndex 
+ 3].Second, false,
+                                                        false);
+                            // calculate ellipse parameter
+                            const double fWR = (fX2 - fX1) / 2.0;
+                            const double fHR = (fY2 - fY1) / 2.0;
+                            const double fCx = (fX1 + fX2) / 2.0;
+                            const double fCy = (fY1 + fY2) / 2.0;
+                            // calculate start angle
+                            double fStartAngle = 0.0;
+                            double fPx = 0.0;
+                            double fPy = 0.0;
+                            getEllipsePointAndAngleFromRayPoint(fStartAngle, 
fPx, fPy, fWR, fHR,
+                                                                fCx, fCy, fX3, 
fY3);
+                            // markup for going to start point
+                            if (rSegment.Command == ARCTO || rSegment.Command 
== CLOCKWISEARCTO)
                             {
-                                nPairIndex += 4;
-                                break;
+                                mpFS->startElementNS(XML_a, XML_lnTo);
+                                mpFS->singleElementNS(XML_a, XML_pt, XML_x,
+                                                      
OString::number(std::lround(fPx)), XML_y,
+                                                      
OString::number(std::lround(fPy)));
+                                mpFS->endElementNS(XML_a, XML_lnTo);
                             }
-                            case 
drawing::EnhancedCustomShapeSegmentCommand::ELLIPTICALQUADRANTX :
-                            case 
drawing::EnhancedCustomShapeSegmentCommand::ELLIPTICALQUADRANTY :
+                            else
                             {
-                                nPairIndex++;
-                                break;
+                                mpFS->startElementNS(XML_a, XML_moveTo);
+                                mpFS->singleElementNS(XML_a, XML_pt, XML_x,
+                                                      
OString::number(std::lround(fPx)), XML_y,
+                                                      
OString::number(std::lround(fPy)));
+                                mpFS->endElementNS(XML_a, XML_moveTo);
                             }
-                            case 
drawing::EnhancedCustomShapeSegmentCommand::QUADRATICCURVETO :
+                            // calculate swing angle
+                            double fEndAngle = 0.0;
+                            getEllipsePointAndAngleFromRayPoint(fEndAngle, 
fPx, fPy, fWR, fHR, fCx,
+                                                                fCy, fX4, fY4);
+                            double fSwingAngle(fEndAngle - fStartAngle);
+                            const bool bIsClockwise(rSegment.Command == 
CLOCKWISEARCTO
+                                                    || rSegment.Command == 
CLOCKWISEARC);
+                            if (bIsClockwise && fSwingAngle < 0)
+                                fSwingAngle += 360.0;
+                            else if (!bIsClockwise && fSwingAngle > 0)
+                                fSwingAngle -= 360.0;
+                            // markup for arcTo
+                            // ToDo: write markup for case zero width or 
height of ellipse
+                            const sal_Int32 nStartAng(std::lround(fStartAngle 
* 60000));
+                            const sal_Int32 nSwingAng(std::lround(fSwingAngle 
* 60000));
+                            mpFS->singleElement(
+                                FSNS(XML_a, XML_arcTo), XML_wR, 
OString::number(std::lround(fWR)),
+                                XML_hR, OString::number(std::lround(fHR)), 
XML_stAng,
+                                OString::number(nStartAng), XML_swAng, 
OString::number(nSwingAng));
+                            fCurrentX = fPx;
+                            fCurrentY = fPy;
+                            bCurrentValid = true;
+                            nPairIndex += 4;
+                        }
+                        break;
+                    }
+                    case ELLIPTICALQUADRANTX:
+                    case ELLIPTICALQUADRANTY:
+                    {
+                        if (nPairIndex >= aPairs.getLength())
+                            bOK = false;
+                        else
+                        {
+                            // read parameters
+                            double fX = 0.0;
+                            aCustomShape2d.GetParameter(fX, 
aPairs[nPairIndex].First, false, false);
+                            double fY = 0.0;
+                            aCustomShape2d.GetParameter(fY, 
aPairs[nPairIndex].Second, false,
+                                                        false);
+
+                            // Prepare parameters for arcTo
+                            if (bCurrentValid)
                             {
-                                if (nPairIndex + 1 >= aPairs.getLength())
-                                    bOK = false;
-                                else
+                                double fWR = std::abs(fCurrentX - fX);
+                                double fHR = std::abs(fCurrentY - fY);
+                                double fStartAngle(0.0);
+                                double fSwingAngle(0.0);
+                                // The starting direction of the arc toggles 
beween X and Y
+                                if ((rSegment.Command == ELLIPTICALQUADRANTX 
&& !(k % 2))
+                                    || (rSegment.Command == 
ELLIPTICALQUADRANTY && (k % 2)))
                                 {
-                                    mpFS->startElementNS(XML_a, XML_quadBezTo);
-                                    for( sal_uInt8 l = 0; l < 2; ++l )
-                                    {
-                                        
WriteCustomGeometryPoint(aPairs[nPairIndex+l], rSdrObjCustomShape);
-                                    }
-                                    mpFS->endElementNS( XML_a, XML_quadBezTo );
-                                    nPairIndex += 2;
+                                    // arc starts horizontal
+                                    fStartAngle = fY < fCurrentY ? 90.0 : 
270.0;
+                                    const bool bClockwise = (fX < fCurrentX && 
fY < fCurrentY)
+                                                            || (fX > fCurrentX 
&& fY > fCurrentY);
+                                    fSwingAngle = bClockwise ? 90.0 : -90.0;
                                 }
-                                break;
-                            }
-                            case 
drawing::EnhancedCustomShapeSegmentCommand::ARCANGLETO :
-                            {
-                                if (nPairIndex + 1 >= aPairs.getLength())
-                                    bOK = false;
                                 else
                                 {
-                                    const EnhancedCustomShape2d aCustoShape2d(
-                                        
const_cast<SdrObjCustomShape&>(rSdrObjCustomShape));
-                                    double fWR = 0.0;
-                                    aCustoShape2d.GetParameter(fWR, 
aPairs[nPairIndex].First, false,
-                                                               false);
-                                    double fHR = 0.0;
-                                    aCustoShape2d.GetParameter(fHR, 
aPairs[nPairIndex].Second,
-                                                               false, false);
-                                    double fStartAngle = 0.0;
-                                    aCustoShape2d.GetParameter(
-                                        fStartAngle, aPairs[nPairIndex + 
1].First, false, false);
-                                    sal_Int32 
nStartAng(std::lround(fStartAngle * 60000));
-                                    double fSwingAng = 0.0;
-                                    aCustoShape2d.GetParameter(
-                                        fSwingAng, aPairs[nPairIndex + 
1].Second, false, false);
-                                    sal_Int32 nSwingAng(std::lround(fSwingAng 
* 60000));
-                                    mpFS->singleElement(FSNS(XML_a, XML_arcTo),
-                                                        XML_wR, 
OString::number(fWR),
-                                                        XML_hR, 
OString::number(fHR),
-                                                        XML_stAng, 
OString::number(nStartAng),
-                                                        XML_swAng, 
OString::number(nSwingAng));
-                                    nPairIndex += 2;
+                                    // arc starts vertical
+                                    fStartAngle = fX < fCurrentX ? 0.0 : 180.0;
+                                    const bool bClockwise = (fX < fCurrentX && 
fY > fCurrentY)
+                                                            || (fX > fCurrentX 
&& fY < fCurrentY);
+                                    fSwingAngle = bClockwise ? 90.0 : -90.0;
                                 }
-                                break;
+                                sal_Int32 nStartAng(std::lround(fStartAngle * 
60000));
+                                sal_Int32 nSwingAng(std::lround(fSwingAngle * 
60000));
+                                mpFS->singleElement(FSNS(XML_a, XML_arcTo), 
XML_wR,
+                                                    
OString::number(std::lround(fWR)), XML_hR,
+                                                    
OString::number(std::lround(fHR)), XML_stAng,
+                                                    
OString::number(nStartAng), XML_swAng,
+                                                    
OString::number(nSwingAng));
                             }
-                            default:
-                                // do nothing
-                                break;
+                            else
+                            {
+                                // faulty path, but we continue with the 
target point
+                                mpFS->startElementNS(XML_a, XML_moveTo);
+                                WriteCustomGeometryPoint(aPairs[nPairIndex], 
aCustomShape2d);
+                                mpFS->endElementNS(XML_a, XML_moveTo);
+                            }
+                            fCurrentX = fX;
+                            fCurrentY = fY;
+                            bCurrentValid = true;
+                            nPairIndex++;
                         }
+                        break;
+                    }
+                    case QUADRATICCURVETO:
+                    {
+                        if (nPairIndex + 1 >= aPairs.getLength())
+                            bOK = false;
+                        else
+                        {
+                            mpFS->startElementNS(XML_a, XML_quadBezTo);
+                            for( sal_uInt8 l = 0; l < 2; ++l )
+                            {
+                                WriteCustomGeometryPoint(aPairs[nPairIndex + 
l], aCustomShape2d);
+                            }
+                            mpFS->endElementNS( XML_a, XML_quadBezTo );
+                            aCustomShape2d.GetParameter(fCurrentX, 
aPairs[nPairIndex + 1].First, false,
+                                                        false);
+                            aCustomShape2d.GetParameter(fCurrentY, 
aPairs[nPairIndex + 1].Second, false,
+                                                        false);
+                            bCurrentValid = true;
+                            nPairIndex += 2;
+                        }
+                        break;
                     }
-                    if (!bOK)
+                    case ARCANGLETO:
+                    {
+                        if (nPairIndex + 1 >= aPairs.getLength())
+                            bOK = false;
+                        else
+                        {
+                            double fWR = 0.0;
+                            aCustomShape2d.GetParameter(fWR, 
aPairs[nPairIndex].First, false, false);
+                            double fHR = 0.0;
+                            aCustomShape2d.GetParameter(fHR, 
aPairs[nPairIndex].Second, false, false);
+                            double fStartAngle = 0.0;
+                            aCustomShape2d.GetParameter(fStartAngle, 
aPairs[nPairIndex + 1].First,
+                                                        false, false);
+                            sal_Int32 nStartAng(std::lround(fStartAngle * 
60000));
+                            double fSwingAng = 0.0;
+                            aCustomShape2d.GetParameter(fSwingAng, 
aPairs[nPairIndex + 1].Second,
+                                                        false, false);
+                            sal_Int32 nSwingAng(std::lround(fSwingAng * 
60000));
+                            mpFS->singleElement(FSNS(XML_a, XML_arcTo), 
XML_wR, OString::number(fWR),
+                                                XML_hR, OString::number(fHR), 
XML_stAng,
+                                                OString::number(nStartAng), 
XML_swAng,
+                                                OString::number(nSwingAng));
+                            double fPx = 0.0;
+                            double fPy = 0.0;
+                            getEllipsePointFromViewAngle(fPx, fPy, fWR, fHR, 
0.0, 0.0, fStartAngle);
+                            double fCx = fCurrentX - fPx;
+                            double fCy = fCurrentY - fPy;
+                            getEllipsePointFromViewAngle(fCurrentX, fCurrentY, 
fWR, fHR, fCx, fCy,
+                                                         fStartAngle + 
fSwingAng);
+                            bCurrentValid = true;
+                            nPairIndex += 2;
+                        }
+                        break;
+                    }
+                    default:
+                        // do nothing
                         break;
                 }
-                mpFS->endElementNS( XML_a, XML_path );
-                mpFS->endElementNS( XML_a, XML_pathLst );
-                mpFS->endElementNS( XML_a, XML_custGeom );
-                return bOK;
-            }
-        }
-    }
-    return false;
+            } // end loop for commands which are repeated
+        } // end loop over all commands of subpath
+        // finish this subpath in any case
+        mpFS->endElementNS(XML_a, XML_path);
+
+        if (!bOK)
+            break; // exit loop if not enough values in aPairs
+
+        // step forward to next subpath
+        nSubpathStartIndex = nNextNcommandIndex + 1;
+        nPathSizeIndex++;
+    } while (nSubpathStartIndex < aSegments.getLength());
+
+    mpFS->endElementNS(XML_a, XML_pathLst);
+    mpFS->endElementNS(XML_a, XML_custGeom);
+    return true; // We have written custGeom even if path is poorly structured.
 }
 
 void DrawingML::WriteCustomGeometryPoint(
     const drawing::EnhancedCustomShapeParameterPair& rParamPair,
-    const SdrObjCustomShape& rSdrObjCustomShape)
+    const EnhancedCustomShape2d& rCustomShape2d)
 {
-    sal_Int32 nX = GetCustomGeometryPointValue(rParamPair.First, 
rSdrObjCustomShape);
-    sal_Int32 nY = GetCustomGeometryPointValue(rParamPair.Second, 
rSdrObjCustomShape);
+    sal_Int32 nX = GetCustomGeometryPointValue(rParamPair.First, 
rCustomShape2d);
+    sal_Int32 nY = GetCustomGeometryPointValue(rParamPair.Second, 
rCustomShape2d);
 
     mpFS->singleElementNS(XML_a, XML_pt, XML_x, OString::number(nX), XML_y, 
OString::number(nY));
 }
 
-sal_Int32 DrawingML::GetCustomGeometryPointValue(
-    const css::drawing::EnhancedCustomShapeParameter& rParam,
-    const SdrObjCustomShape& rSdrObjCustomShape)
+void DrawingML::WriteEmptyCustomGeometry()
 {
-    const EnhancedCustomShape2d aCustoShape2d(const_cast< SdrObjCustomShape& 
>(rSdrObjCustomShape));
-    double fValue = 0.0;
-    aCustoShape2d.GetParameter(fValue, rParam, false, false);
-    sal_Int32 nValue(std::lround(fValue));
-
-    return nValue;
-}
-
-// version for SdrObjCustomShape
-void DrawingML::WritePolyPolygon(const 
css::uno::Reference<css::drawing::XShape>& rXShape,
-                                 const tools::PolyPolygon& rPolyPolygon, const 
bool bClosed)
-{
-    // In case of Writer, the parent element is <wps:spPr>, and there the
-    // <a:custGeom> element is not optional.
-    if (rPolyPolygon.Count() < 1 && GetDocumentType() != DOCUMENT_DOCX)
-        return;
-
+    // This method is used for export to docx in case WriteCustomGeometry 
fails.
     mpFS->startElementNS(XML_a, XML_custGeom);
     mpFS->singleElementNS(XML_a, XML_avLst);
     mpFS->singleElementNS(XML_a, XML_gdLst);
     mpFS->singleElementNS(XML_a, XML_ahLst);
     mpFS->singleElementNS(XML_a, XML_rect, XML_l, "0", XML_t, "0", XML_r, "r", 
XML_b, "b");
-
-    mpFS->startElementNS( XML_a, XML_pathLst );
-
-    const tools::Rectangle aRect( rPolyPolygon.GetBoundRect() );
-
-    // tdf#101122
-    std::optional<OString> sFill;
-    if (HasEnhancedCustomShapeSegmentCommand(rXShape, 
css::drawing::EnhancedCustomShapeSegmentCommand::NOFILL))
-        sFill = "none"; // for possible values see ST_PathFillMode in OOXML 
standard
-
-    // Put all polygons of rPolyPolygon in the same path element
-    // to subtract the overlapped areas.
-    mpFS->startElementNS( XML_a, XML_path,
-            XML_fill, sFill,
-            XML_w, OString::number(aRect.GetWidth()),
-            XML_h, OString::number(aRect.GetHeight()) );
-
-    for( sal_uInt16 i = 0; i < rPolyPolygon.Count(); i ++ )
-    {
-
-        const tools::Polygon& rPoly = rPolyPolygon[ i ];
-
-        if( rPoly.GetSize() > 0 )
-        {
-            mpFS->startElementNS(XML_a, XML_moveTo);
-
-            mpFS->singleElementNS( XML_a, XML_pt,
-                                   XML_x, OString::number(rPoly[0].X() - 
aRect.Left()),
-                                   XML_y, OString::number(rPoly[0].Y() - 
aRect.Top()) );
-
-            mpFS->endElementNS( XML_a, XML_moveTo );
-        }
-
-        for( sal_uInt16 j = 1; j < rPoly.GetSize(); j ++ )
-        {
-            PolyFlags flags = rPoly.GetFlags(j);
-            if( flags == PolyFlags::Control )
-            {
-                // a:cubicBezTo can only contain 3 a:pt elements, so we need 
to make sure of this
-                if( j+2 < rPoly.GetSize() && rPoly.GetFlags(j+1) == 
PolyFlags::Control && rPoly.GetFlags(j+2) != PolyFlags::Control )
-                {
-
-                    mpFS->startElementNS(XML_a, XML_cubicBezTo);
-                    for( sal_uInt8 k = 0; k <= 2; ++k )
-                    {
-                        mpFS->singleElementNS(XML_a, XML_pt,
-                                              XML_x, 
OString::number(rPoly[j+k].X() - aRect.Left()),
-                                              XML_y, 
OString::number(rPoly[j+k].Y() - aRect.Top()));
-
-                    }
-                    mpFS->endElementNS( XML_a, XML_cubicBezTo );
-                    j += 2;
-                }
-            }
-            else if( flags == PolyFlags::Normal )
-            {
-                mpFS->startElementNS(XML_a, XML_lnTo);
-                mpFS->singleElementNS( XML_a, XML_pt,
-                                       XML_x, OString::number(rPoly[j].X() - 
aRect.Left()),
-                                       XML_y, OString::number(rPoly[j].Y() - 
aRect.Top()) );
-                mpFS->endElementNS( XML_a, XML_lnTo );
-            }
-        }
-    }
-    if (bClosed)
-        mpFS->singleElementNS( XML_a, XML_close);
-    mpFS->endElementNS( XML_a, XML_path );
-
-    mpFS->endElementNS( XML_a, XML_pathLst );
-
-    mpFS->endElementNS( XML_a, XML_custGeom );
+    mpFS->singleElementNS(XML_a, XML_pathLst);
+    mpFS->endElementNS(XML_a, XML_custGeom);
 }
 
 // version for SdrPathObj
@@ -4647,44 +4954,6 @@ void DrawingML::WriteSoftEdgeEffect(const 
css::uno::Reference<css::beans::XPrope
     WriteShapeEffect(u"softEdge", aProps);
 }
 
-bool DrawingML::HasEnhancedCustomShapeSegmentCommand(
-    const css::uno::Reference<css::drawing::XShape>& rXShape, const sal_Int16 
nCommand)
-{
-    try
-    {
-        uno::Reference<beans::XPropertySet> xPropSet(rXShape, 
uno::UNO_QUERY_THROW);
-        if (!GetProperty(xPropSet, "CustomShapeGeometry"))
-            return false;
-        Sequence<PropertyValue> aCustomShapeGeometryProps;
-        mAny >>= aCustomShapeGeometryProps;
-        for (const beans::PropertyValue& rGeomProp : 
std::as_const(aCustomShapeGeometryProps))
-        {
-            if (rGeomProp.Name == "Path")
-            {
-                uno::Sequence<beans::PropertyValue> aPathProps;
-                rGeomProp.Value >>= aPathProps;
-                for (const beans::PropertyValue& rPathProp : 
std::as_const(aPathProps))
-                {
-                    if (rPathProp.Name == "Segments")
-                    {
-                        uno::Sequence<drawing::EnhancedCustomShapeSegment> 
aSegments;
-                        rPathProp.Value >>= aSegments;
-                        for (const auto& rSegment : std::as_const(aSegments))
-                        {
-                            if (rSegment.Command == nCommand)
-                                return true;
-                        }
-                    }
-                }
-            }
-        }
-    }
-    catch (const ::uno::Exception&)
-    {
-    }
-    return false;
-}
-
 void DrawingML::Write3DEffects( const Reference< XPropertySet >& xPropSet, 
bool bIsText )
 {
     // check existence of the grab bag
diff --git a/oox/source/export/shapes.cxx b/oox/source/export/shapes.cxx
index 466b392c1a0e..7bc505e77481 100644
--- a/oox/source/export/shapes.cxx
+++ b/oox/source/export/shapes.cxx
@@ -740,7 +740,7 @@ ShapeExport& ShapeExport::WriteCustomShape( const 
Reference< XShape >& xShape )
 
     bool bHasGeometrySeq(false);
     Sequence< PropertyValue > aGeometrySeq;
-    OUString sShapeType;
+    OUString sShapeType("non-primitive"); // default in ODF
     if (GETA(CustomShapeGeometry))
     {
         SAL_INFO("oox.shape", "got custom shape geometry");
@@ -903,9 +903,6 @@ ShapeExport& ShapeExport::WriteCustomShape( const 
Reference< XShape >& xShape )
 
     // visual shape properties
     pFS->startElementNS(mnXmlNamespace, XML_spPr);
-    // moon is flipped in MSO, and mso-spt89 (right up arrow) is mapped to 
leftUpArrow
-    if ( sShapeType == "moon" || sShapeType == "mso-spt89" )
-        bFlipH = !bFlipH;
 
     // we export non-primitive shapes to custom geometry
     // we also export non-ooxml shapes which have handles/equations to custom 
geometry, because
@@ -925,8 +922,6 @@ ShapeExport& ShapeExport::WriteCustomShape( const 
Reference< XShape >& xShape )
         bCustGeom = false;
         bOnDenylist = true;
     }
-    else if( bHasHandles )
-        bCustGeom = true;
 
     bool bPresetWriteSuccessful = false;
     // Let the custom shapes what has name and preset information in OOXML, to 
be written
@@ -941,28 +936,16 @@ ShapeExport& ShapeExport::WriteCustomShape( const 
Reference< XShape >& xShape )
     // If preset writing has problems try to write the shape as it done before
     if (bPresetWriteSuccessful)
         ;// Already written do nothing.
-    else if (bHasHandles && bCustGeom)
-    {
-        WriteShapeTransformation( xShape, XML_a, bFlipH, bFlipV, false, true 
);// do not flip, polypolygon coordinates are flipped already
-        tools::PolyPolygon aPolyPolygon( 
rSdrObjCustomShape.GetLineGeometry(true) );
-        sal_Int32 nRotation = 0;
-        // The RotateAngle property's value is independent from any flipping, 
and that's exactly what we need here.
-        uno::Reference<beans::XPropertySet> xPropertySet(xShape, 
uno::UNO_QUERY);
-        uno::Reference<beans::XPropertySetInfo> xPropertySetInfo = 
xPropertySet->getPropertySetInfo();
-        if (xPropertySetInfo->hasPropertyByName("RotateAngle"))
-            xPropertySet->getPropertyValue("RotateAngle") >>= nRotation;
-        // Remove rotation
-        bool bInvertRotation = bFlipH != bFlipV;
-        if (nRotation != 0)
-            aPolyPolygon.Rotate(Point(0,0), 
Degree10(static_cast<sal_Int16>(bInvertRotation ? nRotation/10 : 
3600-nRotation/10)));
-        WritePolyPolygon(xShape, aPolyPolygon, false);
-    }
     else if (bCustGeom)
     {
         WriteShapeTransformation( xShape, XML_a, bFlipH, bFlipV );
         bool bSuccess = WriteCustomGeometry(xShape, rSdrObjCustomShape);
-        if (!bSuccess)
-            WritePresetShape( sPresetShape );
+        // In case of Writer, the parent element is <wps:spPr>, and there the 
<a:custGeom> element
+        // is not optional.
+        if (!bSuccess && GetDocumentType() == DOCUMENT_DOCX)
+        {
+            WriteEmptyCustomGeometry();
+        }
     }
     else if (bOnDenylist && bHasHandles && nAdjustmentValuesIndex !=-1 && 
!sShapeType.startsWith("mso-spt"))
     {
diff --git a/sd/qa/unit/data/odp/tdf147978_enhancedPath_viewBox.odp 
b/sd/qa/unit/data/odp/tdf147978_enhancedPath_viewBox.odp
new file mode 100644
index 000000000000..15d7046811c8
Binary files /dev/null and 
b/sd/qa/unit/data/odp/tdf147978_enhancedPath_viewBox.odp differ
diff --git a/sd/qa/unit/data/xml/tdf92001_0.xml 
b/sd/qa/unit/data/xml/tdf92001_0.xml
index afc1fd6ef10a..1bacf2f2559c 100644
--- a/sd/qa/unit/data/xml/tdf92001_0.xml
+++ b/sd/qa/unit/data/xml/tdf92001_0.xml
@@ -24,16 +24,52 @@
    <PropertyValue name="Path">
     <Path>
      <PropertyValue name="Coordinates">
-      <Coordinates/>
+      <Coordinates>
+       <EnhancedCustomShapeParameterPair>
+        <First value="0" type="0"/>
+        <Second value="5677" type="0"/>
+       </EnhancedCustomShapeParameterPair>
+       <EnhancedCustomShapeParameterPair>
+        <First value="8631" type="0"/>
+        <Second value="5677" type="0"/>
+       </EnhancedCustomShapeParameterPair>
+       <EnhancedCustomShapeParameterPair>
+        <First value="8631" type="0"/>
+        <Second value="0" type="0"/>
+       </EnhancedCustomShapeParameterPair>
+       <EnhancedCustomShapeParameterPair>
+        <First value="21600" type="0"/>
+        <Second value="10800" type="0"/>
+       </EnhancedCustomShapeParameterPair>
+       <EnhancedCustomShapeParameterPair>
+        <First value="8631" type="0"/>
+        <Second value="21600" type="0"/>
+       </EnhancedCustomShapeParameterPair>
+       <EnhancedCustomShapeParameterPair>
+        <First value="8631" type="0"/>
+        <Second value="15923" type="0"/>
+       </EnhancedCustomShapeParameterPair>
+       <EnhancedCustomShapeParameterPair>
+        <First value="0" type="0"/>
+        <Second value="15923" type="0"/>
+       </EnhancedCustomShapeParameterPair>
+      </Coordinates>
      </PropertyValue>
      <PropertyValue name="Segments">
-      <Segments/>
+      <Segments>
+       <EnhancedCustomShapeSegment command="1" count="1"/>
+       <EnhancedCustomShapeSegment command="2" count="6"/>
+       <EnhancedCustomShapeSegment command="4" count="0"/>
+       <EnhancedCustomShapeSegment command="5" count="0"/>
+      </Segments>
      </PropertyValue>
+     <PropertyValue name="SubViewSize" handle="0" 
propertyState="DIRECT_VALUE"/>
+     <PropertyValue name="TextFrames" handle="0" propertyState="DIRECT_VALUE"/>
     </Path>
    </PropertyValue>
    <PropertyValue name="Type" value="ooxml-non-primitive" handle="0" 
propertyState="DIRECT_VALUE"/>
    <PropertyValue name="ViewBox">
-    <ViewBox x="0" y="0" width="7040880" height="4663440"/>
+    <ViewBox x="0" y="0" width="0" height="0"/>
    </PropertyValue>
   </CustomShapeGeometry>
  </XShape>
diff --git a/sd/qa/unit/export-tests-ooxml2.cxx 
b/sd/qa/unit/export-tests-ooxml2.cxx
index 1951d2f9eae6..78f43f2160eb 100644
--- a/sd/qa/unit/export-tests-ooxml2.cxx
+++ b/sd/qa/unit/export-tests-ooxml2.cxx
@@ -1067,29 +1067,27 @@ void SdOOXMLExportTest2::testTdf111798()
     xDocShRef->DoClose();
     xmlDocUniquePtr pXmlDoc = parseExport(tempFile, "ppt/slides/slide1.xml");
 
-    const OUString data[][29] =
+    const OUString data[][26] =
     {
         {
             "2700000", "2458080", "2414880", "1439640", "1440000",
-            "moveTo",  "0",    "3000",
-            "lnTo[1]", "3000", "3000",
-            "lnTo[2]", "3000", "4000",
-            "lnTo[3]", "4000", "2000",
-            "lnTo[4]", "3000", "0",
-            "lnTo[5]", "3000", "1000",
-            "lnTo[6]", "0",    "1000",
-            "lnTo[7]", "0",    "3000"
+            "moveTo",  "0",     "5400",
+            "lnTo[1]", "16200", "5400",
+            "lnTo[2]", "16200", "0",
+            "lnTo[3]", "21600", "10800",
+            "lnTo[4]", "16200", "21600",
+            "lnTo[5]", "16200", "16200",
+            "lnTo[6]", "0",     "16200"
         },
         {
             "2700000", "6778080", "2414880", "1439640", "1440000",
-            "moveTo",  "3000", "0",
-            "lnTo[1]", "3000", "3000",
-            "lnTo[2]", "4000", "3000",
-            "lnTo[3]", "2000", "4000",
-            "lnTo[4]", "0", "3000",
-            "lnTo[5]", "1000", "3000",
-            "lnTo[6]", "1000", "0",
-            "lnTo[7]", "3000", "0"
+            "moveTo",  "5400",  "0",
+            "lnTo[1]", "5400",  "16200",
+            "lnTo[2]", "0",     "16200",
+            "lnTo[3]", "10800", "21600",
+            "lnTo[4]", "21600", "16200",
+            "lnTo[5]", "16200", "16200",
+            "lnTo[6]", "16200", "0"
         }
     };
 
diff --git a/sd/qa/unit/export-tests-ooxml3.cxx 
b/sd/qa/unit/export-tests-ooxml3.cxx
index db33ac9b88c4..40031977345d 100644
--- a/sd/qa/unit/export-tests-ooxml3.cxx
+++ b/sd/qa/unit/export-tests-ooxml3.cxx
@@ -125,6 +125,7 @@ public:
     void testTdf143315();
     void testTdf147121();
     void testTdf140912_PicturePlaceholder();
+    void testEnhancedPathViewBox();
 
     CPPUNIT_TEST_SUITE(SdOOXMLExportTest3);
 
@@ -200,6 +201,7 @@ public:
     CPPUNIT_TEST(testTdf143315);
     CPPUNIT_TEST(testTdf147121);
     CPPUNIT_TEST(testTdf140912_PicturePlaceholder);
+    CPPUNIT_TEST(testEnhancedPathViewBox);
     CPPUNIT_TEST_SUITE_END();
 
     virtual void registerNamespaces(xmlXPathContextPtr& pXmlXPathCtx) override
@@ -1896,6 +1898,20 @@ void 
SdOOXMLExportTest3::testTdf140912_PicturePlaceholder()
     xDocShRef->DoClose();
 }
 
+void SdOOXMLExportTest3::testEnhancedPathViewBox()
+{
+    auto xDocShRef = loadURL(
+        
m_directories.getURLFromSrc(u"sd/qa/unit/data/odp/tdf147978_enhancedPath_viewBox.odp"),
+        ODP);
+    xDocShRef = saveAndReload(xDocShRef.get(), PPTX);
+    auto xShapeProps(getShapeFromPage(0, 0, xDocShRef));
+    awt::Rectangle aBoundRectangle;
+    xShapeProps->getPropertyValue("BoundRect") >>= aBoundRectangle;
+    // The shape has a Bézier curve which does not touch the right edge. Prior 
to the fix the curve
+    // was stretched to touch the edge, resulting in 5098 curve width instead 
of 2045.
+    CPPUNIT_ASSERT_EQUAL(sal_Int32(2045), aBoundRectangle.Width);
+}
+
 CPPUNIT_TEST_SUITE_REGISTRATION(SdOOXMLExportTest3);
 
 CPPUNIT_PLUGIN_IMPLEMENT();
diff --git 
a/sw/qa/extras/ooxmlexport/data/tdf147978_enhancedPath_commandABVW.odt 
b/sw/qa/extras/ooxmlexport/data/tdf147978_enhancedPath_commandABVW.odt
new file mode 100644
index 000000000000..d9ef07db5920
Binary files /dev/null and 
b/sw/qa/extras/ooxmlexport/data/tdf147978_enhancedPath_commandABVW.odt differ
diff --git a/sw/qa/extras/ooxmlexport/ooxmlexport17.cxx 
b/sw/qa/extras/ooxmlexport/ooxmlexport17.cxx
index ea1d894f67ad..f23721767fc9 100644
--- a/sw/qa/extras/ooxmlexport/ooxmlexport17.cxx
+++ b/sw/qa/extras/ooxmlexport/ooxmlexport17.cxx
@@ -266,6 +266,22 @@ DECLARE_OOXMLEXPORT_TEST(testTdf144668, "tdf144668.odt")
     CPPUNIT_ASSERT_EQUAL(OUString("[001]"), getProperty<OUString>(xPara2, 
"ListLabelString"));
 }
 
+CPPUNIT_TEST_FIXTURE(Test, testTdf147978enhancedPathABVW)
+{
+    load(DATA_DIRECTORY, "tdf147978_enhancedPath_commandABVW.odt");
+    CPPUNIT_ASSERT(mxComponent);
+    save("Office Open XML Text", maTempFile);
+    mxComponent->dispose();
+    mxComponent = loadFromDesktop(maTempFile.GetURL(), 
"com.sun.star.text.TextDocument");
+    // Make sure the new implemented export for commands A,B,V and W use the 
correct arc between
+    // the given two points, here the short one.
+    for (sal_Int16 i = 1 ; i <= 4; ++i)
+    {
+        uno::Reference<drawing::XShape> xShape = getShape(i);
+        CPPUNIT_ASSERT_EQUAL(sal_Int32(506), 
getProperty<awt::Rectangle>(xShape, "BoundRect").Height);
+    }
+}
+
 CPPUNIT_PLUGIN_IMPLEMENT();
 
 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/qa/extras/ooxmlexport/ooxmlexport7.cxx 
b/sw/qa/extras/ooxmlexport/ooxmlexport7.cxx
index a16de671a6ba..2960f4a2c6c7 100644
--- a/sw/qa/extras/ooxmlexport/ooxmlexport7.cxx
+++ b/sw/qa/extras/ooxmlexport/ooxmlexport7.cxx
@@ -1133,24 +1133,30 @@ CPPUNIT_TEST_FIXTURE(Test, testFlipAndRotateCustomShape)
     CPPUNIT_ASSERT_EQUAL(1, getShapes());
     CPPUNIT_ASSERT_EQUAL(1, getPages());
     xmlDocUniquePtr pXmlDoc = parseExport("word/document.xml");
-    // there should be no flipH and flipV attributes in this case
+    // there should be no flipH
     assertXPathNoAttribute(pXmlDoc, "//a:xfrm", "flipH");
-    assertXPathNoAttribute(pXmlDoc, "//a:xfrm", "flipV");
+    // flipV should be there
+    assertXPath(pXmlDoc, "//a:xfrm", "flipV", "1");
     // check rotation angle
-    assertXPath(pXmlDoc, "//a:xfrm", "rot", "13500000");
+    assertXPath(pXmlDoc, "//a:xfrm", "rot", "8100000");
+    // point values depend on path size, values as of March 2022
+    assertXPath(pXmlDoc, "//a:custGeom/a:pathLst/a:path", "w", "21600");
+    assertXPath(pXmlDoc, "//a:custGeom/a:pathLst/a:path", "h", "21600");
     // check the first few coordinates of the polygon
     CPPUNIT_ASSERT_DOUBLES_EQUAL(
-        2351, getXPath(pXmlDoc, 
"//a:custGeom/a:pathLst/a:path/a:lnTo[1]/a:pt", "x").toInt32(), 1);
+        0, getXPath(pXmlDoc, "//a:custGeom/a:pathLst/a:path/a:moveTo/a:pt", 
"x").toInt32(), 1);
     CPPUNIT_ASSERT_DOUBLES_EQUAL(
-        3171, getXPath(pXmlDoc, 
"//a:custGeom/a:pathLst/a:path/a:lnTo[1]/a:pt", "y").toInt32(), 1);
+        15831, getXPath(pXmlDoc, 
"//a:custGeom/a:pathLst/a:path/a:moveTo/a:pt", "y").toInt32(), 1);
     CPPUNIT_ASSERT_DOUBLES_EQUAL(
-        1695, getXPath(pXmlDoc, 
"//a:custGeom/a:pathLst/a:path/a:lnTo[2]/a:pt", "x").toInt32(), 1);
+        6098, getXPath(pXmlDoc, 
"//a:custGeom/a:pathLst/a:path/a:lnTo[1]/a:pt", "x").toInt32(), 1);
     CPPUNIT_ASSERT_DOUBLES_EQUAL(
-        3171, getXPath(pXmlDoc, 
"//a:custGeom/a:pathLst/a:path/a:lnTo[2]/a:pt", "y").toInt32(), 1);
+        10062, getXPath(pXmlDoc, 
"//a:custGeom/a:pathLst/a:path/a:lnTo[1]/a:pt", "y").toInt32(), 1);
     CPPUNIT_ASSERT_DOUBLES_EQUAL(
-        1695, getXPath(pXmlDoc, 
"//a:custGeom/a:pathLst/a:path/a:lnTo[3]/a:pt", "x").toInt32(), 1);
+        13284, getXPath(pXmlDoc, 
"//a:custGeom/a:pathLst/a:path/a:lnTo[4]/a:pt", "x").toInt32(), 1);
     CPPUNIT_ASSERT_DOUBLES_EQUAL(
-        1701, getXPath(pXmlDoc, 
"//a:custGeom/a:pathLst/a:path/a:lnTo[3]/a:pt", "y").toInt32(), 1);
+        6098, getXPath(pXmlDoc, 
"//a:custGeom/a:pathLst/a:path/a:lnTo[4]/a:pt", "y").toInt32(), 1);
+    // check path is closed
+    assertXPath(pXmlDoc, "//a:close", 1);
 }
 
 CPPUNIT_TEST_FIXTURE(Test, testTdf92335)
diff --git a/sw/source/filter/ww8/docxsdrexport.cxx 
b/sw/source/filter/ww8/docxsdrexport.cxx
index 7166ed49565f..98257391c9cc 100644
--- a/sw/source/filter/ww8/docxsdrexport.cxx
+++ b/sw/source/filter/ww8/docxsdrexport.cxx
@@ -346,26 +346,6 @@ tools::Polygon lcl_CreateContourPolygon(SdrObject* pSdrObj)
             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 mirrored 
horizontally. 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);

Reply via email to