summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTibor Nagy <nagy.tibor2@nisz.hu>2022-04-29 14:08:17 +0200
committerLászló Németh <nemeth@numbertext.org>2022-05-10 16:51:54 +0200
commit4cc3a144ef2ecc019da42bb9b115267a18e9559f (patch)
tree973ccba5b45ac2722bf29b80cb0d79cf87c497a3
parent1d8c31ea2555b339d5a4bc5449d40d546045435b (diff)
tdf#147919 PPTX export: fix curved and bent connector shape
by calculating and exporting adjustment values to avoid of connector lines with bad turns overlapping the connected shapes. Change-Id: I0441508f4fe98b3482e2955df5f41f2cfc2ffb4e Reviewed-on: https://gerrit.libreoffice.org/c/core/+/134109 Tested-by: Jenkins Tested-by: László Németh <nemeth@numbertext.org> Reviewed-by: László Németh <nemeth@numbertext.org>
-rw-r--r--oox/source/export/shapes.cxx306
-rw-r--r--sd/qa/unit/data/odp/tdf147919.odpbin0 -> 14790 bytes
-rw-r--r--sd/qa/unit/export-tests-ooxml1.cxx62
3 files changed, 340 insertions, 28 deletions
diff --git a/oox/source/export/shapes.cxx b/oox/source/export/shapes.cxx
index 3308d5ab4492..ed129784ba51 100644
--- a/oox/source/export/shapes.cxx
+++ b/oox/source/export/shapes.cxx
@@ -1411,49 +1411,276 @@ void ShapeExport::WriteGraphicObjectShapePart( const Reference< XShape >& xShape
pFS->endElementNS( mnXmlNamespace, XML_pic );
}
+static void lcl_Rotate(sal_Int32 nAngle, Point center, awt::Point& pt)
+{
+ sal_Int16 nCos, nSin;
+ switch (nAngle)
+ {
+ case 90:
+ nCos = 0;
+ nSin = 1;
+ break;
+ case 180:
+ nCos = -1;
+ nSin = 0;
+ break;
+ case 270:
+ nCos = 0;
+ nSin = -1;
+ break;
+ default:
+ return;
+ }
+ sal_Int32 x = pt.X - center.X();
+ sal_Int32 y = pt.Y - center.Y();
+ pt.X = center.X() + x * nCos - y * nSin;
+ pt.Y = center.Y() + y * nCos + x * nSin;
+}
+
+static void lcl_FlipHFlipV(tools::Polygon aPoly, sal_Int32 nAngle, bool& rFlipH, bool& rFlipV)
+{
+ Point aStart = aPoly[0];
+ Point aEnd = aPoly[aPoly.GetSize() - 1];
+
+ if (aStart.X() > aEnd.X() && aStart.Y() > aEnd.Y())
+ {
+ if (nAngle)
+ {
+ if (nAngle == 90)
+ rFlipH = true;
+ if (nAngle == 270)
+ rFlipV = true;
+ }
+ else // 0°
+ {
+ rFlipH = true;
+ rFlipV = true;
+ }
+ }
+
+ if (aStart.X() < aEnd.X() && aStart.Y() < aEnd.Y())
+ {
+ if (nAngle)
+ {
+ if (nAngle != 270)
+ {
+ rFlipH = true;
+ rFlipV = true;
+ }
+ else
+ rFlipH = true;
+ }
+ }
+
+ if (aStart.Y() < aEnd.Y() && aStart.X() > aEnd.X())
+ {
+ if (nAngle)
+ {
+ if (nAngle == 180)
+ rFlipV = true;
+ if (nAngle == 270)
+ {
+ rFlipV = true;
+ rFlipH = true;
+ }
+ }
+ else // 0°
+ {
+ rFlipH = true;
+ }
+ }
+
+ if (aStart.Y() > aEnd.Y() && aStart.X() < aEnd.X())
+ {
+ if (nAngle)
+ {
+ if (nAngle == 90)
+ {
+ rFlipH = true;
+ rFlipV = true;
+ }
+ if (nAngle == 180)
+ rFlipH = true;
+ }
+ else // 0°
+ rFlipV = true;
+ }
+}
+
+static sal_Int32 lcl_GetAngle(tools::Polygon aPoly)
+{
+ sal_Int32 nAngle;
+ Point aStartPoint = aPoly[0];
+ Point aEndPoint = aPoly[aPoly.GetSize() - 1];
+ if (aStartPoint.X() == aPoly[1].X())
+ {
+ if ((aStartPoint.X() < aEndPoint.X() && aStartPoint.Y() > aEndPoint.Y())
+ || (aStartPoint.X() > aEndPoint.X() && aStartPoint.Y() < aEndPoint.Y()))
+ {
+ nAngle = 90;
+ }
+ else
+ nAngle = 270;
+ }
+ else
+ {
+ if (aStartPoint.X() > aPoly[1].X())
+ nAngle = 180;
+ else
+ nAngle = 0;
+ }
+
+ return nAngle;
+}
+
+// Adjust value decide the position, where the connector should turn.
+static void lcl_GetConnectorAdjustValue(const Reference<XShape>& xShape, tools::Polygon aPoly,
+ ConnectorType eConnectorType,
+ std::vector<std::pair<sal_Int32, sal_Int32>>& rAvList)
+{
+ sal_Int32 nAdjCount = 0;
+ if (eConnectorType == ConnectorType_CURVE)
+ {
+ if (aPoly.GetSize() == 4)
+ {
+ if ((aPoly[0].X() == aPoly[1].X() && aPoly[2].X() == aPoly[3].X())
+ || (aPoly[0].Y() == aPoly[1].Y() && aPoly[2].Y() == aPoly[3].Y()))
+ {
+ nAdjCount = 1; // curvedConnector3
+ }
+ else
+ nAdjCount = 0; // curvedConnector2
+ }
+ else if (aPoly.GetSize() > 4)
+ {
+ if ((aPoly[2].X() == aPoly[3].X() && aPoly[3].X() == aPoly[4].X())
+ || (aPoly[2].Y() == aPoly[3].Y() && aPoly[3].Y() == aPoly[4].Y()))
+ {
+ nAdjCount = 3; // curvedConnector5
+ }
+ else
+ nAdjCount = 2; // curvedConnector4
+ }
+ }
+ else
+ {
+ switch (aPoly.GetSize())
+ {
+ case 3:
+ nAdjCount = 0; // bentConnector2
+ break;
+ case 4:
+ nAdjCount = 1; // bentConnector3
+ break;
+ case 5:
+ nAdjCount = 2; // bentConnector4
+ break;
+ case 6:
+ nAdjCount = 3; // bentConnector5
+ break;
+ }
+ }
+
+ if (nAdjCount)
+ {
+ sal_Int32 nAdjustValue;
+ Point aStart = aPoly[0];
+ Point aEnd = aPoly[aPoly.GetSize() - 1];
+
+ for (sal_Int32 i = 1; i <= nAdjCount; ++i)
+ {
+ Point aPt = aPoly[i];
+
+ if (aEnd.Y() == aStart.Y())
+ aEnd.setY(aStart.Y() + 1);
+ if (aEnd.X() == aStart.X())
+ aEnd.setX(aStart.X() + 1);
+
+ bool bVertical = aPoly[1].X() - aStart.X() != 0 ? true : false;
+ // vertical and horizon alternate
+ if (i % 2 == 1)
+ bVertical = !bVertical;
+
+ if (eConnectorType == ConnectorType_CURVE)
+ {
+ awt::Size aSize = xShape->getSize();
+ awt::Point aShapePosition = xShape->getPosition();
+ tools::Rectangle aBoundRect = aPoly.GetBoundRect();
+
+ if (bVertical)
+ {
+ if ((aBoundRect.GetSize().Height() - aSize.Height) == 1)
+ aPt.setY(aPoly[i + 1].Y());
+ else if (aStart.Y() > aPt.Y())
+ aPt.setY(aShapePosition.Y);
+ else
+ aPt.setY(aShapePosition.Y + aSize.Height);
+ }
+ else
+ {
+ if ((aBoundRect.GetSize().Width() - aSize.Width) == 1)
+ aPt.setX(aPoly[i + 1].X());
+ else if (aStart.X() > aPt.X())
+ aPt.setX(aShapePosition.X);
+ else
+ aPt.setX(aShapePosition.X + aSize.Width);
+ }
+ }
+
+ if (bVertical)
+ nAdjustValue = ((aPt.Y() - aStart.Y()) * 100000) / (aEnd.Y() - aStart.Y());
+ else
+ nAdjustValue = ((aPt.X() - aStart.X()) * 100000) / (aEnd.X() - aStart.X());
+
+ rAvList.emplace_back(i, nAdjustValue);
+ }
+ }
+}
+
ShapeExport& ShapeExport::WriteConnectorShape( const Reference< XShape >& xShape )
{
bool bFlipH = false;
bool bFlipV = false;
+ sal_Int32 nAngle = 0;
SAL_INFO("oox.shape", "write connector shape");
FSHelperPtr pFS = GetFS();
- const char* sGeometry = "line";
+ OUString sGeometry;
+ std::vector<std::pair<sal_Int32, sal_Int32>> aAdjustValueList;
Reference< XPropertySet > rXPropSet( xShape, UNO_QUERY );
Reference< XPropertyState > rXPropState( xShape, UNO_QUERY );
awt::Point aStartPoint, aEndPoint;
Reference< XShape > rXShapeA;
Reference< XShape > rXShapeB;
PropertyState eState;
- ConnectorType eConnectorType;
- if( GETAD( EdgeKind ) ) {
- mAny >>= eConnectorType;
+ ConnectorType eConnectorType = ConnectorType_STANDARD;
+ GET(eConnectorType, EdgeKind);
- switch( eConnectorType ) {
- case ConnectorType_CURVE:
- sGeometry = "curvedConnector3";
- break;
- case ConnectorType_STANDARD:
- sGeometry = "bentConnector3";
- break;
- default:
- case ConnectorType_LINE:
- case ConnectorType_LINES:
- sGeometry = "straightConnector1";
- break;
- }
+ switch( eConnectorType ) {
+ case ConnectorType_CURVE:
+ sGeometry = "curvedConnector";
+ break;
+ case ConnectorType_STANDARD:
+ sGeometry = "bentConnector";
+ break;
+ default:
+ case ConnectorType_LINE:
+ case ConnectorType_LINES:
+ sGeometry = "straightConnector1";
+ break;
+ }
- if( GETAD( EdgeStartPoint ) ) {
- mAny >>= aStartPoint;
- if( GETAD( EdgeEndPoint ) ) {
- mAny >>= aEndPoint;
- }
+ if( GETAD( EdgeStartPoint ) ) {
+ mAny >>= aStartPoint;
+ if( GETAD( EdgeEndPoint ) ) {
+ mAny >>= aEndPoint;
}
- GET( rXShapeA, EdgeStartConnection );
- GET( rXShapeB, EdgeEndConnection );
}
+ GET( rXShapeA, EdgeStartConnection );
+ GET( rXShapeB, EdgeEndConnection );
+
// Position is relative to group in Word, but relative to anchor of group in API.
if (GetDocumentType() == DOCUMENT_DOCX && !mbUserShapes && m_xParent.is())
{
@@ -1464,17 +1691,40 @@ ShapeExport& ShapeExport::WriteConnectorShape( const Reference< XShape >& xShape
aEndPoint.Y -= aParentPos.Y;
}
EscherConnectorListEntry aConnectorEntry( xShape, aStartPoint, rXShapeA, aEndPoint, rXShapeB );
+
+ if (eConnectorType == ConnectorType_CURVE || eConnectorType == ConnectorType_STANDARD)
+ {
+ tools::PolyPolygon aPolyPolygon = EscherPropertyContainer::GetPolyPolygon(xShape);
+ if (aPolyPolygon.Count() > 0)
+ {
+ tools::Polygon aPoly = aPolyPolygon.GetObject(0);
+ lcl_GetConnectorAdjustValue(xShape, aPoly, eConnectorType, aAdjustValueList);
+ nAngle = lcl_GetAngle(aPoly);
+ lcl_FlipHFlipV(aPoly, nAngle, bFlipH, bFlipV);
+ if (nAngle)
+ {
+ Point center((aEndPoint.X + aStartPoint.X) / 2, (aEndPoint.Y + aStartPoint.Y) / 2);
+ lcl_Rotate(nAngle, center, aStartPoint);
+ lcl_Rotate(nAngle, center, aEndPoint);
+ nAngle *= 60000;
+ }
+ sGeometry = sGeometry + OUString::number(aAdjustValueList.size() + 2);
+ }
+ }
+
tools::Rectangle aRect( Point( aStartPoint.X, aStartPoint.Y ), Point( aEndPoint.X, aEndPoint.Y ) );
if( aRect.getWidth() < 0 ) {
- bFlipH = true;
aRect.SetLeft(aEndPoint.X);
aRect.setWidth( aStartPoint.X - aEndPoint.X );
+ if (eConnectorType == ConnectorType_LINE)
+ bFlipH = true;
}
if( aRect.getHeight() < 0 ) {
- bFlipV = true;
aRect.SetTop(aEndPoint.Y);
aRect.setHeight( aStartPoint.Y - aEndPoint.Y );
+ if (eConnectorType == ConnectorType_LINE)
+ bFlipV = true;
}
// tdf#99810 connector shape (cxnSp) is not valid with namespace 'wps'
@@ -1504,9 +1754,9 @@ ShapeExport& ShapeExport::WriteConnectorShape( const Reference< XShape >& xShape
// visual shape properties
pFS->startElementNS(mnXmlNamespace, XML_spPr);
- WriteTransformation( xShape, aRect, XML_a, bFlipH, bFlipV );
+ WriteTransformation( xShape, aRect, XML_a, bFlipH, bFlipV, nAngle );
// TODO: write adjustments (ppt export doesn't work well there either)
- WritePresetShape( sGeometry );
+ WritePresetShape( sGeometry.toUtf8(), aAdjustValueList);
Reference< XPropertySet > xShapeProps( xShape, UNO_QUERY );
if( xShapeProps.is() )
WriteOutline( xShapeProps );
diff --git a/sd/qa/unit/data/odp/tdf147919.odp b/sd/qa/unit/data/odp/tdf147919.odp
new file mode 100644
index 000000000000..2676b0e1c08f
--- /dev/null
+++ b/sd/qa/unit/data/odp/tdf147919.odp
Binary files differ
diff --git a/sd/qa/unit/export-tests-ooxml1.cxx b/sd/qa/unit/export-tests-ooxml1.cxx
index db33355b9bc6..a1dac4fbf63c 100644
--- a/sd/qa/unit/export-tests-ooxml1.cxx
+++ b/sd/qa/unit/export-tests-ooxml1.cxx
@@ -48,6 +48,7 @@ using namespace css;
class SdOOXMLExportTest1 : public SdModelTestBaseXML
{
public:
+ void testTdf147919();
void testTdf130165();
void testTdf124781();
void testTdf144914();
@@ -117,6 +118,7 @@ public:
CPPUNIT_TEST_SUITE(SdOOXMLExportTest1);
+ CPPUNIT_TEST(testTdf147919);
CPPUNIT_TEST(testTdf130165);
CPPUNIT_TEST(testTdf124781);
CPPUNIT_TEST(testTdf144914);
@@ -213,6 +215,66 @@ void checkFontAttributes( const SdrTextObj* pObj, ItemValue nVal, sal_uInt32 nId
}
+void SdOOXMLExportTest1::testTdf147919()
+{
+ sd::DrawDocShellRef xDocShRef
+ = loadURL(m_directories.getURLFromSrc(u"/sd/qa/unit/data/odp/tdf147919.odp"), ODP);
+ utl::TempFile tempFile;
+ xDocShRef = saveAndReload(xDocShRef.get(), PPTX, &tempFile);
+ xDocShRef->DoClose();
+
+ xmlDocUniquePtr pXmlDoc = parseExport(tempFile, "ppt/slides/slide1.xml");
+ assertXPath(pXmlDoc, "/p:sld/p:cSld/p:spTree/p:cxnSp[1]/p:spPr/a:prstGeom", "prst",
+ "bentConnector2");
+
+ assertXPath(pXmlDoc, "/p:sld/p:cSld/p:spTree/p:cxnSp[2]/p:spPr/a:prstGeom/a:avLst/a:gd", "name", "adj1");
+ assertXPath(pXmlDoc, "/p:sld/p:cSld/p:spTree/p:cxnSp[2]/p:spPr/a:prstGeom", "prst",
+ "bentConnector3");
+
+ assertXPath(pXmlDoc, "/p:sld/p:cSld/p:spTree/p:cxnSp[3]/p:spPr/a:xfrm", "flipH", "1");
+ assertXPath(pXmlDoc, "/p:sld/p:cSld/p:spTree/p:cxnSp[3]/p:spPr/a:xfrm", "rot", "16200000");
+ assertXPath(pXmlDoc, "/p:sld/p:cSld/p:spTree/p:cxnSp[3]/p:spPr/a:prstGeom/a:avLst/a:gd[1]", "name", "adj1");
+ assertXPath(pXmlDoc, "/p:sld/p:cSld/p:spTree/p:cxnSp[3]/p:spPr/a:prstGeom/a:avLst/a:gd[2]", "name", "adj2");
+ assertXPath(pXmlDoc, "/p:sld/p:cSld/p:spTree/p:cxnSp[3]/p:spPr/a:prstGeom", "prst",
+ "bentConnector4");
+
+ assertXPath(pXmlDoc, "/p:sld/p:cSld/p:spTree/p:cxnSp[4]/p:spPr/a:xfrm", "flipH", "1");
+ assertXPath(pXmlDoc, "/p:sld/p:cSld/p:spTree/p:cxnSp[4]/p:spPr/a:xfrm", "flipV", "1");
+ assertXPath(pXmlDoc, "/p:sld/p:cSld/p:spTree/p:cxnSp[4]/p:spPr/a:xfrm", "rot", "10800000");
+ assertXPath(pXmlDoc, "/p:sld/p:cSld/p:spTree/p:cxnSp[4]/p:spPr/a:prstGeom/a:avLst/a:gd[1]", "name", "adj1");
+ assertXPath(pXmlDoc, "/p:sld/p:cSld/p:spTree/p:cxnSp[4]/p:spPr/a:prstGeom/a:avLst/a:gd[2]", "name", "adj2");
+ assertXPath(pXmlDoc, "/p:sld/p:cSld/p:spTree/p:cxnSp[4]/p:spPr/a:prstGeom/a:avLst/a:gd[3]", "name", "adj3");
+ assertXPath(pXmlDoc, "/p:sld/p:cSld/p:spTree/p:cxnSp[4]/p:spPr/a:prstGeom", "prst",
+ "bentConnector5");
+
+ assertXPath(pXmlDoc, "/p:sld/p:cSld/p:spTree/p:cxnSp[5]/p:spPr/a:xfrm", "flipH", "1");
+ assertXPath(pXmlDoc, "/p:sld/p:cSld/p:spTree/p:cxnSp[5]/p:spPr/a:xfrm", "rot", "16200000");
+ assertXPath(pXmlDoc, "/p:sld/p:cSld/p:spTree/p:cxnSp[5]/p:spPr/a:prstGeom", "prst",
+ "curvedConnector2");
+
+ assertXPath(pXmlDoc, "/p:sld/p:cSld/p:spTree/p:cxnSp[6]/p:spPr/a:xfrm", "flipH", "1");
+ assertXPath(pXmlDoc, "/p:sld/p:cSld/p:spTree/p:cxnSp[6]/p:spPr/a:xfrm", "rot", "16200000");
+ assertXPath(pXmlDoc, "/p:sld/p:cSld/p:spTree/p:cxnSp[6]/p:spPr/a:prstGeom/a:avLst/a:gd", "name", "adj1");
+ assertXPath(pXmlDoc, "/p:sld/p:cSld/p:spTree/p:cxnSp[6]/p:spPr/a:prstGeom", "prst",
+ "curvedConnector3");
+
+ assertXPath(pXmlDoc, "/p:sld/p:cSld/p:spTree/p:cxnSp[7]/p:spPr/a:xfrm", "flipH", "1");
+ assertXPath(pXmlDoc, "/p:sld/p:cSld/p:spTree/p:cxnSp[7]/p:spPr/a:xfrm", "flipV", "1");
+ assertXPath(pXmlDoc, "/p:sld/p:cSld/p:spTree/p:cxnSp[7]/p:spPr/a:xfrm", "rot", "10800000");
+ assertXPath(pXmlDoc, "/p:sld/p:cSld/p:spTree/p:cxnSp[7]/p:spPr/a:prstGeom/a:avLst/a:gd[1]", "name", "adj1");
+ assertXPath(pXmlDoc, "/p:sld/p:cSld/p:spTree/p:cxnSp[7]/p:spPr/a:prstGeom/a:avLst/a:gd[2]", "name", "adj2");
+ assertXPath(pXmlDoc, "/p:sld/p:cSld/p:spTree/p:cxnSp[7]/p:spPr/a:prstGeom", "prst",
+ "curvedConnector4");
+
+ assertXPath(pXmlDoc, "/p:sld/p:cSld/p:spTree/p:cxnSp[8]/p:spPr/a:xfrm", "flipV", "1");
+ assertXPath(pXmlDoc, "/p:sld/p:cSld/p:spTree/p:cxnSp[8]/p:spPr/a:xfrm", "rot", "16200000");
+ assertXPath(pXmlDoc, "/p:sld/p:cSld/p:spTree/p:cxnSp[8]/p:spPr/a:prstGeom/a:avLst/a:gd[1]", "name", "adj1");
+ assertXPath(pXmlDoc, "/p:sld/p:cSld/p:spTree/p:cxnSp[8]/p:spPr/a:prstGeom/a:avLst/a:gd[2]", "name", "adj2");
+ assertXPath(pXmlDoc, "/p:sld/p:cSld/p:spTree/p:cxnSp[8]/p:spPr/a:prstGeom/a:avLst/a:gd[3]", "name", "adj3");
+ assertXPath(pXmlDoc, "/p:sld/p:cSld/p:spTree/p:cxnSp[8]/p:spPr/a:prstGeom", "prst",
+ "curvedConnector5");
+}
+
void SdOOXMLExportTest1::testTdf130165()
{
sd::DrawDocShellRef xDocShRef