diff options
author | Tibor Nagy <nagy.tibor2@nisz.hu> | 2022-04-29 14:08:17 +0200 |
---|---|---|
committer | László Németh <nemeth@numbertext.org> | 2022-05-04 12:46:36 +0200 |
commit | 1f8c6efbfea10997f188962d036f5c7db4c13f8a (patch) | |
tree | 08caa6674a06b64b3bbfbad76e638264416322bf /oox | |
parent | d1ac8df139a2a65db45d1970ccc0b80e17d827f6 (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: I615a20f3ba3c5d03ae73d87848dc41e9ecd29ae5
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/133635
Tested-by: László Németh <nemeth@numbertext.org>
Reviewed-by: László Németh <nemeth@numbertext.org>
Diffstat (limited to 'oox')
-rw-r--r-- | oox/source/export/shapes.cxx | 306 |
1 files changed, 278 insertions, 28 deletions
diff --git a/oox/source/export/shapes.cxx b/oox/source/export/shapes.cxx index 3308d5ab4492..de1527ecda0f 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[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 ); |