From 3d7466b92a450d7ca4f45fef9a664143fbb3c386 Mon Sep 17 00:00:00 2001 From: Regina Henschel Date: Sat, 23 Apr 2022 21:52:29 +0200 Subject: Do not start a:path with lnTo in export to OOXML This is a follow up to commit 2029b2f6dd0109c5892e5ac5640022b31fe42fd2 The commands A, W, T or L of a draw:enhanced-path draw a line from current point to start of the arc or end of line, respectivly. If there is no current point the path is faulty and behavior is not defined in ODF. LibreOffice is tolerant and makes a move to the start point of the arc or to the end point of the line. With this patch we do the same now in export to OOXML, so the user gets the same shape geometry as in LO. If a path starts with lnTo, MS Office will show nothing. I wouldn't care about user-created faulty paths, but LO produces such faulty path when an EllipseRibbon shape from binary MS Office is imported. Even when that will be fixed, we need the fix here, because the faulty path had been written to document markup and will be used when such document is opened. Change-Id: Ic52ec3bc78231b26efb592ddadee2e3042fdc065 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/133349 Tested-by: Jenkins Reviewed-by: Regina Henschel --- oox/qa/unit/data/FaultyPathStart.odp | Bin 0 -> 14582 bytes oox/qa/unit/export.cxx | 20 ++++++++++++++++++++ oox/source/export/drawingml.cxx | 25 +++++++++++++++++++------ 3 files changed, 39 insertions(+), 6 deletions(-) create mode 100644 oox/qa/unit/data/FaultyPathStart.odp (limited to 'oox') diff --git a/oox/qa/unit/data/FaultyPathStart.odp b/oox/qa/unit/data/FaultyPathStart.odp new file mode 100644 index 000000000000..219795ffa58b Binary files /dev/null and b/oox/qa/unit/data/FaultyPathStart.odp differ diff --git a/oox/qa/unit/export.cxx b/oox/qa/unit/export.cxx index b4eebc537250..7a1bda7cb5bd 100644 --- a/oox/qa/unit/export.cxx +++ b/oox/qa/unit/export.cxx @@ -632,6 +632,26 @@ CPPUNIT_TEST_FIXTURE(Test, testTdf109169_OctagonBevel) assertXPath(pXmlDoc, "//a:pathLst/a:path[5]", "fill", "lightenLess"); assertXPath(pXmlDoc, "//a:pathLst/a:path[6]", "fill", "lighten"); } + +CPPUNIT_TEST_FIXTURE(Test, testFaultyPathCommandsAWT) +{ + // The odp file contains shapes whose path starts with command A, W, T or L. That is a faulty + // path. LO is tolerant and renders it so that is makes a moveTo to the start point of the arc or + // the end of the line respectively. Export to OOXML does the same now and writes a moveTo + // instead of the normally used lnTo. If a lnTo is written, MS Office shows nothing of the shape. + OUString aURL = m_directories.getURLFromSrc(DATA_DIRECTORY) + "FaultyPathStart.odp"; + + loadAndSave(aURL, "Impress Office Open XML"); + + // Verify the markup: + std::unique_ptr pStream = parseExportStream(getTempFile(), "ppt/slides/slide1.xml"); + xmlDocUniquePtr pXmlDoc = parseXmlStream(pStream.get()); + // First child of a:path should be a moveTo in all four shapes. + assertXPath(pXmlDoc, "//p:spTree/p:sp[1]/p:spPr/a:custGeom/a:pathLst/a:path/a:moveTo"); + assertXPath(pXmlDoc, "//p:spTree/p:sp[2]/p:spPr/a:custGeom/a:pathLst/a:path/a:moveTo"); + assertXPath(pXmlDoc, "//p:spTree/p:sp[3]/p:spPr/a:custGeom/a:pathLst/a:path/a:moveTo"); + assertXPath(pXmlDoc, "//p:spTree/p:sp[4]/p:spPr/a:custGeom/a:pathLst/a:path/a:moveTo"); +} } CPPUNIT_PLUGIN_IMPLEMENT(); diff --git a/oox/source/export/drawingml.cxx b/oox/source/export/drawingml.cxx index 327b567c4c40..78eac3d00f60 100644 --- a/oox/source/export/drawingml.cxx +++ b/oox/source/export/drawingml.cxx @@ -4218,10 +4218,21 @@ bool DrawingML::WriteCustomGeometrySegment( { if (rnPairIndex >= rPairs.getLength()) return false; - - mpFS->startElementNS(XML_a, XML_lnTo); - WriteCustomGeometryPoint(rPairs[rnPairIndex], rCustomShape2d); - mpFS->endElementNS(XML_a, XML_lnTo); + // LINETO without valid current point is a faulty path. LO is tolerant and makes a + // moveTo instead. Do the same on export. MS OFFICE requires a current point for lnTo, + // otherwise it shows nothing of the shape. + if (rbCurrentValid) + { + mpFS->startElementNS(XML_a, XML_lnTo); + WriteCustomGeometryPoint(rPairs[rnPairIndex], rCustomShape2d); + mpFS->endElementNS(XML_a, XML_lnTo); + } + else + { + mpFS->startElementNS(XML_a, XML_moveTo); + WriteCustomGeometryPoint(rPairs[rnPairIndex], rCustomShape2d); + mpFS->endElementNS(XML_a, XML_moveTo); + } rCustomShape2d.GetParameter(rfCurrentX, rPairs[rnPairIndex].First, false, false); rCustomShape2d.GetParameter(rfCurrentY, rPairs[rnPairIndex].Second, false, false); rbCurrentValid = true; @@ -4284,7 +4295,8 @@ bool DrawingML::WriteCustomGeometrySegment( getEllipsePointFromViewAngle(fSx, fSy, fWR, fHR, fCx, fCy, fStartAngle); // write markup for going to start point - if (eCommand == ANGLEELLIPSETO) + // lnTo requires a valid current point + if (eCommand == ANGLEELLIPSETO && rbCurrentValid) { mpFS->startElementNS(XML_a, XML_lnTo); mpFS->singleElementNS(XML_a, XML_pt, XML_x, OString::number(std::lround(fSx)), @@ -4347,7 +4359,8 @@ bool DrawingML::WriteCustomGeometrySegment( getEllipsePointAndAngleFromRayPoint(fStartAngle, fPx, fPy, fWR, fHR, fCx, fCy, fX3, fY3); // markup for going to start point - if (eCommand == ARCTO || eCommand == CLOCKWISEARCTO) + // lnTo requires a valid current point. + if ((eCommand == ARCTO || eCommand == CLOCKWISEARCTO) && rbCurrentValid) { mpFS->startElementNS(XML_a, XML_lnTo); mpFS->singleElementNS(XML_a, XML_pt, XML_x, OString::number(std::lround(fPx)), -- cgit