summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/oox/drawingml/shape.hxx6
-rw-r--r--oox/source/drawingml/diagram/diagramlayoutatoms.cxx107
-rw-r--r--oox/source/drawingml/diagram/diagramlayoutatoms.hxx1
-rw-r--r--oox/source/drawingml/diagram/layoutatomvisitorbase.hxx4
-rw-r--r--oox/source/drawingml/diagram/layoutatomvisitors.cxx22
-rw-r--r--oox/source/drawingml/diagram/layoutatomvisitors.hxx4
-rw-r--r--sd/qa/unit/data/pptx/smartart-org-chart2.pptxbin0 -> 64524 bytes
-rw-r--r--sd/qa/unit/import-tests-smartart.cxx60
8 files changed, 168 insertions, 36 deletions
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
--- /dev/null
+++ b/sd/qa/unit/data/pptx/smartart-org-chart2.pptx
Binary files 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();