include/oox/drawingml/shape.hxx | 6 oox/source/drawingml/diagram/diagramlayoutatoms.cxx | 107 ++++++++++++----- oox/source/drawingml/diagram/diagramlayoutatoms.hxx | 1 oox/source/drawingml/diagram/layoutatomvisitorbase.hxx | 4 oox/source/drawingml/diagram/layoutatomvisitors.cxx | 22 ++- oox/source/drawingml/diagram/layoutatomvisitors.hxx | 4 sd/qa/unit/data/pptx/smartart-org-chart2.pptx |binary sd/qa/unit/import-tests-smartart.cxx | 60 +++++++++ 8 files changed, 168 insertions(+), 36 deletions(-)
New commits: commit d4aa418fbfb9bd23e05fa739f20363bc299570d5 Author: Grzegorz Araminowicz <grzegorz.araminow...@collabora.com> AuthorDate: Sun Jul 7 14:12:05 2019 +0200 Commit: Miklos Vajna <vmik...@collabora.com> CommitDate: Wed Jul 10 12:20:01 2019 +0200 SmartArt: improve organization chart layout layout shapes in two steps: * first calculate vertical child shapes count for every shape (taking into accout hierBranch alg variable) * then actual layout using that count to calculate size for subtrees Change-Id: I2e5ca34ed3383aa9502c52511cc1fb2bee215572 Reviewed-on: https://gerrit.libreoffice.org/75195 Tested-by: Jenkins Reviewed-by: Miklos Vajna <vmik...@collabora.com> Reviewed-on: https://gerrit.libreoffice.org/75311 diff --git a/include/oox/drawingml/shape.hxx b/include/oox/drawingml/shape.hxx index 1f8b163b5d35..5aa6f00318a8 100644 --- a/include/oox/drawingml/shape.hxx +++ b/include/oox/drawingml/shape.hxx @@ -225,6 +225,9 @@ public: double getAspectRatio() const { return mfAspectRatio; } + void setVerticalShapesCount(sal_Int32 nVerticalShapesCount) { mnVerticalShapesCount = nVerticalShapesCount; } + sal_Int32 getVerticalShapesCount() const { return mnVerticalShapesCount; } + /// Changes reference semantics to value semantics for fill properties. void cloneFillProperties(); @@ -361,6 +364,9 @@ private: /// Aspect ratio for an in-diagram shape. double mfAspectRatio = 0; + + /// Number of child shapes to be layouted vertically inside org chart in-diagram shape. + sal_Int32 mnVerticalShapesCount = 0; }; } } diff --git a/oox/source/drawingml/diagram/diagramlayoutatoms.cxx b/oox/source/drawingml/diagram/diagramlayoutatoms.cxx index f3c3f52dce04..7f77880b28e8 100644 --- a/oox/source/drawingml/diagram/diagramlayoutatoms.cxx +++ b/oox/source/drawingml/diagram/diagramlayoutatoms.cxx @@ -418,6 +418,40 @@ sal_Int32 AlgAtom::getConnectorType() return oox::XML_rightArrow; } +sal_Int32 AlgAtom::getVerticalShapesCount(const ShapePtr& rShape) +{ + if (rShape->getChildren().empty()) + return (rShape->getSubType() != XML_conn) ? 1 : 0; + + sal_Int32 nDir = XML_fromL; + if (mnType == XML_hierRoot) + nDir = XML_fromT; + else if (maMap.count(XML_linDir)) + nDir = maMap.find(XML_linDir)->second; + + const sal_Int32 nSecDir = maMap.count(XML_secLinDir) ? maMap.find(XML_secLinDir)->second : 0; + + sal_Int32 nCount = 0; + if (nDir == XML_fromT || nDir == XML_fromB) + { + for (ShapePtr& pChild : rShape->getChildren()) + nCount += pChild->getVerticalShapesCount(); + } + else if ((nDir == XML_fromL || nDir == XML_fromR) && nSecDir == XML_fromT) + { + for (ShapePtr& pChild : rShape->getChildren()) + nCount += pChild->getVerticalShapesCount(); + nCount = (nCount + 1) / 2; + } + else + { + for (ShapePtr& pChild : rShape->getChildren()) + nCount = std::max(nCount, pChild->getVerticalShapesCount()); + } + + return nCount; +} + void AlgAtom::layoutShape( const ShapePtr& rShape, const std::vector<Constraint>& rConstraints ) { @@ -660,6 +694,9 @@ void AlgAtom::layoutShape( const ShapePtr& rShape, case XML_hierChild: case XML_hierRoot: { + if (rShape->getChildren().empty() || rShape->getSize().Width == 0 || rShape->getSize().Height == 0) + break; + // hierRoot is the manager -> employees vertical linear path, // hierChild is the first employee -> last employee horizontal // linear path. @@ -669,31 +706,20 @@ void AlgAtom::layoutShape( const ShapePtr& rShape, else if (maMap.count(XML_linDir)) nDir = maMap.find(XML_linDir)->second; - if (rShape->getChildren().empty() || rShape->getSize().Width == 0 - || rShape->getSize().Height == 0) - break; + const sal_Int32 nSecDir = maMap.count(XML_secLinDir) ? maMap.find(XML_secLinDir)->second : 0; sal_Int32 nCount = rShape->getChildren().size(); if (mnType == XML_hierChild) { - // Connectors should not influence the size of non-connect - // shapes. + // Connectors should not influence the size of non-connect shapes. nCount = std::count_if( rShape->getChildren().begin(), rShape->getChildren().end(), [](const ShapePtr& pShape) { return pShape->getSubType() != XML_conn; }); } - // A manager node's height should be independent from if it has - // assistants and employees, compensate for that. - bool bTop = mnType == XML_hierRoot && rShape->getInternalName() == "hierRoot1"; - - // Add spacing, so connectors have a chance to be visible. - double fSpace = (nCount > 1 || bTop) ? 0.3 : 0; - - double fHeightScale = 1.0; - if (mnType == XML_hierRoot && nCount < 3 && bTop) - fHeightScale = fHeightScale * nCount / 3; + const double fSpaceWidth = 0.1; + const double fSpaceHeight = 0.3; if (mnType == XML_hierRoot && nCount == 3) { @@ -704,18 +730,31 @@ void AlgAtom::layoutShape( const ShapePtr& rShape, std::swap(rChildren[1], rChildren[2]); } + sal_Int32 nHorizontalShapesCount = 1; + if (nSecDir == XML_fromT) + nHorizontalShapesCount = 2; + else if (nDir == XML_fromL || nDir == XML_fromR) + nHorizontalShapesCount = nCount; + awt::Size aChildSize = rShape->getSize(); - if (nDir == XML_fromT) - { - aChildSize.Height /= (nCount + nCount * fSpace); - } - else - aChildSize.Width /= nCount; - aChildSize.Height *= fHeightScale; + aChildSize.Height /= (rShape->getVerticalShapesCount() + (rShape->getVerticalShapesCount() - 1) * fSpaceHeight); + aChildSize.Width /= (nHorizontalShapesCount + (nHorizontalShapesCount - 1) * fSpaceWidth); + awt::Size aConnectorSize = aChildSize; aConnectorSize.Width = 1; awt::Point aChildPos(0, 0); + + // indent children to show they are descendants, not siblings + if (mnType == XML_hierChild && nHorizontalShapesCount == 1) + { + const double fChildIndent = 0.1; + aChildPos.X = aChildSize.Width * fChildIndent; + aChildSize.Width *= (1 - 2 * fChildIndent); + } + + sal_Int32 nIdx = 0; + sal_Int32 nRowHeight = 0; for (auto& pChild : rShape->getChildren()) { pChild->setPosition(aChildPos); @@ -729,13 +768,27 @@ void AlgAtom::layoutShape( const ShapePtr& rShape, continue; } - pChild->setSize(aChildSize); - pChild->setChildSize(aChildSize); + awt::Size aCurrSize = aChildSize; + aCurrSize.Height *= pChild->getVerticalShapesCount() + (pChild->getVerticalShapesCount() - 1) * fSpaceHeight; - if (nDir == XML_fromT) - aChildPos.Y += aChildSize.Height + aChildSize.Height * fSpace; + pChild->setSize(aCurrSize); + pChild->setChildSize(aCurrSize); + + if (nDir == XML_fromT || nDir == XML_fromB) + aChildPos.Y += aCurrSize.Height + aChildSize.Height * fSpaceHeight; else - aChildPos.X += aChildSize.Width; + aChildPos.X += aCurrSize.Width + aCurrSize.Width * fSpaceWidth; + + nRowHeight = std::max(nRowHeight, aCurrSize.Height); + + if (nSecDir == XML_fromT && nIdx % 2 == 1) + { + aChildPos.X = 0; + aChildPos.Y += nRowHeight + aChildSize.Height * fSpaceHeight; + nRowHeight = 0; + } + + nIdx++; } break; diff --git a/oox/source/drawingml/diagram/diagramlayoutatoms.hxx b/oox/source/drawingml/diagram/diagramlayoutatoms.hxx index d77a98135c02..91028971473e 100644 --- a/oox/source/drawingml/diagram/diagramlayoutatoms.hxx +++ b/oox/source/drawingml/diagram/diagramlayoutatoms.hxx @@ -160,6 +160,7 @@ public: { mnType = nToken; } void addParam( sal_Int32 nType, sal_Int32 nVal ) { maMap[nType]=nVal; } + sal_Int32 getVerticalShapesCount(const ShapePtr& rShape); void layoutShape( const ShapePtr& rShape, const std::vector<Constraint>& rConstraints ); diff --git a/oox/source/drawingml/diagram/layoutatomvisitorbase.hxx b/oox/source/drawingml/diagram/layoutatomvisitorbase.hxx index 7007cf283070..ff12f82e2f96 100644 --- a/oox/source/drawingml/diagram/layoutatomvisitorbase.hxx +++ b/oox/source/drawingml/diagram/layoutatomvisitorbase.hxx @@ -50,7 +50,8 @@ public: mpCurrentNode(pRootPoint), mnCurrIdx(0), mnCurrStep(0), - mnCurrCnt(0) + mnCurrCnt(0), + meLookFor(LAYOUT_NODE) {} void defaultVisit(LayoutAtom const& rAtom); @@ -67,6 +68,7 @@ protected: sal_Int32 mnCurrIdx; sal_Int32 mnCurrStep; sal_Int32 mnCurrCnt; + enum {LAYOUT_NODE, CONSTRAINT, ALGORITHM} meLookFor; }; class ShallowPresNameVisitor : public LayoutAtomVisitorBase diff --git a/oox/source/drawingml/diagram/layoutatomvisitors.cxx b/oox/source/drawingml/diagram/layoutatomvisitors.cxx index 31b853664577..4bfadc3affe8 100644 --- a/oox/source/drawingml/diagram/layoutatomvisitors.cxx +++ b/oox/source/drawingml/diagram/layoutatomvisitors.cxx @@ -37,11 +37,18 @@ void ShapeCreationVisitor::visit(ConstraintAtom& /*rAtom*/) void ShapeCreationVisitor::visit(AlgAtom& rAtom) { - mpParentShape->setAspectRatio(rAtom.getAspectRatio()); + if (meLookFor == ALGORITHM) + { + mpParentShape->setAspectRatio(rAtom.getAspectRatio()); + mpParentShape->setVerticalShapesCount(rAtom.getVerticalShapesCount(mpParentShape)); + } } void ShapeCreationVisitor::visit(LayoutNode& rAtom) { + if (meLookFor != LAYOUT_NODE) + return; + // stop processing if it's not a child of previous LayoutNode const DiagramData::PointsNameMap::const_iterator aDataNode = mrDgm.getData()->getPointsPresNameMap().find(rAtom.getName()); @@ -108,17 +115,22 @@ void ShapeCreationVisitor::visit(LayoutNode& rAtom) mpParentShape=pCurrParent; // process children + meLookFor = LAYOUT_NODE; defaultVisit(rAtom); - // restore parent - mpParentShape=pPreviousParent; - mpCurrentNode = pPreviousNode; - // remove unneeded empty group shapes pCurrParent->getChildren().erase( std::remove_if(pCurrParent->getChildren().begin(), pCurrParent->getChildren().end(), [] (const ShapePtr & aChild) { return aChild->getServiceName() == "com.sun.star.drawing.GroupShape" && aChild->getChildren().empty(); }), pCurrParent->getChildren().end()); + + meLookFor = ALGORITHM; + defaultVisit(rAtom); + meLookFor = LAYOUT_NODE; + + // restore parent + mpParentShape=pPreviousParent; + mpCurrentNode = pPreviousNode; } void ShapeCreationVisitor::visit(ShapeAtom& /*rAtom*/) diff --git a/oox/source/drawingml/diagram/layoutatomvisitors.hxx b/oox/source/drawingml/diagram/layoutatomvisitors.hxx index 2950fb01a17c..656f61d79e6a 100644 --- a/oox/source/drawingml/diagram/layoutatomvisitors.hxx +++ b/oox/source/drawingml/diagram/layoutatomvisitors.hxx @@ -74,8 +74,7 @@ class ShapeLayoutingVisitor : public LayoutAtomVisitorBase { public: ShapeLayoutingVisitor(const Diagram& rDgm, const dgm::Point* pRootPoint) : - LayoutAtomVisitorBase(rDgm, pRootPoint), - meLookFor(LAYOUT_NODE) + LayoutAtomVisitorBase(rDgm, pRootPoint) {} using LayoutAtomVisitorBase::visit; @@ -86,7 +85,6 @@ public: private: std::vector<Constraint> maConstraints; - enum {LAYOUT_NODE, CONSTRAINT, ALGORITHM} meLookFor; }; } } diff --git a/sd/qa/unit/data/pptx/smartart-org-chart2.pptx b/sd/qa/unit/data/pptx/smartart-org-chart2.pptx new file mode 100644 index 000000000000..5e2be2167976 Binary files /dev/null and b/sd/qa/unit/data/pptx/smartart-org-chart2.pptx differ diff --git a/sd/qa/unit/import-tests-smartart.cxx b/sd/qa/unit/import-tests-smartart.cxx index e40b364d6b58..ffb3a8add28a 100644 --- a/sd/qa/unit/import-tests-smartart.cxx +++ b/sd/qa/unit/import-tests-smartart.cxx @@ -37,6 +37,28 @@ uno::Reference<drawing::XShape> getChildShape(const uno::Reference<drawing::XSha return xRet; } + +uno::Reference<drawing::XShape> findChildShapeByText(const uno::Reference<drawing::XShape>& xShape, + const OUString& sText) +{ + uno::Reference<text::XText> xText(xShape, uno::UNO_QUERY); + if (xText.is() && xText->getString() == sText) + return xShape; + + uno::Reference<container::XIndexAccess> xGroup(xShape, uno::UNO_QUERY); + if (!xGroup.is()) + return uno::Reference<drawing::XShape>(); + + for (sal_Int32 i = 0; i < xGroup->getCount(); i++) + { + uno::Reference<drawing::XShape> xChildShape(xGroup->getByIndex(i), uno::UNO_QUERY); + uno::Reference<drawing::XShape> xReturnShape = findChildShapeByText(xChildShape, sText); + if (xReturnShape.is()) + return xReturnShape; + } + + return uno::Reference<drawing::XShape>(); +} } class SdImportTestSmartArt : public SdModelTestBase @@ -81,6 +103,7 @@ public: void testBulletList(); void testRecursion(); void testDataFollow(); + void testOrgChart2(); CPPUNIT_TEST_SUITE(SdImportTestSmartArt); @@ -123,6 +146,7 @@ public: CPPUNIT_TEST(testBulletList); CPPUNIT_TEST(testRecursion); CPPUNIT_TEST(testDataFollow); + CPPUNIT_TEST(testOrgChart2); CPPUNIT_TEST_SUITE_END(); }; @@ -1357,6 +1381,42 @@ void SdImportTestSmartArt::testDataFollow() xDocShRef->DoClose(); } +void SdImportTestSmartArt::testOrgChart2() +{ + sd::DrawDocShellRef xDocShRef = loadURL(m_directories.getURLFromSrc("/sd/qa/unit/data/pptx/smartart-org-chart2.pptx"), PPTX); + uno::Reference<drawing::XShape> xGroup(getShapeFromPage(0, 0, xDocShRef), uno::UNO_QUERY); + + uno::Reference<drawing::XShape> xShapeC1 = findChildShapeByText(xGroup, "C1"); + uno::Reference<drawing::XShape> xShapeC2 = findChildShapeByText(xGroup, "C2"); + uno::Reference<drawing::XShape> xShapeC3 = findChildShapeByText(xGroup, "C3"); + uno::Reference<drawing::XShape> xShapeC4 = findChildShapeByText(xGroup, "C4"); + uno::Reference<drawing::XShape> xShapeD1 = findChildShapeByText(xGroup, "D1"); + uno::Reference<drawing::XShape> xShapeD2 = findChildShapeByText(xGroup, "D2"); + + CPPUNIT_ASSERT(xShapeC1.is()); + CPPUNIT_ASSERT(xShapeC2.is()); + CPPUNIT_ASSERT(xShapeC3.is()); + CPPUNIT_ASSERT(xShapeC4.is()); + CPPUNIT_ASSERT(xShapeD1.is()); + CPPUNIT_ASSERT(xShapeD2.is()); + + CPPUNIT_ASSERT_EQUAL(xShapeC1->getPosition().Y, xShapeC2->getPosition().Y); + CPPUNIT_ASSERT_GREATEREQUAL(xShapeC1->getPosition().X + xShapeC1->getSize().Width, xShapeC2->getPosition().X); + + CPPUNIT_ASSERT_EQUAL(xShapeC3->getPosition().X, xShapeC4->getPosition().X); + CPPUNIT_ASSERT_GREATEREQUAL(xShapeC3->getPosition().Y + xShapeC3->getSize().Height, xShapeC4->getPosition().Y); + + CPPUNIT_ASSERT_EQUAL(xShapeD1->getPosition().X, xShapeD2->getPosition().X); + CPPUNIT_ASSERT_GREATEREQUAL(xShapeD1->getPosition().Y + xShapeD1->getSize().Height, xShapeD2->getPosition().Y); + + CPPUNIT_ASSERT_GREATEREQUAL(xShapeC2->getPosition().X, xShapeD1->getPosition().X); + CPPUNIT_ASSERT_GREATEREQUAL(xShapeC2->getPosition().Y + xShapeC2->getSize().Height, xShapeD1->getPosition().Y); + + CPPUNIT_ASSERT_GREATEREQUAL(xShapeD1->getPosition().X + xShapeD1->getSize().Width, xShapeC4->getPosition().X); + + xDocShRef->DoClose(); +} + CPPUNIT_TEST_SUITE_REGISTRATION(SdImportTestSmartArt); CPPUNIT_PLUGIN_IMPLEMENT(); _______________________________________________ Libreoffice-commits mailing list libreoffice-comm...@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/libreoffice-commits