Related: tdf#117761 oox smartart: backport fixes related to list types

This is a combination of 7 commits.

This is the 1st commit:

oox: ignore SmartArt "fallback" with empty shape list

This way at least something shows up in the import result. Far from
perfect, though.

(cherry picked from commit a7e86beb00e9635ea4556ef4f8f8e24ff9965391)

Conflicts:
	sd/qa/unit/import-tests-smartart.cxx

This is the commit #2:

oox smartart: fix width of shapes with agl=lin, linDir=fromT

Smaller width only makes sense in the fromL/fromR cases, I think.

(cherry picked from commit b61d2784271bf8b042642c378f50e8b446682548)

This is the commit #3:

oox smartart: work towards accessing parent constraints

The "Vertical Box List" preset in PowerPoint contains two linear
layouts, the inner one nested in the outer one. All the constraints are
stated on the parent layout. This commit doesn't actually read those
constraints yet, but adds infrastructure to look up parents of layout
nodes and also to get the constraints of a layout node.

No functional changes intended.

(cherry picked from commit 923061d17d4b5b8bf0d4a8111b270c99a7c649a9)

This is the commit #4:

oox smartart, linear layout: take width from constraints

Finally the "parent text" of the test document now has correct width.

(cherry picked from commit b083b0808121d19f398a9f6ead195ae7e14ed047)

This is the commit #5:

oox smartart, linear layout: take horizontal position from constraints

Both horizontal size and position looks sane now.

(cherry picked from commit 397b64afc62a5632a6648598558a4d2c3ca0d283)

This is the commit #6:

oox smartart, vertical bracket list: fix node counter condition

The visible effect of this was that the 2nd level text nodes were
missing from the layout result.

The cause is that when it comes to counting nodes of a condition, we
assumed that the current layout node is a presentation of a model node,
but this is not necessarily true.

Fix the problem doing a "first presentation child of", then a "presentation
of" navigation in that case, which leads us to the correct model node,
so counting its children works.

(An alternative way of getting non-zero children would be a
"presentation parent of" navigation, followed by a "presentation of"
navigation, but that would lead us to the document root, so we would
count the number of 1st level elements, not the correct 2nd level
elements.)

(cherry picked from commit e49c42d17f50c8b0cac9db08dedc375dd5aa8a98)

This is the commit #7:

oox smartart, table list: fix too large width of children

It's possible all children request 100% of space, need to scale down in
that case. This means that children other than the first one is now
readable in the layout result.

(cherry picked from commit c753a896aa32577e780d531032a08a699b960229)

Change-Id: Iae5a073d458598e7b5059ebdf435d50ce7c7df80
Reviewed-on: https://gerrit.libreoffice.org/63115
Tested-by: Jenkins
Reviewed-by: Andras Timar <andras.timar@collabora.com>
diff --git a/oox/inc/drawingml/diagram/diagram.hxx b/oox/inc/drawingml/diagram/diagram.hxx
index 5277d98..a528668 100644
--- a/oox/inc/drawingml/diagram/diagram.hxx
+++ b/oox/inc/drawingml/diagram/diagram.hxx
@@ -39,7 +39,8 @@
                  const OUString& rDataModelPath,
                  const OUString& rLayoutPath,
                  const OUString& rQStylePath,
                  const OUString& rColorStylePath );
                  const OUString& rColorStylePath,
                  const oox::core::Relations& rRelations );

void loadDiagram( const ShapePtr& pShape,
                  core::XmlFilterBase& rFilter,
diff --git a/oox/source/drawingml/diagram/constraintlistcontext.cxx b/oox/source/drawingml/diagram/constraintlistcontext.cxx
index 99e3f3d..cc71c89 100644
--- a/oox/source/drawingml/diagram/constraintlistcontext.cxx
+++ b/oox/source/drawingml/diagram/constraintlistcontext.cxx
@@ -50,7 +50,7 @@
    case DGM_TOKEN( constr ):
    {
        std::shared_ptr< ConstraintAtom > pNode( new ConstraintAtom(mpNode->getLayoutNode()) );
        mpNode->addChild( pNode );
        LayoutAtom::connect(mpNode, pNode);

        Constraint& rConstraint = pNode->getConstraint();
        rConstraint.mnFor = rAttribs.getToken( XML_for, XML_none );
diff --git a/oox/source/drawingml/diagram/diagram.cxx b/oox/source/drawingml/diagram/diagram.cxx
index e7a2df7..4664a55 100644
--- a/oox/source/drawingml/diagram/diagram.cxx
+++ b/oox/source/drawingml/diagram/diagram.cxx
@@ -30,6 +30,7 @@
#include <drawingml/fillproperties.hxx>
#include <oox/ppt/pptshapegroupcontext.hxx>
#include <oox/ppt/pptshape.hxx>
#include <oox/token/namespaces.hxx>

#include "diagramlayoutatoms.hxx"
#include "layoutatomvisitors.hxx"
@@ -375,12 +376,57 @@
    rFilter.importFragment( rxHandler, xSerializer );
}

namespace
{
/**
 * A fragment handler that just counts the number of <dsp:sp> elements in a
 * fragment.
 */
class DiagramShapeCounter : public oox::core::FragmentHandler2
{
public:
    DiagramShapeCounter(oox::core::XmlFilterBase& rFilter, const OUString& rFragmentPath,
                        sal_Int32& nCounter);
    oox::core::ContextHandlerRef onCreateContext(sal_Int32 nElement,
                                                 const AttributeList& rAttribs) override;

private:
    sal_Int32& m_nCounter;
};

DiagramShapeCounter::DiagramShapeCounter(oox::core::XmlFilterBase& rFilter,
                                         const OUString& rFragmentPath, sal_Int32& nCounter)
    : FragmentHandler2(rFilter, rFragmentPath)
    , m_nCounter(nCounter)
{
}

oox::core::ContextHandlerRef DiagramShapeCounter::onCreateContext(sal_Int32 nElement,
                                                                  const AttributeList& /*rAttribs*/)
{
    switch (nElement)
    {
        case DSP_TOKEN(drawing):
        case DSP_TOKEN(spTree):
            return this;
        case DSP_TOKEN(sp):
            ++m_nCounter;
            break;
        default:
            break;
    }

    return nullptr;
}
}

void loadDiagram( ShapePtr const & pShape,
                  core::XmlFilterBase& rFilter,
                  const OUString& rDataModelPath,
                  const OUString& rLayoutPath,
                  const OUString& rQStylePath,
                  const OUString& rColorStylePath )
                  const OUString& rColorStylePath,
                  const oox::core::Relations& rRelations )
{
    DiagramPtr pDiagram( new Diagram );

@@ -407,11 +453,26 @@

        // Pass the info to pShape
        for (auto const& extDrawing : pData->getExtDrawings())
                pShape->addExtDrawingRelId(extDrawing);
        {
            OUString aFragmentPath = rRelations.getFragmentPathFromRelId(extDrawing);
            // Ignore RelIds which don't resolve to a fragment path.
            if (aFragmentPath.isEmpty())
                continue;

            sal_Int32 nCounter = 0;
            rtl::Reference<core::FragmentHandler> xCounter(
                new DiagramShapeCounter(rFilter, aFragmentPath, nCounter));
            rFilter.importFragment(xCounter);
            // Ignore ext drawings which don't actually have any shapes.
            if (nCounter == 0)
                continue;

            pShape->addExtDrawingRelId(extDrawing);
        }
    }

    // extLst is present, lets bet on that and ignore the rest of the data from here
    if( pData->getExtDrawings().empty() )
    if( pShape->getExtDrawings().empty() )
    {
        // layout
        if( !rLayoutPath.isEmpty() )
diff --git a/oox/source/drawingml/diagram/diagramlayoutatoms.cxx b/oox/source/drawingml/diagram/diagramlayoutatoms.cxx
index e0fd7d2..c1aaf6e 100644
--- a/oox/source/drawingml/diagram/diagramlayoutatoms.cxx
+++ b/oox/source/drawingml/diagram/diagramlayoutatoms.cxx
@@ -35,6 +35,27 @@
using namespace ::com::sun::star::xml::sax;
using namespace ::oox::core;

namespace
{
/// Looks up the value of the rInternalName -> nProperty key in rProperties.
oox::OptValue<sal_Int32> findProperty(const oox::drawingml::LayoutPropertyMap& rProperties,
                                      const OUString& rInternalName, sal_Int32 nProperty)
{
    oox::OptValue<sal_Int32> oRet;

    auto it = rProperties.find(rInternalName);
    if (it != rProperties.end())
    {
        const oox::drawingml::LayoutProperty& rProperty = it->second;
        auto itProperty = rProperty.find(nProperty);
        if (itProperty != rProperty.end())
            oRet = itProperty->second;
    }

    return oRet;
}
}

namespace oox { namespace drawingml {

IteratorAttr::IteratorAttr( )
@@ -145,6 +166,36 @@
    return nullptr;
}

namespace
{
/**
 * Takes the connection list from rLayoutNode, navigates from rFrom on an edge
 * of type nType, using a direction determined by bSourceToDestination.
 */
OUString navigate(const LayoutNode& rLayoutNode, sal_Int32 nType, const OUString& rFrom,
                  bool bSourceToDestination)
{
    for (const auto& rConnection : rLayoutNode.getDiagram().getData()->getConnections())
    {
        if (rConnection.mnType != nType)
            continue;

        if (bSourceToDestination)
        {
            if (rConnection.msSourceId == rFrom)
                return rConnection.msDestId;
        }
        else
        {
            if (rConnection.msDestId == rFrom)
                return rConnection.msSourceId;
        }
    }

    return OUString();
}
}

sal_Int32 ConditionAtom::getNodeCount() const
{
    sal_Int32 nCount = 0;
@@ -153,9 +204,21 @@
    {
        OUString sNodeId = "";

        for (const auto& aCxn : mrLayoutNode.getDiagram().getData()->getConnections())
            if (aCxn.mnType == XML_presOf && aCxn.msDestId == pPoint->msModelId)
                sNodeId = aCxn.msSourceId;
        sNodeId
            = navigate(mrLayoutNode, XML_presOf, pPoint->msModelId, /*bSourceToDestination*/ false);

        if (sNodeId.isEmpty())
        {
            // The current layout node is not a presentation of anything. Look
            // up the first presentation child of the layout node.
            OUString sFirstPresChildId = navigate(mrLayoutNode, XML_presParOf, pPoint->msModelId,
                                                  /*bSourceToDestination*/ true);
            if (!sFirstPresChildId.isEmpty())
                // It has a presentation child: is that a presentation of a
                // model node?
                sNodeId = navigate(mrLayoutNode, XML_presOf, sFirstPresChildId,
                                   /*bSourceToDestination*/ false);
        }

        if (!sNodeId.isEmpty())
        {
@@ -224,8 +287,22 @@
}

void AlgAtom::layoutShape( const ShapePtr& rShape,
                           const std::vector<Constraint>& rConstraints ) const
                           const std::vector<Constraint>& rOwnConstraints ) const
{
    // Algorithm result may depend on the parent constraints as well.
    std::vector<Constraint> aParentConstraints;
    const LayoutNode* pParent = getLayoutNode().getParentLayoutNode();
    if (pParent)
    {
        for (const auto& pChild : pParent->getChildren())
        {
            auto pConstraintAtom = dynamic_cast<ConstraintAtom*>(pChild.get());
            if (pConstraintAtom)
                pConstraintAtom->parseConstraint(aParentConstraints);
        }
    }
    const std::vector<Constraint>& rConstraints = rOwnConstraints.empty() ? aParentConstraints : rOwnConstraints;

    switch(mnType)
    {
        case XML_composite:
@@ -356,9 +433,10 @@
            double fSpace = 0.3;

            awt::Size aChildSize = rShape->getSize();

            aChildSize.Width /= (nCount + (nCount-1)*fSpace);
            aChildSize.Height /= (nCount + (nCount-1)*fSpace);
            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)
@@ -366,12 +444,64 @@
            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)
            {
                if (rConstraint.msForName.isEmpty())
                    continue;

                LayoutProperty& rProperty = aProperties[rConstraint.msForName];
                if (rConstraint.mnType == XML_w)
                    rProperty[XML_w] = rShape->getSize().Width * rConstraint.mfFactor;
            }

            // See if children requested more than 100% space in total: scale
            // down in that case.
            sal_Int32 nTotalWidth = 0;
            bool bSpaceFromConstraints = false;
            for (auto & aCurrShape : rShape->getChildren())
            {
                oox::OptValue<sal_Int32> oWidth
                    = findProperty(aProperties, aCurrShape->getInternalName(), XML_w);

                awt::Size aSize = aChildSize;
                if (oWidth.has())
                {
                    aSize.Width = oWidth.get();
                    bSpaceFromConstraints = true;
                }
                if (nDir == XML_fromL || nDir == XML_fromR)
                    nTotalWidth += aSize.Width;
            }

            double fWidthScale = 1.0;
            if (nTotalWidth > rShape->getSize().Width && nTotalWidth)
            {
                fWidthScale = rShape->getSize().Width;
                fWidthScale /= nTotalWidth;
            }

            // Don't add automatic space if we take space from constraints.
            if (bSpaceFromConstraints)
                fSpace = 0;

            for (auto& aCurrShape : rShape->getChildren())
            {
                // Extract properties relevant for this shape from constraints.
                oox::OptValue<sal_Int32> oWidth
                    = findProperty(aProperties, aCurrShape->getInternalName(), XML_w);

                aCurrShape->setPosition(aCurrPos);
                aCurrShape->setSize(aChildSize);

                awt::Size aSize = aChildSize;
                if (oWidth.has())
                    aSize.Width = oWidth.get();
                aSize.Width *= fWidthScale;
                aCurrShape->setSize(aSize);

                aCurrShape->setChildSize(aChildSize);
                aCurrPos.X += nIncX * (aChildSize.Width + fSpace*aChildSize.Width);
                aCurrPos.X += nIncX * (aSize.Width + fSpace*aSize.Width);
                aCurrPos.Y += nIncY * (aChildSize.Height + fSpace*aChildSize.Height);
            }
            break;
@@ -753,6 +883,18 @@
    return true;
}

const LayoutNode* LayoutNode::getParentLayoutNode() const
{
    for (LayoutAtomPtr pAtom = getParent(); pAtom; pAtom = pAtom->getParent())
    {
        auto pLayoutNode = dynamic_cast<LayoutNode*>(pAtom.get());
        if (pLayoutNode)
            return pLayoutNode;
    }

    return nullptr;
}

void ShapeAtom::accept( LayoutAtomVisitor& rVisitor )
{
    rVisitor.visit(*this);
diff --git a/oox/source/drawingml/diagram/diagramlayoutatoms.hxx b/oox/source/drawingml/diagram/diagramlayoutatoms.hxx
index 5ab34b0..3d4d9c0 100644
--- a/oox/source/drawingml/diagram/diagramlayoutatoms.hxx
+++ b/oox/source/drawingml/diagram/diagramlayoutatoms.hxx
@@ -106,17 +106,30 @@
    const OUString& getName() const
        { return msName; }

private:
    void addChild( const LayoutAtomPtr & pNode )
        { mpChildNodes.push_back( pNode ); }
    void setParent(const LayoutAtomPtr& pParent) { mpParent = pParent; }

public:
    virtual const std::vector<LayoutAtomPtr>& getChildren() const
        { return mpChildNodes; }

    LayoutAtomPtr getParent() const { return mpParent.lock(); }

    static void connect(const LayoutAtomPtr& pParent, const LayoutAtomPtr& pChild)
    {
        pParent->addChild(pChild);
        pChild->setParent(pParent);
    }

    // dump for debug
    void dump(int level = 0);

protected:
    const LayoutNode&            mrLayoutNode;
    std::vector< LayoutAtomPtr > mpChildNodes;
    std::weak_ptr<LayoutAtom> mpParent;
    OUString                     msName;
};

@@ -238,6 +251,8 @@
    bool setupShape( const ShapePtr& rShape,
                     const dgm::Point* pPresNode ) const;

    const LayoutNode* getParentLayoutNode() const;

private:
    const Diagram&               mrDgm;
    VarMap                       mVariables;
diff --git a/oox/source/drawingml/diagram/layoutnodecontext.cxx b/oox/source/drawingml/diagram/layoutnodecontext.cxx
index 9bbe8b3..257f490 100644
--- a/oox/source/drawingml/diagram/layoutnodecontext.cxx
+++ b/oox/source/drawingml/diagram/layoutnodecontext.cxx
@@ -105,14 +105,14 @@
            {
                // CT_When
                ConditionAtomPtr pNode( new ConditionAtom(mpNode->getLayoutNode(), false, rAttribs.getFastAttributeList()) );
                mpNode->addChild( pNode );
                LayoutAtom::connect(mpNode, pNode);
                return new IfContext( *this, rAttribs, pNode );
            }
            case DGM_TOKEN( else ):
            {
                // CT_Otherwise
                ConditionAtomPtr pNode( new ConditionAtom(mpNode->getLayoutNode(), true, rAttribs.getFastAttributeList()) );
                mpNode->addChild( pNode );
                LayoutAtom::connect(mpNode, pNode);
                return new IfContext( *this, rAttribs, pNode );
            }
            default:
@@ -182,7 +182,7 @@
    case DGM_TOKEN( layoutNode ):
    {
        LayoutNodePtr pNode( new LayoutNode(mpNode->getLayoutNode().getDiagram()) );
        mpNode->addChild( pNode );
        LayoutAtom::connect(mpNode, pNode);
        pNode->setChildOrder( rAttribs.getToken( XML_chOrder, XML_b ) );
        pNode->setMoveWith( rAttribs.getString( XML_moveWith ).get() );
        pNode->setStyleLabel( rAttribs.getString( XML_styleLbl ).get() );
@@ -210,7 +210,7 @@
        pShape->setDiagramRotation(rAttribs.getInteger(XML_rot, 0) * PER_DEGREE);

        ShapeAtomPtr pAtom( new ShapeAtom(mpNode->getLayoutNode(), pShape) );
        mpNode->addChild( pAtom );
        LayoutAtom::connect(mpNode, pAtom);
        return new ShapeContext( *this, ShapePtr(), pShape );
    }
    case DGM_TOKEN( extLst ):
@@ -219,21 +219,21 @@
    {
        // CT_Algorithm
        AlgAtomPtr pAtom( new AlgAtom(mpNode->getLayoutNode()) );
        mpNode->addChild( pAtom );
        LayoutAtom::connect(mpNode, pAtom);
        return new AlgorithmContext( *this, rAttribs, pAtom );
    }
    case DGM_TOKEN( choose ):
    {
        // CT_Choose
        LayoutAtomPtr pAtom( new ChooseAtom(mpNode->getLayoutNode()) );
        mpNode->addChild( pAtom );
        LayoutAtom::connect(mpNode, pAtom);
        return new ChooseContext( *this, rAttribs, pAtom );
    }
    case DGM_TOKEN( forEach ):
    {
        // CT_ForEach
        ForEachAtomPtr pAtom( new ForEachAtom(mpNode->getLayoutNode(), rAttribs.getFastAttributeList()) );
        mpNode->addChild( pAtom );
        LayoutAtom::connect(mpNode, pAtom);
        return new ForEachContext( *this, rAttribs, pAtom );
    }
    case DGM_TOKEN( constrLst ):
diff --git a/oox/source/drawingml/graphicshapecontext.cxx b/oox/source/drawingml/graphicshapecontext.cxx
index f8cfc0d..135966e 100644
--- a/oox/source/drawingml/graphicshapecontext.cxx
+++ b/oox/source/drawingml/graphicshapecontext.cxx
@@ -262,7 +262,8 @@
                    getFragmentPathFromRelId( msDm ),
                    getFragmentPathFromRelId( msLo ),
                    getFragmentPathFromRelId( msQs ),
                    getFragmentPathFromRelId( msCs ));
                    getFragmentPathFromRelId( msCs ),
                    getRelations());
        SAL_INFO("oox.drawingml", "DiagramGraphicDataContext::onCreateContext: added shape " << mpShapePtr->getName()
                 << " of type " << mpShapePtr->getServiceName()
                 << ", position: " << mpShapePtr->getPosition().X
diff --git a/oox/source/ppt/dgmimport.cxx b/oox/source/ppt/dgmimport.cxx
index c10dc48..191ec17 100644
--- a/oox/source/ppt/dgmimport.cxx
+++ b/oox/source/ppt/dgmimport.cxx
@@ -68,6 +68,7 @@

    Reference<drawing::XShapes> xParentShape(getParentShape(),
                                             UNO_QUERY_THROW);
    oox::core::Relations aRelations("");
    oox::drawingml::ShapePtr pShape(
        new oox::drawingml::Shape( "com.sun.star.drawing.DiagramShape" ) );
    drawingml::loadDiagram(pShape,
@@ -75,7 +76,8 @@
                           "",
                           aFragmentPath,
                           "",
                           "");
                           "",
                           aRelations);
    oox::drawingml::ThemePtr pTheme(
        new oox::drawingml::Theme());
    basegfx::B2DHomMatrix aMatrix;
diff --git a/sd/qa/unit/data/pptx/smartart-vertial-box-list.pptx b/sd/qa/unit/data/pptx/smartart-vertial-box-list.pptx
new file mode 100644
index 0000000..b67d99e
--- /dev/null
+++ b/sd/qa/unit/data/pptx/smartart-vertial-box-list.pptx
Binary files differ
diff --git a/sd/qa/unit/data/pptx/table-list.pptx b/sd/qa/unit/data/pptx/table-list.pptx
new file mode 100644
index 0000000..bc5fe74
--- /dev/null
+++ b/sd/qa/unit/data/pptx/table-list.pptx
Binary files differ
diff --git a/sd/qa/unit/data/pptx/vertical-bracket-list.pptx b/sd/qa/unit/data/pptx/vertical-bracket-list.pptx
new file mode 100644
index 0000000..bef9d74
--- /dev/null
+++ b/sd/qa/unit/data/pptx/vertical-bracket-list.pptx
Binary files differ
diff --git a/sd/qa/unit/import-tests-smartart.cxx b/sd/qa/unit/import-tests-smartart.cxx
index e86dab7..d561f13 100644
--- a/sd/qa/unit/import-tests-smartart.cxx
+++ b/sd/qa/unit/import-tests-smartart.cxx
@@ -26,6 +26,9 @@
    void testDir();
    void testMaxDepth();
    void testRotation();
    void testVertialBoxList();
    void testVertialBracketList();
    void testTableList();

    CPPUNIT_TEST_SUITE(SdImportTestSmartArt);

@@ -36,6 +39,9 @@
    CPPUNIT_TEST(testDir);
    CPPUNIT_TEST(testMaxDepth);
    CPPUNIT_TEST(testRotation);
    CPPUNIT_TEST(testVertialBoxList);
    CPPUNIT_TEST(testVertialBracketList);
    CPPUNIT_TEST(testTableList);

    CPPUNIT_TEST_SUITE_END();
};
@@ -185,6 +191,91 @@
    xDocShRef->DoClose();
}

void SdImportTestSmartArt::testVertialBoxList()
{
    sd::DrawDocShellRef xDocShRef = loadURL(
        m_directories.getURLFromSrc("/sd/qa/unit/data/pptx/smartart-vertial-box-list.pptx"), PPTX);
    uno::Reference<drawing::XShapes> xShapeGroup(getShapeFromPage(0, 0, xDocShRef),
                                                 uno::UNO_QUERY_THROW);
    // Without the accompanying fix in place, this test would have failed with
    // 'actual: 0'.
    CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32>(2), xShapeGroup->getCount());

    uno::Reference<drawing::XShapes> xFirstChild(xShapeGroup->getByIndex(0), uno::UNO_QUERY);
    CPPUNIT_ASSERT(xFirstChild.is());
    uno::Reference<drawing::XShape> xParentText(xFirstChild->getByIndex(1), uno::UNO_QUERY);
    CPPUNIT_ASSERT(xParentText.is());
    // Without the accompanying fix in place, this test would have failed with
    // 'actual: 7361', i.e. the width was not the 70% of the parent as the
    // constraint wanted.
    CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32>(11852), xParentText->getSize().Width);

    uno::Reference<drawing::XShape> xChildText(xShapeGroup->getByIndex(1), uno::UNO_QUERY);
    CPPUNIT_ASSERT(xChildText.is());
    // Without the accompanying fix in place, this test would have failed with
    // 'actual: 7361' (and with the fix: 'actual: 16932', i.e. the width of the
    // parent).
    CPPUNIT_ASSERT_GREATER(static_cast<sal_Int32>(10000), xChildText->getSize().Width);

    // Assert that the right edge of the parent text is closer to the slide
    // boundary than the right edge of the parent text.
    // Without the accompanying fix in place, this test would have failed with
    // 'Expected greater than: 25656, Actual  : 21165'.
    CPPUNIT_ASSERT_GREATER(xParentText->getPosition().X + xParentText->getSize().Width,
                           xChildText->getPosition().X + xChildText->getSize().Width);

    xDocShRef->DoClose();
}

void SdImportTestSmartArt::testVertialBracketList()
{
    sd::DrawDocShellRef xDocShRef = loadURL(
        m_directories.getURLFromSrc("/sd/qa/unit/data/pptx/vertical-bracket-list.pptx"), PPTX);
    uno::Reference<drawing::XShapes> xShapeGroup(getShapeFromPage(0, 0, xDocShRef),
                                                 uno::UNO_QUERY_THROW);
    CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32>(1), xShapeGroup->getCount());

    uno::Reference<drawing::XShapes> xFirstChild(xShapeGroup->getByIndex(0), uno::UNO_QUERY);
    CPPUNIT_ASSERT(xFirstChild.is());
    // Without the accompanying fix in place, this test would have failed with
    // 'actual: 2', i.e. one child shape (with its "A" text) was missing.
    CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32>(3), xFirstChild->getCount());

    xDocShRef->DoClose();
}

void SdImportTestSmartArt::testTableList()
{
    sd::DrawDocShellRef xDocShRef = loadURL(
        m_directories.getURLFromSrc("/sd/qa/unit/data/pptx/table-list.pptx"), PPTX);
    uno::Reference<drawing::XShapes> xShapeGroup(getShapeFromPage(0, 0, xDocShRef), uno::UNO_QUERY);
    CPPUNIT_ASSERT(xShapeGroup.is());
    CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32>(3), xShapeGroup->getCount());

    uno::Reference<text::XText> xParentText(xShapeGroup->getByIndex(0), uno::UNO_QUERY);
    CPPUNIT_ASSERT(xParentText.is());
    CPPUNIT_ASSERT_EQUAL(OUString("Parent"), xParentText->getString());
    uno::Reference<drawing::XShape> xParent(xParentText, uno::UNO_QUERY);
    CPPUNIT_ASSERT(xParent.is());
    int nParentRight = xParent->getPosition().X + xParent->getSize().Width;

    uno::Reference<drawing::XShapes> xChildren(xShapeGroup->getByIndex(1), uno::UNO_QUERY);
    CPPUNIT_ASSERT(xChildren.is());
    uno::Reference<text::XText> xChild2Text(xChildren->getByIndex(1), uno::UNO_QUERY);
    CPPUNIT_ASSERT(xChild2Text.is());
    CPPUNIT_ASSERT_EQUAL(OUString("Child 2"), xChild2Text->getString());
    uno::Reference<drawing::XShape> xChild2(xChild2Text, uno::UNO_QUERY);
    CPPUNIT_ASSERT(xChild2.is());
    int nChild2Right = xChild2->getPosition().X + xChild2->getSize().Width;

    // Without the accompanying fix in place, this test would have failed with
    // 'Expected less than: 100, Actual  : 22014', i.e. the second child was
    // shifted to the right too much.
    CPPUNIT_ASSERT_LESS(100, abs(nChild2Right - nParentRight));

    xDocShRef->DoClose();
}

CPPUNIT_TEST_SUITE_REGISTRATION(SdImportTestSmartArt);

CPPUNIT_PLUGIN_IMPLEMENT();