oox/qa/unit/data/tdf142602_CustomShapeArrows.odt |binary
 oox/qa/unit/export.cxx                           |  190 ++++++++++++
 oox/source/export/DMLPresetShapeExport.cxx       |  352 +++++++++++++++++++++--
 3 files changed, 516 insertions(+), 26 deletions(-)

New commits:
commit 504808fb5c372a94c6299eec90a281655eef47e4
Author:     Daniel Arato (NISZ) <arato.dan...@nisz.hu>
AuthorDate: Fri Sep 10 13:51:24 2021 +0200
Commit:     László Németh <nem...@numbertext.org>
CommitDate: Tue Sep 21 13:26:54 2021 +0200

    tdf#142602 DOCX: export adjustment points of custom shape arrows
    
    When exporting custom shape arrows LO used to fall back to writing
    out their plain vertex coordinates, losing the customizability of
    the shape after loading from file. This commit changes the export
    of some of the most commonly used arrow custom shapes to proper
    adjustment value export.
    
    Follow-up to commit 63cd67e5e18f01aca303131e148c80398a181a41
    "tdf#92525 tdf#142398: fix export of simple custom shapes".
    
    Change-Id: If248556764bdb7e00cfde4ebe5b32bb380b1fa19
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/121901
    Tested-by: László Németh <nem...@numbertext.org>
    Reviewed-by: László Németh <nem...@numbertext.org>

diff --git a/oox/qa/unit/data/tdf142602_CustomShapeArrows.odt 
b/oox/qa/unit/data/tdf142602_CustomShapeArrows.odt
new file mode 100644
index 000000000000..bc0357a1a867
Binary files /dev/null and b/oox/qa/unit/data/tdf142602_CustomShapeArrows.odt 
differ
diff --git a/oox/qa/unit/export.cxx b/oox/qa/unit/export.cxx
index 0e597adb2854..b649d546ead9 100644
--- a/oox/qa/unit/export.cxx
+++ b/oox/qa/unit/export.cxx
@@ -154,6 +154,196 @@ CPPUNIT_TEST_FIXTURE(Test, testDmlGroupshapePolygon)
     assertXPath(pXmlDoc, "//wpg:grpSpPr/a:xfrm/a:chExt", "cx", "5328360");
     assertXPath(pXmlDoc, "//wps:spPr/a:xfrm/a:ext", "cx", "5328360");
 }
+
+CPPUNIT_TEST_FIXTURE(Test, testCustomShapeArrowExport)
+{
+    // Given a document with a few different kinds of arrow shapes in it:
+    OUString aURL = m_directories.getURLFromSrc(DATA_DIRECTORY) + 
"tdf142602_CustomShapeArrows.odt";
+    // When saving that to DOCX:
+    loadAndSave(aURL, "Office Open XML Text");
+
+    // Then the shapes should retain their correct control values.
+    uno::Reference<packages::zip::XZipFileAccess2> xNameAccess
+        = packages::zip::ZipFileAccess::createWithURL(mxComponentContext, 
getTempFile().GetURL());
+    uno::Reference<io::XInputStream> 
xInputStream(xNameAccess->getByName("word/document.xml"),
+                                                  uno::UNO_QUERY);
+    std::unique_ptr<SvStream> 
pStream(utl::UcbStreamHelper::CreateStream(xInputStream, true));
+    xmlDocUniquePtr pXmlDoc = parseXmlStream(pStream.get());
+
+    // Without the fix the output OOXML would have no <a:prstGeom> tags in it.
+
+    // Right arrow
+    assertXPath(pXmlDoc,
+                
"//w:r/mc:AlternateContent[1]/mc:Choice/w:drawing/wp:anchor/a:graphic/"
+                "a:graphicData/wps:wsp/wps:spPr/a:prstGeom",
+                "prst", "rightArrow");
+    assertXPath(pXmlDoc,
+                
"//w:r/mc:AlternateContent[1]/mc:Choice/w:drawing/wp:anchor/a:graphic/"
+                "a:graphicData/wps:wsp/wps:spPr/a:prstGeom/a:avLst/a:gd[1]",
+                "fmla", "val 50000");
+    assertXPath(pXmlDoc,
+                
"//w:r/mc:AlternateContent[1]/mc:Choice/w:drawing/wp:anchor/a:graphic/"
+                "a:graphicData/wps:wsp/wps:spPr/a:prstGeom/a:avLst/a:gd[2]",
+                "fmla", "val 46321");
+
+    // Left arrow
+    assertXPath(pXmlDoc,
+                
"//w:r/mc:AlternateContent[2]/mc:Choice/w:drawing/wp:anchor/a:graphic/"
+                "a:graphicData/wps:wsp/wps:spPr/a:prstGeom",
+                "prst", "leftArrow");
+    assertXPath(pXmlDoc,
+                
"//w:r/mc:AlternateContent[2]/mc:Choice/w:drawing/wp:anchor/a:graphic/"
+                "a:graphicData/wps:wsp/wps:spPr/a:prstGeom/a:avLst/a:gd[1]",
+                "fmla", "val 50000");
+    assertXPath(pXmlDoc,
+                
"//w:r/mc:AlternateContent[2]/mc:Choice/w:drawing/wp:anchor/a:graphic/"
+                "a:graphicData/wps:wsp/wps:spPr/a:prstGeom/a:avLst/a:gd[2]",
+                "fmla", "val 52939");
+
+    // Down arrow
+    assertXPath(pXmlDoc,
+                
"//w:r/mc:AlternateContent[3]/mc:Choice/w:drawing/wp:anchor/a:graphic/"
+                "a:graphicData/wps:wsp/wps:spPr/a:prstGeom",
+                "prst", "downArrow");
+    assertXPath(pXmlDoc,
+                
"//w:r/mc:AlternateContent[3]/mc:Choice/w:drawing/wp:anchor/a:graphic/"
+                "a:graphicData/wps:wsp/wps:spPr/a:prstGeom/a:avLst/a:gd[1]",
+                "fmla", "val 50000");
+    assertXPath(pXmlDoc,
+                
"//w:r/mc:AlternateContent[3]/mc:Choice/w:drawing/wp:anchor/a:graphic/"
+                "a:graphicData/wps:wsp/wps:spPr/a:prstGeom/a:avLst/a:gd[2]",
+                "fmla", "val 59399");
+
+    // Up arrow
+    assertXPath(pXmlDoc,
+                
"//w:r/mc:AlternateContent[4]/mc:Choice/w:drawing/wp:anchor/a:graphic/"
+                "a:graphicData/wps:wsp/wps:spPr/a:prstGeom",
+                "prst", "upArrow");
+    assertXPath(pXmlDoc,
+                
"//w:r/mc:AlternateContent[4]/mc:Choice/w:drawing/wp:anchor/a:graphic/"
+                "a:graphicData/wps:wsp/wps:spPr/a:prstGeom/a:avLst/a:gd[1]",
+                "fmla", "val 50000");
+    assertXPath(pXmlDoc,
+                
"//w:r/mc:AlternateContent[4]/mc:Choice/w:drawing/wp:anchor/a:graphic/"
+                "a:graphicData/wps:wsp/wps:spPr/a:prstGeom/a:avLst/a:gd[2]",
+                "fmla", "val 63885");
+
+    // Left-right arrow
+    assertXPath(pXmlDoc,
+                
"//w:r/mc:AlternateContent[5]/mc:Choice/w:drawing/wp:anchor/a:graphic/"
+                "a:graphicData/wps:wsp/wps:spPr/a:prstGeom",
+                "prst", "leftRightArrow");
+    assertXPath(pXmlDoc,
+                
"//w:r/mc:AlternateContent[5]/mc:Choice/w:drawing/wp:anchor/a:graphic/"
+                "a:graphicData/wps:wsp/wps:spPr/a:prstGeom/a:avLst/a:gd[1]",
+                "fmla", "val 50000");
+    assertXPath(pXmlDoc,
+                
"//w:r/mc:AlternateContent[5]/mc:Choice/w:drawing/wp:anchor/a:graphic/"
+                "a:graphicData/wps:wsp/wps:spPr/a:prstGeom/a:avLst/a:gd[2]",
+                "fmla", "val 53522");
+
+    // Up-down arrow
+    assertXPath(pXmlDoc,
+                
"//w:r/mc:AlternateContent[6]/mc:Choice/w:drawing/wp:anchor/a:graphic/"
+                "a:graphicData/wps:wsp/wps:spPr/a:prstGeom",
+                "prst", "upDownArrow");
+    assertXPath(pXmlDoc,
+                
"//w:r/mc:AlternateContent[6]/mc:Choice/w:drawing/wp:anchor/a:graphic/"
+                "a:graphicData/wps:wsp/wps:spPr/a:prstGeom/a:avLst/a:gd[1]",
+                "fmla", "val 50000");
+    assertXPath(pXmlDoc,
+                
"//w:r/mc:AlternateContent[6]/mc:Choice/w:drawing/wp:anchor/a:graphic/"
+                "a:graphicData/wps:wsp/wps:spPr/a:prstGeom/a:avLst/a:gd[2]",
+                "fmla", "val 62743");
+
+    // Right arrow callout
+    assertXPath(pXmlDoc,
+                
"//w:r/mc:AlternateContent[7]/mc:Choice/w:drawing/wp:anchor/a:graphic/"
+                "a:graphicData/wps:wsp/wps:spPr/a:prstGeom",
+                "prst", "rightArrowCallout");
+    assertXPath(pXmlDoc,
+                
"//w:r/mc:AlternateContent[7]/mc:Choice/w:drawing/wp:anchor/a:graphic/"
+                "a:graphicData/wps:wsp/wps:spPr/a:prstGeom/a:avLst/a:gd[1]",
+                "fmla", "val 25002");
+    assertXPath(pXmlDoc,
+                
"//w:r/mc:AlternateContent[7]/mc:Choice/w:drawing/wp:anchor/a:graphic/"
+                "a:graphicData/wps:wsp/wps:spPr/a:prstGeom/a:avLst/a:gd[2]",
+                "fmla", "val 25000");
+    assertXPath(pXmlDoc,
+                
"//w:r/mc:AlternateContent[7]/mc:Choice/w:drawing/wp:anchor/a:graphic/"
+                "a:graphicData/wps:wsp/wps:spPr/a:prstGeom/a:avLst/a:gd[3]",
+                "fmla", "val 25052");
+    assertXPath(pXmlDoc,
+                
"//w:r/mc:AlternateContent[7]/mc:Choice/w:drawing/wp:anchor/a:graphic/"
+                "a:graphicData/wps:wsp/wps:spPr/a:prstGeom/a:avLst/a:gd[4]",
+                "fmla", "val 66667");
+
+    // Left arrow callout
+    assertXPath(pXmlDoc,
+                
"//w:r/mc:AlternateContent[8]/mc:Choice/w:drawing/wp:anchor/a:graphic/"
+                "a:graphicData/wps:wsp/wps:spPr/a:prstGeom",
+                "prst", "leftArrowCallout");
+    assertXPath(pXmlDoc,
+                
"//w:r/mc:AlternateContent[8]/mc:Choice/w:drawing/wp:anchor/a:graphic/"
+                "a:graphicData/wps:wsp/wps:spPr/a:prstGeom/a:avLst/a:gd[1]",
+                "fmla", "val 25002");
+    assertXPath(pXmlDoc,
+                
"//w:r/mc:AlternateContent[8]/mc:Choice/w:drawing/wp:anchor/a:graphic/"
+                "a:graphicData/wps:wsp/wps:spPr/a:prstGeom/a:avLst/a:gd[2]",
+                "fmla", "val 25000");
+    assertXPath(pXmlDoc,
+                
"//w:r/mc:AlternateContent[8]/mc:Choice/w:drawing/wp:anchor/a:graphic/"
+                "a:graphicData/wps:wsp/wps:spPr/a:prstGeom/a:avLst/a:gd[3]",
+                "fmla", "val 25057");
+    assertXPath(pXmlDoc,
+                
"//w:r/mc:AlternateContent[8]/mc:Choice/w:drawing/wp:anchor/a:graphic/"
+                "a:graphicData/wps:wsp/wps:spPr/a:prstGeom/a:avLst/a:gd[4]",
+                "fmla", "val 66673");
+
+    // Down arrow callout
+    assertXPath(pXmlDoc,
+                
"//w:r/mc:AlternateContent[9]/mc:Choice/w:drawing/wp:anchor/a:graphic/"
+                "a:graphicData/wps:wsp/wps:spPr/a:prstGeom",
+                "prst", "downArrowCallout");
+    assertXPath(pXmlDoc,
+                
"//w:r/mc:AlternateContent[9]/mc:Choice/w:drawing/wp:anchor/a:graphic/"
+                "a:graphicData/wps:wsp/wps:spPr/a:prstGeom/a:avLst/a:gd[1]",
+                "fmla", "val 29415");
+    assertXPath(pXmlDoc,
+                
"//w:r/mc:AlternateContent[9]/mc:Choice/w:drawing/wp:anchor/a:graphic/"
+                "a:graphicData/wps:wsp/wps:spPr/a:prstGeom/a:avLst/a:gd[2]",
+                "fmla", "val 29413");
+    assertXPath(pXmlDoc,
+                
"//w:r/mc:AlternateContent[9]/mc:Choice/w:drawing/wp:anchor/a:graphic/"
+                "a:graphicData/wps:wsp/wps:spPr/a:prstGeom/a:avLst/a:gd[3]",
+                "fmla", "val 16667");
+    assertXPath(pXmlDoc,
+                
"//w:r/mc:AlternateContent[9]/mc:Choice/w:drawing/wp:anchor/a:graphic/"
+                "a:graphicData/wps:wsp/wps:spPr/a:prstGeom/a:avLst/a:gd[4]",
+                "fmla", "val 66667");
+
+    // Up arrow callout
+    assertXPath(pXmlDoc,
+                
"//w:r/mc:AlternateContent[10]/mc:Choice/w:drawing/wp:anchor/a:graphic/"
+                "a:graphicData/wps:wsp/wps:spPr/a:prstGeom",
+                "prst", "upArrowCallout");
+    assertXPath(pXmlDoc,
+                
"//w:r/mc:AlternateContent[10]/mc:Choice/w:drawing/wp:anchor/a:graphic/"
+                "a:graphicData/wps:wsp/wps:spPr/a:prstGeom/a:avLst/a:gd[1]",
+                "fmla", "val 31033");
+    assertXPath(pXmlDoc,
+                
"//w:r/mc:AlternateContent[10]/mc:Choice/w:drawing/wp:anchor/a:graphic/"
+                "a:graphicData/wps:wsp/wps:spPr/a:prstGeom/a:avLst/a:gd[2]",
+                "fmla", "val 31030");
+    assertXPath(pXmlDoc,
+                
"//w:r/mc:AlternateContent[10]/mc:Choice/w:drawing/wp:anchor/a:graphic/"
+                "a:graphicData/wps:wsp/wps:spPr/a:prstGeom/a:avLst/a:gd[3]",
+                "fmla", "val 16667");
+    assertXPath(pXmlDoc,
+                
"//w:r/mc:AlternateContent[10]/mc:Choice/w:drawing/wp:anchor/a:graphic/"
+                "a:graphicData/wps:wsp/wps:spPr/a:prstGeom/a:avLst/a:gd[4]",
+                "fmla", "val 66660");
+}
 }
 
 CPPUNIT_PLUGIN_IMPLEMENT();
diff --git a/oox/source/export/DMLPresetShapeExport.cxx 
b/oox/source/export/DMLPresetShapeExport.cxx
index 318ce11207d9..b374eb6b6c1a 100644
--- a/oox/source/export/DMLPresetShapeExport.cxx
+++ b/oox/source/export/DMLPresetShapeExport.cxx
@@ -640,13 +640,69 @@ bool DMLPresetShapeExporter::WriteShapeWithAVlist()
         }
         if (sShapeType == "downArrow")
         {
-            // TODO
-            return false;
+            auto aPointX = GetAdjustmentPointXValue(0);
+            auto aPointY = GetAdjustmentPointYValue(0);
+            if (!aPointX.nCurrVal.has_value() || !aPointX.nMaxVal.has_value()
+                || !aPointX.nMinVal.has_value() || 
!aPointY.nCurrVal.has_value()
+                || !aPointY.nMaxVal.has_value() || 
!aPointY.nMinVal.has_value())
+                return false;
+
+            m_pDMLexporter->WriteShapeTransformation(m_xShape, XML_a, 
IsXFlipped(), IsYFlipped(),
+                                                     false, false);
+            tools::Long nMaxVal1 = 100000;
+            tools::Long nMaxVal2
+                = 100000 * m_xShape->getSize().Height
+                  / std::min(m_xShape->getSize().Width, 
m_xShape->getSize().Height);
+            tools::Long nVal1 = std::lround((*aPointX.nMaxVal - 
*aPointX.nCurrVal)
+                                            / (*aPointX.nMaxVal - 
*aPointX.nMinVal) * nMaxVal1);
+            tools::Long nVal2 = std::lround((*aPointY.nMaxVal - 
*aPointY.nCurrVal)
+                                            / (*aPointY.nMaxVal - 
*aPointY.nMinVal) * nMaxVal2);
+            return StartAVListWriting()
+                   && WriteAV(u"adj1", OUString(u"val " + 
OUString::number(nVal1)))
+                   && WriteAV(u"adj2", OUString(u"val " + 
OUString::number(nVal2)))
+                   && EndAVListWriting();
         }
         if (sShapeType == "downArrowCallout")
         {
-            // TODO
-            return false;
+            auto aNeckFromBox = GetAdjustmentPointXValue(1);
+            auto aHeadFromNeck = GetAdjustmentPointXValue(2);
+            auto aHeadHeight = GetAdjustmentPointYValue(1);
+            auto aBoxHeight = GetAdjustmentPointYValue(0);
+            if (!aNeckFromBox.nCurrVal.has_value() || 
!aNeckFromBox.nMaxVal.has_value()
+                || !aNeckFromBox.nMinVal.has_value() || 
!aHeadFromNeck.nCurrVal.has_value()
+                || !aHeadFromNeck.nMaxVal.has_value() || 
!aHeadFromNeck.nMinVal.has_value()
+                || !aHeadHeight.nCurrVal.has_value() || 
!aHeadHeight.nMaxVal.has_value()
+                || !aHeadHeight.nMinVal.has_value() || 
!aBoxHeight.nCurrVal.has_value()
+                || !aBoxHeight.nMaxVal.has_value() || 
!aBoxHeight.nMinVal.has_value())
+                return false;
+
+            m_pDMLexporter->WriteShapeTransformation(m_xShape, XML_a, 
IsXFlipped(), IsYFlipped(),
+                                                     false, false);
+            tools::Long nMaxVal1
+                = 100000 * m_xShape->getSize().Width
+                  / std::min(m_xShape->getSize().Width, 
m_xShape->getSize().Height);
+            tools::Long nMaxVal2
+                = 50000 * m_xShape->getSize().Width
+                  / std::min(m_xShape->getSize().Width, 
m_xShape->getSize().Height);
+            tools::Long nMaxVal3
+                = 100000 * m_xShape->getSize().Height
+                  / std::min(m_xShape->getSize().Width, 
m_xShape->getSize().Height);
+            tools::Long nVal1
+                = std::lround((*aNeckFromBox.nMaxVal - *aNeckFromBox.nCurrVal)
+                              / (*aNeckFromBox.nMaxVal - 
*aNeckFromBox.nMinVal) * nMaxVal1);
+            tools::Long nVal2 = std::lround((10800 - *aHeadFromNeck.nCurrVal)
+                                            / (10800 - *aHeadFromNeck.nMinVal) 
* nMaxVal2);
+            tools::Long nVal3
+                = std::lround((*aHeadHeight.nMaxVal - *aHeadHeight.nCurrVal)
+                              / (*aHeadHeight.nMaxVal - *aHeadHeight.nMinVal) 
* nMaxVal3);
+            tools::Long nVal4 = std::lround((*aBoxHeight.nCurrVal - 
*aBoxHeight.nMinVal)
+                                            / (21600 - *aBoxHeight.nMinVal) * 
100000);
+            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)))
+                   && WriteAV(u"adj4", OUString(u"val " + 
OUString::number(nVal4)))
+                   && EndAVListWriting();
         }
         if (sShapeType == "ellipse")
         {
@@ -893,13 +949,69 @@ bool DMLPresetShapeExporter::WriteShapeWithAVlist()
         }
         if (sShapeType == "leftArrow")
         {
-            // TODO
-            return false;
+            auto aPointX = GetAdjustmentPointXValue(0);
+            auto aPointY = GetAdjustmentPointYValue(0);
+            if (!aPointX.nCurrVal.has_value() || !aPointX.nMaxVal.has_value()
+                || !aPointX.nMinVal.has_value() || 
!aPointY.nCurrVal.has_value()
+                || !aPointY.nMaxVal.has_value() || 
!aPointY.nMinVal.has_value())
+                return false;
+
+            m_pDMLexporter->WriteShapeTransformation(m_xShape, XML_a, 
IsXFlipped(), IsYFlipped(),
+                                                     false, false);
+            tools::Long nMaxVal1 = 100000;
+            tools::Long nMaxVal2
+                = 100000
+                  * (double(m_xShape->getSize().Width)
+                     / std::min(m_xShape->getSize().Width, 
m_xShape->getSize().Height));
+            tools::Long nVal1 = std::lround((*aPointY.nMaxVal - 
*aPointY.nCurrVal)
+                                            / (*aPointY.nMaxVal - 
*aPointY.nMinVal) * nMaxVal1);
+            tools::Long nVal2 = std::lround((*aPointX.nCurrVal - 
*aPointX.nMinVal)
+                                            / (*aPointX.nMaxVal - 
*aPointX.nMinVal) * nMaxVal2);
+            return StartAVListWriting()
+                   && WriteAV(u"adj1", OUString(u"val " + 
OUString::number(nVal1)))
+                   && WriteAV(u"adj2", OUString(u"val " + 
OUString::number(nVal2)))
+                   && EndAVListWriting();
         }
         if (sShapeType == "leftArrowCallout")
         {
-            // TODO
-            return false;
+            auto aBoxWidth = GetAdjustmentPointXValue(0);
+            auto aNeckLength = GetAdjustmentPointXValue(1);
+            auto aNeckFromBox = GetAdjustmentPointYValue(1);
+            auto aHeadFromNeck = GetAdjustmentPointYValue(2);
+            if (!aBoxWidth.nCurrVal.has_value() || 
!aBoxWidth.nMaxVal.has_value()
+                || !aBoxWidth.nMinVal.has_value() || 
!aNeckLength.nCurrVal.has_value()
+                || !aNeckLength.nMaxVal.has_value() || 
!aNeckLength.nMinVal.has_value()
+                || !aNeckFromBox.nCurrVal.has_value() || 
!aNeckFromBox.nMaxVal.has_value()
+                || !aNeckFromBox.nMinVal.has_value() || 
!aHeadFromNeck.nCurrVal.has_value()
+                || !aHeadFromNeck.nMaxVal.has_value() || 
!aHeadFromNeck.nMinVal.has_value())
+                return false;
+
+            m_pDMLexporter->WriteShapeTransformation(m_xShape, XML_a, 
IsXFlipped(), IsYFlipped(),
+                                                     false, false);
+            tools::Long nMaxVal1
+                = 100000 * m_xShape->getSize().Height
+                  / std::min(m_xShape->getSize().Width, 
m_xShape->getSize().Height);
+            tools::Long nMaxVal2
+                = 50000 * m_xShape->getSize().Height
+                  / std::min(m_xShape->getSize().Width, 
m_xShape->getSize().Height);
+            tools::Long nMaxVal3
+                = 100000 * m_xShape->getSize().Width
+                  / std::min(m_xShape->getSize().Width, 
m_xShape->getSize().Height);
+            tools::Long nVal1
+                = std::lround((*aNeckFromBox.nMaxVal - *aNeckFromBox.nCurrVal)
+                              / (*aNeckFromBox.nMaxVal - 
*aNeckFromBox.nMinVal) * nMaxVal1);
+            tools::Long nVal2 = std::lround((10800 - *aHeadFromNeck.nCurrVal)
+                                            / (10800 - *aHeadFromNeck.nMinVal) 
* nMaxVal2);
+            tools::Long nVal3 = std::lround((*aNeckLength.nCurrVal - 
*aNeckLength.nMinVal)
+                                            / (21600 - *aNeckLength.nMinVal) * 
nMaxVal3);
+            tools::Long nVal4 = std::lround((*aBoxWidth.nMaxVal - 
*aBoxWidth.nCurrVal)
+                                            / (*aBoxWidth.nMaxVal - 
*aBoxWidth.nMinVal) * 100000);
+            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)))
+                   && WriteAV(u"adj4", OUString(u"val " + 
OUString::number(nVal4)))
+                   && EndAVListWriting();
         }
         if (sShapeType == "leftBrace")
         {
@@ -913,18 +1025,74 @@ bool DMLPresetShapeExporter::WriteShapeWithAVlist()
         }
         if (sShapeType == "leftCircularArrow")
         {
-            // TODO
+            // LO does not have this type, not necessary to map
             return false;
         }
         if (sShapeType == "leftRightArrow")
         {
-            // TODO
-            return false;
+            auto aPointX = GetAdjustmentPointXValue(0);
+            auto aPointY = GetAdjustmentPointYValue(0);
+            if (!aPointX.nCurrVal.has_value() || !aPointX.nMaxVal.has_value()
+                || !aPointX.nMinVal.has_value() || 
!aPointY.nCurrVal.has_value()
+                || !aPointY.nMaxVal.has_value() || 
!aPointY.nMinVal.has_value())
+                return false;
+
+            m_pDMLexporter->WriteShapeTransformation(m_xShape, XML_a, 
IsXFlipped(), IsYFlipped(),
+                                                     false, false);
+            tools::Long nMaxVal1 = 100000;
+            tools::Long nMaxVal2
+                = 50000
+                  * (double(m_xShape->getSize().Width)
+                     / std::min(m_xShape->getSize().Width, 
m_xShape->getSize().Height));
+            tools::Long nVal1 = std::lround((*aPointY.nMaxVal - 
*aPointY.nCurrVal)
+                                            / (*aPointY.nMaxVal - 
*aPointY.nMinVal) * nMaxVal1);
+            tools::Long nVal2 = std::lround((*aPointX.nCurrVal - 
*aPointX.nMinVal)
+                                            / (*aPointX.nMaxVal - 
*aPointX.nMinVal) * nMaxVal2);
+            return StartAVListWriting()
+                   && WriteAV(u"adj1", OUString(u"val " + 
OUString::number(nVal1)))
+                   && WriteAV(u"adj2", OUString(u"val " + 
OUString::number(nVal2)))
+                   && EndAVListWriting();
         }
         if (sShapeType == "leftRightArrowCallout")
         {
-            // TODO
-            return false;
+            auto aNeckFromBox = GetAdjustmentPointXValue(1);
+            auto aHeadFromNeck = GetAdjustmentPointXValue(2);
+            auto aHeadHeight = GetAdjustmentPointYValue(1);
+            auto aBoxHeight = GetAdjustmentPointYValue(0);
+            if (!aNeckFromBox.nCurrVal.has_value() || 
!aNeckFromBox.nMaxVal.has_value()
+                || !aNeckFromBox.nMinVal.has_value() || 
!aHeadFromNeck.nCurrVal.has_value()
+                || !aHeadFromNeck.nMaxVal.has_value() || 
!aHeadFromNeck.nMinVal.has_value()
+                || !aHeadHeight.nCurrVal.has_value() || 
!aHeadHeight.nMaxVal.has_value()
+                || !aHeadHeight.nMinVal.has_value() || 
!aBoxHeight.nCurrVal.has_value()
+                || !aBoxHeight.nMaxVal.has_value() || 
!aBoxHeight.nMinVal.has_value())
+                return false;
+
+            m_pDMLexporter->WriteShapeTransformation(m_xShape, XML_a, 
IsXFlipped(), IsYFlipped(),
+                                                     false, false);
+            tools::Long nMaxVal1
+                = 100000 * m_xShape->getSize().Width
+                  / std::min(m_xShape->getSize().Width, 
m_xShape->getSize().Height);
+            tools::Long nMaxVal2
+                = 50000 * m_xShape->getSize().Width
+                  / std::min(m_xShape->getSize().Width, 
m_xShape->getSize().Height);
+            tools::Long nMaxVal3
+                = 100000 * m_xShape->getSize().Height
+                  / std::min(m_xShape->getSize().Width, 
m_xShape->getSize().Height);
+            tools::Long nVal1
+                = std::lround((*aNeckFromBox.nMaxVal - *aNeckFromBox.nCurrVal)
+                              / (*aNeckFromBox.nMaxVal - 
*aNeckFromBox.nMinVal) * nMaxVal1);
+            tools::Long nVal2 = std::lround((10800 - *aHeadFromNeck.nCurrVal)
+                                            / (10800 - *aHeadFromNeck.nMinVal) 
* nMaxVal2);
+            tools::Long nVal3 = std::lround((*aHeadHeight.nCurrVal - 
*aHeadHeight.nMinVal)
+                                            / (21600 - *aHeadHeight.nMinVal) * 
nMaxVal3);
+            tools::Long nVal4 = std::lround((*aBoxHeight.nCurrVal - 
*aBoxHeight.nMinVal)
+                                            / (10800 - *aBoxHeight.nMinVal) * 
100000);
+            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)))
+                   && WriteAV(u"adj4", OUString(u"val " + 
OUString::number(nVal4)))
+                   && EndAVListWriting();
         }
         if (sShapeType == "leftRightCircularArrow")
         {
@@ -938,13 +1106,14 @@ bool DMLPresetShapeExporter::WriteShapeWithAVlist()
         }
         if (sShapeType == "leftRightUpArrow")
         {
-            // TODO
+            // TODO?
+            // MS Word stretches the arrow to fit the bounding box; LO doesn't
             return false;
         }
         if (sShapeType == "leftUpArrow")
         {
-            // TODO
-            return false;
+            // MS Word's and LO's interpretations of what a leftUpArrow should 
look like
+            // are too different to find a compromise :(
         }
         if (sShapeType == "lightningBolt")
         {
@@ -1105,13 +1274,70 @@ bool DMLPresetShapeExporter::WriteShapeWithAVlist()
         }
         if (sShapeType == "rightArrow")
         {
-            // TODO
-            return false;
+            auto aPointX = GetAdjustmentPointXValue(0);
+            auto aPointY = GetAdjustmentPointYValue(0);
+            if (!aPointX.nCurrVal.has_value() || !aPointX.nMaxVal.has_value()
+                || !aPointX.nMinVal.has_value() || 
!aPointY.nCurrVal.has_value()
+                || !aPointY.nMaxVal.has_value() || 
!aPointY.nMinVal.has_value())
+                return false;
+
+            m_pDMLexporter->WriteShapeTransformation(m_xShape, XML_a, 
IsXFlipped(), IsYFlipped(),
+                                                     false, false);
+            tools::Long nMaxVal1 = 100000;
+            tools::Long nMaxVal2
+                = 100000
+                  * (double(m_xShape->getSize().Width)
+                     / std::min(m_xShape->getSize().Width, 
m_xShape->getSize().Height));
+            tools::Long nVal1 = std::lround((*aPointY.nMaxVal - 
*aPointY.nCurrVal)
+                                            / (*aPointY.nMaxVal - 
*aPointY.nMinVal) * nMaxVal1);
+            tools::Long nVal2 = std::lround((*aPointX.nMaxVal - 
*aPointX.nCurrVal)
+                                            / (*aPointX.nMaxVal - 
*aPointX.nMinVal) * nMaxVal2);
+            return StartAVListWriting()
+                   && WriteAV(u"adj1", OUString(u"val " + 
OUString::number(nVal1)))
+                   && WriteAV(u"adj2", OUString(u"val " + 
OUString::number(nVal2)))
+                   && EndAVListWriting();
         }
         if (sShapeType == "rightArrowCallout")
         {
-            // TODO
-            return false;
+            auto aBoxWidth = GetAdjustmentPointXValue(0);
+            auto aNeckLength = GetAdjustmentPointXValue(1);
+            auto aNeckFromBox = GetAdjustmentPointYValue(1);
+            auto aHeadFromNeck = GetAdjustmentPointYValue(2);
+            if (!aBoxWidth.nCurrVal.has_value() || 
!aBoxWidth.nMaxVal.has_value()
+                || !aBoxWidth.nMinVal.has_value() || 
!aNeckLength.nCurrVal.has_value()
+                || !aNeckLength.nMaxVal.has_value() || 
!aNeckLength.nMinVal.has_value()
+                || !aNeckFromBox.nCurrVal.has_value() || 
!aNeckFromBox.nMaxVal.has_value()
+                || !aNeckFromBox.nMinVal.has_value() || 
!aHeadFromNeck.nCurrVal.has_value()
+                || !aHeadFromNeck.nMaxVal.has_value() || 
!aHeadFromNeck.nMinVal.has_value())
+                return false;
+
+            m_pDMLexporter->WriteShapeTransformation(m_xShape, XML_a, 
IsXFlipped(), IsYFlipped(),
+                                                     false, false);
+            tools::Long nMaxVal1
+                = 100000 * m_xShape->getSize().Height
+                  / std::min(m_xShape->getSize().Width, 
m_xShape->getSize().Height);
+            tools::Long nMaxVal2
+                = 50000 * m_xShape->getSize().Height
+                  / std::min(m_xShape->getSize().Width, 
m_xShape->getSize().Height);
+            tools::Long nMaxVal3
+                = 100000 * m_xShape->getSize().Width
+                  / std::min(m_xShape->getSize().Width, 
m_xShape->getSize().Height);
+            tools::Long nVal1
+                = std::lround((*aNeckFromBox.nMaxVal - *aNeckFromBox.nCurrVal)
+                              / (*aNeckFromBox.nMaxVal - 
*aNeckFromBox.nMinVal) * nMaxVal1);
+            tools::Long nVal2 = std::lround((10800 - *aHeadFromNeck.nCurrVal)
+                                            / (10800 - *aHeadFromNeck.nMinVal) 
* nMaxVal2);
+            tools::Long nVal3
+                = std::lround((*aNeckLength.nMaxVal - *aNeckLength.nCurrVal)
+                              / (*aNeckLength.nMaxVal - *aNeckLength.nMinVal) 
* nMaxVal3);
+            tools::Long nVal4 = std::lround((*aBoxWidth.nCurrVal - 
*aBoxWidth.nMinVal)
+                                            / (21600 - *aBoxWidth.nMinVal) * 
100000);
+            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)))
+                   && WriteAV(u"adj4", OUString(u"val " + 
OUString::number(nVal4)))
+                   && EndAVListWriting();
         }
         if (sShapeType == "rightBrace")
         {
@@ -1299,18 +1525,92 @@ bool DMLPresetShapeExporter::WriteShapeWithAVlist()
         }
         if (sShapeType == "upArrowCallout")
         {
-            // TODO
-            return false;
+            auto aNeckFromBox = GetAdjustmentPointXValue(1);
+            auto aHeadFromNeck = GetAdjustmentPointXValue(2);
+            auto aHeadHeight = GetAdjustmentPointYValue(1);
+            auto aBoxHeight = GetAdjustmentPointYValue(0);
+            if (!aNeckFromBox.nCurrVal.has_value() || 
!aNeckFromBox.nMaxVal.has_value()
+                || !aNeckFromBox.nMinVal.has_value() || 
!aHeadFromNeck.nCurrVal.has_value()
+                || !aHeadFromNeck.nMaxVal.has_value() || 
!aHeadFromNeck.nMinVal.has_value()
+                || !aHeadHeight.nCurrVal.has_value() || 
!aHeadHeight.nMaxVal.has_value()
+                || !aHeadHeight.nMinVal.has_value() || 
!aBoxHeight.nCurrVal.has_value()
+                || !aBoxHeight.nMaxVal.has_value() || 
!aBoxHeight.nMinVal.has_value())
+                return false;
+
+            m_pDMLexporter->WriteShapeTransformation(m_xShape, XML_a, 
IsXFlipped(), IsYFlipped(),
+                                                     false, false);
+            tools::Long nMaxVal1
+                = 100000 * m_xShape->getSize().Width
+                  / std::min(m_xShape->getSize().Width, 
m_xShape->getSize().Height);
+            tools::Long nMaxVal2
+                = 50000 * m_xShape->getSize().Width
+                  / std::min(m_xShape->getSize().Width, 
m_xShape->getSize().Height);
+            tools::Long nMaxVal3
+                = 100000 * m_xShape->getSize().Height
+                  / std::min(m_xShape->getSize().Width, 
m_xShape->getSize().Height);
+            tools::Long nVal1
+                = std::lround((*aNeckFromBox.nMaxVal - *aNeckFromBox.nCurrVal)
+                              / (*aNeckFromBox.nMaxVal - 
*aNeckFromBox.nMinVal) * nMaxVal1);
+            tools::Long nVal2 = std::lround((10800 - *aHeadFromNeck.nCurrVal)
+                                            / (10800 - *aHeadFromNeck.nMinVal) 
* nMaxVal2);
+            tools::Long nVal3 = std::lround((*aHeadHeight.nCurrVal - 
*aHeadHeight.nMinVal)
+                                            / (21600 - *aHeadHeight.nMinVal) * 
nMaxVal3);
+            tools::Long nVal4 = std::lround((*aBoxHeight.nCurrVal - 
*aBoxHeight.nMinVal)
+                                            / (10800 - *aBoxHeight.nMinVal) * 
100000);
+            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)))
+                   && WriteAV(u"adj4", OUString(u"val " + 
OUString::number(nVal4)))
+                   && EndAVListWriting();
         }
         if (sShapeType == "upDownArrow")
         {
-            // TODO
-            return false;
+            auto aPointX = GetAdjustmentPointXValue(0);
+            auto aPointY = GetAdjustmentPointYValue(0);
+            if (!aPointX.nCurrVal.has_value() || !aPointX.nMaxVal.has_value()
+                || !aPointX.nMinVal.has_value() || 
!aPointY.nCurrVal.has_value()
+                || !aPointY.nMaxVal.has_value() || 
!aPointY.nMinVal.has_value())
+                return false;
+
+            m_pDMLexporter->WriteShapeTransformation(m_xShape, XML_a, 
IsXFlipped(), IsYFlipped(),
+                                                     false, false);
+            tools::Long nMaxVal1 = 100000;
+            tools::Long nMaxVal2
+                = 50000 * m_xShape->getSize().Height
+                  / std::min(m_xShape->getSize().Width, 
m_xShape->getSize().Height);
+            tools::Long nVal1 = std::lround((*aPointX.nMaxVal - 
*aPointX.nCurrVal)
+                                            / (*aPointX.nMaxVal - 
*aPointX.nMinVal) * nMaxVal1);
+            tools::Long nVal2 = std::lround((*aPointY.nCurrVal - 
*aPointY.nMinVal)
+                                            / (*aPointY.nMaxVal - 
*aPointY.nMinVal) * nMaxVal2);
+            return StartAVListWriting()
+                   && WriteAV(u"adj1", OUString(u"val " + 
OUString::number(nVal1)))
+                   && WriteAV(u"adj2", OUString(u"val " + 
OUString::number(nVal2)))
+                   && EndAVListWriting();
         }
         if (sShapeType == "upArrow")
         {
-            // TODO
-            return false;
+            auto aPointX = GetAdjustmentPointXValue(0);
+            auto aPointY = GetAdjustmentPointYValue(0);
+            if (!aPointX.nCurrVal.has_value() || !aPointX.nMaxVal.has_value()
+                || !aPointX.nMinVal.has_value() || 
!aPointY.nCurrVal.has_value()
+                || !aPointY.nMaxVal.has_value() || 
!aPointY.nMinVal.has_value())
+                return false;
+
+            m_pDMLexporter->WriteShapeTransformation(m_xShape, XML_a, 
IsXFlipped(), IsYFlipped(),
+                                                     false, false);
+            tools::Long nMaxVal1 = 100000;
+            tools::Long nMaxVal2
+                = 100000 * m_xShape->getSize().Height
+                  / std::min(m_xShape->getSize().Width, 
m_xShape->getSize().Height);
+            tools::Long nVal1 = std::lround((*aPointX.nMaxVal - 
*aPointX.nCurrVal)
+                                            / (*aPointX.nMaxVal - 
*aPointX.nMinVal) * nMaxVal1);
+            tools::Long nVal2 = std::lround((*aPointY.nCurrVal - 
*aPointY.nMinVal)
+                                            / (*aPointY.nMaxVal - 
*aPointY.nMinVal) * nMaxVal2);
+            return StartAVListWriting()
+                   && WriteAV(u"adj1", OUString(u"val " + 
OUString::number(nVal1)))
+                   && WriteAV(u"adj2", OUString(u"val " + 
OUString::number(nVal2)))
+                   && EndAVListWriting();
         }
         if (sShapeType == "upDownArrowCallout")
         {

Reply via email to