[Libreoffice-commits] core.git: Branch 'libreoffice-7-2' - include/oox oox/Library_oox.mk oox/source sw/qa

Attila Bakos (NISZ) (via logerrit) logerrit at kemper.freedesktop.org
Thu Jun 24 08:34:14 UTC 2021


 include/oox/export/DMLPresetShapeExport.hxx                   |  138 +
 oox/Library_oox.mk                                            |    1 
 oox/source/export/DMLPresetShapeExport.cxx                    | 1287 ++++++++++
 oox/source/export/shapes.cxx                                  |   15 
 sw/qa/extras/ooxmlexport/data/testCustomShapePresetExport.odt |binary
 sw/qa/extras/ooxmlexport/ooxmlexport16.cxx                    |   31 
 6 files changed, 1471 insertions(+), 1 deletion(-)

New commits:
commit 0cfa76c0a1883289f8406551d0494cc2a30df926
Author:     Attila Bakos (NISZ) <bakos.attilakaroly at nisz.hu>
AuthorDate: Fri May 14 13:53:32 2021 +0200
Commit:     Xisco Fauli <xiscofauli at libreoffice.org>
CommitDate: Thu Jun 24 10:33:36 2021 +0200

    tdf#92525 tdf#142398: fix export of simple custom shapes
    
    Most of the custom shapes were exported with DrawingML custom
    geometry instead of DOCX preset shapes, losing their preset type,
    adjust handles and text position. Add a preset exporter class to
    handle all possible preset shapes later, and fix the export of
    the following shapes now: "bevel", "blockArc", "bracePair",
    "bracketPair", "hexagon", "octagon", "parallelogram", "plus",
    "roundRect" and "triangle".
    
    tdf#92525: keep text position in triangles with different
    adjustments, too.
    
    tdf#142398: part 1: export simple shapes as preset shapes.
    
    Change-Id: I6aee74f7670bea8c1fe5909cbf307778ea728669
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/115606
    Tested-by: László Németh <nemeth at numbertext.org>
    Reviewed-by: László Németh <nemeth at numbertext.org>
    (cherry picked from commit 63cd67e5e18f01aca303131e148c80398a181a41)
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/117715
    Tested-by: Jenkins
    Reviewed-by: Xisco Fauli <xiscofauli at libreoffice.org>

diff --git a/include/oox/export/DMLPresetShapeExport.hxx b/include/oox/export/DMLPresetShapeExport.hxx
new file mode 100644
index 000000000000..1ab460d26845
--- /dev/null
+++ b/include/oox/export/DMLPresetShapeExport.hxx
@@ -0,0 +1,138 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+* This file is part of the LibreOffice project.
+*
+* This Source Code Form is subject to the terms of the Mozilla Public
+* License, v. 2.0. If a copy of the MPL was not distributed with this
+* file, You can obtain one at http://mozilla.org/MPL/2.0/.
+*/
+
+#ifndef INCLUDED_OOX_EXPORT_DMLPRESETSHAPEXPORT_HXX
+#define INCLUDED_OOX_EXPORT_DMLPRESETSHAPEXPORT_HXX
+
+#include <com/sun/star/awt/Rectangle.hpp>
+#include <com/sun/star/uno/Any.hxx>
+#include <com/sun/star/uno/Reference.hxx>
+#include <com/sun/star/uno/Sequence.hxx>
+
+#include <rtl/ustring.hxx>
+#include <sal/types.h>
+
+#include <string_view>
+
+#include <oox/export/drawingml.hxx>
+
+namespace com::sun::star::beans
+{
+struct PropertyValue;
+}
+
+namespace com::sun::star::drawing
+{
+class XShape;
+struct EnhancedCustomShapeAdjustmentValue;
+}
+
+namespace oox::core
+{
+class XmlFilterBase;
+}
+
+namespace oox::drawingml
+{
+/// Class for exporting the custom shapes to OOXML preset ones, if possible.
+/// This functionality needed for keeping the information for the office programs
+/// about the shape type, and geometry data. Before these shapes were exported
+/// with custom geometry, and they kept their geometry but has no information
+/// about the shape itself. This lead to lost textbox size/position/padding for
+/// example.
+class DMLPresetShapeExporter
+{
+private:
+    // the shape to export
+    css::uno::Reference<css::drawing::XShape> m_xShape;
+    // the DMLwriter
+    DrawingML* m_pDMLexporter;
+    // the type of the custom shape (diamond/rectangle/circle/triangle...)
+    OUString m_sPresetShapeType;
+    // True if the shape has points where its geometry can be modified
+    bool m_bHasHandleValues;
+    // The first the x the second the y coordinate, of flipping
+    std::pair<bool, bool> m_bIsFlipped;
+
+    // Custom Shape Geometry information for export:
+
+    // The adjusting values stored in this sequence:
+    css::uno::Sequence<css::drawing::EnhancedCustomShapeAdjustmentValue> m_AdjustmentValues;
+    // Shapes what have adjusting points, the range of these points
+    // and the index of the value stored in this sequence:
+    css::uno::Sequence<css::uno::Sequence<css::beans::PropertyValue>> m_HandleValues;
+
+    //TODO:
+    //css::awt::Rectangle m_ViewBox;
+    //css::uno::Sequence<css::beans::PropertyValue> m_Path;
+    //css::uno::Sequence<OUString> m_Equations;
+
+public:
+    DMLPresetShapeExporter() = delete;
+    ~DMLPresetShapeExporter();
+
+    DMLPresetShapeExporter(DrawingML* pDMLExporter,
+                           css::uno::Reference<css::drawing::XShape> xShape);
+
+    // Writes the preset shape to the xml
+    bool WriteShape();
+
+private:
+    struct AdjustmentPointValueBase
+    {
+        double nMaxVal;
+        double nMinVal;
+        double nCurrVal;
+    };
+
+    typedef AdjustmentPointValueBase RadiusAdjustmentValue;
+    typedef AdjustmentPointValueBase AngleAdjustmentValue;
+    typedef AdjustmentPointValueBase XAdjustmentValue;
+    typedef AdjustmentPointValueBase YAdjustmentValue;
+
+    // Returns true, if the shape has adjusting points
+    bool HasHandleValue();
+
+    // Returns true if the shape flipped.
+    bool IsXFlipped() { return m_bIsFlipped.first; };
+    bool IsYFlipped() { return m_bIsFlipped.second; };
+
+    // Returns with the shape type, like triangle for example
+    OUString GetShapeType();
+    // Returns with the handle points
+    css::uno::Sequence<css::uno::Sequence<css::beans::PropertyValue>> GetHandleValues();
+    // Returns with the adjustment values
+    css::uno::Sequence<css::drawing::EnhancedCustomShapeAdjustmentValue> GetAdjustmentValues();
+    // Returns with the raw value of the given property of the shape geometry.
+    css::uno::Any GetHandleValueOfModificationPoint(sal_Int32 nPoint, std::u16string_view sType);
+    // Returns with the appropriate value of the handle point.
+    RadiusAdjustmentValue GetAdjustmentPointRadiusValue(sal_Int32 nPoint);
+    AngleAdjustmentValue GetAdjustmentPointAngleValue(sal_Int32 nPoint);
+    XAdjustmentValue GetAdjustmentPointXValue(sal_Int32 nPoint);
+    YAdjustmentValue GetAdjustmentPointYValue(sal_Int32 nPoint);
+
+    // Writes one adjustment point.
+    bool WriteAV(const OUString& sValName, const OUString& sVal);
+    // Opens/Closes the AVlist tag.
+    bool StartAVListWriting();
+    bool EndAVListWriting();
+
+    // Finds the given value in the sequence
+    static css::uno::Any FindHandleValue(css::uno::Sequence<css::beans::PropertyValue> aValues,
+                                         std::u16string_view sKey);
+    // Writes and converts the adjustment points from sdr to ooxml ones per shape type.
+    bool WriteShapeWithAVlist();
+
+}; // end of DMLPresetShapeExporter class
+
+} // end of namespace oox::drawingml
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/oox/Library_oox.mk b/oox/Library_oox.mk
index 287a4bb70003..6550a3942c88 100644
--- a/oox/Library_oox.mk
+++ b/oox/Library_oox.mk
@@ -217,6 +217,7 @@ $(eval $(call gb_Library_add_exception_objects,oox,\
     oox/source/export/chartexport \
     oox/source/export/ColorPropertySet \
     oox/source/export/drawingml \
+    oox/source/export/DMLPresetShapeExport \
     oox/source/export/shapes \
     oox/source/export/vmlexport \
     oox/source/helper/attributelist \
diff --git a/oox/source/export/DMLPresetShapeExport.cxx b/oox/source/export/DMLPresetShapeExport.cxx
new file mode 100644
index 000000000000..8e4ebafcce98
--- /dev/null
+++ b/oox/source/export/DMLPresetShapeExport.cxx
@@ -0,0 +1,1287 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+* This file is part of the LibreOffice project.
+*
+* This Source Code Form is subject to the terms of the Mozilla Public
+* License, v. 2.0. If a copy of the MPL was not distributed with this
+* file, You can obtain one at http://mozilla.org/MPL/2.0/.
+*/
+
+#include <oox/export/DMLPresetShapeExport.hxx>
+#include <oox/token/tokens.hxx>
+
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/beans/PropertyValue.hpp>
+#include <com/sun/star/drawing/EnhancedCustomShapeAdjustmentValue.hpp>
+#include <com/sun/star/drawing/EnhancedCustomShapeParameterPair.hpp>
+#include <com/sun/star/drawing/XShape.hpp>
+
+#include <osl/diagnose.h>
+#include <filter/msfilter/util.hxx>
+
+#include <string_view>
+
+using namespace ::css;
+using namespace ::css::drawing;
+
+namespace oox::drawingml
+{
+// DMLPresetShapeExporter class
+
+// ctor
+DMLPresetShapeExporter::DMLPresetShapeExporter(DrawingML* pDMLExporter,
+                                               css::uno::Reference<css::drawing::XShape> xShape)
+    : m_pDMLexporter(pDMLExporter)
+{
+    // This class only work with custom shapes!
+    OSL_ASSERT(xShape->getShapeType() == "com.sun.star.drawing.CustomShape");
+
+    m_xShape = xShape;
+    m_bHasHandleValues = false;
+    uno::Reference<beans::XPropertySet> xShapeProps(m_xShape, uno::UNO_QUERY);
+    css::uno::Sequence<css::beans::PropertyValue> aCustomShapeGeometry
+        = xShapeProps->getPropertyValue("CustomShapeGeometry")
+              .get<uno::Sequence<beans::PropertyValue>>();
+
+    for (sal_uInt32 i = 0; i < aCustomShapeGeometry.size(); i++)
+    {
+        if (aCustomShapeGeometry[i].Name == "Type")
+        {
+            m_sPresetShapeType = aCustomShapeGeometry[i].Value.get<OUString>();
+        }
+        if (aCustomShapeGeometry[i].Name == "Handles")
+        {
+            m_bHasHandleValues = true;
+            m_HandleValues
+                = aCustomShapeGeometry[i]
+                      .Value
+                      .get<css::uno::Sequence<css::uno::Sequence<css::beans::PropertyValue>>>();
+        }
+        if (aCustomShapeGeometry[i].Name == "AdjustmentValues")
+        {
+            m_AdjustmentValues
+                = aCustomShapeGeometry[i]
+                      .Value
+                      .get<css::uno::Sequence<css::drawing::EnhancedCustomShapeAdjustmentValue>>();
+        }
+        if (aCustomShapeGeometry[i].Name == "MirroredX")
+        {
+            m_bIsFlipped.first = aCustomShapeGeometry[i].Value.get<bool>();
+        }
+        if (aCustomShapeGeometry[i].Name == "MirroredY")
+        {
+            m_bIsFlipped.second = aCustomShapeGeometry[i].Value.get<bool>();
+        }
+        //if (aCustomShapeGeometry[i].Name == "Equations")
+        //{
+        //    m_Equations = aCustomShapeGeometry[i].Value.get<css::uno::Sequence<OUString>>();
+        //}
+        //if (aCustomShapeGeometry[i].Name == "Path")
+        //{
+        //    m_Path = aCustomShapeGeometry[i]
+        //                 .Value.get<css::uno::Sequence<css::beans::PropertyValue>>();
+        //}
+        //if (aCustomShapeGeometry[i].Name == "ViewBox")
+        //{
+        //    m_ViewBox = aCustomShapeGeometry[i].Value.get<css::awt::Rectangle>();
+        //}
+    }
+};
+
+// dtor
+DMLPresetShapeExporter::~DMLPresetShapeExporter(){
+    // Do nothing
+};
+
+bool DMLPresetShapeExporter::HasHandleValue() { return m_bHasHandleValues; }
+
+OUString DMLPresetShapeExporter::GetShapeType() { return m_sPresetShapeType; }
+
+css::uno::Sequence<css::uno::Sequence<css::beans::PropertyValue>>
+DMLPresetShapeExporter::GetHandleValues()
+{
+    return m_HandleValues;
+};
+
+css::uno::Sequence<css::drawing::EnhancedCustomShapeAdjustmentValue>
+DMLPresetShapeExporter::GetAdjustmentValues()
+{
+    return m_AdjustmentValues;
+};
+
+css::uno::Any DMLPresetShapeExporter::GetHandleValueOfModificationPoint(sal_Int32 nPoint,
+                                                                        std::u16string_view sType)
+{
+    uno::Any aRet;
+    if (GetHandleValues().getLength() > nPoint)
+    {
+        for (sal_Int32 i = 0; i < GetHandleValues()[nPoint].getLength(); i++)
+        {
+            if (GetHandleValues()[nPoint][i].Name == sType)
+            {
+                aRet = GetHandleValues()[nPoint][i].Value;
+                break;
+            }
+        }
+    }
+    return aRet;
+};
+
+DMLPresetShapeExporter::RadiusAdjustmentValue
+DMLPresetShapeExporter::GetAdjustmentPointRadiusValue(sal_Int32 nPoint)
+{
+    RadiusAdjustmentValue aRet;
+    auto aValPos = GetHandleValueOfModificationPoint(nPoint, u"Position")
+                       .get<EnhancedCustomShapeParameterPair>();
+    aRet.nMinVal = GetHandleValueOfModificationPoint(nPoint, u"RadiusRangeMinimum")
+                       .get<EnhancedCustomShapeParameter>()
+                       .Value.get<double>();
+    aRet.nMaxVal = GetHandleValueOfModificationPoint(nPoint, u"RadiusRangeMaximum")
+                       .get<EnhancedCustomShapeParameter>()
+                       .Value.get<double>();
+    aRet.nCurrVal = GetAdjustmentValues()[aValPos.First.Value.get<long>()].Value.get<double>();
+    return aRet;
+};
+
+DMLPresetShapeExporter::AngleAdjustmentValue
+DMLPresetShapeExporter::GetAdjustmentPointAngleValue(sal_Int32 nPoint)
+{
+    AngleAdjustmentValue aRet;
+    auto aValPos = GetHandleValueOfModificationPoint(nPoint, u"Position")
+                       .get<EnhancedCustomShapeParameterPair>();
+    aRet.nMinVal = 0;
+    aRet.nMaxVal = 360;
+    aRet.nCurrVal = GetAdjustmentValues()[aValPos.Second.Value.get<long>()].Value.get<double>();
+    return aRet;
+};
+
+DMLPresetShapeExporter::XAdjustmentValue
+DMLPresetShapeExporter::GetAdjustmentPointXValue(sal_Int32 nPoint)
+{
+    XAdjustmentValue aRet;
+    auto aValPos = GetHandleValueOfModificationPoint(nPoint, u"Position")
+                       .get<EnhancedCustomShapeParameterPair>();
+    aRet.nMinVal = GetHandleValueOfModificationPoint(nPoint, u"RangeXMinimum")
+                       .get<EnhancedCustomShapeParameter>()
+                       .Value.get<double>();
+    aRet.nMaxVal = GetHandleValueOfModificationPoint(nPoint, u"RangeXMaximum")
+                       .get<EnhancedCustomShapeParameter>()
+                       .Value.get<double>();
+    aRet.nCurrVal = GetAdjustmentValues()[aValPos.First.Value.get<long>()].Value.get<double>();
+    return aRet;
+};
+
+DMLPresetShapeExporter::YAdjustmentValue
+DMLPresetShapeExporter::GetAdjustmentPointYValue(sal_Int32 nPoint)
+{
+    YAdjustmentValue aRet;
+    auto aValPos = GetHandleValueOfModificationPoint(nPoint, u"Position")
+                       .get<EnhancedCustomShapeParameterPair>();
+    aRet.nMinVal = GetHandleValueOfModificationPoint(nPoint, u"RangeYMinimum")
+                       .get<EnhancedCustomShapeParameter>()
+                       .Value.get<double>();
+    aRet.nMaxVal = GetHandleValueOfModificationPoint(nPoint, u"RangeYMinimum")
+                       .get<EnhancedCustomShapeParameter>()
+                       .Value.get<double>();
+    aRet.nCurrVal = GetAdjustmentValues()[aValPos.Second.Value.get<long>()].Value.get<double>();
+    return aRet;
+};
+
+bool DMLPresetShapeExporter::WriteShape()
+{
+    if (m_pDMLexporter && m_xShape)
+    {
+        // Case 1: We do not have adjustment points of the shape: just export it as preset
+        if (!m_bHasHandleValues)
+        {
+            OUString sShapeType = GetShapeType();
+            const char* sPresetShape
+                = msfilter::util::GetOOXMLPresetGeometry(sShapeType.toUtf8().getStr());
+            m_pDMLexporter->WriteShapeTransformation(m_xShape, XML_a, IsXFlipped(), IsYFlipped(),
+                                                     false, false);
+            m_pDMLexporter->WritePresetShape(sPresetShape);
+            return true;
+        }
+        else // Case2: There are adjustment points what have to be converted and exported.
+        {
+            return WriteShapeWithAVlist();
+        }
+    }
+    return false;
+};
+
+bool DMLPresetShapeExporter::WriteAV(const OUString& sValName, const OUString& sVal)
+{
+    try
+    {
+        m_pDMLexporter->GetFS()->singleElementNS(XML_a, XML_gd, XML_name, sValName, XML_fmla, sVal);
+        return true;
+    }
+    catch (...)
+    {
+        return false;
+    }
+};
+
+bool DMLPresetShapeExporter::StartAVListWriting()
+{
+    try
+    {
+        const char* pShape
+            = msfilter::util::GetOOXMLPresetGeometry(GetShapeType().toUtf8().getStr());
+        m_pDMLexporter->GetFS()->startElementNS(XML_a, XML_prstGeom, XML_prst, pShape);
+        m_pDMLexporter->GetFS()->startElementNS(XML_a, XML_avLst);
+        return true;
+    }
+    catch (...)
+    {
+        return false;
+    }
+};
+bool DMLPresetShapeExporter::EndAVListWriting()
+{
+    try
+    {
+        m_pDMLexporter->GetFS()->endElementNS(XML_a, XML_avLst);
+        m_pDMLexporter->GetFS()->endElementNS(XML_a, XML_prstGeom);
+        return true;
+    }
+    catch (...)
+    {
+        return false;
+    }
+};
+
+bool DMLPresetShapeExporter::WriteShapeWithAVlist()
+{
+    // Remark: This method is under development. If a shape type is implemented, the corresponding,
+    // return must be set to true. False means nothing done true, export done. There are many
+    // types which do not have pairs in LO, they are do not have to be mapped, because import
+    // filter it does with GrabBag, this method only maps the SDR ones to OOXML shapes.
+
+    OString sShapeType(msfilter::util::GetOOXMLPresetGeometry(GetShapeType().toUtf8().getStr()));
+
+    // OOXML uses 60th of degree, so 360 degree is 21 600 000 60thdeg
+    const tools::Long nConstOfMaxDegreeOf60th = 21600000;
+    try
+    {
+        if (sShapeType == "accentBorderCallout1")
+        {
+            // LO does not have this type, so it does not necessary to be mapped.
+            return false;
+        }
+        if (sShapeType == "accentBorderCallout2")
+        {
+            // LO does not have this type, so it does not necessary to be mapped.
+            return false;
+        }
+        if (sShapeType == "accentBorderCallout3")
+        {
+            // LO does not have this type, so it does not necessary to be mapped.
+            return false;
+        }
+        if (sShapeType == "accentCallout1")
+        {
+            // LO does not have this type, so it does not necessary to be mapped.
+            return false;
+        }
+        if (sShapeType == "accentCallout2")
+        {
+            // LO does not have this type, so it does not necessary to be mapped.
+            return false;
+        }
+        if (sShapeType == "accentCallout3")
+        {
+            // LO does not have this type, so it does not necessary to be mapped.
+            return false;
+        }
+        if (sShapeType == "actionButtonBackPrevious")
+        {
+            // LO does not have this type, so it does not necessary to be mapped.
+            return false;
+        }
+        if (sShapeType == "actionButtonBeginning")
+        {
+            // LO does not have this type, so it does not necessary to be mapped.
+            return false;
+        }
+        if (sShapeType == "actionButtonBlank")
+        {
+            // LO does not have this type, so it does not necessary to be mapped.
+            return false;
+        }
+        if (sShapeType == "actionButtonDocument")
+        {
+            // LO does not have this type, so it does not necessary to be mapped.
+            return false;
+        }
+        if (sShapeType == "actionButtonEnd")
+        {
+            // LO does not have this type, so it does not necessary to be mapped.
+            return false;
+        }
+        if (sShapeType == "actionButtonForwardNext")
+        {
+            // LO does not have this type, so it does not necessary to be mapped.
+            return false;
+        }
+        if (sShapeType == "actionButtonHelp")
+        {
+            // LO does not have this type, so it does not necessary to be mapped.
+            return false;
+        }
+        if (sShapeType == "actionButtonHome")
+        {
+            // LO does not have this type, so it does not necessary to be mapped.
+            return false;
+        }
+        if (sShapeType == "actionButtonInformation")
+        {
+            // LO does not have this type, so it does not necessary to be mapped.
+            return false;
+        }
+        if (sShapeType == "actionButtonMovie")
+        {
+            // LO does not have this type, so it does not necessary to be mapped.
+            return false;
+        }
+        if (sShapeType == "actionButtonReturn")
+        {
+            // LO does not have this type, so it does not necessary to be mapped.
+            return false;
+        }
+        if (sShapeType == "actionButtonSound")
+        {
+            // LO does not have this type, so it does not necessary to be mapped.
+            return false;
+        }
+        if (sShapeType == "arc")
+        {
+            // LO does not have handle points for this, so CustGeom is enough.
+            return false;
+        }
+        if (sShapeType == "bentArrow")
+        {
+            // LO has only one type, which have to be rotated, without handling points
+            // So CustGeom enough.
+            return false;
+        }
+        if (sShapeType == "bentConnector2")
+        {
+            // CustGeom Enough
+            return false;
+        }
+        if (sShapeType == "bentConnector3")
+        {
+            // CustGeom Enough
+            return false;
+        }
+        if (sShapeType == "bentConnector4")
+        {
+            // CustGeom Enough
+            return false;
+        }
+        if (sShapeType == "bentConnector5")
+        {
+            // CustGeom Enough
+            return false;
+        }
+        if (sShapeType == "bentUpArrow")
+        {
+            // CustGeom Enough, no handle points
+            return false;
+        }
+        if (sShapeType == "bevel")
+        {
+            m_pDMLexporter->WriteShapeTransformation(m_xShape, XML_a, IsXFlipped(), IsYFlipped(),
+                                                     false, false);
+            auto aPoint1 = GetAdjustmentPointXValue(0);
+            tools::Long nVal1
+                = std::lround(aPoint1.nCurrVal / (aPoint1.nMaxVal - aPoint1.nMinVal) * 50000);
+            return StartAVListWriting()
+                   && WriteAV(u"adj", OUString(u"val " + OUString::number(nVal1)))
+                   && EndAVListWriting();
+        }
+        if (sShapeType == "blockArc")
+        {
+            m_pDMLexporter->WriteShapeTransformation(m_xShape, XML_a, IsXFlipped(), IsYFlipped(),
+                                                     false, false);
+            auto aPointR = GetAdjustmentPointRadiusValue(0);
+            auto aPointA = GetAdjustmentPointAngleValue(0);
+            tools::Long nVal1
+                = std::lround((aPointA.nCurrVal < 0 ? 360 + aPointA.nCurrVal : aPointA.nCurrVal)
+                              / (aPointA.nMaxVal - aPointA.nMinVal) * nConstOfMaxDegreeOf60th);
+            tools::Long nVal2 = std::lround(
+                (aPointA.nCurrVal > 180 ? 360 - aPointA.nCurrVal : 180 - aPointA.nCurrVal)
+                / (aPointA.nMaxVal - aPointA.nMinVal) * nConstOfMaxDegreeOf60th);
+            tools::Long nVal3 = std::lround(
+                50000 - (aPointR.nCurrVal / (aPointR.nMaxVal - aPointR.nMinVal) * 50000));
+            return StartAVListWriting()
+                   && WriteAV(u"adj1", OUString(u"val " + OUString::number(nVal1)))
+                   && WriteAV(u"adj2", OUString(u"val " + OUString::number(nVal2)))
+                   && WriteAV(u"adj3", OUString(u"val " + OUString::number(nVal3)))
+                   && EndAVListWriting();
+        }
+        if (sShapeType == "borderCallout1")
+        {
+            // LO does not have this type, so it does not necessary to be mapped.
+            return false;
+        }
+        if (sShapeType == "borderCallout2")
+        {
+            // LO does not have this type, so it does not necessary to be mapped.
+            return false;
+        }
+        if (sShapeType == "borderCallout3")
+        {
+            // LO does not have this type, so it does not necessary to be mapped.
+            return false;
+        }
+        if (sShapeType == "bracePair")
+        {
+            m_pDMLexporter->WriteShapeTransformation(m_xShape, XML_a, IsXFlipped(), IsYFlipped(),
+                                                     false, false);
+            auto aPoint1 = GetAdjustmentPointXValue(0);
+            tools::Long nVal1
+                = std::lround(aPoint1.nCurrVal / (aPoint1.nMaxVal - aPoint1.nMinVal) * 25000);
+            return StartAVListWriting()
+                   && WriteAV(u"adj", OUString(u"val " + OUString::number(nVal1)))
+                   && EndAVListWriting();
+        }
+        if (sShapeType == "bracketPair")
+        {
+            m_pDMLexporter->WriteShapeTransformation(m_xShape, XML_a, IsXFlipped(), IsYFlipped(),
+                                                     false, false);
+            auto aPoint1 = GetAdjustmentPointYValue(0);
+            tools::Long nVal1
+                = std::lround(aPoint1.nCurrVal / (aPoint1.nMaxVal - aPoint1.nMinVal) * 50000);
+            return StartAVListWriting()
+                   && WriteAV(u"adj", OUString(u"val " + OUString::number(nVal1)))
+                   && EndAVListWriting();
+        }
+        if (sShapeType == "callout1")
+        {
+            // LO does not have this type, so it does not necessary to be mapped.
+            return false;
+        }
+        if (sShapeType == "callout2")
+        {
+            // LO does not have this type, so it does not necessary to be mapped.
+            return false;
+        }
+        if (sShapeType == "callout3")
+        {
+            // LO does not have this type, so it does not necessary to be mapped.
+            return false;
+        }
+        if (sShapeType == "can")
+        {
+            return false;
+            // Do the export as before.
+        }
+        if (sShapeType == "chartPlus")
+        {
+            // LO does not have this type, so it does not necessary to be mapped.
+            return false;
+        }
+        if (sShapeType == "chartStar")
+        {
+            // LO does not have this type, so it does not necessary to be mapped.
+            return false;
+        }
+        if (sShapeType == "chartX")
+        {
+            // LO does not have this type, so it does not necessary to be mapped.
+            return false;
+        }
+        if (sShapeType == "chord")
+        {
+            // CustGeom, because LO does not have handle points
+            return false;
+        }
+        if (sShapeType == "circularArrow")
+        {
+            // LO does not have this type, so it does not necessary to be mapped.
+            return false;
+        }
+        if (sShapeType == "cloud")
+        {
+            // CustGeom enough
+            return false;
+        }
+        if (sShapeType == "cloudCallout")
+        {
+            return false;
+            // Works fine without this, so export it like before.
+        }
+        if (sShapeType == "cornerTabs")
+        {
+            // LO does not have this type, so it does not necessary to be mapped.
+            return false;
+        }
+        if (sShapeType == "cube")
+        {
+            // Works fine without this, so export it like before.
+            return false;
+        }
+        if (sShapeType == "curvedConnector2")
+        {
+            // Not necessary to be mapped
+            return false;
+        }
+        if (sShapeType == "curvedConnector3")
+        {
+            // Not necessary to be mapped
+            return false;
+        }
+        if (sShapeType == "curvedConnector4")
+        {
+            // Not necessary to be mapped
+            return false;
+        }
+        if (sShapeType == "curvedConnector5")
+        {
+            // Not necessary to be mapped
+            return false;
+        }
+        if (sShapeType == "curvedDownArrow")
+        {
+            // LO does not have this type, so it does not necessary to be mapped.
+            return false;
+        }
+        if (sShapeType == "curvedLeftArrow")
+        {
+            // LO does not have this type, so it does not necessary to be mapped.
+            return false;
+        }
+        if (sShapeType == "curvedRightArrow")
+        {
+            // LO does not have this type, so it does not necessary to be mapped.
+            return false;
+        }
+        if (sShapeType == "curvedUpArrow")
+        {
+            // LO does not have this type, so it does not necessary to be mapped.
+            return false;
+        }
+        if (sShapeType == "decagon")
+        {
+            // LO does not have this type, so it does not necessary to be mapped.
+            return false;
+        }
+        if (sShapeType == "diagStripe")
+        {
+            // LO does not have this type, so it does not necessary to be mapped.
+            return false;
+        }
+        if (sShapeType == "diamond")
+        {
+            // It does not have handle points so it do not have to be mapped.
+            return false;
+        }
+        if (sShapeType == "dodecagon")
+        {
+            // LO does not have this type, so it does not necessary to be mapped.
+            return false;
+        }
+        if (sShapeType == "donut")
+        {
+            // TODO
+            return false;
+        }
+        if (sShapeType == "doubleWave")
+        {
+            // LO does not have this type, so it does not necessary to be mapped.
+            return false;
+        }
+        if (sShapeType == "downArrow")
+        {
+            // TODO
+            return false;
+        }
+        if (sShapeType == "downArrowCallout")
+        {
+            // TODO
+            return false;
+        }
+        if (sShapeType == "ellipse")
+        {
+            // Does not have handle points, so preset enough.
+            return false;
+        }
+        if (sShapeType == "ellipseRibbon")
+        {
+            // LO does not have this type, so it does not necessary to be mapped.
+            return false;
+        }
+        if (sShapeType == "ellipseRibbon2")
+        {
+            // LO does not have this type, so it does not necessary to be mapped.
+            return false;
+        }
+        if (sShapeType == "flowChartAlternateProcess")
+        {
+            // Does not have handle points, so preset enough.
+            return false;
+        }
+        if (sShapeType == "flowChartCollate")
+        {
+            // Does not have handle points, so preset enough.
+            return false;
+        }
+        if (sShapeType == "flowChartConnector")
+        {
+            // Does not have handle points, so preset enough.
+            return false;
+        }
+        if (sShapeType == "flowChartDecision")
+        {
+            // Does not have handle points, so preset enough.
+            return false;
+        }
+        if (sShapeType == "flowChartDecision")
+        {
+            // Does not have handle points, so preset enough.
+            return false;
+        }
+        if (sShapeType == "flowChartDelay")
+        {
+            // Does not have handle points, so preset enough.
+            return false;
+        }
+        if (sShapeType == "flowChartDisplay")
+        {
+            // Does not have handle points, so preset enough.
+            return false;
+        }
+        if (sShapeType == "flowChartDocument")
+        {
+            // Does not have handle points, so preset enough.
+            return false;
+        }
+        if (sShapeType == "flowChartExtract")
+        {
+            // Does not have handle points, so preset enough.
+            return false;
+        }
+        if (sShapeType == "flowChartInputOutput")
+        {
+            // Does not have handle points, so preset enough.
+            return false;
+        }
+        if (sShapeType == "flowChartInternalStorage")
+        {
+            // Does not have handle points, so preset enough.
+            return false;
+        }
+        if (sShapeType == "flowChartMagneticDisk")
+        {
+            // Does not have handle points, so preset enough.
+            return false;
+        }
+        if (sShapeType == "flowChartMagneticDrum")
+        {
+            // Does not have handle points, so preset enough.
+            return false;
+        }
+        if (sShapeType == "flowChartMagneticTape")
+        {
+            // Does not have handle points, so preset enough.
+            return false;
+        }
+        if (sShapeType == "flowChartManualInput")
+        {
+            // Does not have handle points, so preset enough.
+            return false;
+        }
+        if (sShapeType == "flowChartManualOperation")
+        {
+            // Does not have handle points, so preset enough.
+            return false;
+        }
+        if (sShapeType == "flowChartMerge")
+        {
+            // Does not have handle points, so preset enough.
+            return false;
+        }
+        if (sShapeType == "flowChartMultidocument")
+        {
+            // Does not have handle points, so preset enough.
+            return false;
+        }
+        if (sShapeType == "flowChartOfflineStorage")
+        {
+            // Does not have handle points, so preset enough.
+            return false;
+        }
+        if (sShapeType == "flowChartOffpageConnector")
+        {
+            // Does not have handle points, so preset enough.
+            return false;
+        }
+        if (sShapeType == "flowChartOnlineStorage")
+        {
+            // Does not have handle points, so preset enough.
+            return false;
+        }
+        if (sShapeType == "flowChartOr")
+        {
+            // Does not have handle points, so preset enough.
+            return false;
+        }
+        if (sShapeType == "flowChartDecision")
+        {
+            // Does not have handle points, so preset enough.
+            return false;
+        }
+        if (sShapeType == "flowChartPredefinedProcess")
+        {
+            // Does not have handle points, so preset enough.
+            return false;
+        }
+        if (sShapeType == "flowChartPreparation")
+        {
+            // Does not have handle points, so preset enough.
+            return false;
+        }
+        if (sShapeType == "flowChartPunchedCard")
+        {
+            // Does not have handle points, so preset enough.
+            return false;
+        }
+        if (sShapeType == "flowChartPunchedTape")
+        {
+            // Does not have handle points, so preset enough.
+            return false;
+        }
+        if (sShapeType == "flowChartSort")
+        {
+            // Does not have handle points, so preset enough.
+            return false;
+        }
+        if (sShapeType == "flowChartSummingJunction")
+        {
+            // Does not have handle points, so preset enough.
+            return false;
+        }
+        if (sShapeType == "flowChartTerminator")
+        {
+            // Does not have handle points, so preset enough.
+            return false;
+        }
+        if (sShapeType == "foldedCorner")
+        {
+            // TODO
+            return false;
+        }
+        if (sShapeType == "frame")
+        {
+            // TODO
+            return false;
+        }
+        if (sShapeType == "funnel")
+        {
+            // Not found in word
+            return false;
+        }
+        if (sShapeType == "gear6")
+        {
+            // Not found in word
+            return false;
+        }
+        if (sShapeType == "gear9")
+        {
+            // Not found in word
+            return false;
+        }
+        if (sShapeType == "halfFrame")
+        {
+            // LO does not have this type, not necessary to map
+            return false;
+        }
+        if (sShapeType == "heart")
+        {
+            // TODO
+            return false;
+        }
+        if (sShapeType == "heptagon")
+        {
+            // LO does not have this type, not necessary to map
+            return false;
+        }
+        if (sShapeType == "hexagon")
+        {
+            m_pDMLexporter->WriteShapeTransformation(m_xShape, XML_a, IsXFlipped(), IsYFlipped(),
+                                                     false, false);
+            auto aPoint1 = GetAdjustmentPointXValue(0);
+            tools::Long nMaxVal = 50000 * m_xShape->getSize().Width
+                                  / std::min(m_xShape->getSize().Width, m_xShape->getSize().Height);
+            tools::Long nVal1
+                = std::lround(aPoint1.nCurrVal / (aPoint1.nMaxVal - aPoint1.nMinVal) * nMaxVal);
+            return StartAVListWriting()
+                   && WriteAV(u"adj", OUString(u"val " + OUString::number(nVal1)))
+                   && WriteAV(u"vf", OUString(u"val " + OUString::number(115470)))
+                   && EndAVListWriting();
+        }
+        if (sShapeType == "homePlate")
+        {
+            // Not found in word
+            return false;
+        }
+        if (sShapeType == "horizontalScroll")
+        {
+            // TODO
+            return false;
+        }
+        if (sShapeType == "irregularSeal1")
+        {
+            // Not found in word
+            return false;
+        }
+        if (sShapeType == "irregularSeal2")
+        {
+            // Not found in word
+            return false;
+        }
+        if (sShapeType == "leftArrow")
+        {
+            // TODO
+            return false;
+        }
+        if (sShapeType == "leftArrowCallout")
+        {
+            // TODO
+            return false;
+        }
+        if (sShapeType == "leftBrace")
+        {
+            // TODO
+            return false;
+        }
+        if (sShapeType == "leftBracket")
+        {
+            // TODO
+            return false;
+        }
+        if (sShapeType == "leftCircularArrow")
+        {
+            // TODO
+            return false;
+        }
+        if (sShapeType == "leftRightArrow")
+        {
+            // TODO
+            return false;
+        }
+        if (sShapeType == "leftRightArrowCallout")
+        {
+            // TODO
+            return false;
+        }
+        if (sShapeType == "leftRightCircularArrow")
+        {
+            // Not found in word
+            return false;
+        }
+        if (sShapeType == "leftRightRibbon")
+        {
+            // LO does not have this type so mapping not necessary
+            return false;
+        }
+        if (sShapeType == "leftRightUpArrow")
+        {
+            // TODO
+            return false;
+        }
+        if (sShapeType == "leftUpArrow")
+        {
+            // TODO
+            return false;
+        }
+        if (sShapeType == "lightningBolt")
+        {
+            // Difference between the SDR and OOXML variants, custgeom?
+            return false;
+        }
+        if (sShapeType == "line")
+        {
+            // Not necessary
+            return false;
+        }
+        if (sShapeType == "lineInv")
+        {
+            // Not necessary
+            return false;
+        }
+        if (sShapeType == "mathDivide")
+        {
+            // LO does not have this type so mapping not necessary
+            return false;
+        }
+        if (sShapeType == "mathEqual")
+        {
+            // LO does not have this type so mapping not necessary
+            return false;
+        }
+        if (sShapeType == "mathMinus")
+        {
+            // LO does not have this type so mapping not necessary
+            return false;
+        }
+        if (sShapeType == "mathMultiply")
+        {
+            // LO does not have this type so mapping not necessary
+            return false;
+        }
+        if (sShapeType == "mathNotEqual")
+        {
+            // LO does not have this type so mapping not necessary
+            return false;
+        }
+        if (sShapeType == "mathPlus")
+        {
+            // LO does not have this type so mapping not necessary
+            return false;
+        }
+        if (sShapeType == "nonIsoscelesTrapezoid")
+        {
+            // TODO
+            return false;
+        }
+        if (sShapeType == "noSmoking")
+        {
+            // TODO
+            return false;
+        }
+        if (sShapeType == "notchedRightArrow")
+        {
+            // TODO
+            return false;
+        }
+        if (sShapeType == "octagon")
+        {
+            m_pDMLexporter->WriteShapeTransformation(m_xShape, XML_a, IsXFlipped(), IsYFlipped(),
+                                                     false, false);
+            auto aPoint1 = GetAdjustmentPointXValue(0);
+            tools::Long nVal1
+                = std::lround(aPoint1.nCurrVal / (aPoint1.nMaxVal - aPoint1.nMinVal) * 50000);
+            return StartAVListWriting()
+                   && WriteAV(u"adj", OUString(u"val " + OUString::number(nVal1)))
+                   && EndAVListWriting();
+        }
+        if (sShapeType == "parallelogram")
+        {
+            m_pDMLexporter->WriteShapeTransformation(m_xShape, XML_a, IsXFlipped(), IsYFlipped(),
+                                                     false, false);
+            auto aPoint1 = GetAdjustmentPointXValue(0);
+            tools::Long nMaxVal = 100000 * m_xShape->getSize().Width
+                                  / std::min(m_xShape->getSize().Width, m_xShape->getSize().Height);
+            tools::Long nVal1
+                = std::lround(aPoint1.nCurrVal / (aPoint1.nMaxVal - aPoint1.nMinVal) * nMaxVal);
+            return StartAVListWriting()
+                   && WriteAV(u"adj", OUString(u"val " + OUString::number(nVal1)))
+                   && EndAVListWriting();
+        }
+        if (sShapeType == "pentagon")
+        {
+            // TODO
+            return false;
+        }
+        if (sShapeType == "pie")
+        {
+            // TODO
+            return false;
+        }
+        if (sShapeType == "pieWedge")
+        {
+            // Not found in word.
+            return false;
+        }
+        if (sShapeType == "plaque")
+        {
+            // TODO
+            return false;
+        }
+        if (sShapeType == "plaqueTabs")
+        {
+            // LO does not have this, so not necessary to map.
+            return false;
+        }
+        if (sShapeType == "plus")
+        {
+            m_pDMLexporter->WriteShapeTransformation(m_xShape, XML_a, IsXFlipped(), IsYFlipped(),
+                                                     false, false);
+            auto aPoint1 = GetAdjustmentPointXValue(0);
+            tools::Long nVal1
+                = std::lround(aPoint1.nCurrVal / (aPoint1.nMaxVal - aPoint1.nMinVal) * 50000);
+            return StartAVListWriting()
+                   && WriteAV(u"adj", OUString(u"val " + OUString::number(nVal1)))
+                   && EndAVListWriting();
+        }
+        if (sShapeType == "quadArrow")
+        {
+            // TODO
+            return false;
+        }
+        if (sShapeType == "quadArrowCallout")
+        {
+            // TODO
+            return false;
+        }
+        if (sShapeType == "rect")
+        {
+            // preset enough without AV points.
+            return false;
+        }
+        if (sShapeType == "ribbon")
+        {
+            // LO does not have this, so not necessary to map.
+            return false;
+        }
+        if (sShapeType == "ribbon2")
+        {
+            // LO does not have this, so not necessary to map.
+            return false;
+        }
+        if (sShapeType == "rightArrow")
+        {
+            // TODO
+            return false;
+        }
+        if (sShapeType == "rightArrowCallout")
+        {
+            // TODO
+            return false;
+        }
+        if (sShapeType == "rightBrace")
+        {
+            // TODO
+            return false;
+        }
+        if (sShapeType == "rightBracket")
+        {
+            // TODO
+            return false;
+        }
+        if (sShapeType == "round1Rect")
+        {
+            // LO does not have this, so not necessary to map.
+            return false;
+        }
+        if (sShapeType == "round2DiagRect")
+        {
+            // LO does not have this, so not necessary to map.
+            return false;
+        }
+        if (sShapeType == "round2SameRect")
+        {
+            // LO does not have this, so not necessary to map.
+            return false;
+        }
+        if (sShapeType == "roundRect")
+        {
+            m_pDMLexporter->WriteShapeTransformation(m_xShape, XML_a, IsXFlipped(), IsYFlipped(),
+                                                     false, false);
+            tools::Long nVal1 = 0;
+            if (m_xShape->getSize().Width >= m_xShape->getSize().Height)
+            {
+                auto aPointX = GetAdjustmentPointXValue(0);
+                nVal1 = std::lround(aPointX.nCurrVal / (aPointX.nMaxVal - aPointX.nMinVal) * 50000);
+            }
+            else
+            {
+                auto aPointY = GetAdjustmentPointYValue(0);
+                nVal1 = std::lround(aPointY.nCurrVal / (aPointY.nMaxVal - aPointY.nMinVal) * 50000);
+            }
+            return StartAVListWriting()
+                   && WriteAV(u"adj", OUString(u"val " + OUString::number(nVal1)))
+                   && EndAVListWriting();
+        }
+        if (sShapeType == "rtTriangle")
+        {
+            // Does not have AV points not necessary to map
+            return false;
+        }
+        if (sShapeType == "smileyFace")
+        {
+            // TODO
+            return false;
+        }
+        if (sShapeType == "snip1Rect")
+        {
+            // LO does not have this, so not necessary to map.
+            return false;
+        }
+        if (sShapeType == "snip2DiagRect")
+        {
+            // LO does not have this, so not necessary to map.
+            return false;
+        }
+        if (sShapeType == "snip2SameRect")
+        {
+            // LO does not have this, so not necessary to map.
+            return false;
+        }
+        if (sShapeType == "snipRoundRect")
+        {
+            // LO does not have this, so not necessary to map.
+            return false;
+        }
+        if (sShapeType == "squareTabs")
+        {
+            // LO does not have this, so not necessary to map.
+            return false;
+        }
+        if (sShapeType == "star10")
+        {
+            // LO does not have this, so not necessary to map.
+            return false;
+        }
+        if (sShapeType == "star12")
+        {
+            // TODO
+            return false;
+        }
+        if (sShapeType == "star16")
+        {
+            // LO does not have this, so not necessary to map.
+            return false;
+        }
+        if (sShapeType == "star24")
+        {
+            // TODO
+            return false;
+        }
+        if (sShapeType == "star32")
+        {
+            // LO does not have this, so not necessary to map.
+            return false;
+        }
+        if (sShapeType == "star4")
+        {
+            // TODO
+            return false;
+        }
+        if (sShapeType == "star5")
+        {
+            // TODO
+            return false;
+        }
+        if (sShapeType == "star6")
+        {
+            // TODO
+            return false;
+        }
+        if (sShapeType == "star7")
+        {
+            // LO does not have this, so not necessary to map.
+            return false;
+        }
+        if (sShapeType == "star8")
+        {
+            // TODO
+            return false;
+        }
+        if (sShapeType == "straightConnector1")
+        {
+            // Not necessary to map.
+            return false;
+        }
+        if (sShapeType == "stripedRightArrow")
+        {
+            // TODO
+            return false;
+        }
+        if (sShapeType == "sun")
+        {
+            // TODO
+            return false;
+        }
+        if (sShapeType == "swooshArrow")
+        {
+            // Not found in word.
+            return false;
+        }
+        if (sShapeType == "teardrop")
+        {
+            // TODO
+            return false;
+        }
+        if (sShapeType == "trapezoid")
+        {
+            // Preset enough.
+            return false;
+        }
+        if (sShapeType == "triangle")
+        {
+            m_pDMLexporter->WriteShapeTransformation(m_xShape, XML_a, IsXFlipped(), IsYFlipped(),
+                                                     false, false);
+            auto aPoint1 = GetAdjustmentPointXValue(0);
+            tools::Long nMaxVal = 100000;
+            tools::Long nVal1
+                = std::lround(aPoint1.nCurrVal / (aPoint1.nMaxVal - aPoint1.nMinVal) * nMaxVal);
+            return StartAVListWriting()
+                   && WriteAV(u"adj", OUString(u"val " + OUString::number(nVal1)))
+                   && EndAVListWriting();
+        }
+        if (sShapeType == "upArrowCallout")
+        {
+            // TODO
+            return false;
+        }
+        if (sShapeType == "upDownArrow")
+        {
+            // TODO
+            return false;
+        }
+        if (sShapeType == "upArrow")
+        {
+            // TODO
+            return false;
+        }
+        if (sShapeType == "upDownArrowCallout")
+        {
+            // TODO
+            return false;
+        }
+        if (sShapeType == "uturnArrow")
+        {
+            // LO does not have like this.
+            return false;
+        }
+        if (sShapeType == "verticalScroll")
+        {
+            // TODO
+            return false;
+        }
+        if (sShapeType == "wave")
+        {
+            // LO does not have.
+            return false;
+        }
+        if (sShapeType == "wedgeEllipseCallout")
+        {
+            // TODO
+            return false;
+        }
+        if (sShapeType == "wedgeRectCallout")
+        {
+            // TODO
+            return false;
+        }
+        if (sShapeType == "wedgeRoundRectCallout")
+        {
+            // TODO
+            return false;
+        }
+    }
+    catch (...)
+    {
+        // Problem detected with the writing, aborting and trying to find another way.
+        return false;
+    }
+
+    // Default, nothing happened return.
+    return false;
+};
+}
diff --git a/oox/source/export/shapes.cxx b/oox/source/export/shapes.cxx
index f45b0aabc61d..d52d6696be2a 100644
--- a/oox/source/export/shapes.cxx
+++ b/oox/source/export/shapes.cxx
@@ -73,6 +73,7 @@
 #include <oox/export/chartexport.hxx>
 #include <oox/mathml/export.hxx>
 #include <basegfx/numeric/ftools.hxx>
+#include <oox/export/DMLPresetShapeExport.hxx>
 
 using namespace ::css;
 using namespace ::css::beans;
@@ -832,7 +833,19 @@ ShapeExport& ShapeExport::WriteCustomShape( const Reference< XShape >& xShape )
     else if( bHasHandles )
         bCustGeom = true;
 
-    if (bHasHandles && bCustGeom)
+    bool bPresetWriteSuccessful = false;
+    // Let the custom shapes what has name and preset information in OOXML, to be written
+    // as preset ones with parameters. Try that with this converter class.
+    if (!sShapeType.startsWith("ooxml") && GetDocumentType() == DOCUMENT_DOCX
+        && xShape->getShapeType() == "com.sun.star.drawing.CustomShape")
+    {
+        DMLPresetShapeExporter aCustomShapeConverter(this, xShape);
+        bPresetWriteSuccessful = aCustomShapeConverter.WriteShape();
+    }
+    // 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) );
diff --git a/sw/qa/extras/ooxmlexport/data/testCustomShapePresetExport.odt b/sw/qa/extras/ooxmlexport/data/testCustomShapePresetExport.odt
new file mode 100644
index 000000000000..4f132e760460
Binary files /dev/null and b/sw/qa/extras/ooxmlexport/data/testCustomShapePresetExport.odt differ
diff --git a/sw/qa/extras/ooxmlexport/ooxmlexport16.cxx b/sw/qa/extras/ooxmlexport/ooxmlexport16.cxx
index 5666c3f1b1d1..2b3d92b55701 100644
--- a/sw/qa/extras/ooxmlexport/ooxmlexport16.cxx
+++ b/sw/qa/extras/ooxmlexport/ooxmlexport16.cxx
@@ -129,6 +129,37 @@ DECLARE_OOXMLEXPORT_EXPORTONLY_TEST(testGutterTop, "gutter-top.docx")
     assertXPath(pXmlSettings, "/w:settings/w:gutterAtTop", 1);
 }
 
+DECLARE_OOXMLEXPORT_EXPORTONLY_TEST(testCustomShapePresetExport, "testCustomShapePresetExport.odt")
+{
+    // Check if the load failed.
+    CPPUNIT_ASSERT(getPages());
+
+    // Check all shapes of the file
+    int nCount = 0;
+    for (int i = 1; i <= getShapes(); i++)
+    {
+        uno::Reference<beans::XPropertySet> xProperties(getShape(i), uno::UNO_QUERY);
+        if (!xProperties->getPropertySetInfo()->hasPropertyByName("CustomShapeGeometry"))
+            continue;
+        // Get the custom shape property
+        auto aCustomShapeGeometry = xProperties->getPropertyValue("CustomShapeGeometry")
+                                        .get<uno::Sequence<beans::PropertyValue>>();
+        // Find for shape type
+        for (const auto& aCustomGeometryIterator : std::as_const(aCustomShapeGeometry))
+        {
+            if (aCustomGeometryIterator.Name == "Type")
+                CPPUNIT_ASSERT_MESSAGE(
+                    "This is an ooxml preset shape with custom geometry! Shape type lost!",
+                    aCustomGeometryIterator.Value.get<OUString>() != "ooxml-non-primitive");
+            // Without the fix, all shapes have ooxml-non-primitive type, and lost their
+            // real type (like triangle) with the textbox padding.
+        }
+        nCount++;
+    }
+    // Without the fix the count does not match.
+    CPPUNIT_ASSERT_EQUAL(17, nCount);
+}
+
 DECLARE_OOXMLEXPORT_EXPORTONLY_TEST(testTdf69635, "tdf69635.docx")
 {
     xmlDocUniquePtr pXmlHeader1 = parseExport("word/header1.xml");


More information about the Libreoffice-commits mailing list