summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/oox/drawingml/shape.hxx14
-rw-r--r--oox/source/drawingml/diagram/diagramlayoutatoms.cxx176
-rw-r--r--oox/source/drawingml/diagram/diagramlayoutatoms.hxx9
-rw-r--r--oox/source/drawingml/diagram/layoutatomvisitors.cxx48
-rw-r--r--oox/source/drawingml/diagram/layoutatomvisitors.hxx2
-rw-r--r--oox/source/drawingml/diagram/layoutnodecontext.cxx2
-rw-r--r--oox/source/drawingml/shape.cxx2
-rw-r--r--sd/qa/unit/data/pptx/smartart-accent-process.pptxbin0 -> 46326 bytes
-rw-r--r--sd/qa/unit/data/pptx/smartart-continuous-block-process.pptxbin0 -> 46204 bytes
-rw-r--r--sd/qa/unit/import-tests-smartart.cxx126
10 files changed, 352 insertions, 27 deletions
diff --git a/include/oox/drawingml/shape.hxx b/include/oox/drawingml/shape.hxx
index 6028a11c2bc0..e04a58beb4a6 100644
--- a/include/oox/drawingml/shape.hxx
+++ b/include/oox/drawingml/shape.hxx
@@ -214,6 +214,14 @@ public:
const LinkedTxbxAttr& getLinkedTxbxAttributes() { return maLinkedTxbxAttr; };
bool isLinkedTxbx() { return mbHasLinkedTxbx; };
+ void setZOrder(sal_Int32 nZOrder) { mnZOrder = nZOrder; }
+
+ sal_Int32 getZOrder() const { return mnZOrder; }
+
+ void setZOrderOff(sal_Int32 nZOrderOff) { mnZOrderOff = nZOrderOff; }
+
+ sal_Int32 getZOrderOff() const { return mnZOrderOff; }
+
protected:
css::uno::Reference< css::drawing::XShape > const &
@@ -327,6 +335,12 @@ private:
bool mbHasLinkedTxbx; // this text box has linked text box ?
css::uno::Sequence<css::beans::PropertyValue> maDiagramDoms;
+
+ /// Z-Order.
+ sal_Int32 mnZOrder = 0;
+
+ /// Z-Order offset.
+ sal_Int32 mnZOrderOff = 0;
};
} }
diff --git a/oox/source/drawingml/diagram/diagramlayoutatoms.cxx b/oox/source/drawingml/diagram/diagramlayoutatoms.cxx
index c1aaf6e07025..502470933e8f 100644
--- a/oox/source/drawingml/diagram/diagramlayoutatoms.cxx
+++ b/oox/source/drawingml/diagram/diagramlayoutatoms.cxx
@@ -54,6 +54,50 @@ oox::OptValue<sal_Int32> findProperty(const oox::drawingml::LayoutPropertyMap& r
return oRet;
}
+
+/**
+ * Determines if nUnit is a font unit (measured in points) or not (measured in
+ * millimeters).
+ */
+bool isFontUnit(sal_Int32 nUnit)
+{
+ return nUnit == oox::XML_primFontSz || nUnit == oox::XML_secFontSz;
+}
+
+/// Determines the connector shape type from a linear alg.
+sal_Int32 getConnectorType(const oox::drawingml::LayoutNode* pNode)
+{
+ sal_Int32 nType = oox::XML_rightArrow;
+
+ if (!pNode)
+ return nType;
+
+ for (const auto& pChild : pNode->getChildren())
+ {
+ auto pAlgAtom = dynamic_cast<oox::drawingml::AlgAtom*>(pChild.get());
+ if (!pAlgAtom)
+ continue;
+
+ if (pAlgAtom->getType() != oox::XML_lin)
+ continue;
+
+ sal_Int32 nDir = oox::XML_fromL;
+ if (pAlgAtom->getMap().count(oox::XML_linDir))
+ nDir = pAlgAtom->getMap().find(oox::XML_linDir)->second;
+
+ switch (nDir)
+ {
+ case oox::XML_fromL:
+ nType = oox::XML_rightArrow;
+ break;
+ case oox::XML_fromR:
+ nType = oox::XML_leftArrow;
+ break;
+ }
+ }
+
+ return nType;
+}
}
namespace oox { namespace drawingml {
@@ -269,13 +313,15 @@ void ConstraintAtom::accept( LayoutAtomVisitor& rVisitor )
rVisitor.visit(*this);
}
-void ConstraintAtom::parseConstraint(std::vector<Constraint>& rConstraints) const
+void ConstraintAtom::parseConstraint(std::vector<Constraint>& rConstraints,
+ bool bRequireForName) const
{
+ if (bRequireForName && maConstraint.msForName.isEmpty())
+ return;
+
// accepting only basic equality constraints
- if (!maConstraint.msForName.isEmpty() &&
- (maConstraint.mnOperator == XML_none || maConstraint.mnOperator == XML_equ) &&
- maConstraint.mnType != XML_none &&
- maConstraint.mfValue == 0)
+ if ((maConstraint.mnOperator == XML_none || maConstraint.mnOperator == XML_equ)
+ && maConstraint.mnType != XML_none)
{
rConstraints.push_back(maConstraint);
}
@@ -290,7 +336,7 @@ void AlgAtom::layoutShape( const ShapePtr& rShape,
const std::vector<Constraint>& rOwnConstraints ) const
{
// Algorithm result may depend on the parent constraints as well.
- std::vector<Constraint> aParentConstraints;
+ std::vector<Constraint> aMergedConstraints;
const LayoutNode* pParent = getLayoutNode().getParentLayoutNode();
if (pParent)
{
@@ -298,10 +344,12 @@ void AlgAtom::layoutShape( const ShapePtr& rShape,
{
auto pConstraintAtom = dynamic_cast<ConstraintAtom*>(pChild.get());
if (pConstraintAtom)
- pConstraintAtom->parseConstraint(aParentConstraints);
+ pConstraintAtom->parseConstraint(aMergedConstraints, /*bRequireForName=*/true);
}
}
- const std::vector<Constraint>& rConstraints = rOwnConstraints.empty() ? aParentConstraints : rOwnConstraints;
+ aMergedConstraints.insert(aMergedConstraints.end(), rOwnConstraints.begin(),
+ rOwnConstraints.end());
+ const std::vector<Constraint>& rConstraints = aMergedConstraints;
switch(mnType)
{
@@ -327,7 +375,19 @@ void AlgAtom::layoutShape( const ShapePtr& rShape,
if (aRefType != aRef->second.end())
aProperties[rConstr.msForName][rConstr.mnType] = aRefType->second * rConstr.mfFactor;
else
- aProperties[rConstr.msForName][rConstr.mnType] = 0; // TODO: val
+ {
+ // Values are never in EMU, while oox::drawingml::Shape
+ // position and size are always in EMU.
+ double fUnitFactor = 0;
+ if (isFontUnit(rConstr.mnRefType))
+ // Points -> EMU.
+ fUnitFactor = EMU_PER_PT;
+ else
+ // Millimeters -> EMU.
+ fUnitFactor = EMU_PER_HMM * 100;
+ aProperties[rConstr.msForName][rConstr.mnType]
+ = rConstr.mfValue * fUnitFactor;
+ }
}
}
@@ -376,7 +436,54 @@ void AlgAtom::layoutShape( const ShapePtr& rShape,
}
case XML_conn:
+ {
+ if (rShape->getSubType() == XML_conn)
+ {
+ // There is no shape type "conn", replace it by an arrow based
+ // on the direction of the parent linear layout.
+ sal_Int32 nType = getConnectorType(pParent);
+
+ rShape->setSubType(nType);
+ rShape->getCustomShapeProperties()->setShapePresetType(nType);
+ }
+
+ // Parse constraints to adjust the size.
+ std::vector<Constraint> aDirectConstraints;
+ const LayoutNode& rLayoutNode = getLayoutNode();
+ for (const auto& pChild : rLayoutNode.getChildren())
+ {
+ auto pConstraintAtom = dynamic_cast<ConstraintAtom*>(pChild.get());
+ if (pConstraintAtom)
+ pConstraintAtom->parseConstraint(aDirectConstraints, /*bRequireForName=*/false);
+ }
+
+ LayoutPropertyMap aProperties;
+ LayoutProperty& rParent = aProperties[""];
+ rParent[XML_w] = rShape->getSize().Width;
+ rParent[XML_h] = rShape->getSize().Height;
+ rParent[XML_l] = 0;
+ rParent[XML_t] = 0;
+ rParent[XML_r] = rShape->getSize().Width;
+ rParent[XML_b] = rShape->getSize().Height;
+ for (const auto& rConstr : aDirectConstraints)
+ {
+ const LayoutPropertyMap::const_iterator aRef
+ = aProperties.find(rConstr.msRefForName);
+ if (aRef != aProperties.end())
+ {
+ const LayoutProperty::const_iterator aRefType
+ = aRef->second.find(rConstr.mnRefType);
+ if (aRefType != aRef->second.end())
+ aProperties[rConstr.msForName][rConstr.mnType]
+ = aRefType->second * rConstr.mfFactor;
+ }
+ }
+ awt::Size aSize;
+ aSize.Width = rParent[XML_w];
+ aSize.Height = rParent[XML_h];
+ rShape->setSize(aSize);
break;
+ }
case XML_cycle:
{
@@ -428,22 +535,9 @@ void AlgAtom::layoutShape( const ShapePtr& rShape,
const sal_Int32 nIncX = nDir==XML_fromL ? 1 : (nDir==XML_fromR ? -1 : 0);
const sal_Int32 nIncY = nDir==XML_fromT ? 1 : (nDir==XML_fromB ? -1 : 0);
- // TODO: get values from constraints
sal_Int32 nCount = rShape->getChildren().size();
double fSpace = 0.3;
- awt::Size aChildSize = rShape->getSize();
- if (nDir == XML_fromL || nDir == XML_fromR)
- aChildSize.Width /= (nCount + (nCount-1)*fSpace);
- else if (nDir == XML_fromT || nDir == XML_fromB)
- aChildSize.Height /= (nCount + (nCount-1)*fSpace);
-
- awt::Point aCurrPos(0, 0);
- if (nIncX == -1)
- aCurrPos.X = rShape->getSize().Width - aChildSize.Width;
- if (nIncY == -1)
- aCurrPos.Y = rShape->getSize().Height - aChildSize.Height;
-
// Find out which contraint is relevant for which (internal) name.
LayoutPropertyMap aProperties;
for (const auto& rConstraint : rConstraints)
@@ -454,8 +548,24 @@ void AlgAtom::layoutShape( const ShapePtr& rShape,
LayoutProperty& rProperty = aProperties[rConstraint.msForName];
if (rConstraint.mnType == XML_w)
rProperty[XML_w] = rShape->getSize().Width * rConstraint.mfFactor;
+
+ // TODO: get values from differently named constraints as well
+ if (rConstraint.msForName == "sibTrans" && rConstraint.mnType == XML_w)
+ fSpace = rConstraint.mfFactor;
}
+ awt::Size aChildSize = rShape->getSize();
+ if (nDir == XML_fromL || nDir == XML_fromR)
+ aChildSize.Width /= (nCount + (nCount-1)*fSpace);
+ else if (nDir == XML_fromT || nDir == XML_fromB)
+ aChildSize.Height /= (nCount + (nCount-1)*fSpace);
+
+ awt::Point aCurrPos(0, 0);
+ if (nIncX == -1)
+ aCurrPos.X = rShape->getSize().Width - aChildSize.Width;
+ if (nIncY == -1)
+ aCurrPos.Y = rShape->getSize().Height - aChildSize.Height;
+
// See if children requested more than 100% space in total: scale
// down in that case.
sal_Int32 nTotalWidth = 0;
@@ -500,7 +610,7 @@ void AlgAtom::layoutShape( const ShapePtr& rShape,
aSize.Width *= fWidthScale;
aCurrShape->setSize(aSize);
- aCurrShape->setChildSize(aChildSize);
+ aCurrShape->setChildSize(aSize);
aCurrPos.X += nIncX * (aSize.Width + fSpace*aSize.Width);
aCurrPos.Y += nIncY * (aChildSize.Height + fSpace*aChildSize.Height);
}
@@ -711,13 +821,27 @@ void AlgAtom::layoutShape( const ShapePtr& rShape,
}
ParamMap::const_iterator aBulletLvl = maMap.find(XML_stBulletLvl);
+ int nStartBulletsAtLevel = 0;
if (aBulletLvl != maMap.end())
+ {
nBaseLevel -= aBulletLvl->second;
+ nStartBulletsAtLevel = aBulletLvl->second;
+ }
for (auto & aParagraph : pTextBody->getParagraphs())
{
sal_Int32 nLevel = aParagraph->getProperties().getLevel();
aParagraph->getProperties().setLevel(nLevel - nBaseLevel);
+ if (nStartBulletsAtLevel > 0 && nLevel >= nStartBulletsAtLevel)
+ {
+ // It is not possible to change the bullet style for text.
+ sal_Int32 nLeftMargin = 285750 * (nLevel - nStartBulletsAtLevel) / EMU_PER_HMM;
+ aParagraph->getProperties().getParaLeftMargin() = nLeftMargin;
+ aParagraph->getProperties().getFirstLineIndentation() = -285750 / EMU_PER_HMM;
+ OUString aBulletChar = OUString::fromUtf8(u8"•");
+ aParagraph->getProperties().getBulletList().setBulletChar(aBulletChar);
+ aParagraph->getProperties().getBulletList().setSuffixNone();
+ }
}
// explicit alignment
@@ -821,8 +945,10 @@ bool LayoutNode::setupShape( const ShapePtr& rShape, const dgm::Point* pPresNode
if( aVecIter->second != -1 )
rPara.getProperties().setLevel(aVecIter->second);
- rPara.addRun(
- aDataNode2->second->mpShape->getTextBody()->getParagraphs().front()->getRuns().front());
+ std::shared_ptr<TextParagraph> pSourceParagraph
+ = aDataNode2->second->mpShape->getTextBody()->getParagraphs().front();
+ for (const auto& pRun : pSourceParagraph->getRuns())
+ rPara.addRun(pRun);
rPara.getProperties().apply(
aDataNode2->second->mpShape->getTextBody()->getParagraphs().front()->getProperties());
}
diff --git a/oox/source/drawingml/diagram/diagramlayoutatoms.hxx b/oox/source/drawingml/diagram/diagramlayoutatoms.hxx
index 3d4d9c05aae2..500495b6f2ca 100644
--- a/oox/source/drawingml/diagram/diagramlayoutatoms.hxx
+++ b/oox/source/drawingml/diagram/diagramlayoutatoms.hxx
@@ -141,7 +141,7 @@ public:
virtual void accept( LayoutAtomVisitor& ) override;
Constraint& getConstraint()
{ return maConstraint; }
- void parseConstraint(std::vector<Constraint>& rConstraints) const;
+ void parseConstraint(std::vector<Constraint>& rConstraints, bool bRequireForName) const;
private:
Constraint maConstraint;
};
@@ -162,6 +162,13 @@ public:
{ maMap[nType]=nVal; }
void layoutShape( const ShapePtr& rShape,
const std::vector<Constraint>& rConstraints ) const;
+
+ /// Gives access to <dgm:alg type="..."/>.
+ sal_Int32 getType() const { return mnType; }
+
+ /// Gives access to <dgm:param type="..." val="..."/>.
+ const ParamMap& getMap() const { return maMap; }
+
private:
sal_Int32 mnType;
ParamMap maMap;
diff --git a/oox/source/drawingml/diagram/layoutatomvisitors.cxx b/oox/source/drawingml/diagram/layoutatomvisitors.cxx
index ce8e6ab72b9e..49a664c1e821 100644
--- a/oox/source/drawingml/diagram/layoutatomvisitors.cxx
+++ b/oox/source/drawingml/diagram/layoutatomvisitors.cxx
@@ -46,6 +46,14 @@ void ShapeCreationVisitor::visit(AlgAtom& rAtom)
void ShapeCreationVisitor::visit(ForEachAtom& rAtom)
{
+ if (rAtom.iterator().mnAxis == XML_followSib)
+ {
+ // If the axis is the follow sibling, then the last atom should not be
+ // visited.
+ if (mnCurrIdx + mnCurrStep >= mnCurrCnt)
+ return;
+ }
+
const std::vector<LayoutAtomPtr>& rChildren=rAtom.getChildren();
sal_Int32 nChildren=1;
@@ -65,7 +73,11 @@ void ShapeCreationVisitor::visit(ForEachAtom& rAtom)
rAtom.iterator().mnCnt==-1 ? nChildren : rAtom.iterator().mnCnt);
const sal_Int32 nOldIdx=mnCurrIdx;
+ const sal_Int32 nOldStep = mnCurrStep;
+ const sal_Int32 nOldCnt = mnCurrCnt;
const sal_Int32 nStep=rAtom.iterator().mnStep;
+ mnCurrStep = nStep;
+ mnCurrCnt = nCnt;
for( mnCurrIdx=0; mnCurrIdx<nCnt && nStep>0; mnCurrIdx+=nStep )
{
// TODO there is likely some conditions
@@ -75,6 +87,8 @@ void ShapeCreationVisitor::visit(ForEachAtom& rAtom)
// and restore idx
mnCurrIdx = nOldIdx;
+ mnCurrStep = nOldStep;
+ mnCurrCnt = nOldCnt;
}
void ShapeCreationVisitor::visit(ConditionAtom& rAtom)
@@ -166,6 +180,38 @@ void ShapeCreationVisitor::visit(LayoutNode& rAtom)
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());
+
+ // Offset the children from their default z-order stacking, if necessary.
+ std::vector<ShapePtr>& rChildren = pCurrParent->getChildren();
+ for (size_t i = 0; i < rChildren.size(); ++i)
+ rChildren[i]->setZOrder(i);
+
+ for (size_t i = 0; i < rChildren.size(); ++i)
+ {
+ const ShapePtr& pChild = rChildren[i];
+ sal_Int32 nZOrderOff = pChild->getZOrderOff();
+ if (nZOrderOff <= 0)
+ continue;
+
+ // Increase my ZOrder by nZOrderOff.
+ pChild->setZOrder(pChild->getZOrder() + nZOrderOff);
+ pChild->setZOrderOff(0);
+
+ for (sal_Int32 j = 0; j < nZOrderOff; ++j)
+ {
+ size_t nIndex = i + j + 1;
+ if (nIndex >= rChildren.size())
+ break;
+
+ // Decrease the ZOrder of the next nZOrderOff elements by one.
+ const ShapePtr& pNext = rChildren[nIndex];
+ pNext->setZOrder(pNext->getZOrder() - 1);
+ }
+ }
+
+ // Now that the ZOrders are adjusted, sort the children.
+ std::sort(rChildren.begin(), rChildren.end(),
+ [](const ShapePtr& a, const ShapePtr& b) { return a->getZOrder() < b->getZOrder(); });
}
void ShapeCreationVisitor::visit(ShapeAtom& /*rAtom*/)
@@ -235,7 +281,7 @@ void ShapeLayoutingVisitor::defaultVisit(LayoutAtom const & rAtom)
void ShapeLayoutingVisitor::visit(ConstraintAtom& rAtom)
{
if (meLookFor == CONSTRAINT)
- rAtom.parseConstraint(maConstraints);
+ rAtom.parseConstraint(maConstraints, /*bRequireForName=*/true);
}
void ShapeLayoutingVisitor::visit(AlgAtom& rAtom)
diff --git a/oox/source/drawingml/diagram/layoutatomvisitors.hxx b/oox/source/drawingml/diagram/layoutatomvisitors.hxx
index 299739186315..f395f6a68668 100644
--- a/oox/source/drawingml/diagram/layoutatomvisitors.hxx
+++ b/oox/source/drawingml/diagram/layoutatomvisitors.hxx
@@ -33,6 +33,8 @@ class ShapeCreationVisitor : public LayoutAtomVisitor
ShapePtr mpParentShape;
const Diagram& mrDgm;
sal_Int32 mnCurrIdx;
+ sal_Int32 mnCurrStep = 0;
+ sal_Int32 mnCurrCnt = 0;
const dgm::Point* mpCurrentNode;
void defaultVisit(LayoutAtom const & rAtom);
diff --git a/oox/source/drawingml/diagram/layoutnodecontext.cxx b/oox/source/drawingml/diagram/layoutnodecontext.cxx
index 257f490f7c7c..ad62ba5d712b 100644
--- a/oox/source/drawingml/diagram/layoutnodecontext.cxx
+++ b/oox/source/drawingml/diagram/layoutnodecontext.cxx
@@ -209,6 +209,8 @@ LayoutNodeContext::onCreateContext( ::sal_Int32 aElement,
pShape->setDiagramRotation(rAttribs.getInteger(XML_rot, 0) * PER_DEGREE);
+ pShape->setZOrderOff(rAttribs.getInteger(XML_zOrderOff, 0));
+
ShapeAtomPtr pAtom( new ShapeAtom(mpNode->getLayoutNode(), pShape) );
LayoutAtom::connect(mpNode, pAtom);
return new ShapeContext( *this, ShapePtr(), pShape );
diff --git a/oox/source/drawingml/shape.cxx b/oox/source/drawingml/shape.cxx
index 2926614b8efc..16bc511743b1 100644
--- a/oox/source/drawingml/shape.cxx
+++ b/oox/source/drawingml/shape.cxx
@@ -175,6 +175,8 @@ Shape::Shape( const ShapePtr& pSourceShape )
, maLinkedTxbxAttr()
, mbHasLinkedTxbx(false)
, maDiagramDoms( pSourceShape->maDiagramDoms )
+, mnZOrder(pSourceShape->mnZOrder)
+, mnZOrderOff(pSourceShape->mnZOrderOff)
{}
Shape::~Shape()
diff --git a/sd/qa/unit/data/pptx/smartart-accent-process.pptx b/sd/qa/unit/data/pptx/smartart-accent-process.pptx
new file mode 100644
index 000000000000..8710e7f170a9
--- /dev/null
+++ b/sd/qa/unit/data/pptx/smartart-accent-process.pptx
Binary files differ
diff --git a/sd/qa/unit/data/pptx/smartart-continuous-block-process.pptx b/sd/qa/unit/data/pptx/smartart-continuous-block-process.pptx
new file mode 100644
index 000000000000..b2ef58f0bbb3
--- /dev/null
+++ b/sd/qa/unit/data/pptx/smartart-continuous-block-process.pptx
Binary files differ
diff --git a/sd/qa/unit/import-tests-smartart.cxx b/sd/qa/unit/import-tests-smartart.cxx
index d561f138f16f..b83de20890b1 100644
--- a/sd/qa/unit/import-tests-smartart.cxx
+++ b/sd/qa/unit/import-tests-smartart.cxx
@@ -14,6 +14,8 @@
#include <com/sun/star/style/ParagraphAdjust.hpp>
#include <com/sun/star/text/XText.hpp>
+#include <comphelper/sequenceashashmap.hxx>
+
using namespace ::com::sun::star;
class SdImportTestSmartArt : public SdModelTestBase
@@ -29,6 +31,8 @@ public:
void testVertialBoxList();
void testVertialBracketList();
void testTableList();
+ void testAccentProcess();
+ void testContinuousBlockProcess();
CPPUNIT_TEST_SUITE(SdImportTestSmartArt);
@@ -42,6 +46,8 @@ public:
CPPUNIT_TEST(testVertialBoxList);
CPPUNIT_TEST(testVertialBracketList);
CPPUNIT_TEST(testTableList);
+ CPPUNIT_TEST(testAccentProcess);
+ CPPUNIT_TEST(testContinuousBlockProcess);
CPPUNIT_TEST_SUITE_END();
};
@@ -276,6 +282,126 @@ void SdImportTestSmartArt::testTableList()
xDocShRef->DoClose();
}
+void SdImportTestSmartArt::testAccentProcess()
+{
+ sd::DrawDocShellRef xDocShRef = loadURL(
+ m_directories.getURLFromSrc("/sd/qa/unit/data/pptx/smartart-accent-process.pptx"), PPTX);
+ uno::Reference<drawing::XShapes> xGroup(getShapeFromPage(0, 0, xDocShRef), uno::UNO_QUERY);
+ CPPUNIT_ASSERT(xGroup.is());
+ // 3 children: first pair, connector, second pair.
+ CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32>(3), xGroup->getCount());
+ uno::Reference<drawing::XShape> xGroupShape(xGroup, uno::UNO_QUERY);
+ CPPUNIT_ASSERT(xGroupShape.is());
+
+ // The pair is a parent (shape + text) and a child, so 3 shapes in total.
+ // The order is importent, first is at the back, last is at the front.
+ uno::Reference<drawing::XShapes> xFirstPair(xGroup->getByIndex(0), uno::UNO_QUERY);
+ CPPUNIT_ASSERT(xFirstPair.is());
+ CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32>(3), xFirstPair->getCount());
+
+ uno::Reference<text::XText> xFirstParentText(xFirstPair->getByIndex(1), uno::UNO_QUERY);
+ CPPUNIT_ASSERT(xFirstParentText.is());
+ CPPUNIT_ASSERT_EQUAL(OUString("a"), xFirstParentText->getString());
+ uno::Reference<drawing::XShape> xFirstParent(xFirstParentText, uno::UNO_QUERY);
+ CPPUNIT_ASSERT(xFirstParent.is());
+ int nFirstParentTop = xFirstParent->getPosition().Y;
+
+ uno::Reference<text::XText> xFirstChildText(xFirstPair->getByIndex(2), uno::UNO_QUERY);
+ CPPUNIT_ASSERT(xFirstChildText.is());
+ CPPUNIT_ASSERT_EQUAL(OUString("b"), xFirstChildText->getString());
+ uno::Reference<drawing::XShape> xFirstChild(xFirstChildText, uno::UNO_QUERY);
+ CPPUNIT_ASSERT(xFirstChildText.is());
+
+ {
+ uno::Reference<container::XEnumerationAccess> xParasAccess(xFirstChildText, uno::UNO_QUERY);
+ uno::Reference<container::XEnumeration> xParas = xParasAccess->createEnumeration();
+ uno::Reference<beans::XPropertySet> xPara(xParas->nextElement(), uno::UNO_QUERY);
+ // Without the accompanying fix in place, this test would have failed
+ // with 'Expected: 0; Actual : 1270', i.e. there was a large
+ // unexpected left margin.
+ CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32>(0),
+ xPara->getPropertyValue("ParaLeftMargin").get<sal_Int32>());
+
+ uno::Reference<container::XIndexAccess> xRules(xPara->getPropertyValue("NumberingRules"),
+ uno::UNO_QUERY);
+ comphelper::SequenceAsHashMap aRule(xRules->getByIndex(1));
+ CPPUNIT_ASSERT_EQUAL(OUString::fromUtf8(u8"•"), aRule["BulletChar"].get<OUString>());
+ }
+
+ int nFirstChildTop = xFirstChild->getPosition().Y;
+ int nFirstChildRight = xFirstChild->getPosition().X + xFirstChild->getSize().Width;
+
+ // First child is below the first parent.
+ // Without the accompanying fix in place, this test would have failed with
+ // 'Expected less than: 3881, Actual : 3881', i.e. xFirstChild was not
+ // below xFirstParent (a good position is 9081).
+ CPPUNIT_ASSERT_LESS(nFirstChildTop, nFirstParentTop);
+
+ // Make sure that we have an arrow shape between the two pairs.
+ uno::Reference<beans::XPropertySet> xArrow(xGroup->getByIndex(1), uno::UNO_QUERY);
+ CPPUNIT_ASSERT(xArrow.is());
+ comphelper::SequenceAsHashMap aCustomShapeGeometry(
+ xArrow->getPropertyValue("CustomShapeGeometry"));
+ // Without the accompanying fix in place, this test would have failed, i.e.
+ // the custom shape lacked a type -> arrow was not visible.
+ CPPUNIT_ASSERT(aCustomShapeGeometry["Type"].has<OUString>());
+ OUString aType = aCustomShapeGeometry["Type"].get<OUString>();
+ CPPUNIT_ASSERT_EQUAL(OUString("ooxml-rightArrow"), aType);
+
+ // Make sure that height of the arrow is less than its width.
+ uno::Reference<drawing::XShape> xArrowShape(xArrow, uno::UNO_QUERY);
+ CPPUNIT_ASSERT(xArrowShape.is());
+ awt::Size aArrowSize = xArrowShape->getSize();
+ CPPUNIT_ASSERT_LESS(aArrowSize.Width, aArrowSize.Height);
+
+ uno::Reference<drawing::XShapes> xSecondPair(xGroup->getByIndex(2), uno::UNO_QUERY);
+ CPPUNIT_ASSERT(xSecondPair.is());
+ CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32>(3), xSecondPair->getCount());
+ uno::Reference<text::XText> xSecondParentText(xSecondPair->getByIndex(1), uno::UNO_QUERY);
+ CPPUNIT_ASSERT(xFirstParentText.is());
+ // Without the accompanying fix in place, this test would have failed with
+ // 'Expected: cc; Actual : c', i.e. non-first runs on data points were ignored.
+ CPPUNIT_ASSERT_EQUAL(OUString("cc"), xSecondParentText->getString());
+ uno::Reference<drawing::XShape> xSecondParent(xSecondParentText, uno::UNO_QUERY);
+ CPPUNIT_ASSERT(xSecondParent.is());
+ int nSecondParentLeft = xSecondParent->getPosition().X;
+ // Without the accompanying fix in place, this test would have failed with
+ // 'Expected less than: 12700; Actual : 18540', i.e. the "b" and "c"
+ // shapes overlapped.
+ CPPUNIT_ASSERT_LESS(nSecondParentLeft, nFirstChildRight);
+
+ xDocShRef->DoClose();
+}
+
+void SdImportTestSmartArt::testContinuousBlockProcess()
+{
+ sd::DrawDocShellRef xDocShRef = loadURL(
+ m_directories.getURLFromSrc("/sd/qa/unit/data/pptx/smartart-continuous-block-process.pptx"),
+ PPTX);
+ uno::Reference<drawing::XShapes> xGroup(getShapeFromPage(0, 0, xDocShRef), uno::UNO_QUERY);
+ CPPUNIT_ASSERT(xGroup.is());
+ // 2 children: background, foreground.
+ CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32>(2), xGroup->getCount());
+
+ uno::Reference<drawing::XShapes> xLinear(xGroup->getByIndex(1), uno::UNO_QUERY);
+ CPPUNIT_ASSERT(xLinear.is());
+ // 3 children: A, B and C.
+ CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32>(3), xLinear->getCount());
+
+ uno::Reference<text::XText> xA(xLinear->getByIndex(0), uno::UNO_QUERY);
+ CPPUNIT_ASSERT(xA.is());
+ CPPUNIT_ASSERT_EQUAL(OUString("A"), xA->getString());
+ uno::Reference<drawing::XShape> xAShape(xA, uno::UNO_QUERY);
+ CPPUNIT_ASSERT(xAShape.is());
+ // Without the accompanying fix in place, this test would have failed: the
+ // theoretically correct value is 5462 mm100 (16933 is the total width, and
+ // need to divide that to 1, 0.5, 1, 0.5 and 1 units), while the old value
+ // was 4703 and the new one is 5461.
+ CPPUNIT_ASSERT_GREATER(static_cast<sal_Int32>(5000), xAShape->getSize().Width);
+
+ xDocShRef->DoClose();
+}
+
CPPUNIT_TEST_SUITE_REGISTRATION(SdImportTestSmartArt);
CPPUNIT_PLUGIN_IMPLEMENT();