summaryrefslogtreecommitdiffstats
path: root/oox/source/drawingml/diagram
diff options
context:
space:
mode:
Diffstat (limited to 'oox/source/drawingml/diagram')
-rw-r--r--oox/source/drawingml/diagram/constraintlistcontext.cxx78
-rw-r--r--oox/source/drawingml/diagram/constraintlistcontext.hxx43
-rw-r--r--oox/source/drawingml/diagram/datamodel.cxx475
-rw-r--r--oox/source/drawingml/diagram/datamodel.hxx205
-rw-r--r--oox/source/drawingml/diagram/datamodelcontext.cxx375
-rw-r--r--oox/source/drawingml/diagram/datamodelcontext.hxx45
-rw-r--r--oox/source/drawingml/diagram/diagram.cxx468
-rw-r--r--oox/source/drawingml/diagram/diagram.hxx170
-rw-r--r--oox/source/drawingml/diagram/diagramdefinitioncontext.cxx99
-rw-r--r--oox/source/drawingml/diagram/diagramdefinitioncontext.hxx44
-rw-r--r--oox/source/drawingml/diagram/diagramfragmenthandler.cxx236
-rw-r--r--oox/source/drawingml/diagram/diagramfragmenthandler.hxx103
-rw-r--r--oox/source/drawingml/diagram/diagramlayoutatoms.cxx1964
-rw-r--r--oox/source/drawingml/diagram/diagramlayoutatoms.hxx325
-rw-r--r--oox/source/drawingml/diagram/layoutatomvisitorbase.cxx176
-rw-r--r--oox/source/drawingml/diagram/layoutatomvisitorbase.hxx98
-rw-r--r--oox/source/drawingml/diagram/layoutatomvisitors.cxx260
-rw-r--r--oox/source/drawingml/diagram/layoutatomvisitors.hxx96
-rw-r--r--oox/source/drawingml/diagram/layoutnodecontext.cxx309
-rw-r--r--oox/source/drawingml/diagram/layoutnodecontext.hxx43
-rw-r--r--oox/source/drawingml/diagram/rulelistcontext.cxx58
-rw-r--r--oox/source/drawingml/diagram/rulelistcontext.hxx42
22 files changed, 5712 insertions, 0 deletions
diff --git a/oox/source/drawingml/diagram/constraintlistcontext.cxx b/oox/source/drawingml/diagram/constraintlistcontext.cxx
new file mode 100644
index 000000000..cc1f13026
--- /dev/null
+++ b/oox/source/drawingml/diagram/constraintlistcontext.cxx
@@ -0,0 +1,78 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "constraintlistcontext.hxx"
+#include <oox/helper/attributelist.hxx>
+#include <oox/token/namespaces.hxx>
+#include <oox/token/tokens.hxx>
+
+using namespace ::oox::core;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::xml::sax;
+
+namespace oox::drawingml {
+
+// CT_ConstraintLists
+ConstraintListContext::ConstraintListContext( ContextHandler2Helper const & rParent,
+ const LayoutAtomPtr &pNode )
+ : ContextHandler2( rParent )
+ , mpNode( pNode )
+{
+ assert( pNode && "Node must NOT be NULL" );
+}
+
+ConstraintListContext::~ConstraintListContext()
+{
+}
+
+ContextHandlerRef
+ConstraintListContext::onCreateContext( ::sal_Int32 aElement,
+ const AttributeList& rAttribs )
+{
+ switch( aElement )
+ {
+ case DGM_TOKEN( constr ):
+ {
+ auto pNode = std::make_shared<ConstraintAtom>(mpNode->getLayoutNode());
+ LayoutAtom::connect(mpNode, pNode);
+
+ Constraint& rConstraint = pNode->getConstraint();
+ rConstraint.mnFor = rAttribs.getToken( XML_for, XML_none );
+ rConstraint.msForName = rAttribs.getString( XML_forName, "" );
+ rConstraint.mnPointType = rAttribs.getToken( XML_ptType, XML_none );
+ rConstraint.mnType = rAttribs.getToken( XML_type, XML_none );
+ rConstraint.mnRefFor = rAttribs.getToken( XML_refFor, XML_none );
+ rConstraint.msRefForName = rAttribs.getString( XML_refForName, "" );
+ rConstraint.mnRefType = rAttribs.getToken( XML_refType, XML_none );
+ rConstraint.mnRefPointType = rAttribs.getToken( XML_refPtType, XML_none );
+ rConstraint.mfFactor = rAttribs.getDouble( XML_fact, 1.0 );
+ rConstraint.mfValue = rAttribs.getDouble( XML_val, 0.0 );
+ rConstraint.mnOperator = rAttribs.getToken( XML_op, XML_none );
+ break;
+ }
+ default:
+ break;
+ }
+
+ return this;
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/oox/source/drawingml/diagram/constraintlistcontext.hxx b/oox/source/drawingml/diagram/constraintlistcontext.hxx
new file mode 100644
index 000000000..ba3b59355
--- /dev/null
+++ b/oox/source/drawingml/diagram/constraintlistcontext.hxx
@@ -0,0 +1,43 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_OOX_SOURCE_DRAWINGML_DIAGRAM_CONSTRAINTLISTCONTEXT_HXX
+#define INCLUDED_OOX_SOURCE_DRAWINGML_DIAGRAM_CONSTRAINTLISTCONTEXT_HXX
+
+#include <oox/core/contexthandler2.hxx>
+#include "diagramlayoutatoms.hxx"
+
+namespace oox::drawingml {
+
+class ConstraintListContext : public ::oox::core::ContextHandler2
+{
+public:
+ ConstraintListContext( ContextHandler2Helper const & rParent, const LayoutAtomPtr &pNode );
+ virtual ~ConstraintListContext() override;
+
+ virtual ::oox::core::ContextHandlerRef onCreateContext( ::sal_Int32 Element, const AttributeList& rAttribs ) override;
+private:
+ LayoutAtomPtr mpNode;
+};
+
+}
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/oox/source/drawingml/diagram/datamodel.cxx b/oox/source/drawingml/diagram/datamodel.cxx
new file mode 100644
index 000000000..469f40436
--- /dev/null
+++ b/oox/source/drawingml/diagram/datamodel.cxx
@@ -0,0 +1,475 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "datamodel.hxx"
+#include <rtl/ustrbuf.hxx>
+#include <sal/log.hxx>
+#include <drawingml/fillproperties.hxx>
+#include <drawingml/textbody.hxx>
+#include <drawingml/textparagraph.hxx>
+#include <drawingml/textrun.hxx>
+#include <oox/drawingml/shape.hxx>
+#include <comphelper/xmltools.hxx>
+
+#include <unordered_set>
+#include <fstream>
+
+using namespace ::com::sun::star;
+
+namespace oox::drawingml {
+
+namespace dgm {
+
+void Connection::dump() const
+{
+ SAL_INFO(
+ "oox.drawingml",
+ "cnx modelId " << msModelId << ", srcId " << msSourceId << ", dstId "
+ << msDestId << ", parTransId " << msParTransId << ", presId "
+ << msPresId << ", sibTransId " << msSibTransId << ", srcOrd "
+ << mnSourceOrder << ", dstOrd " << mnDestOrder);
+}
+
+void Point::dump() const
+{
+ SAL_INFO(
+ "oox.drawingml",
+ "pt text " << mpShape.get() << ", cnxId " << msCnxId << ", modelId "
+ << msModelId << ", type " << mnType);
+}
+
+} // oox::drawingml::dgm namespace
+
+DiagramData::DiagramData() :
+ mpFillProperties( std::make_shared<FillProperties>() )
+{
+}
+
+const dgm::Point* DiagramData::getRootPoint() const
+{
+ for (const auto & aCurrPoint : maPoints)
+ if (aCurrPoint.mnType == XML_doc)
+ return &aCurrPoint;
+
+ SAL_WARN("oox.drawingml", "No root point");
+ return nullptr;
+}
+
+void DiagramData::dump() const
+{
+ SAL_INFO("oox.drawingml", "Dgm: DiagramData # of cnx: " << maConnections.size() );
+ for (const auto& rConnection : maConnections)
+ rConnection.dump();
+
+ SAL_INFO("oox.drawingml", "Dgm: DiagramData # of pt: " << maPoints.size() );
+ for (const auto& rPoint : maPoints)
+ rPoint.dump();
+}
+
+void DiagramData::getChildrenString(OUStringBuffer& rBuf, const dgm::Point* pPoint, sal_Int32 nLevel) const
+{
+ if (!pPoint)
+ return;
+
+ if (nLevel > 0)
+ {
+ for (sal_Int32 i = 0; i < nLevel-1; i++)
+ rBuf.append('\t');
+ rBuf.append('+');
+ rBuf.append(' ');
+ rBuf.append(pPoint->mpShape->getTextBody()->toString());
+ rBuf.append('\n');
+ }
+
+ std::vector<const dgm::Point*> aChildren;
+ for (const auto& rCxn : maConnections)
+ if (rCxn.mnType == XML_parOf && rCxn.msSourceId == pPoint->msModelId)
+ {
+ if (rCxn.mnSourceOrder >= static_cast<sal_Int32>(aChildren.size()))
+ aChildren.resize(rCxn.mnSourceOrder + 1);
+ const auto pChild = maPointNameMap.find(rCxn.msDestId);
+ if (pChild != maPointNameMap.end())
+ aChildren[rCxn.mnSourceOrder] = pChild->second;
+ }
+
+ for (auto pChild : aChildren)
+ getChildrenString(rBuf, pChild, nLevel + 1);
+}
+
+OUString DiagramData::getString() const
+{
+ OUStringBuffer aBuf;
+ const dgm::Point* pPoint = getRootPoint();
+ getChildrenString(aBuf, pPoint, 0);
+ return aBuf.makeStringAndClear();
+}
+
+std::vector<std::pair<OUString, OUString>> DiagramData::getChildren(const OUString& rParentId) const
+{
+ const OUString sModelId = rParentId.isEmpty() ? getRootPoint()->msModelId : rParentId;
+ std::vector<std::pair<OUString, OUString>> aChildren;
+ for (const auto& rCxn : maConnections)
+ if (rCxn.mnType == XML_parOf && rCxn.msSourceId == sModelId)
+ {
+ if (rCxn.mnSourceOrder >= static_cast<sal_Int32>(aChildren.size()))
+ aChildren.resize(rCxn.mnSourceOrder + 1);
+ const auto pChild = maPointNameMap.find(rCxn.msDestId);
+ if (pChild != maPointNameMap.end())
+ aChildren[rCxn.mnSourceOrder] = std::make_pair(
+ pChild->second->msModelId,
+ pChild->second->mpShape->getTextBody()->toString());
+ }
+
+ // HACK: empty items shouldn't appear there
+ aChildren.erase(std::remove_if(aChildren.begin(), aChildren.end(),
+ [](const std::pair<OUString, OUString>& aItem) { return aItem.first.isEmpty(); }),
+ aChildren.end());
+
+ return aChildren;
+}
+
+void DiagramData::addConnection(sal_Int32 nType, const OUString& sSourceId, const OUString& sDestId)
+{
+ sal_Int32 nMaxOrd = -1;
+ for (const auto& aCxn : maConnections)
+ if (aCxn.mnType == nType && aCxn.msSourceId == sSourceId)
+ nMaxOrd = std::max(nMaxOrd, aCxn.mnSourceOrder);
+
+ dgm::Connection& rCxn = maConnections.emplace_back();
+ rCxn.mnType = nType;
+ rCxn.msSourceId = sSourceId;
+ rCxn.msDestId = sDestId;
+ rCxn.mnSourceOrder = nMaxOrd + 1;
+}
+
+OUString DiagramData::addNode(const OUString& rText)
+{
+ const dgm::Point& rDataRoot = *getRootPoint();
+ OUString sPresRoot;
+ for (const auto& aCxn : maConnections)
+ if (aCxn.mnType == XML_presOf && aCxn.msSourceId == rDataRoot.msModelId)
+ sPresRoot = aCxn.msDestId;
+
+ if (sPresRoot.isEmpty())
+ return OUString();
+
+ OUString sNewNodeId = OStringToOUString(comphelper::xml::generateGUIDString(), RTL_TEXTENCODING_UTF8);
+
+ dgm::Point aDataPoint;
+ aDataPoint.mnType = XML_node;
+ aDataPoint.msModelId = sNewNodeId;
+ aDataPoint.mpShape = std::make_shared<Shape>();
+ aDataPoint.mpShape->setTextBody(std::make_shared<TextBody>());
+ TextRunPtr pTextRun = std::make_shared<TextRun>();
+ pTextRun->getText() = rText;
+ aDataPoint.mpShape->getTextBody()->addParagraph().addRun(pTextRun);
+
+ OUString sDataSibling;
+ for (const auto& aCxn : maConnections)
+ if (aCxn.mnType == XML_parOf && aCxn.msSourceId == rDataRoot.msModelId)
+ sDataSibling = aCxn.msDestId;
+
+ OUString sPresSibling;
+ for (const auto& aCxn : maConnections)
+ if (aCxn.mnType == XML_presOf && aCxn.msSourceId == sDataSibling)
+ sPresSibling = aCxn.msDestId;
+
+ dgm::Point aPresPoint;
+ aPresPoint.mnType = XML_pres;
+ aPresPoint.msModelId = OStringToOUString(comphelper::xml::generateGUIDString(), RTL_TEXTENCODING_UTF8);
+ aPresPoint.mpShape = std::make_shared<Shape>();
+ aPresPoint.msPresentationAssociationId = aDataPoint.msModelId;
+ if (!sPresSibling.isEmpty())
+ {
+ // no idea where to get these values from, so copy from previous sibling
+ const dgm::Point* pSiblingPoint = maPointNameMap[sPresSibling];
+ aPresPoint.msPresentationLayoutName = pSiblingPoint->msPresentationLayoutName;
+ aPresPoint.msPresentationLayoutStyleLabel = pSiblingPoint->msPresentationLayoutStyleLabel;
+ aPresPoint.mnLayoutStyleIndex = pSiblingPoint->mnLayoutStyleIndex;
+ aPresPoint.mnLayoutStyleCount = pSiblingPoint->mnLayoutStyleCount;
+ }
+
+ addConnection(XML_parOf, rDataRoot.msModelId, aDataPoint.msModelId);
+ addConnection(XML_presParOf, sPresRoot, aPresPoint.msModelId);
+ addConnection(XML_presOf, aDataPoint.msModelId, aPresPoint.msModelId);
+
+ // adding at the end, so that references are not invalidated in between
+ maPoints.push_back(aDataPoint);
+ maPoints.push_back(aPresPoint);
+
+ build();
+ return sNewNodeId;
+}
+
+bool DiagramData::removeNode(const OUString& rNodeId)
+{
+ // check if it doesn't have children
+ for (const auto& aCxn : maConnections)
+ if (aCxn.mnType == XML_parOf && aCxn.msSourceId == rNodeId)
+ {
+ SAL_WARN("oox.drawingml", "Node has children - can't be removed");
+ return false;
+ }
+
+ dgm::Connection aParCxn;
+ for (const auto& aCxn : maConnections)
+ if (aCxn.mnType == XML_parOf && aCxn.msDestId == rNodeId)
+ aParCxn = aCxn;
+
+ std::unordered_set<OUString> aIdsToRemove;
+ aIdsToRemove.insert(rNodeId);
+ if (!aParCxn.msParTransId.isEmpty())
+ aIdsToRemove.insert(aParCxn.msParTransId);
+ if (!aParCxn.msSibTransId.isEmpty())
+ aIdsToRemove.insert(aParCxn.msSibTransId);
+
+ for (const dgm::Point& rPoint : maPoints)
+ if (aIdsToRemove.count(rPoint.msPresentationAssociationId))
+ aIdsToRemove.insert(rPoint.msModelId);
+
+ // insert also transition nodes
+ for (const auto& aCxn : maConnections)
+ if (aIdsToRemove.count(aCxn.msSourceId) || aIdsToRemove.count(aCxn.msDestId))
+ if (!aCxn.msPresId.isEmpty())
+ aIdsToRemove.insert(aCxn.msPresId);
+
+ // remove connections
+ maConnections.erase(std::remove_if(maConnections.begin(), maConnections.end(),
+ [aIdsToRemove](const dgm::Connection& rCxn) {
+ return aIdsToRemove.count(rCxn.msSourceId) || aIdsToRemove.count(rCxn.msDestId);
+ }),
+ maConnections.end());
+
+ // remove data and presentation nodes
+ maPoints.erase(std::remove_if(maPoints.begin(), maPoints.end(),
+ [aIdsToRemove](const dgm::Point& rPoint) {
+ return aIdsToRemove.count(rPoint.msModelId);
+ }),
+ maPoints.end());
+
+ // TODO: fix source/dest order
+
+ build();
+ return true;
+}
+
+#ifdef DEBUG_OOX_DIAGRAM
+OString normalizeDotName( const OUString& rStr )
+{
+ OUStringBuffer aBuf;
+ aBuf.append('N');
+
+ const sal_Int32 nLen(rStr.getLength());
+ sal_Int32 nCurrIndex(0);
+ while( nCurrIndex < nLen )
+ {
+ const sal_Int32 aChar=rStr.iterateCodePoints(&nCurrIndex);
+ if( aChar != '-' && aChar != '{' && aChar != '}' )
+ aBuf.append((sal_Unicode)aChar);
+ }
+
+ return OUStringToOString(aBuf.makeStringAndClear(),
+ RTL_TEXTENCODING_UTF8);
+}
+#endif
+
+static sal_Int32 calcDepth( const OUString& rNodeName,
+ const dgm::Connections& rCnx )
+{
+ // find length of longest path in 'isChild' graph, ending with rNodeName
+ for (auto const& elem : rCnx)
+ {
+ if( !elem.msParTransId.isEmpty() &&
+ !elem.msSibTransId.isEmpty() &&
+ !elem.msSourceId.isEmpty() &&
+ !elem.msDestId.isEmpty() &&
+ elem.mnType == XML_parOf &&
+ rNodeName == elem.msDestId )
+ {
+ return calcDepth(elem.msSourceId, rCnx) + 1;
+ }
+ }
+
+ return 0;
+}
+
+void DiagramData::build()
+{
+ // build name-object maps
+ maPointNameMap.clear();
+ maPointsPresNameMap.clear();
+ maConnectionNameMap.clear();
+ maPresOfNameMap.clear();
+
+#ifdef DEBUG_OOX_DIAGRAM
+ std::ofstream output("tree.dot");
+
+ output << "digraph datatree {" << std::endl;
+#endif
+ dgm::Points& rPoints = getPoints();
+ for (auto & point : rPoints)
+ {
+#ifdef DEBUG_OOX_DIAGRAM
+ output << "\t"
+ << normalizeDotName(point.msModelId).getStr()
+ << "[";
+
+ if( !point.msPresentationLayoutName.isEmpty() )
+ output << "label=\""
+ << OUStringToOString(
+ point.msPresentationLayoutName,
+ RTL_TEXTENCODING_UTF8).getStr() << "\", ";
+ else
+ output << "label=\""
+ << OUStringToOString(
+ point.msModelId,
+ RTL_TEXTENCODING_UTF8).getStr() << "\", ";
+
+ switch( point.mnType )
+ {
+ case XML_doc: output << "style=filled, color=red"; break;
+ case XML_asst: output << "style=filled, color=green"; break;
+ default:
+ case XML_node: output << "style=filled, color=blue"; break;
+ case XML_pres: output << "style=filled, color=yellow"; break;
+ case XML_parTrans: output << "color=grey"; break;
+ case XML_sibTrans: output << " "; break;
+ }
+
+ output << "];" << std::endl;
+#endif
+
+ // does currpoint have any text set?
+ if( point.mpShape &&
+ point.mpShape->getTextBody() &&
+ !point.mpShape->getTextBody()->isEmpty() )
+ {
+#ifdef DEBUG_OOX_DIAGRAM
+ static sal_Int32 nCount=0;
+ output << "\t"
+ << "textNode" << nCount
+ << " ["
+ << "label=\""
+ << OUStringToOString(
+ point.mpShape->getTextBody()->toString(),
+ RTL_TEXTENCODING_UTF8).getStr()
+ << "\"" << "];" << std::endl;
+ output << "\t"
+ << normalizeDotName(point.msModelId).getStr()
+ << " -> "
+ << "textNode" << nCount++
+ << ";" << std::endl;
+#endif
+ }
+
+ const bool bInserted1 = getPointNameMap().insert(
+ std::make_pair(point.msModelId,&point)).second;
+
+ SAL_WARN_IF(!bInserted1, "oox.drawingml", "DiagramData::build(): non-unique point model id");
+
+ if( !point.msPresentationLayoutName.isEmpty() )
+ {
+ DiagramData::PointsNameMap::value_type::second_type& rVec=
+ getPointsPresNameMap()[point.msPresentationLayoutName];
+ rVec.push_back(&point);
+ }
+ }
+
+ const dgm::Connections& rConnections = getConnections();
+ for (auto const& connection : rConnections)
+ {
+#ifdef DEBUG_OOX_DIAGRAM
+ if( !connection.msParTransId.isEmpty() ||
+ !connection.msSibTransId.isEmpty() )
+ {
+ if( !connection.msSourceId.isEmpty() ||
+ !connection.msDestId.isEmpty() )
+ {
+ output << "\t"
+ << normalizeDotName(connection.msSourceId).getStr()
+ << " -> "
+ << normalizeDotName(connection.msParTransId).getStr()
+ << " -> "
+ << normalizeDotName(connection.msSibTransId).getStr()
+ << " -> "
+ << normalizeDotName(connection.msDestId).getStr()
+ << " [style=dotted,"
+ << ((connection.mnType == XML_presOf) ? " color=red, " : ((connection.mnType == XML_presParOf) ? " color=green, " : " "))
+ << "label=\""
+ << OUStringToOString(connection.msModelId,
+ RTL_TEXTENCODING_UTF8 ).getStr()
+ << "\"];" << std::endl;
+ }
+ else
+ {
+ output << "\t"
+ << normalizeDotName(connection.msParTransId).getStr()
+ << " -> "
+ << normalizeDotName(connection.msSibTransId).getStr()
+ << " ["
+ << ((connection.mnType == XML_presOf) ? " color=red, " : ((connection.mnType == XML_presParOf) ? " color=green, " : " "))
+ << "label=\""
+ << OUStringToOString(connection.msModelId,
+ RTL_TEXTENCODING_UTF8 ).getStr()
+ << "\"];" << std::endl;
+ }
+ }
+ else if( !connection.msSourceId.isEmpty() ||
+ !connection.msDestId.isEmpty() )
+ output << "\t"
+ << normalizeDotName(connection.msSourceId).getStr()
+ << " -> "
+ << normalizeDotName(connection.msDestId).getStr()
+ << " [label=\""
+ << OUStringToOString(connection.msModelId,
+ RTL_TEXTENCODING_UTF8 ).getStr()
+ << ((connection.mnType == XML_presOf) ? "\", color=red]" : ((connection.mnType == XML_presParOf) ? "\", color=green]" : "\"]"))
+ << ";" << std::endl;
+#endif
+
+ const bool bInserted1 = maConnectionNameMap.insert(
+ std::make_pair(connection.msModelId,&connection)).second;
+
+ SAL_WARN_IF(!bInserted1, "oox.drawingml", "DiagramData::build(): non-unique connection model id");
+
+ if( connection.mnType == XML_presOf )
+ {
+ DiagramData::StringMap::value_type::second_type& rVec = getPresOfNameMap()[connection.msDestId];
+ rVec[connection.mnDestOrder] = { connection.msSourceId, sal_Int32(0) };
+ }
+ }
+
+ // assign outline levels
+ DiagramData::StringMap& rStringMap = getPresOfNameMap();
+ for (auto & elemPresOf : rStringMap)
+ {
+ for (auto & elem : elemPresOf.second)
+ {
+ const sal_Int32 nDepth = calcDepth(elem.second.msSourceId, getConnections());
+ elem.second.mnDepth = nDepth != 0 ? nDepth : -1;
+ }
+ }
+#ifdef DEBUG_OOX_DIAGRAM
+ output << "}" << std::endl;
+#endif
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/oox/source/drawingml/diagram/datamodel.hxx b/oox/source/drawingml/diagram/datamodel.hxx
new file mode 100644
index 000000000..aee953b6f
--- /dev/null
+++ b/oox/source/drawingml/diagram/datamodel.hxx
@@ -0,0 +1,205 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_OOX_SOURCE_DRAWINGML_DIAGRAM_DATAMODEL_HXX
+#define INCLUDED_OOX_SOURCE_DRAWINGML_DIAGRAM_DATAMODEL_HXX
+
+#include <map>
+#include <memory>
+#include <vector>
+
+#include <rtl/ustring.hxx>
+
+#include <oox/drawingml/drawingmltypes.hxx>
+#include <oox/token/tokens.hxx>
+#include <svx/DiagramDataInterface.hxx>
+
+namespace oox::drawingml {
+
+namespace dgm {
+
+/** A Connection
+ */
+struct Connection
+{
+ Connection() :
+ mnType( 0 ),
+ mnSourceOrder( 0 ),
+ mnDestOrder( 0 )
+ {}
+
+ void dump() const;
+
+ sal_Int32 mnType;
+ OUString msModelId;
+ OUString msSourceId;
+ OUString msDestId;
+ OUString msParTransId;
+ OUString msPresId;
+ OUString msSibTransId;
+ sal_Int32 mnSourceOrder;
+ sal_Int32 mnDestOrder;
+
+};
+
+typedef std::vector< Connection > Connections;
+
+/** A point
+ */
+struct Point
+{
+ Point() :
+ mnType(0),
+ mnMaxChildren(-1),
+ mnPreferredChildren(-1),
+ mnDirection(XML_norm),
+ mnResizeHandles(XML_rel),
+ mnCustomAngle(-1),
+ mnPercentageNeighbourWidth(-1),
+ mnPercentageNeighbourHeight(-1),
+ mnPercentageOwnWidth(-1),
+ mnPercentageOwnHeight(-1),
+ mnIncludeAngleScale(-1),
+ mnRadiusScale(-1),
+ mnWidthScale(-1),
+ mnHeightScale(-1),
+ mnWidthOverride(-1),
+ mnHeightOverride(-1),
+ mnLayoutStyleCount(-1),
+ mnLayoutStyleIndex(-1),
+
+ mbOrgChartEnabled(false),
+ mbBulletEnabled(false),
+ mbCoherent3DOffset(false),
+ mbCustomHorizontalFlip(false),
+ mbCustomVerticalFlip(false),
+ mbCustomText(false),
+ mbIsPlaceholder(false)
+ {}
+ void dump() const;
+
+ ShapePtr mpShape;
+
+ OUString msCnxId;
+ OUString msModelId;
+ OUString msColorTransformCategoryId;
+ OUString msColorTransformTypeId;
+ OUString msLayoutCategoryId;
+ OUString msLayoutTypeId;
+ OUString msPlaceholderText;
+ OUString msPresentationAssociationId;
+ OUString msPresentationLayoutName;
+ OUString msPresentationLayoutStyleLabel;
+ OUString msQuickStyleCategoryId;
+ OUString msQuickStyleTypeId;
+
+ sal_Int32 mnType;
+ sal_Int32 mnMaxChildren;
+ sal_Int32 mnPreferredChildren;
+ sal_Int32 mnDirection;
+ OptValue<sal_Int32> moHierarchyBranch;
+ sal_Int32 mnResizeHandles;
+ sal_Int32 mnCustomAngle;
+ sal_Int32 mnPercentageNeighbourWidth;
+ sal_Int32 mnPercentageNeighbourHeight;
+ sal_Int32 mnPercentageOwnWidth;
+ sal_Int32 mnPercentageOwnHeight;
+ sal_Int32 mnIncludeAngleScale;
+ sal_Int32 mnRadiusScale;
+ sal_Int32 mnWidthScale;
+ sal_Int32 mnHeightScale;
+ sal_Int32 mnWidthOverride;
+ sal_Int32 mnHeightOverride;
+ sal_Int32 mnLayoutStyleCount;
+ sal_Int32 mnLayoutStyleIndex;
+
+ bool mbOrgChartEnabled;
+ bool mbBulletEnabled;
+ bool mbCoherent3DOffset;
+ bool mbCustomHorizontalFlip;
+ bool mbCustomVerticalFlip;
+ bool mbCustomText;
+ bool mbIsPlaceholder;
+};
+
+typedef std::vector< Point > Points;
+
+}
+
+class DiagramData : public DiagramDataInterface
+{
+public:
+ typedef std::map< OUString, dgm::Point* > PointNameMap;
+ typedef std::map< OUString,
+ std::vector<dgm::Point*> > PointsNameMap;
+ typedef std::map< OUString, const dgm::Connection* > ConnectionNameMap;
+ struct SourceIdAndDepth
+ {
+ OUString msSourceId;
+ sal_Int32 mnDepth = 0;
+ };
+ /// Tracks connections: destination id -> {destination order, details} map.
+ typedef std::map< OUString,
+ std::map<sal_Int32, SourceIdAndDepth > > StringMap;
+
+ DiagramData();
+ virtual ~DiagramData() {}
+ void build();
+ FillPropertiesPtr & getFillProperties()
+ { return mpFillProperties; }
+ dgm::Connections & getConnections()
+ { return maConnections; }
+ dgm::Points & getPoints()
+ { return maPoints; }
+ StringMap & getPresOfNameMap()
+ { return maPresOfNameMap; }
+ PointNameMap & getPointNameMap()
+ { return maPointNameMap; }
+ PointsNameMap & getPointsPresNameMap()
+ { return maPointsPresNameMap; }
+ ::std::vector<OUString> &getExtDrawings()
+ { return maExtDrawings; }
+ const dgm::Point* getRootPoint() const;
+ void dump() const;
+ OUString getString() const override;
+ std::vector<std::pair<OUString, OUString>> getChildren(const OUString& rParentId) const override;
+ OUString addNode(const OUString& rText) override;
+ bool removeNode(const OUString& rNodeId) override;
+
+private:
+ void getChildrenString(OUStringBuffer& rBuf, const dgm::Point* pPoint, sal_Int32 nLevel) const;
+ void addConnection(sal_Int32 nType, const OUString& sSourceId, const OUString& sDestId);
+
+ ::std::vector<OUString> maExtDrawings;
+ FillPropertiesPtr mpFillProperties;
+ dgm::Connections maConnections;
+ dgm::Points maPoints;
+ PointNameMap maPointNameMap;
+ PointsNameMap maPointsPresNameMap;
+ ConnectionNameMap maConnectionNameMap;
+ StringMap maPresOfNameMap;
+};
+
+typedef std::shared_ptr< DiagramData > DiagramDataPtr;
+
+}
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/oox/source/drawingml/diagram/datamodelcontext.cxx b/oox/source/drawingml/diagram/datamodelcontext.cxx
new file mode 100644
index 000000000..46324c183
--- /dev/null
+++ b/oox/source/drawingml/diagram/datamodelcontext.cxx
@@ -0,0 +1,375 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "datamodelcontext.hxx"
+#include <oox/helper/attributelist.hxx>
+#include <drawingml/misccontexts.hxx>
+#include <drawingml/shapepropertiescontext.hxx>
+#include <drawingml/textbodycontext.hxx>
+#include <oox/token/namespaces.hxx>
+#include <oox/token/tokens.hxx>
+
+using namespace ::oox::core;
+using namespace ::com::sun::star::xml::sax;
+using namespace ::com::sun::star::uno;
+
+namespace oox::drawingml {
+
+namespace {
+
+// CT_CxnList
+class CxnListContext
+ : public ContextHandler2
+{
+public:
+ CxnListContext( ContextHandler2Helper const & rParent,
+ dgm::Connections & aConnections )
+ : ContextHandler2( rParent )
+ , mrConnection( aConnections )
+ {
+ }
+
+ virtual ContextHandlerRef
+ onCreateContext( sal_Int32 aElementToken,
+ const AttributeList& rAttribs ) override
+ {
+ switch( aElementToken )
+ {
+ case DGM_TOKEN( cxn ):
+ {
+ mrConnection.emplace_back( );
+ dgm::Connection& rConnection=mrConnection.back();
+
+ rConnection.mnType = rAttribs.getToken( XML_type, XML_parOf );
+ rConnection.msModelId = rAttribs.getString( XML_modelId ).get();
+ rConnection.msSourceId = rAttribs.getString( XML_srcId ).get();
+ rConnection.msDestId = rAttribs.getString( XML_destId ).get();
+ rConnection.msPresId = rAttribs.getString( XML_presId ).get();
+ rConnection.msSibTransId = rAttribs.getString( XML_sibTransId ).get();
+ rConnection.msParTransId = rAttribs.getString( XML_parTransId ).get();
+ rConnection.mnSourceOrder = rAttribs.getInteger( XML_srcOrd, 0 );
+ rConnection.mnDestOrder = rAttribs.getInteger( XML_destOrd, 0 );
+
+ // skip CT_extLst
+ return nullptr;
+ }
+ default:
+ break;
+ }
+
+ return this;
+ }
+private:
+ dgm::Connections& mrConnection;
+};
+
+// CT_presLayoutVars
+class PresLayoutVarsContext
+ : public ContextHandler2
+{
+public:
+ PresLayoutVarsContext( ContextHandler2Helper const & rParent,
+ dgm::Point & rPoint ) :
+ ContextHandler2( rParent ),
+ mrPoint( rPoint )
+ {
+ }
+ virtual ContextHandlerRef
+ onCreateContext( sal_Int32 aElementToken,
+ const AttributeList& rAttribs ) override
+ {
+ switch( aElementToken )
+ {
+ // TODO
+ case DGM_TOKEN( animLvl ):
+ case DGM_TOKEN( animOne ):
+ break;
+ case DGM_TOKEN( bulletEnabled ):
+ mrPoint.mbBulletEnabled = rAttribs.getBool( XML_val, false );
+ break;
+ case DGM_TOKEN( chMax ):
+ mrPoint.mnMaxChildren = rAttribs.getInteger( XML_val, -1 );
+ break;
+ case DGM_TOKEN( chPref ):
+ mrPoint.mnPreferredChildren = rAttribs.getInteger( XML_val, -1 );
+ break;
+ case DGM_TOKEN( dir ):
+ mrPoint.mnDirection = rAttribs.getToken( XML_val, XML_norm );
+ break;
+ case DGM_TOKEN( hierBranch ):
+ mrPoint.moHierarchyBranch = rAttribs.getToken( XML_val );
+ break;
+ case DGM_TOKEN( orgChart ):
+ mrPoint.mbOrgChartEnabled = rAttribs.getBool( XML_val, false );
+ break;
+ case DGM_TOKEN( resizeHandles ):
+ mrPoint.mnResizeHandles = rAttribs.getToken( XML_val, XML_rel );
+ break;
+ default:
+ break;
+ }
+
+ return this;
+ }
+
+private:
+ dgm::Point& mrPoint;
+};
+
+// CT_prSet
+class PropertiesContext
+ : public ContextHandler2
+{
+public:
+ PropertiesContext( ContextHandler2Helper const & rParent,
+ dgm::Point & rPoint,
+ const AttributeList& rAttribs ) :
+ ContextHandler2( rParent ),
+ mrPoint( rPoint )
+ {
+ mrPoint.msColorTransformCategoryId = rAttribs.getString( XML_csCatId, "" );
+ mrPoint.msColorTransformTypeId = rAttribs.getString( XML_csTypeId, "" );
+ mrPoint.msLayoutCategoryId = rAttribs.getString( XML_loCatId, "" );
+ mrPoint.msLayoutTypeId = rAttribs.getString( XML_loTypeId, "" );
+ mrPoint.msPlaceholderText = rAttribs.getString( XML_phldrT, "" );
+ mrPoint.msPresentationAssociationId = rAttribs.getString( XML_presAssocID, "" );
+ mrPoint.msPresentationLayoutName = rAttribs.getString( XML_presName, "" );
+ mrPoint.msPresentationLayoutStyleLabel = rAttribs.getString( XML_presStyleLbl, "" );
+ mrPoint.msQuickStyleCategoryId = rAttribs.getString( XML_qsCatId, "" );
+ mrPoint.msQuickStyleTypeId = rAttribs.getString( XML_qsTypeId, "" );
+
+ mrPoint.mnCustomAngle = rAttribs.getInteger( XML_custAng, -1 );
+ mrPoint.mnPercentageNeighbourWidth = rAttribs.getInteger( XML_custLinFactNeighborX, -1 );
+ mrPoint.mnPercentageNeighbourHeight = rAttribs.getInteger( XML_custLinFactNeighborY, -1 );
+ mrPoint.mnPercentageOwnWidth = rAttribs.getInteger( XML_custLinFactX, -1 );
+ mrPoint.mnPercentageOwnHeight = rAttribs.getInteger( XML_custLinFactY, -1 );
+ mrPoint.mnIncludeAngleScale = rAttribs.getInteger( XML_custRadScaleInc, -1 );
+ mrPoint.mnRadiusScale = rAttribs.getInteger( XML_custRadScaleRad, -1 );
+ mrPoint.mnWidthScale = rAttribs.getInteger( XML_custScaleX, -1 );
+ mrPoint.mnHeightScale = rAttribs.getInteger( XML_custScaleY, -1 );
+ mrPoint.mnWidthOverride = rAttribs.getInteger( XML_custSzX, -1 );
+ mrPoint.mnHeightOverride = rAttribs.getInteger( XML_custSzY, -1 );
+ mrPoint.mnLayoutStyleCount = rAttribs.getInteger( XML_presStyleCnt, -1 );
+ mrPoint.mnLayoutStyleIndex = rAttribs.getInteger( XML_presStyleIdx, -1 );
+
+ mrPoint.mbCoherent3DOffset = rAttribs.getBool( XML_coherent3DOff, false );
+ mrPoint.mbCustomHorizontalFlip = rAttribs.getBool( XML_custFlipHor, false );
+ mrPoint.mbCustomVerticalFlip = rAttribs.getBool( XML_custFlipVert, false );
+ mrPoint.mbCustomText = rAttribs.getBool( XML_custT, false );
+ mrPoint.mbIsPlaceholder = rAttribs.getBool( XML_phldr, false );
+ }
+
+ virtual ContextHandlerRef
+ onCreateContext( sal_Int32 aElementToken,
+ const AttributeList& ) override
+ {
+ switch( aElementToken )
+ {
+ case DGM_TOKEN( presLayoutVars ):
+ return new PresLayoutVarsContext( *this, mrPoint );
+ case DGM_TOKEN( style ):
+ // skip CT_shapeStyle
+ return nullptr;
+ default:
+ break;
+ }
+ return this;
+ }
+
+private:
+ dgm::Point& mrPoint;
+};
+
+// CL_Pt
+class PtContext
+ : public ContextHandler2
+{
+public:
+ PtContext( ContextHandler2Helper const & rParent,
+ const AttributeList& rAttribs,
+ dgm::Point & rPoint):
+ ContextHandler2( rParent ),
+ mrPoint( rPoint )
+ {
+ mrPoint.msModelId = rAttribs.getString( XML_modelId ).get();
+
+ // the default type is XML_node
+ const sal_Int32 nType = rAttribs.getToken( XML_type, XML_node );
+ mrPoint.mnType = nType;
+
+ // ignore the cxnId unless it is this type. See 5.15.3.1.3 in Primer
+ if( ( nType == XML_parTrans ) || ( nType == XML_sibTrans ) )
+ mrPoint.msCnxId = rAttribs.getString( XML_cxnId ).get();
+ }
+
+ virtual ContextHandlerRef
+ onCreateContext( sal_Int32 aElementToken,
+ const AttributeList& rAttribs ) override
+ {
+ switch( aElementToken )
+ {
+ case DGM_TOKEN( extLst ):
+ return nullptr;
+ case DGM_TOKEN( prSet ):
+ return new PropertiesContext( *this, mrPoint, rAttribs );
+ case DGM_TOKEN( spPr ):
+ if( !mrPoint.mpShape )
+ mrPoint.mpShape = std::make_shared<Shape>();
+ return new ShapePropertiesContext( *this, *(mrPoint.mpShape) );
+ case DGM_TOKEN( t ):
+ {
+ TextBodyPtr xTextBody = std::make_shared<TextBody>();
+ if( !mrPoint.mpShape )
+ mrPoint.mpShape = std::make_shared<Shape>();
+ mrPoint.mpShape->setTextBody( xTextBody );
+ return new TextBodyContext( *this, *xTextBody );
+ }
+ default:
+ break;
+ }
+ return this;
+ }
+
+private:
+ dgm::Point& mrPoint;
+};
+
+// CT_PtList
+class PtListContext
+ : public ContextHandler2
+{
+public:
+ PtListContext( ContextHandler2Helper const & rParent, dgm::Points& rPoints) :
+ ContextHandler2( rParent ),
+ mrPoints( rPoints )
+ {}
+ virtual ContextHandlerRef
+ onCreateContext( sal_Int32 aElementToken,
+ const AttributeList& rAttribs ) override
+ {
+ switch( aElementToken )
+ {
+ case DGM_TOKEN( pt ):
+ {
+ // CT_Pt
+ mrPoints.emplace_back( );
+ return new PtContext( *this, rAttribs, mrPoints.back() );
+ }
+ default:
+ break;
+ }
+ return this;
+ }
+
+private:
+ dgm::Points& mrPoints;
+};
+
+// CT_BackgroundFormatting
+class BackgroundFormattingContext
+ : public ContextHandler2
+{
+public:
+ BackgroundFormattingContext( ContextHandler2Helper const & rParent, DiagramDataPtr const & pModel )
+ : ContextHandler2( rParent )
+ , mpDataModel( pModel )
+ {
+ assert( pModel && "the data model MUST NOT be NULL" );
+ }
+
+ virtual ContextHandlerRef
+ onCreateContext( sal_Int32 aElementToken,
+ const AttributeList& rAttribs ) override
+ {
+ switch( aElementToken )
+ {
+ case A_TOKEN( blipFill ):
+ case A_TOKEN( gradFill ):
+ case A_TOKEN( grpFill ):
+ case A_TOKEN( noFill ):
+ case A_TOKEN( pattFill ):
+ case A_TOKEN( solidFill ):
+ // EG_FillProperties
+ return FillPropertiesContext::createFillContext(
+ *this, aElementToken, rAttribs, *mpDataModel->getFillProperties() );
+ case A_TOKEN( effectDag ):
+ case A_TOKEN( effectLst ):
+ // TODO
+ // EG_EffectProperties
+ break;
+ default:
+ break;
+ }
+ return this;
+ }
+private:
+ DiagramDataPtr mpDataModel;
+};
+
+}
+
+DataModelContext::DataModelContext( ContextHandler2Helper const & rParent,
+ const DiagramDataPtr & pDataModel )
+ : ContextHandler2( rParent )
+ , mpDataModel( pDataModel )
+{
+ assert( pDataModel && "Data Model must not be NULL" );
+}
+
+DataModelContext::~DataModelContext()
+{
+ // some debug
+ mpDataModel->dump();
+}
+
+ContextHandlerRef
+DataModelContext::onCreateContext( ::sal_Int32 aElement,
+ const AttributeList& rAttribs )
+{
+ switch( aElement )
+ {
+ case DGM_TOKEN( cxnLst ):
+ // CT_CxnList
+ return new CxnListContext( *this, mpDataModel->getConnections() );
+ case DGM_TOKEN( ptLst ):
+ // CT_PtList
+ return new PtListContext( *this, mpDataModel->getPoints() );
+ case DGM_TOKEN( bg ):
+ // CT_BackgroundFormatting
+ return new BackgroundFormattingContext( *this, mpDataModel );
+ case DGM_TOKEN( whole ):
+ // CT_WholeE2oFormatting
+ // TODO
+ return nullptr;
+ case DGM_TOKEN( extLst ):
+ case A_TOKEN( ext ):
+ break;
+ case DSP_TOKEN( dataModelExt ):
+ mpDataModel->getExtDrawings().push_back( rAttribs.getString( XML_relId ).get() );
+ break;
+ default:
+ break;
+ }
+
+ return this;
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/oox/source/drawingml/diagram/datamodelcontext.hxx b/oox/source/drawingml/diagram/datamodelcontext.hxx
new file mode 100644
index 000000000..4148a0172
--- /dev/null
+++ b/oox/source/drawingml/diagram/datamodelcontext.hxx
@@ -0,0 +1,45 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_OOX_SOURCE_DRAWINGML_DIAGRAM_DATAMODELCONTEXT_HXX
+#define INCLUDED_OOX_SOURCE_DRAWINGML_DIAGRAM_DATAMODELCONTEXT_HXX
+
+#include <oox/core/contexthandler2.hxx>
+#include "datamodel.hxx"
+
+namespace oox::drawingml {
+
+// CT_DataModel
+class DataModelContext final : public ::oox::core::ContextHandler2
+{
+public:
+ DataModelContext( ::oox::core::ContextHandler2Helper const & rParent, const DiagramDataPtr & pDataModelPtr );
+ virtual ~DataModelContext() override;
+
+ virtual ::oox::core::ContextHandlerRef onCreateContext( ::sal_Int32 Element, const ::oox::AttributeList& rAttribs ) override;
+
+private:
+ DiagramDataPtr mpDataModel;
+};
+
+}
+
+#endif // INCLUDED_OOX_SOURCE_DRAWINGML_DIAGRAM_DATAMODELCONTEXT_HXX
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/oox/source/drawingml/diagram/diagram.cxx b/oox/source/drawingml/diagram/diagram.cxx
new file mode 100644
index 000000000..d9f35df42
--- /dev/null
+++ b/oox/source/drawingml/diagram/diagram.cxx
@@ -0,0 +1,468 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <oox/drawingml/diagram/diagram.hxx>
+#include "diagram.hxx"
+#include <com/sun/star/awt/Point.hpp>
+#include <com/sun/star/awt/Size.hpp>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/drawing/XShape.hpp>
+#include <com/sun/star/drawing/XShapes.hpp>
+#include <com/sun/star/xml/dom/XDocument.hpp>
+#include <com/sun/star/xml/sax/XFastSAXSerializable.hpp>
+#include <sal/log.hxx>
+#include <editeng/unoprnms.hxx>
+#include <drawingml/fillproperties.hxx>
+#include <drawingml/customshapeproperties.hxx>
+#include <oox/token/namespaces.hxx>
+#include <basegfx/matrix/b2dhommatrix.hxx>
+#include <svx/svdpage.hxx>
+
+#include "diagramlayoutatoms.hxx"
+#include "layoutatomvisitors.hxx"
+#include "diagramfragmenthandler.hxx"
+
+using namespace ::com::sun::star;
+
+namespace oox::drawingml {
+
+static void sortChildrenByZOrder(const ShapePtr& pShape)
+{
+ std::vector<ShapePtr>& rChildren = pShape->getChildren();
+
+ // Offset the children from their default z-order stacking, if necessary.
+ 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(); });
+
+ // Apply also for children.
+ for (const auto& rChild : rChildren)
+ sortChildrenByZOrder(rChild);
+}
+
+/// Removes empty group shapes, now that their spacing influenced the layout.
+static void removeUnneededGroupShapes(const ShapePtr& pShape)
+{
+ std::vector<ShapePtr>& rChildren = pShape->getChildren();
+
+ rChildren.erase(std::remove_if(rChildren.begin(), rChildren.end(),
+ [](const ShapePtr& aChild) {
+ return aChild->getServiceName()
+ == "com.sun.star.drawing.GroupShape"
+ && aChild->getChildren().empty();
+ }),
+ rChildren.end());
+
+ for (const auto& pChild : rChildren)
+ {
+ removeUnneededGroupShapes(pChild);
+ }
+}
+
+void Diagram::addTo( const ShapePtr & pParentShape )
+{
+ if (pParentShape->getSize().Width == 0 || pParentShape->getSize().Height == 0)
+ SAL_WARN("oox.drawingml", "Diagram cannot be correctly laid out. Size: "
+ << pParentShape->getSize().Width << "x" << pParentShape->getSize().Height);
+
+ pParentShape->setChildSize(pParentShape->getSize());
+
+ const dgm::Point* pRootPoint = mpData->getRootPoint();
+ if (mpLayout->getNode() && pRootPoint)
+ {
+ // create Shape hierarchy
+ ShapeCreationVisitor aCreationVisitor(*this, pRootPoint, pParentShape);
+ mpLayout->getNode()->setExistingShape(pParentShape);
+ mpLayout->getNode()->accept(aCreationVisitor);
+
+ // layout shapes - now all shapes are created
+ ShapeLayoutingVisitor aLayoutingVisitor(*this, pRootPoint);
+ mpLayout->getNode()->accept(aLayoutingVisitor);
+
+ sortChildrenByZOrder(pParentShape);
+ removeUnneededGroupShapes(pParentShape);
+ }
+
+ ShapePtr pBackground = std::make_shared<Shape>("com.sun.star.drawing.CustomShape");
+ pBackground->setSubType(XML_rect);
+ pBackground->getCustomShapeProperties()->setShapePresetType(XML_rect);
+ pBackground->setSize(pParentShape->getSize());
+ pBackground->getFillProperties() = *mpData->getFillProperties();
+ pBackground->setLocked(true);
+ auto& aChildren = pParentShape->getChildren();
+ aChildren.insert(aChildren.begin(), pBackground);
+}
+
+Diagram::Diagram(const ShapePtr& pShape)
+ : mpShape(pShape)
+{
+}
+
+uno::Sequence<beans::PropertyValue> Diagram::getDomsAsPropertyValues() const
+{
+ sal_Int32 length = maMainDomMap.size();
+
+ if (maDataRelsMap.hasElements())
+ ++length;
+
+ uno::Sequence<beans::PropertyValue> aValue(length);
+ beans::PropertyValue* pValue = aValue.getArray();
+ for (auto const& mainDom : maMainDomMap)
+ {
+ pValue->Name = mainDom.first;
+ pValue->Value <<= mainDom.second;
+ ++pValue;
+ }
+
+ if (maDataRelsMap.hasElements())
+ {
+ pValue->Name = "OOXDiagramDataRels";
+ pValue->Value <<= maDataRelsMap;
+ ++pValue;
+ }
+
+ return aValue;
+}
+
+static uno::Reference<xml::dom::XDocument> loadFragment(
+ core::XmlFilterBase& rFilter,
+ const OUString& rFragmentPath )
+{
+ // load diagramming fragments into DOM representation, that later
+ // gets serialized back to SAX events and parsed
+ return rFilter.importFragment( rFragmentPath );
+}
+
+static uno::Reference<xml::dom::XDocument> loadFragment(
+ core::XmlFilterBase& rFilter,
+ const rtl::Reference< core::FragmentHandler >& rxHandler )
+{
+ return loadFragment( rFilter, rxHandler->getFragmentPath() );
+}
+
+static void importFragment( core::XmlFilterBase& rFilter,
+ const uno::Reference<xml::dom::XDocument>& rXDom,
+ const OUString& rDocName,
+ const DiagramPtr& pDiagram,
+ const rtl::Reference< core::FragmentHandler >& rxHandler )
+{
+ DiagramDomMap& rMainDomMap = pDiagram->getDomMap();
+ rMainDomMap[rDocName] = rXDom;
+
+ uno::Reference<xml::sax::XFastSAXSerializable> xSerializer(
+ rXDom, uno::UNO_QUERY_THROW);
+
+ // now serialize DOM tree into internal data structures
+ 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 oox::core::Relations& rRelations )
+{
+ DiagramPtr pDiagram = std::make_shared<Diagram>(pShape);
+
+ DiagramDataPtr pData = std::make_shared<DiagramData>();
+ pDiagram->setData( pData );
+
+ DiagramLayoutPtr pLayout = std::make_shared<DiagramLayout>(*pDiagram);
+ pDiagram->setLayout( pLayout );
+
+ // data
+ if( !rDataModelPath.isEmpty() )
+ {
+ rtl::Reference< core::FragmentHandler > xRefDataModel(
+ new DiagramDataFragmentHandler( rFilter, rDataModelPath, pData ));
+
+ importFragment(rFilter,
+ loadFragment(rFilter,xRefDataModel),
+ "OOXData",
+ pDiagram,
+ xRefDataModel);
+
+ pDiagram->getDataRelsMap() = pShape->resolveRelationshipsOfTypeFromOfficeDoc( rFilter,
+ xRefDataModel->getFragmentPath(), "image" );
+
+ // Pass the info to pShape
+ for (auto const& extDrawing : pData->getExtDrawings())
+ {
+ 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( pShape->getExtDrawings().empty() )
+ {
+ // layout
+ if( !rLayoutPath.isEmpty() )
+ {
+ rtl::Reference< core::FragmentHandler > xRefLayout(
+ new DiagramLayoutFragmentHandler( rFilter, rLayoutPath, pLayout ));
+
+ importFragment(rFilter,
+ loadFragment(rFilter,xRefLayout),
+ "OOXLayout",
+ pDiagram,
+ xRefLayout);
+ }
+
+ // style
+ if( !rQStylePath.isEmpty() )
+ {
+ rtl::Reference< core::FragmentHandler > xRefQStyle(
+ new DiagramQStylesFragmentHandler( rFilter, rQStylePath, pDiagram->getStyles() ));
+
+ importFragment(rFilter,
+ loadFragment(rFilter,xRefQStyle),
+ "OOXStyle",
+ pDiagram,
+ xRefQStyle);
+ }
+ }
+ else
+ {
+ // We still want to add the XDocuments to the DiagramDomMap
+ DiagramDomMap& rMainDomMap = pDiagram->getDomMap();
+ rMainDomMap[OUString("OOXLayout")] = loadFragment(rFilter,rLayoutPath);
+ rMainDomMap[OUString("OOXStyle")] = loadFragment(rFilter,rQStylePath);
+ }
+
+ // colors
+ if( !rColorStylePath.isEmpty() )
+ {
+ rtl::Reference< core::FragmentHandler > xRefColorStyle(
+ new ColorFragmentHandler( rFilter, rColorStylePath, pDiagram->getColors() ));
+
+ importFragment(rFilter,
+ loadFragment(rFilter,xRefColorStyle),
+ "OOXColor",
+ pDiagram,
+ xRefColorStyle);
+ }
+
+ if( !pData->getExtDrawings().empty() )
+ {
+ const DiagramColorMap::const_iterator aColor = pDiagram->getColors().find("node0");
+ if( aColor != pDiagram->getColors().end() && !aColor->second.maTextFillColors.empty())
+ {
+ // TODO(F1): well, actually, there might be *several* color
+ // definitions in it, after all it's called list.
+ pShape->setFontRefColorForNodes(DiagramColor::getColorByIndex(aColor->second.maTextFillColors, -1));
+ }
+ }
+
+ // collect data, init maps
+ pData->build();
+
+ // diagram loaded. now lump together & attach to shape
+ pDiagram->addTo(pShape);
+ pShape->setDiagramData(pData);
+ pShape->setDiagramDoms(pDiagram->getDomsAsPropertyValues());
+}
+
+void loadDiagram(ShapePtr const& pShape,
+ DiagramDataPtr pDiagramData,
+ const uno::Reference<xml::dom::XDocument>& layoutDom,
+ const uno::Reference<xml::dom::XDocument>& styleDom,
+ const uno::Reference<xml::dom::XDocument>& colorDom,
+ core::XmlFilterBase& rFilter)
+{
+ DiagramPtr pDiagram = std::make_shared<Diagram>(pShape);
+
+ pDiagram->setData(pDiagramData);
+
+ DiagramLayoutPtr pLayout = std::make_shared<DiagramLayout>(*pDiagram);
+ pDiagram->setLayout(pLayout);
+
+ // layout
+ if (layoutDom.is())
+ {
+ rtl::Reference<core::FragmentHandler> xRefLayout(
+ new DiagramLayoutFragmentHandler(rFilter, OUString(), pLayout));
+
+ importFragment(rFilter, layoutDom, "OOXLayout", pDiagram, xRefLayout);
+ }
+
+ // style
+ if (styleDom.is())
+ {
+ rtl::Reference<core::FragmentHandler> xRefQStyle(
+ new DiagramQStylesFragmentHandler(rFilter, OUString(), pDiagram->getStyles()));
+
+ importFragment(rFilter, styleDom, "OOXStyle", pDiagram, xRefQStyle);
+ }
+
+ // colors
+ if (colorDom.is())
+ {
+ rtl::Reference<core::FragmentHandler> xRefColorStyle(
+ new ColorFragmentHandler(rFilter, OUString(), pDiagram->getColors()));
+
+ importFragment(rFilter, colorDom, "OOXColor", pDiagram, xRefColorStyle);
+ }
+
+ // diagram loaded. now lump together & attach to shape
+ pDiagram->addTo(pShape);
+}
+
+void reloadDiagram(SdrObject* pObj, core::XmlFilterBase& rFilter)
+{
+ DiagramDataPtr pDiagramData = std::dynamic_pointer_cast<DiagramData>(pObj->GetDiagramData());
+ if (!pDiagramData)
+ return;
+
+ pObj->getChildrenOfSdrObject()->ClearSdrObjList();
+
+ uno::Reference<css::drawing::XShape> xShape(pObj->getUnoShape(), uno::UNO_QUERY_THROW);
+ uno::Reference<beans::XPropertySet> xPropSet(xShape, uno::UNO_QUERY_THROW);
+
+ uno::Reference<xml::dom::XDocument> layoutDom;
+ uno::Reference<xml::dom::XDocument> styleDom;
+ uno::Reference<xml::dom::XDocument> colorDom;
+
+ // retrieve the doms from the GrabBag
+ uno::Sequence<beans::PropertyValue> propList;
+ xPropSet->getPropertyValue(UNO_NAME_MISC_OBJ_INTEROPGRABBAG) >>= propList;
+ for (const auto& rProp : std::as_const(propList))
+ {
+ OUString propName = rProp.Name;
+ if (propName == "OOXLayout")
+ rProp.Value >>= layoutDom;
+ else if (propName == "OOXStyle")
+ rProp.Value >>= styleDom;
+ else if (propName == "OOXColor")
+ rProp.Value >>= colorDom;
+ }
+
+ ShapePtr pShape = std::make_shared<Shape>();
+ pShape->setDiagramType();
+ pShape->setSize(awt::Size(xShape->getSize().Width * EMU_PER_HMM,
+ xShape->getSize().Height * EMU_PER_HMM));
+
+ loadDiagram(pShape, pDiagramData, layoutDom, styleDom, colorDom, rFilter);
+
+ uno::Reference<drawing::XShapes> xShapes(xShape, uno::UNO_QUERY_THROW);
+ basegfx::B2DHomMatrix aTransformation;
+ aTransformation.translate(xShape->getPosition().X * EMU_PER_HMM,
+ xShape->getPosition().Y * EMU_PER_HMM);
+ for (auto const& child : pShape->getChildren())
+ child->addShape(rFilter, rFilter.getCurrentTheme(), xShapes, aTransformation, pShape->getFillProperties());
+}
+
+const oox::drawingml::Color&
+DiagramColor::getColorByIndex(const std::vector<oox::drawingml::Color>& rColors, sal_Int32 nIndex)
+{
+ assert(!rColors.empty());
+ if (nIndex == -1)
+ {
+ return rColors[rColors.size() - 1];
+ }
+
+ return rColors[nIndex % rColors.size()];
+}
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/oox/source/drawingml/diagram/diagram.hxx b/oox/source/drawingml/diagram/diagram.hxx
new file mode 100644
index 000000000..25fa10ed0
--- /dev/null
+++ b/oox/source/drawingml/diagram/diagram.hxx
@@ -0,0 +1,170 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_OOX_SOURCE_DRAWINGML_DIAGRAM_DIAGRAM_HXX
+#define INCLUDED_OOX_SOURCE_DRAWINGML_DIAGRAM_DIAGRAM_HXX
+
+#include <map>
+#include <memory>
+#include <vector>
+
+#include <rtl/ustring.hxx>
+
+#include "datamodel.hxx"
+#include <oox/drawingml/shape.hxx>
+
+namespace com::sun::star {
+ namespace xml::dom { class XDocument; }
+}
+
+namespace oox::drawingml {
+
+class Diagram;
+class LayoutNode;
+typedef std::shared_ptr< LayoutNode > LayoutNodePtr;
+class LayoutAtom;
+typedef std::shared_ptr<LayoutAtom> LayoutAtomPtr;
+
+typedef std::map< OUString, css::uno::Reference<css::xml::dom::XDocument> > DiagramDomMap;
+
+typedef std::map<OUString, LayoutAtomPtr> LayoutAtomMap;
+typedef std::map<const dgm::Point*, ShapePtr> PresPointShapeMap;
+
+class DiagramLayout
+{
+public:
+ DiagramLayout(Diagram& rDgm)
+ : mrDgm(rDgm)
+ {
+ }
+ void setDefStyle( const OUString & sDefStyle )
+ { msDefStyle = sDefStyle; }
+ void setMinVer( const OUString & sMinVer )
+ { msMinVer = sMinVer; }
+ void setUniqueId( const OUString & sUniqueId )
+ { msUniqueId = sUniqueId; }
+ void setTitle( const OUString & sTitle )
+ { msTitle = sTitle; }
+ void setDesc( const OUString & sDesc )
+ { msDesc = sDesc; }
+ Diagram& getDiagram() { return mrDgm; }
+ LayoutNodePtr & getNode()
+ { return mpNode; }
+ const LayoutNodePtr & getNode() const
+ { return mpNode; }
+ DiagramDataPtr & getSampData()
+ { return mpSampData; }
+ const DiagramDataPtr & getSampData() const
+ { return mpSampData; }
+ DiagramDataPtr & getStyleData()
+ { return mpStyleData; }
+ const DiagramDataPtr & getStyleData() const
+ { return mpStyleData; }
+ LayoutAtomMap & getLayoutAtomMap()
+ { return maLayoutAtomMap; }
+ PresPointShapeMap & getPresPointShapeMap()
+ { return maPresPointShapeMap; }
+
+private:
+ Diagram& mrDgm;
+ OUString msDefStyle;
+ OUString msMinVer;
+ OUString msUniqueId;
+
+ OUString msTitle;
+ OUString msDesc;
+ LayoutNodePtr mpNode;
+ DiagramDataPtr mpSampData;
+ DiagramDataPtr mpStyleData;
+ // TODO
+ // catLst
+ // clrData
+
+ LayoutAtomMap maLayoutAtomMap;
+ PresPointShapeMap maPresPointShapeMap;
+};
+
+typedef std::shared_ptr< DiagramLayout > DiagramLayoutPtr;
+
+struct DiagramStyle
+{
+ ShapeStyleRef maFillStyle;
+ ShapeStyleRef maLineStyle;
+ ShapeStyleRef maEffectStyle;
+ ShapeStyleRef maTextStyle;
+};
+
+typedef std::map<OUString,DiagramStyle> DiagramQStyleMap;
+
+struct DiagramColor
+{
+ std::vector<oox::drawingml::Color> maFillColors;
+ std::vector<oox::drawingml::Color> maLineColors;
+ std::vector<oox::drawingml::Color> maEffectColors;
+ std::vector<oox::drawingml::Color> maTextFillColors;
+ std::vector<oox::drawingml::Color> maTextLineColors;
+ std::vector<oox::drawingml::Color> maTextEffectColors;
+
+ static const oox::drawingml::Color&
+ getColorByIndex(const std::vector<oox::drawingml::Color>& rColors, sal_Int32 nIndex);
+};
+
+typedef std::map<OUString,DiagramColor> DiagramColorMap;
+
+class Diagram
+{
+public:
+ explicit Diagram(const ShapePtr& pShape);
+ void setData( const DiagramDataPtr & pData )
+ { mpData = pData; }
+ const DiagramDataPtr& getData() const
+ { return mpData; }
+ void setLayout( const DiagramLayoutPtr & pLayout )
+ { mpLayout = pLayout; }
+ const DiagramLayoutPtr& getLayout() const
+ { return mpLayout; }
+
+ DiagramQStyleMap& getStyles() { return maStyles; }
+ const DiagramQStyleMap& getStyles() const { return maStyles; }
+ DiagramColorMap& getColors() { return maColors; }
+ const DiagramColorMap& getColors() const { return maColors; }
+ DiagramDomMap & getDomMap() { return maMainDomMap; }
+ css::uno::Sequence< css::uno::Sequence< css::uno::Any > > & getDataRelsMap() { return maDataRelsMap; }
+ void addTo( const ShapePtr & pShape );
+
+ css::uno::Sequence<css::beans::PropertyValue> getDomsAsPropertyValues() const;
+ ShapePtr getShape() { return mpShape; }
+
+private:
+ ShapePtr mpShape;
+ DiagramDataPtr mpData;
+ DiagramLayoutPtr mpLayout;
+ DiagramQStyleMap maStyles;
+ DiagramColorMap maColors;
+ DiagramDomMap maMainDomMap;
+ css::uno::Sequence< css::uno::Sequence< css::uno::Any > > maDataRelsMap;
+};
+
+typedef std::shared_ptr< Diagram > DiagramPtr;
+
+}
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/oox/source/drawingml/diagram/diagramdefinitioncontext.cxx b/oox/source/drawingml/diagram/diagramdefinitioncontext.cxx
new file mode 100644
index 000000000..02d377703
--- /dev/null
+++ b/oox/source/drawingml/diagram/diagramdefinitioncontext.cxx
@@ -0,0 +1,99 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "diagramdefinitioncontext.hxx"
+#include "datamodel.hxx"
+#include "datamodelcontext.hxx"
+#include "layoutnodecontext.hxx"
+#include <oox/helper/attributelist.hxx>
+#include <oox/token/namespaces.hxx>
+
+using namespace ::oox::core;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::xml::sax;
+
+namespace oox::drawingml {
+
+// CT_DiagramDefinition
+DiagramDefinitionContext::DiagramDefinitionContext( ContextHandler2Helper const & rParent,
+ const AttributeList& rAttributes,
+ const DiagramLayoutPtr &pLayout )
+ : ContextHandler2( rParent )
+ , mpLayout( pLayout )
+{
+ mpLayout->setDefStyle( rAttributes.getString( XML_defStyle ).get() );
+ OUString sValue = rAttributes.getString( XML_minVer ).get();
+ if( sValue.isEmpty() )
+ {
+ sValue = "http://schemas.openxmlformats.org/drawingml/2006/diagram";
+ }
+ mpLayout->setMinVer( sValue );
+ mpLayout->setUniqueId( rAttributes.getString( XML_uniqueId ).get() );
+}
+
+DiagramDefinitionContext::~DiagramDefinitionContext()
+{
+ LayoutNodePtr node = mpLayout->getNode();
+ if (node)
+ node->dump();
+}
+
+ContextHandlerRef
+DiagramDefinitionContext::onCreateContext( ::sal_Int32 aElement,
+ const AttributeList& rAttribs )
+{
+ switch( aElement )
+ {
+ case DGM_TOKEN( title ):
+ mpLayout->setTitle( rAttribs.getString( XML_val ).get() );
+ break;
+ case DGM_TOKEN( desc ):
+ mpLayout->setDesc( rAttribs.getString( XML_val ).get() );
+ break;
+ case DGM_TOKEN( layoutNode ):
+ {
+ LayoutNodePtr pNode = std::make_shared<LayoutNode>(mpLayout->getDiagram());
+ mpLayout->getNode() = pNode;
+ pNode->setChildOrder( rAttribs.getToken( XML_chOrder, XML_b ) );
+ pNode->setMoveWith( rAttribs.getString( XML_moveWith ).get() );
+ pNode->setStyleLabel( rAttribs.getString( XML_styleLbl ).get() );
+ return new LayoutNodeContext( *this, rAttribs, pNode );
+ }
+ case DGM_TOKEN( clrData ):
+ // TODO, does not matter for the UI. skip.
+ return nullptr;
+ case DGM_TOKEN( sampData ):
+ mpLayout->getSampData() = std::make_shared<DiagramData>();
+ return new DataModelContext( *this, mpLayout->getSampData() );
+ case DGM_TOKEN( styleData ):
+ mpLayout->getStyleData() = std::make_shared<DiagramData>();
+ return new DataModelContext( *this, mpLayout->getStyleData() );
+ case DGM_TOKEN( cat ):
+ case DGM_TOKEN( catLst ):
+ // TODO, does not matter for the UI
+ default:
+ break;
+ }
+
+ return this;
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/oox/source/drawingml/diagram/diagramdefinitioncontext.hxx b/oox/source/drawingml/diagram/diagramdefinitioncontext.hxx
new file mode 100644
index 000000000..e92460f5d
--- /dev/null
+++ b/oox/source/drawingml/diagram/diagramdefinitioncontext.hxx
@@ -0,0 +1,44 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_OOX_SOURCE_DRAWINGML_DIAGRAM_DIAGRAMDEFINITIONCONTEXT_HXX
+#define INCLUDED_OOX_SOURCE_DRAWINGML_DIAGRAM_DIAGRAMDEFINITIONCONTEXT_HXX
+
+#include <oox/core/contexthandler2.hxx>
+#include "diagram.hxx"
+
+namespace oox::drawingml {
+
+class DiagramDefinitionContext : public ::oox::core::ContextHandler2
+{
+public:
+ DiagramDefinitionContext( ::oox::core::ContextHandler2Helper const & rParent, const ::oox::AttributeList& rAttributes, const DiagramLayoutPtr &pLayout );
+ virtual ~DiagramDefinitionContext() override;
+
+ virtual ::oox::core::ContextHandlerRef onCreateContext( ::sal_Int32 Element, const ::oox::AttributeList& rAttribs ) override;
+
+private:
+ DiagramLayoutPtr mpLayout;
+};
+
+}
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/oox/source/drawingml/diagram/diagramfragmenthandler.cxx b/oox/source/drawingml/diagram/diagramfragmenthandler.cxx
new file mode 100644
index 000000000..de0018106
--- /dev/null
+++ b/oox/source/drawingml/diagram/diagramfragmenthandler.cxx
@@ -0,0 +1,236 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#include "diagramdefinitioncontext.hxx"
+#include "diagramfragmenthandler.hxx"
+#include "datamodelcontext.hxx"
+#include <drawingml/colorchoicecontext.hxx>
+#include <oox/helper/attributelist.hxx>
+#include <oox/token/namespaces.hxx>
+
+using namespace ::oox::core;
+using namespace ::com::sun::star::xml::sax;
+using namespace ::com::sun::star::uno;
+
+namespace oox::drawingml {
+
+DiagramDataFragmentHandler::DiagramDataFragmentHandler( XmlFilterBase& rFilter,
+ const OUString& rFragmentPath,
+ const DiagramDataPtr& rDataPtr )
+ : FragmentHandler2( rFilter, rFragmentPath )
+ , mpDataPtr( rDataPtr )
+{
+}
+
+DiagramDataFragmentHandler::~DiagramDataFragmentHandler( ) throw ()
+{
+
+}
+
+void SAL_CALL DiagramDataFragmentHandler::endDocument()
+{
+
+}
+
+ContextHandlerRef
+DiagramDataFragmentHandler::onCreateContext( ::sal_Int32 aElement,
+ const AttributeList& )
+{
+ switch( aElement )
+ {
+ case DGM_TOKEN( dataModel ):
+ return new DataModelContext( *this, mpDataPtr );
+ default:
+ break;
+ }
+
+ return this;
+}
+
+DiagramLayoutFragmentHandler::DiagramLayoutFragmentHandler( XmlFilterBase& rFilter,
+ const OUString& rFragmentPath,
+ const DiagramLayoutPtr& rDataPtr )
+ : FragmentHandler2( rFilter, rFragmentPath )
+ , mpDataPtr( rDataPtr )
+{
+}
+
+DiagramLayoutFragmentHandler::~DiagramLayoutFragmentHandler( ) throw ()
+{
+
+}
+
+void SAL_CALL DiagramLayoutFragmentHandler::endDocument()
+{
+
+}
+
+ContextHandlerRef
+DiagramLayoutFragmentHandler::onCreateContext( ::sal_Int32 aElement,
+ const AttributeList& rAttribs )
+{
+ switch( aElement )
+ {
+ case DGM_TOKEN( layoutDef ):
+ return new DiagramDefinitionContext( *this, rAttribs, mpDataPtr );
+ default:
+ break;
+ }
+
+ return this;
+}
+
+DiagramQStylesFragmentHandler::DiagramQStylesFragmentHandler( XmlFilterBase& rFilter,
+ const OUString& rFragmentPath,
+ DiagramQStyleMap& rStylesMap ) :
+ FragmentHandler2( rFilter, rFragmentPath ),
+ maStyleName(),
+ maStyleEntry(),
+ mrStylesMap( rStylesMap )
+{}
+
+::oox::core::ContextHandlerRef DiagramQStylesFragmentHandler::createStyleMatrixContext(
+ sal_Int32 nElement,
+ const AttributeList& rAttribs,
+ ShapeStyleRef& o_rStyle )
+{
+ o_rStyle.mnThemedIdx = (nElement == A_TOKEN(fontRef)) ?
+ rAttribs.getToken( XML_idx, XML_none ) : rAttribs.getInteger( XML_idx, 0 );
+ return new ColorContext( *this, o_rStyle.maPhClr );
+}
+
+::oox::core::ContextHandlerRef DiagramQStylesFragmentHandler::onCreateContext( sal_Int32 nElement,
+ const AttributeList& rAttribs )
+{
+ // state-table like way of navigating the color fragment. we
+ // currently ignore everything except styleLbl in the styleDef
+ // element
+ switch( getCurrentElement() )
+ {
+ case XML_ROOT_CONTEXT:
+ return nElement == DGM_TOKEN(styleDef) ? this : nullptr;
+ case DGM_TOKEN(styleDef):
+ return nElement == DGM_TOKEN(styleLbl) ? this : nullptr;
+ case DGM_TOKEN(styleLbl):
+ return nElement == DGM_TOKEN(style) ? this : nullptr;
+ case DGM_TOKEN(style):
+ {
+ switch( nElement )
+ {
+ case A_TOKEN(lnRef): // CT_StyleMatrixReference
+ return createStyleMatrixContext(nElement,rAttribs,
+ maStyleEntry.maLineStyle);
+ case A_TOKEN(fillRef): // CT_StyleMatrixReference
+ return createStyleMatrixContext(nElement,rAttribs,
+ maStyleEntry.maFillStyle);
+ case A_TOKEN(effectRef): // CT_StyleMatrixReference
+ return createStyleMatrixContext(nElement,rAttribs,
+ maStyleEntry.maEffectStyle);
+ case A_TOKEN(fontRef): // CT_FontReference
+ return createStyleMatrixContext(nElement,rAttribs,
+ maStyleEntry.maTextStyle);
+ }
+ return nullptr;
+ }
+ }
+
+ return nullptr;
+}
+
+void DiagramQStylesFragmentHandler::onStartElement( const AttributeList& rAttribs )
+{
+ if( getCurrentElement() == DGM_TOKEN( styleLbl ) )
+ {
+ maStyleName = rAttribs.getString( XML_name, OUString() );
+ maStyleEntry = mrStylesMap[maStyleName];
+ }
+}
+
+void DiagramQStylesFragmentHandler::onEndElement( )
+{
+ if( getCurrentElement() == DGM_TOKEN(styleLbl) )
+ mrStylesMap[maStyleName] = maStyleEntry;
+}
+
+ColorFragmentHandler::ColorFragmentHandler( ::oox::core::XmlFilterBase& rFilter,
+ const OUString& rFragmentPath,
+ DiagramColorMap& rColorsMap ) :
+ FragmentHandler2(rFilter,rFragmentPath),
+ maColorName(),
+ maColorEntry(),
+ mrColorsMap(rColorsMap)
+{}
+
+::oox::core::ContextHandlerRef ColorFragmentHandler::onCreateContext( sal_Int32 nElement,
+ const AttributeList& /*rAttribs*/ )
+{
+ // state-table like way of navigating the color fragment. we
+ // currently ignore everything except styleLbl in the colorsDef
+ // element
+ switch( getCurrentElement() )
+ {
+ case XML_ROOT_CONTEXT:
+ return nElement == DGM_TOKEN(colorsDef) ? this : nullptr;
+ case DGM_TOKEN(colorsDef):
+ return nElement == DGM_TOKEN(styleLbl) ? this : nullptr;
+ case DGM_TOKEN(styleLbl):
+ {
+ switch( nElement )
+ {
+ // the actual colors - defer to color fragment handlers.
+
+ case DGM_TOKEN(fillClrLst):
+ return new ColorsContext( *this, maColorEntry.maFillColors );
+ case DGM_TOKEN(linClrLst):
+ return new ColorsContext( *this, maColorEntry.maLineColors );
+ case DGM_TOKEN(effectClrLst):
+ return new ColorsContext( *this, maColorEntry.maEffectColors );
+ case DGM_TOKEN(txFillClrLst):
+ return new ColorsContext( *this, maColorEntry.maTextFillColors );
+ case DGM_TOKEN(txLinClrLst):
+ return new ColorsContext( *this, maColorEntry.maTextLineColors );
+ case DGM_TOKEN(txEffectClrLst):
+ return new ColorsContext( *this, maColorEntry.maTextEffectColors );
+ }
+ break;
+ }
+ }
+
+ return nullptr;
+}
+
+void ColorFragmentHandler::onStartElement( const AttributeList& rAttribs )
+{
+ if( getCurrentElement() == DGM_TOKEN(styleLbl) )
+ {
+ maColorName = rAttribs.getString( XML_name, OUString() );
+ maColorEntry = mrColorsMap[maColorName];
+ }
+}
+
+void ColorFragmentHandler::onEndElement( )
+{
+ if( getCurrentElement() == DGM_TOKEN(styleLbl) )
+ mrColorsMap[maColorName] = maColorEntry;
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/oox/source/drawingml/diagram/diagramfragmenthandler.hxx b/oox/source/drawingml/diagram/diagramfragmenthandler.hxx
new file mode 100644
index 000000000..933f1a0fb
--- /dev/null
+++ b/oox/source/drawingml/diagram/diagramfragmenthandler.hxx
@@ -0,0 +1,103 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_OOX_SOURCE_DRAWINGML_DIAGRAM_DIAGRAMFRAGMENTHANDLER_HXX
+#define INCLUDED_OOX_SOURCE_DRAWINGML_DIAGRAM_DIAGRAMFRAGMENTHANDLER_HXX
+
+#include <oox/core/fragmenthandler2.hxx>
+
+#include "diagram.hxx"
+
+namespace oox::drawingml {
+
+class DiagramDataFragmentHandler : public ::oox::core::FragmentHandler2
+{
+public:
+ DiagramDataFragmentHandler(oox::core::XmlFilterBase& rFilter, const OUString& rFragmentPath, const DiagramDataPtr& rDataPtr);
+ virtual ~DiagramDataFragmentHandler() throw() override;
+
+ virtual void SAL_CALL endDocument() override;
+ virtual ::oox::core::ContextHandlerRef onCreateContext( ::sal_Int32 Element, const ::oox::AttributeList& rAttribs ) override;
+
+private:
+
+ DiagramDataPtr mpDataPtr;
+};
+
+class DiagramLayoutFragmentHandler : public ::oox::core::FragmentHandler2
+{
+public:
+ DiagramLayoutFragmentHandler(oox::core::XmlFilterBase& rFilter, const OUString& rFragmentPath, const DiagramLayoutPtr& rDataPtr);
+ virtual ~DiagramLayoutFragmentHandler() throw() override;
+
+ virtual void SAL_CALL endDocument() override;
+ virtual ::oox::core::ContextHandlerRef onCreateContext( ::sal_Int32 Element, const ::oox::AttributeList& rAttribs ) override;
+
+private:
+
+ DiagramLayoutPtr mpDataPtr;
+};
+
+class DiagramQStylesFragmentHandler : public ::oox::core::FragmentHandler2
+{
+public:
+ DiagramQStylesFragmentHandler(
+ oox::core::XmlFilterBase& rFilter,
+ const OUString& rFragmentPath,
+ DiagramQStyleMap& rStylesMap );
+
+ virtual ::oox::core::ContextHandlerRef onCreateContext( sal_Int32 nElement, const AttributeList& rAttribs ) override;
+
+ virtual void onStartElement( const AttributeList& rAttribs ) override;
+ virtual void onEndElement() override;
+
+private:
+ ::oox::core::ContextHandlerRef createStyleMatrixContext(sal_Int32 nElement,
+ const AttributeList& rAttribs,
+ ShapeStyleRef& o_rStyle);
+
+ OUString maStyleName;
+ DiagramStyle maStyleEntry;
+ DiagramQStyleMap& mrStylesMap;
+};
+
+class ColorFragmentHandler : public ::oox::core::FragmentHandler2
+{
+public:
+ ColorFragmentHandler(
+ ::oox::core::XmlFilterBase& rFilter,
+ const OUString& rFragmentPath,
+ DiagramColorMap& rColorMap );
+
+ virtual ::oox::core::ContextHandlerRef onCreateContext( sal_Int32 nElement, const AttributeList& rAttribs ) override;
+
+ virtual void onStartElement( const AttributeList& rAttribs ) override;
+ virtual void onEndElement() override;
+
+private:
+ OUString maColorName;
+ DiagramColor maColorEntry;
+ DiagramColorMap& mrColorsMap;
+};
+
+}
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/oox/source/drawingml/diagram/diagramlayoutatoms.cxx b/oox/source/drawingml/diagram/diagramlayoutatoms.cxx
new file mode 100644
index 000000000..7f926cc9a
--- /dev/null
+++ b/oox/source/drawingml/diagram/diagramlayoutatoms.cxx
@@ -0,0 +1,1964 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "diagramlayoutatoms.hxx"
+
+#include <set>
+
+#include "layoutatomvisitorbase.hxx"
+
+#include <basegfx/numeric/ftools.hxx>
+#include <sal/log.hxx>
+
+#include <oox/helper/attributelist.hxx>
+#include <oox/token/properties.hxx>
+#include <drawingml/fillproperties.hxx>
+#include <drawingml/lineproperties.hxx>
+#include <drawingml/textbody.hxx>
+#include <drawingml/textparagraph.hxx>
+#include <drawingml/textrun.hxx>
+#include <drawingml/customshapeproperties.hxx>
+#include <com/sun/star/drawing/TextFitToSizeType.hpp>
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::uno;
+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;
+}
+
+/**
+ * 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 which UNO property should be set for a given constraint type.
+sal_Int32 getPropertyFromConstraint(sal_Int32 nConstraint)
+{
+ switch (nConstraint)
+ {
+ case oox::XML_lMarg:
+ return oox::PROP_TextLeftDistance;
+ case oox::XML_rMarg:
+ return oox::PROP_TextRightDistance;
+ case oox::XML_tMarg:
+ return oox::PROP_TextUpperDistance;
+ case oox::XML_bMarg:
+ return oox::PROP_TextLowerDistance;
+ }
+
+ return 0;
+}
+
+/**
+ * Determines if pShape is (or contains) a presentation of a data node of type
+ * nType.
+ */
+bool containsDataNodeType(const oox::drawingml::ShapePtr& pShape, sal_Int32 nType)
+{
+ if (pShape->getDataNodeType() == nType)
+ return true;
+
+ for (const auto& pChild : pShape->getChildren())
+ {
+ if (containsDataNodeType(pChild, nType))
+ return true;
+ }
+
+ return false;
+}
+}
+
+namespace oox::drawingml {
+
+IteratorAttr::IteratorAttr( )
+ : mnCnt( -1 )
+ , mbHideLastTrans( true )
+ , mnPtType( 0 )
+ , mnSt( 0 )
+ , mnStep( 1 )
+{
+}
+
+void IteratorAttr::loadFromXAttr( const Reference< XFastAttributeList >& xAttr )
+{
+ AttributeList attr( xAttr );
+ maAxis = attr.getTokenList(XML_axis);
+ mnCnt = attr.getInteger( XML_cnt, -1 );
+ mbHideLastTrans = attr.getBool( XML_hideLastTrans, true );
+ mnSt = attr.getInteger( XML_st, 0 );
+ mnStep = attr.getInteger( XML_step, 1 );
+
+ // better to keep first token instead of error when multiple values
+ std::vector<sal_Int32> aPtTypes = attr.getTokenList(XML_ptType);
+ mnPtType = aPtTypes.empty() ? XML_all : aPtTypes.front();
+}
+
+ConditionAttr::ConditionAttr()
+ : mnFunc( 0 )
+ , mnArg( 0 )
+ , mnOp( 0 )
+ , mnVal( 0 )
+{
+}
+
+void ConditionAttr::loadFromXAttr( const Reference< XFastAttributeList >& xAttr )
+{
+ mnFunc = xAttr->getOptionalValueToken( XML_func, 0 );
+ mnArg = xAttr->getOptionalValueToken( XML_arg, XML_none );
+ mnOp = xAttr->getOptionalValueToken( XML_op, 0 );
+ msVal = xAttr->getOptionalValue( XML_val );
+ mnVal = xAttr->getOptionalValueToken( XML_val, 0 );
+}
+
+void LayoutAtom::dump(int level)
+{
+ SAL_INFO("oox.drawingml", "level = " << level << " - " << msName << " of type " << typeid(*this).name() );
+ for (const auto& pAtom : getChildren())
+ pAtom->dump(level + 1);
+}
+
+ForEachAtom::ForEachAtom(LayoutNode& rLayoutNode, const Reference< XFastAttributeList >& xAttributes) :
+ LayoutAtom(rLayoutNode)
+{
+ maIter.loadFromXAttr(xAttributes);
+}
+
+void ForEachAtom::accept( LayoutAtomVisitor& rVisitor )
+{
+ rVisitor.visit(*this);
+}
+
+LayoutAtomPtr ForEachAtom::getRefAtom()
+{
+ if (!msRef.isEmpty())
+ {
+ const LayoutAtomMap& rLayoutAtomMap = getLayoutNode().getDiagram().getLayout()->getLayoutAtomMap();
+ LayoutAtomMap::const_iterator pRefAtom = rLayoutAtomMap.find(msRef);
+ if (pRefAtom != rLayoutAtomMap.end())
+ return pRefAtom->second;
+ else
+ SAL_WARN("oox.drawingml", "ForEach reference \"" << msRef << "\" not found");
+ }
+ return LayoutAtomPtr();
+}
+
+void ChooseAtom::accept( LayoutAtomVisitor& rVisitor )
+{
+ rVisitor.visit(*this);
+}
+
+ConditionAtom::ConditionAtom(LayoutNode& rLayoutNode, bool isElse, const Reference< XFastAttributeList >& xAttributes) :
+ LayoutAtom(rLayoutNode),
+ mIsElse(isElse)
+{
+ maIter.loadFromXAttr( xAttributes );
+ maCond.loadFromXAttr( xAttributes );
+}
+
+bool ConditionAtom::compareResult(sal_Int32 nOperator, sal_Int32 nFirst, sal_Int32 nSecond)
+{
+ switch (nOperator)
+ {
+ case XML_equ: return nFirst == nSecond;
+ case XML_gt: return nFirst > nSecond;
+ case XML_gte: return nFirst >= nSecond;
+ case XML_lt: return nFirst < nSecond;
+ case XML_lte: return nFirst <= nSecond;
+ case XML_neq: return nFirst != nSecond;
+ default:
+ SAL_WARN("oox.drawingml", "unsupported operator: " << nOperator);
+ return false;
+ }
+}
+
+namespace
+{
+/**
+ * Takes the connection list from rLayoutNode, navigates from rFrom on an edge
+ * of type nType, using a direction determined by bSourceToDestination.
+ */
+OUString navigate(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 calcMaxDepth(const OUString& rNodeName, const dgm::Connections& rConnections)
+{
+ sal_Int32 nMaxLength = 0;
+ for (auto const& aCxn : rConnections)
+ if (aCxn.mnType == XML_parOf && aCxn.msSourceId == rNodeName)
+ nMaxLength = std::max(nMaxLength, calcMaxDepth(aCxn.msDestId, rConnections) + 1);
+
+ return nMaxLength;
+}
+}
+
+sal_Int32 ConditionAtom::getNodeCount(const dgm::Point* pPresPoint) const
+{
+ sal_Int32 nCount = 0;
+ OUString sNodeId = pPresPoint->msPresentationAssociationId;
+
+ // HACK: special case - count children of first child
+ if (maIter.maAxis.size() == 2 && maIter.maAxis[0] == XML_ch && maIter.maAxis[1] == XML_ch)
+ sNodeId = navigate(mrLayoutNode, XML_parOf, sNodeId, /*bSourceToDestination*/ true);
+
+ if (!sNodeId.isEmpty())
+ {
+ for (const auto& aCxn : mrLayoutNode.getDiagram().getData()->getConnections())
+ if (aCxn.mnType == XML_parOf && aCxn.msSourceId == sNodeId)
+ nCount++;
+ }
+
+ return nCount;
+}
+
+bool ConditionAtom::getDecision(const dgm::Point* pPresPoint) const
+{
+ if (mIsElse)
+ return true;
+ if (!pPresPoint)
+ return false;
+
+ switch (maCond.mnFunc)
+ {
+ case XML_var:
+ {
+ if (maCond.mnArg == XML_dir)
+ return compareResult(maCond.mnOp, pPresPoint->mnDirection, maCond.mnVal);
+ else if (maCond.mnArg == XML_hierBranch)
+ {
+ sal_Int32 nHierarchyBranch = pPresPoint->moHierarchyBranch.get(XML_std);
+ if (!pPresPoint->moHierarchyBranch.has())
+ {
+ // If <dgm:hierBranch> is missing in the current presentation
+ // point, ask the parent.
+ OUString aParent = navigate(mrLayoutNode, XML_presParOf, pPresPoint->msModelId,
+ /*bSourceToDestination*/ false);
+ DiagramData::PointNameMap& rPointNameMap
+ = mrLayoutNode.getDiagram().getData()->getPointNameMap();
+ auto it = rPointNameMap.find(aParent);
+ if (it != rPointNameMap.end())
+ {
+ const dgm::Point* pParent = it->second;
+ if (pParent->moHierarchyBranch.has())
+ nHierarchyBranch = pParent->moHierarchyBranch.get();
+ }
+ }
+ return compareResult(maCond.mnOp, nHierarchyBranch, maCond.mnVal);
+ }
+ break;
+ }
+
+ case XML_cnt:
+ return compareResult(maCond.mnOp, getNodeCount(pPresPoint), maCond.msVal.toInt32());
+
+ case XML_maxDepth:
+ {
+ sal_Int32 nMaxDepth = calcMaxDepth(pPresPoint->msPresentationAssociationId, mrLayoutNode.getDiagram().getData()->getConnections());
+ return compareResult(maCond.mnOp, nMaxDepth, maCond.msVal.toInt32());
+ }
+
+ case XML_depth:
+ case XML_pos:
+ case XML_revPos:
+ case XML_posEven:
+ case XML_posOdd:
+ // TODO
+ default:
+ SAL_WARN("oox.drawingml", "unknown function " << maCond.mnFunc);
+ break;
+ }
+
+ return true;
+}
+
+void ConditionAtom::accept( LayoutAtomVisitor& rVisitor )
+{
+ rVisitor.visit(*this);
+}
+
+void ConstraintAtom::accept( LayoutAtomVisitor& rVisitor )
+{
+ rVisitor.visit(*this);
+}
+
+void RuleAtom::accept( LayoutAtomVisitor& rVisitor )
+{
+ rVisitor.visit(*this);
+}
+
+void ConstraintAtom::parseConstraint(std::vector<Constraint>& rConstraints,
+ bool bRequireForName) const
+{
+ // Whitelist for cases where empty forName is handled.
+ if (bRequireForName)
+ {
+ switch (maConstraint.mnType)
+ {
+ case XML_sp:
+ case XML_lMarg:
+ case XML_rMarg:
+ case XML_tMarg:
+ case XML_bMarg:
+ bRequireForName = false;
+ break;
+ }
+ switch (maConstraint.mnPointType)
+ {
+ case XML_sibTrans:
+ bRequireForName = false;
+ break;
+ }
+ }
+
+ if (bRequireForName && maConstraint.msForName.isEmpty())
+ return;
+
+ // accepting only basic equality constraints
+ if ((maConstraint.mnOperator == XML_none || maConstraint.mnOperator == XML_equ)
+ && maConstraint.mnType != XML_none)
+ {
+ rConstraints.push_back(maConstraint);
+ }
+}
+
+void RuleAtom::parseRule(std::vector<Rule>& rRules) const
+{
+ if (!maRule.msForName.isEmpty())
+ {
+ rRules.push_back(maRule);
+ }
+}
+
+void AlgAtom::accept( LayoutAtomVisitor& rVisitor )
+{
+ rVisitor.visit(*this);
+}
+
+sal_Int32 AlgAtom::getConnectorType()
+{
+ sal_Int32 nConnRout = 0;
+ sal_Int32 nBegSty = 0;
+ sal_Int32 nEndSty = 0;
+ if (maMap.count(oox::XML_connRout))
+ nConnRout = maMap.find(oox::XML_connRout)->second;
+ if (maMap.count(oox::XML_begSty))
+ nBegSty = maMap.find(oox::XML_begSty)->second;
+ if (maMap.count(oox::XML_endSty))
+ nEndSty = maMap.find(oox::XML_endSty)->second;
+
+ if (nConnRout == oox::XML_bend)
+ return 0; // was oox::XML_bentConnector3 - connectors are hidden in org chart as they don't work anyway
+ if (nBegSty == oox::XML_arr && nEndSty == oox::XML_arr)
+ return oox::XML_leftRightArrow;
+ if (nBegSty == oox::XML_arr)
+ return oox::XML_leftArrow;
+ if (nEndSty == oox::XML_arr)
+ return oox::XML_rightArrow;
+
+ 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 (const ShapePtr& pChild : rShape->getChildren())
+ nCount += pChild->getVerticalShapesCount();
+ }
+ else if ((nDir == XML_fromL || nDir == XML_fromR) && nSecDir == XML_fromT)
+ {
+ for (const ShapePtr& pChild : rShape->getChildren())
+ nCount += pChild->getVerticalShapesCount();
+ nCount = (nCount + 1) / 2;
+ }
+ else
+ {
+ for (const ShapePtr& pChild : rShape->getChildren())
+ nCount = std::max(nCount, pChild->getVerticalShapesCount());
+ }
+
+ return nCount;
+}
+
+namespace
+{
+/**
+ * Apply rConstraint to the rProperties shared layout state.
+ *
+ * Note that the order in which constraints are applied matters, given that constraints can refer to
+ * each other, and in case A depends on B and A is applied before B, the effect of A won't be
+ * updated when B is applied.
+ */
+void ApplyConstraintToLayout(const Constraint& rConstraint, LayoutPropertyMap& rProperties)
+{
+ // TODO handle the case when we have ptType="...", not forName="...".
+ if (rConstraint.msForName.isEmpty())
+ {
+ return;
+ }
+
+ const LayoutPropertyMap::const_iterator aRef = rProperties.find(rConstraint.msRefForName);
+ if (aRef != rProperties.end())
+ {
+ const LayoutProperty::const_iterator aRefType = aRef->second.find(rConstraint.mnRefType);
+ if (aRefType != aRef->second.end())
+ rProperties[rConstraint.msForName][rConstraint.mnType]
+ = aRefType->second * rConstraint.mfFactor;
+ else
+ {
+ // Values are never in EMU, while oox::drawingml::Shape position and size are always in
+ // EMU.
+ double fUnitFactor = 0;
+ if (isFontUnit(rConstraint.mnRefType))
+ // Points -> EMU.
+ fUnitFactor = EMU_PER_PT;
+ else
+ // Millimeters -> EMU.
+ fUnitFactor = EMU_PER_HMM * 100;
+ rProperties[rConstraint.msForName][rConstraint.mnType]
+ = rConstraint.mfValue * fUnitFactor;
+ }
+ }
+}
+
+/// Does the first data node of this shape have customized text properties?
+bool HasCustomText(const ShapePtr& rShape, LayoutNode& rLayoutNode)
+{
+ const PresPointShapeMap& rPresPointShapeMap
+ = rLayoutNode.getDiagram().getLayout()->getPresPointShapeMap();
+ const DiagramData::StringMap& rPresOfNameMap
+ = rLayoutNode.getDiagram().getData()->getPresOfNameMap();
+ const DiagramData::PointNameMap& rPointNameMap
+ = rLayoutNode.getDiagram().getData()->getPointNameMap();
+ // Get the first presentation node of the shape.
+ const dgm::Point* pPresNode = nullptr;
+ for (const auto& rPair : rPresPointShapeMap)
+ {
+ if (rPair.second == rShape)
+ {
+ pPresNode = rPair.first;
+ break;
+ }
+ }
+ // Get the first data node of the presentation node.
+ dgm::Point* pDataNode = nullptr;
+ if (pPresNode)
+ {
+ auto itPresToData = rPresOfNameMap.find(pPresNode->msModelId);
+ if (itPresToData != rPresOfNameMap.end())
+ {
+ for (const auto& rPair : itPresToData->second)
+ {
+ const DiagramData::SourceIdAndDepth& rItem = rPair.second;
+ auto it = rPointNameMap.find(rItem.msSourceId);
+ if (it != rPointNameMap.end())
+ {
+ pDataNode = it->second;
+ break;
+ }
+ }
+ }
+ }
+
+ // If we have a data node, see if its text is customized or not.
+ if (pDataNode)
+ {
+ return pDataNode->mbCustomText;
+ }
+
+ return false;
+}
+}
+
+void AlgAtom::layoutShape(const ShapePtr& rShape, const std::vector<Constraint>& rConstraints,
+ const std::vector<Rule>& rRules)
+{
+ if (mnType != XML_lin)
+ {
+ // TODO Handle spacing from constraints for non-lin algorithms as well.
+ rShape->getChildren().erase(
+ std::remove_if(rShape->getChildren().begin(), rShape->getChildren().end(),
+ [](const ShapePtr& aChild) {
+ return aChild->getServiceName() == "com.sun.star.drawing.GroupShape"
+ && aChild->getChildren().empty();
+ }),
+ rShape->getChildren().end());
+ }
+
+ switch(mnType)
+ {
+ case XML_composite:
+ {
+ // layout shapes using basic constraints
+
+ LayoutPropertyMap aProperties;
+ LayoutProperty& rParent = aProperties[""];
+
+ sal_Int32 nParentXOffset = 0;
+
+ // Track min/max vertical positions, so we can center everything at the end, if needed.
+ sal_Int32 nVertMin = std::numeric_limits<sal_Int32>::max();
+ sal_Int32 nVertMax = 0;
+
+ if (mfAspectRatio != 1.0)
+ {
+ 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;
+ }
+ else
+ {
+ // Shrink width to be only as large as height.
+ rParent[XML_w] = std::min(rShape->getSize().Width, rShape->getSize().Height);
+ rParent[XML_h] = rShape->getSize().Height;
+ if (rParent[XML_w] < rShape->getSize().Width)
+ nParentXOffset = (rShape->getSize().Width - rParent[XML_w]) / 2;
+ rParent[XML_l] = nParentXOffset;
+ rParent[XML_t] = 0;
+ rParent[XML_r] = rShape->getSize().Width - rParent[XML_l];
+ rParent[XML_b] = rShape->getSize().Height;
+ }
+
+ for (const auto & rConstr : rConstraints)
+ {
+ // Apply direct constraints for all layout nodes.
+ ApplyConstraintToLayout(rConstr, aProperties);
+ }
+
+ for (auto& aCurrShape : rShape->getChildren())
+ {
+ // Apply constraints from the current layout node for this child shape.
+ // Previous child shapes may have changed aProperties.
+ for (const auto& rConstr : rConstraints)
+ {
+ if (rConstr.msForName != aCurrShape->getInternalName())
+ {
+ continue;
+ }
+
+ ApplyConstraintToLayout(rConstr, aProperties);
+ }
+
+ // Apply constraints from the child layout node for this child shape.
+ // This builds on top of the own parent state + the state of previous shapes in the
+ // same composite algorithm.
+ const LayoutNode& rLayoutNode = getLayoutNode();
+ for (const auto& pDirectChild : rLayoutNode.getChildren())
+ {
+ auto pLayoutNode = dynamic_cast<LayoutNode*>(pDirectChild.get());
+ if (!pLayoutNode)
+ {
+ continue;
+ }
+
+ if (pLayoutNode->getName() != aCurrShape->getInternalName())
+ {
+ continue;
+ }
+
+ for (const auto& pChild : pLayoutNode->getChildren())
+ {
+ auto pConstraintAtom = dynamic_cast<ConstraintAtom*>(pChild.get());
+ if (!pConstraintAtom)
+ {
+ continue;
+ }
+
+ const Constraint& rConstraint = pConstraintAtom->getConstraint();
+ if (!rConstraint.msForName.isEmpty())
+ {
+ continue;
+ }
+
+ if (!rConstraint.msRefForName.isEmpty())
+ {
+ continue;
+ }
+
+ // Either an absolute value or a factor of a property.
+ if (rConstraint.mfValue == 0.0 && rConstraint.mnRefType == XML_none)
+ {
+ continue;
+ }
+
+ Constraint aConstraint(rConstraint);
+ aConstraint.msForName = pLayoutNode->getName();
+ aConstraint.msRefForName = pLayoutNode->getName();
+
+ ApplyConstraintToLayout(aConstraint, aProperties);
+ }
+ }
+
+ awt::Size aSize = rShape->getSize();
+ awt::Point aPos(0, 0);
+
+ const LayoutPropertyMap::const_iterator aPropIt = aProperties.find(aCurrShape->getInternalName());
+ if (aPropIt != aProperties.end())
+ {
+ const LayoutProperty& rProp = aPropIt->second;
+ LayoutProperty::const_iterator it, it2;
+
+ if ( (it = rProp.find(XML_w)) != rProp.end() )
+ aSize.Width = std::min(it->second, rShape->getSize().Width);
+ if ( (it = rProp.find(XML_h)) != rProp.end() )
+ aSize.Height = std::min(it->second, rShape->getSize().Height);
+
+ if ( (it = rProp.find(XML_l)) != rProp.end() )
+ aPos.X = it->second;
+ else if ( (it = rProp.find(XML_ctrX)) != rProp.end() )
+ aPos.X = it->second - aSize.Width/2;
+ else if ((it = rProp.find(XML_r)) != rProp.end())
+ aPos.X = it->second - aSize.Width;
+
+ if ( (it = rProp.find(XML_t)) != rProp.end())
+ aPos.Y = it->second;
+ else if ( (it = rProp.find(XML_ctrY)) != rProp.end() )
+ aPos.Y = it->second - aSize.Height/2;
+ else if ((it = rProp.find(XML_b)) != rProp.end())
+ aPos.Y = it->second - aSize.Height;
+
+ if ( (it = rProp.find(XML_l)) != rProp.end() && (it2 = rProp.find(XML_r)) != rProp.end() )
+ aSize.Width = it2->second - it->second;
+ if ( (it = rProp.find(XML_t)) != rProp.end() && (it2 = rProp.find(XML_b)) != rProp.end() )
+ aSize.Height = it2->second - it->second;
+
+ aPos.X += nParentXOffset;
+ aSize.Width = std::min(aSize.Width, rShape->getSize().Width - aPos.X);
+ aSize.Height = std::min(aSize.Height, rShape->getSize().Height - aPos.Y);
+ }
+ else
+ SAL_WARN("oox.drawingml", "composite layout properties not found for shape " << aCurrShape->getInternalName());
+
+ aCurrShape->setSize(aSize);
+ aCurrShape->setChildSize(aSize);
+ aCurrShape->setPosition(aPos);
+
+ nVertMin = std::min(aPos.Y, nVertMin);
+ nVertMax = std::max(aPos.Y + aSize.Height, nVertMax);
+
+ NamedShapePairs& rDiagramFontHeights
+ = getLayoutNode().getDiagram().getShape()->getDiagramFontHeights();
+ auto it = rDiagramFontHeights.find(aCurrShape->getInternalName());
+ if (it != rDiagramFontHeights.end())
+ {
+ // Internal name matches: put drawingml::Shape to the relevant group, for
+ // syncronized font height handling.
+ it->second.insert({ aCurrShape, {} });
+ }
+ }
+
+ // See if all vertical space is used or we have to center the content.
+ if (nVertMin >= 0 && nVertMin <= nVertMax && nVertMax <= rParent[XML_h])
+ {
+ sal_Int32 nDiff = rParent[XML_h] - (nVertMax - nVertMin);
+ if (nDiff > 0)
+ {
+ for (auto& aCurrShape : rShape->getChildren())
+ {
+ awt::Point aPosition = aCurrShape->getPosition();
+ aPosition.Y += nDiff / 2;
+ aCurrShape->setPosition(aPosition);
+ }
+ }
+ }
+ break;
+ }
+
+ 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();
+
+ 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];
+ // keep center position
+ awt::Point aPos = rShape->getPosition();
+ aPos.X += (rShape->getSize().Width - aSize.Width) / 2;
+ aPos.Y += (rShape->getSize().Height - aSize.Height) / 2;
+ rShape->setPosition(aPos);
+ rShape->setSize(aSize);
+ break;
+ }
+
+ case XML_cycle:
+ {
+ if (rShape->getChildren().empty())
+ break;
+
+ const sal_Int32 nStartAngle = maMap.count(XML_stAng) ? maMap.find(XML_stAng)->second : 0;
+ const sal_Int32 nSpanAngle = maMap.count(XML_spanAng) ? maMap.find(XML_spanAng)->second : 360;
+ const sal_Int32 nRotationPath = maMap.count(XML_rotPath) ? maMap.find(XML_rotPath)->second : XML_none;
+ const sal_Int32 nctrShpMap = maMap.count(XML_ctrShpMap) ? maMap.find(XML_ctrShpMap)->second : XML_none;
+ const awt::Size aCenter(rShape->getSize().Width / 2, rShape->getSize().Height / 2);
+ const awt::Size aChildSize(rShape->getSize().Width / 4, rShape->getSize().Height / 4);
+ const awt::Size aConnectorSize(rShape->getSize().Width / 12, rShape->getSize().Height / 12);
+ const sal_Int32 nRadius = std::min(
+ (rShape->getSize().Width - aChildSize.Width) / 2,
+ (rShape->getSize().Height - aChildSize.Height) / 2);
+
+ std::vector<oox::drawingml::ShapePtr> aCycleChildren = rShape->getChildren();
+
+ if (nctrShpMap == XML_fNode)
+ {
+ // first node placed in center, others around
+ oox::drawingml::ShapePtr pCenterShape = aCycleChildren.front();
+ aCycleChildren.erase(aCycleChildren.begin());
+ const awt::Point aCurrPos(aCenter.Width - aChildSize.Width / 2,
+ aCenter.Height - aChildSize.Height / 2);
+ pCenterShape->setPosition(aCurrPos);
+ pCenterShape->setSize(aChildSize);
+ pCenterShape->setChildSize(aChildSize);
+ }
+
+ const sal_Int32 nShapes = aCycleChildren.size();
+ if (nShapes)
+ {
+ const sal_Int32 nConnectorRadius = nRadius * cos(basegfx::deg2rad(nSpanAngle / nShapes));
+ const sal_Int32 nConnectorAngle = nSpanAngle > 0 ? 0 : 180;
+
+ sal_Int32 idx = 0;
+ for (auto & aCurrShape : aCycleChildren)
+ {
+ const double fAngle = static_cast<double>(idx)*nSpanAngle/nShapes + nStartAngle;
+ awt::Size aCurrSize = aChildSize;
+ sal_Int32 nCurrRadius = nRadius;
+ if (aCurrShape->getSubType() == XML_conn)
+ {
+ aCurrSize = aConnectorSize;
+ nCurrRadius = nConnectorRadius;
+ }
+ const awt::Point aCurrPos(
+ aCenter.Width + nCurrRadius*sin(basegfx::deg2rad(fAngle)) - aCurrSize.Width/2,
+ aCenter.Height - nCurrRadius*cos(basegfx::deg2rad(fAngle)) - aCurrSize.Height/2);
+
+ aCurrShape->setPosition(aCurrPos);
+ aCurrShape->setSize(aCurrSize);
+ aCurrShape->setChildSize(aCurrSize);
+
+ if (nRotationPath == XML_alongPath)
+ aCurrShape->setRotation(fAngle * PER_DEGREE);
+
+ // connectors should be handled in conn, but we don't have
+ // reference to previous and next child, so it's easier here
+ if (aCurrShape->getSubType() == XML_conn)
+ aCurrShape->setRotation((nConnectorAngle + fAngle) * PER_DEGREE);
+
+ idx++;
+ }
+ }
+ break;
+ }
+
+ 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.
+ 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 = rShape->getChildren().size();
+
+ if (mnType == XML_hierChild)
+ {
+ // 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; });
+ }
+
+ const double fSpaceWidth = 0.1;
+ const double fSpaceHeight = 0.3;
+
+ if (mnType == XML_hierRoot && nCount == 3)
+ {
+ // Order assistant nodes above employee nodes.
+ std::vector<ShapePtr>& rChildren = rShape->getChildren();
+ if (!containsDataNodeType(rChildren[1], XML_asst)
+ && containsDataNodeType(rChildren[2], XML_asst))
+ 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();
+ 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);
+
+ if (mnType == XML_hierChild && pChild->getSubType() == XML_conn)
+ {
+ // Connectors should not influence the position of
+ // non-connect shapes.
+ pChild->setSize(aConnectorSize);
+ pChild->setChildSize(aConnectorSize);
+ continue;
+ }
+
+ awt::Size aCurrSize = aChildSize;
+ aCurrSize.Height *= pChild->getVerticalShapesCount() + (pChild->getVerticalShapesCount() - 1) * fSpaceHeight;
+
+ pChild->setSize(aCurrSize);
+ pChild->setChildSize(aCurrSize);
+
+ if (nDir == XML_fromT || nDir == XML_fromB)
+ aChildPos.Y += aCurrSize.Height + aChildSize.Height * fSpaceHeight;
+ else
+ 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;
+ }
+
+ case XML_lin:
+ {
+ // spread children evenly across one axis, stretch across second
+
+ if (rShape->getChildren().empty() || rShape->getSize().Width == 0 || rShape->getSize().Height == 0)
+ break;
+
+ const sal_Int32 nDir = maMap.count(XML_linDir) ? maMap.find(XML_linDir)->second : XML_fromL;
+ 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);
+
+ double fCount = rShape->getChildren().size();
+ sal_Int32 nConnectorAngle = 0;
+ switch (nDir)
+ {
+ case XML_fromL: nConnectorAngle = 0; break;
+ case XML_fromR: nConnectorAngle = 180; break;
+ case XML_fromT: nConnectorAngle = 270; break;
+ case XML_fromB: nConnectorAngle = 90; break;
+ }
+
+ awt::Size aSpaceSize;
+
+ // Find out which constraint 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;
+ if (rProperty[XML_w] > rShape->getSize().Width)
+ {
+ rProperty[XML_w] = rShape->getSize().Width;
+ }
+ }
+ if (rConstraint.mnType == XML_h)
+ {
+ rProperty[XML_h] = rShape->getSize().Height * rConstraint.mfFactor;
+ if (rProperty[XML_h] > rShape->getSize().Height)
+ {
+ rProperty[XML_h] = rShape->getSize().Height;
+ }
+ }
+
+ if (rConstraint.mnType == XML_primFontSz && rConstraint.mnFor == XML_des
+ && rConstraint.mnOperator == XML_equ)
+ {
+ NamedShapePairs& rDiagramFontHeights
+ = getLayoutNode().getDiagram().getShape()->getDiagramFontHeights();
+ auto it = rDiagramFontHeights.find(rConstraint.msForName);
+ if (it == rDiagramFontHeights.end())
+ {
+ // Start tracking all shapes with this internal name: they'll have the same
+ // font height.
+ rDiagramFontHeights[rConstraint.msForName] = {};
+ }
+ }
+
+ // TODO: get values from differently named constraints as well
+ if (rConstraint.msForName == "sp" || rConstraint.msForName == "space" || rConstraint.msForName == "sibTrans")
+ {
+ if (rConstraint.mnType == XML_w)
+ aSpaceSize.Width = rShape->getSize().Width * rConstraint.mfFactor;
+ if (rConstraint.mnType == XML_h)
+ aSpaceSize.Height = rShape->getSize().Height * rConstraint.mfFactor;
+ }
+ }
+
+ // first approximation of children size
+ std::set<OUString> aChildrenToShrink;
+ for (const auto& rRule : rRules)
+ {
+ // Consider rules: when scaling down, only change children where the rule allows
+ // doing so.
+ aChildrenToShrink.insert(rRule.msForName);
+ }
+
+ if (nDir == XML_fromT || nDir == XML_fromB)
+ {
+ // TODO consider rules for vertical linear layout as well.
+ aChildrenToShrink.clear();
+ }
+
+ if (!aChildrenToShrink.empty())
+ {
+ // Have scaling info from rules: then only count scaled children.
+ // Also count children which are a fraction of a scaled child.
+ std::set<OUString> aChildrenToShrinkDeps;
+ for (auto& aCurrShape : rShape->getChildren())
+ {
+ if (aChildrenToShrink.find(aCurrShape->getInternalName())
+ == aChildrenToShrink.end())
+ {
+ if (fCount > 1.0)
+ {
+ fCount -= 1.0;
+
+ bool bIsDependency = false;
+ double fFactor = 0;
+ for (const auto& rConstraint : rConstraints)
+ {
+ if (rConstraint.msForName != aCurrShape->getInternalName())
+ {
+ continue;
+ }
+
+ if ((nDir == XML_fromL || nDir == XML_fromR) && rConstraint.mnType != XML_w)
+ {
+ continue;
+ }
+ if ((nDir == XML_fromL || nDir == XML_fromR) && rConstraint.mnType == XML_w)
+ {
+ fFactor = rConstraint.mfFactor;
+ }
+
+ if ((nDir == XML_fromT || nDir == XML_fromB) && rConstraint.mnType != XML_h)
+ {
+ continue;
+ }
+ if ((nDir == XML_fromT || nDir == XML_fromB) && rConstraint.mnType == XML_h)
+ {
+ fFactor = rConstraint.mfFactor;
+ }
+
+ if (aChildrenToShrink.find(rConstraint.msRefForName) == aChildrenToShrink.end())
+ {
+ continue;
+ }
+
+ // At this point we have a child with a size which is a factor of an
+ // other child which will be scaled.
+ fCount += rConstraint.mfFactor;
+ aChildrenToShrinkDeps.insert(aCurrShape->getInternalName());
+ bIsDependency = true;
+ break;
+ }
+
+ if (!bIsDependency && aCurrShape->getServiceName() == "com.sun.star.drawing.GroupShape")
+ {
+ bool bScaleDownEmptySpacing = false;
+ if (nDir == XML_fromL || nDir == XML_fromR)
+ {
+ oox::OptValue<sal_Int32> oWidth = findProperty(aProperties, aCurrShape->getInternalName(), XML_w);
+ bScaleDownEmptySpacing = oWidth.has() && oWidth.get() > 0;
+ }
+ if (!bScaleDownEmptySpacing && (nDir == XML_fromT || nDir == XML_fromB))
+ {
+ oox::OptValue<sal_Int32> oHeight = findProperty(aProperties, aCurrShape->getInternalName(), XML_h);
+ bScaleDownEmptySpacing = oHeight.has() && oHeight.get() > 0;
+ }
+ if (bScaleDownEmptySpacing && aCurrShape->getChildren().empty())
+ {
+ fCount += fFactor;
+ aChildrenToShrinkDeps.insert(aCurrShape->getInternalName());
+ }
+ }
+ }
+ }
+ }
+
+ aChildrenToShrink.insert(aChildrenToShrinkDeps.begin(), aChildrenToShrinkDeps.end());
+
+ // No manual spacing: spacings are children as well.
+ aSpaceSize = awt::Size();
+ }
+ else
+ {
+ // TODO Handle spacing from constraints without rules as well.
+ rShape->getChildren().erase(
+ std::remove_if(rShape->getChildren().begin(), rShape->getChildren().end(),
+ [](const ShapePtr& aChild) {
+ return aChild->getServiceName()
+ == "com.sun.star.drawing.GroupShape"
+ && aChild->getChildren().empty();
+ }),
+ rShape->getChildren().end());
+ fCount = rShape->getChildren().size();
+ }
+ awt::Size aChildSize = rShape->getSize();
+ if (nDir == XML_fromL || nDir == XML_fromR)
+ aChildSize.Width /= fCount;
+ else if (nDir == XML_fromT || nDir == XML_fromB)
+ aChildSize.Height /= fCount;
+
+ 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.
+ awt::Size aTotalSize;
+ for (const auto & aCurrShape : rShape->getChildren())
+ {
+ oox::OptValue<sal_Int32> oWidth = findProperty(aProperties, aCurrShape->getInternalName(), XML_w);
+ oox::OptValue<sal_Int32> oHeight = findProperty(aProperties, aCurrShape->getInternalName(), XML_h);
+ awt::Size aSize = aChildSize;
+ if (oWidth.has())
+ aSize.Width = oWidth.get();
+ if (oHeight.has())
+ aSize.Height = oHeight.get();
+ aTotalSize.Width += aSize.Width;
+ aTotalSize.Height += aSize.Height;
+ }
+
+ aTotalSize.Width += (fCount-1) * aSpaceSize.Width;
+ aTotalSize.Height += (fCount-1) * aSpaceSize.Height;
+
+ double fWidthScale = 1.0;
+ double fHeightScale = 1.0;
+ if (nIncX && aTotalSize.Width > rShape->getSize().Width)
+ fWidthScale = static_cast<double>(rShape->getSize().Width) / aTotalSize.Width;
+ if (nIncY && aTotalSize.Height > rShape->getSize().Height)
+ fHeightScale = static_cast<double>(rShape->getSize().Height) / aTotalSize.Height;
+ aSpaceSize.Width *= fWidthScale;
+ aSpaceSize.Height *= fHeightScale;
+
+ for (auto& aCurrShape : rShape->getChildren())
+ {
+ // Extract properties relevant for this shape from constraints.
+ oox::OptValue<sal_Int32> oWidth = findProperty(aProperties, aCurrShape->getInternalName(), XML_w);
+ oox::OptValue<sal_Int32> oHeight = findProperty(aProperties, aCurrShape->getInternalName(), XML_h);
+
+ awt::Size aSize = aChildSize;
+ if (oWidth.has())
+ aSize.Width = oWidth.get();
+ if (oHeight.has())
+ aSize.Height = oHeight.get();
+ if (aChildrenToShrink.empty()
+ || aChildrenToShrink.find(aCurrShape->getInternalName())
+ != aChildrenToShrink.end())
+ {
+ aSize.Width *= fWidthScale;
+ }
+ if (aChildrenToShrink.empty()
+ || aChildrenToShrink.find(aCurrShape->getInternalName())
+ != aChildrenToShrink.end())
+ {
+ aSize.Height *= fHeightScale;
+ }
+ aCurrShape->setSize(aSize);
+ aCurrShape->setChildSize(aSize);
+
+ // center in the other axis - probably some parameter controls it
+ if (nIncX)
+ aCurrPos.Y = (rShape->getSize().Height - aSize.Height) / 2;
+ if (nIncY)
+ aCurrPos.X = (rShape->getSize().Width - aSize.Width) / 2;
+ if (aCurrPos.X < 0)
+ {
+ aCurrPos.X = 0;
+ }
+ if (aCurrPos.Y < 0)
+ {
+ aCurrPos.Y = 0;
+ }
+
+ aCurrShape->setPosition(aCurrPos);
+
+ aCurrPos.X += nIncX * (aSize.Width + aSpaceSize.Width);
+ aCurrPos.Y += nIncY * (aSize.Height + aSpaceSize.Height);
+
+ // connectors should be handled in conn, but we don't have
+ // reference to previous and next child, so it's easier here
+ if (aCurrShape->getSubType() == XML_conn)
+ aCurrShape->setRotation(nConnectorAngle * PER_DEGREE);
+ }
+
+ // Newer shapes are behind older ones by default. Reverse this if requested.
+ sal_Int32 nChildOrder = XML_b;
+ const LayoutNode* pParentLayoutNode = nullptr;
+ for (LayoutAtomPtr pAtom = getParent(); pAtom; pAtom = pAtom->getParent())
+ {
+ auto pLayoutNode = dynamic_cast<LayoutNode*>(pAtom.get());
+ if (pLayoutNode)
+ {
+ pParentLayoutNode = pLayoutNode;
+ break;
+ }
+ }
+ if (pParentLayoutNode)
+ {
+ nChildOrder = pParentLayoutNode->getChildOrder();
+ }
+ if (nChildOrder == XML_t)
+ {
+ std::reverse(rShape->getChildren().begin(), rShape->getChildren().end());
+ }
+
+ break;
+ }
+
+ case XML_pyra:
+ {
+ if (rShape->getChildren().empty() || rShape->getSize().Width == 0 || rShape->getSize().Height == 0)
+ break;
+
+ // const sal_Int32 nDir = maMap.count(XML_linDir) ? maMap.find(XML_linDir)->second : XML_fromT;
+ // const sal_Int32 npyraAcctPos = maMap.count(XML_pyraAcctPos) ? maMap.find(XML_pyraAcctPos)->second : XML_bef;
+ // const sal_Int32 ntxDir = maMap.count(XML_txDir) ? maMap.find(XML_txDir)->second : XML_fromT;
+ // const sal_Int32 npyraLvlNode = maMap.count(XML_pyraLvlNode) ? maMap.find(XML_pyraLvlNode)->second : XML_level;
+ // uncomment when use in code.
+
+ sal_Int32 nCount = rShape->getChildren().size();
+ double fAspectRatio = 0.32;
+
+ awt::Size aChildSize = rShape->getSize();
+ aChildSize.Width /= nCount;
+ aChildSize.Height /= nCount;
+
+ awt::Point aCurrPos(0, 0);
+ aCurrPos.X = fAspectRatio*aChildSize.Width*(nCount-1);
+ aCurrPos.Y = fAspectRatio*aChildSize.Height;
+
+ for (auto & aCurrShape : rShape->getChildren())
+ {
+ aCurrShape->setPosition(aCurrPos);
+ aCurrPos.X -= aChildSize.Height/(nCount-1);
+ aChildSize.Width += aChildSize.Height;
+ aCurrShape->setSize(aChildSize);
+ aCurrShape->setChildSize(aChildSize);
+ aCurrPos.Y += (aChildSize.Height);
+ }
+ break;
+ }
+
+ case XML_snake:
+ {
+ // find optimal grid to layout children that have fixed aspect ratio
+
+ if (rShape->getChildren().empty() || rShape->getSize().Width == 0 || rShape->getSize().Height == 0)
+ break;
+
+ // Parse constraints.
+ double fChildAspectRatio = rShape->getChildren()[0]->getAspectRatio();
+ double fShapeHeight = rShape->getSize().Height;
+ double fShapeWidth = rShape->getSize().Width;
+ // Check if we have a child aspect ratio. If so, need to shrink one dimension to
+ // achieve that ratio.
+ if (fChildAspectRatio && fShapeHeight
+ && fChildAspectRatio < (fShapeWidth / fShapeHeight))
+ {
+ fShapeWidth = fShapeHeight * fChildAspectRatio;
+ }
+
+ double fSpaceFromConstraint = 1.0;
+ LayoutPropertyMap aPropertiesByName;
+ std::map<sal_Int32, LayoutProperty> aPropertiesByType;
+ LayoutProperty& rParent = aPropertiesByName[""];
+ rParent[XML_w] = fShapeWidth;
+ rParent[XML_h] = fShapeHeight;
+ for (const auto& rConstr : rConstraints)
+ {
+ if (rConstr.mnRefType == XML_w || rConstr.mnRefType == XML_h)
+ {
+ if (rConstr.mnType == XML_sp && rConstr.msForName.isEmpty())
+ fSpaceFromConstraint = rConstr.mfFactor;
+ }
+
+ auto itRefForName = aPropertiesByName.find(rConstr.msRefForName);
+ if (itRefForName == aPropertiesByName.end())
+ {
+ continue;
+ }
+
+ auto it = itRefForName->second.find(rConstr.mnRefType);
+ if (it == itRefForName->second.end())
+ {
+ continue;
+ }
+
+ if (rConstr.mfValue != 0.0)
+ {
+ continue;
+ }
+
+ sal_Int32 nValue = it->second * rConstr.mfFactor;
+
+ if (rConstr.mnPointType == XML_none)
+ {
+ aPropertiesByName[rConstr.msForName][rConstr.mnType] = nValue;
+ }
+ else
+ {
+ aPropertiesByType[rConstr.mnPointType][rConstr.mnType] = nValue;
+ }
+ }
+
+ std::vector<sal_Int32> aShapeWidths(rShape->getChildren().size());
+ for (size_t i = 0; i < rShape->getChildren().size(); ++i)
+ {
+ ShapePtr pChild = rShape->getChildren()[i];
+ if (!pChild->getDataNodeType())
+ {
+ // TODO handle the case when the requirement applies by name, not by point type.
+ aShapeWidths[i] = fShapeWidth;
+ continue;
+ }
+
+ auto itNodeType = aPropertiesByType.find(pChild->getDataNodeType());
+ if (itNodeType == aPropertiesByType.end())
+ {
+ aShapeWidths[i] = fShapeWidth;
+ continue;
+ }
+
+ auto it = itNodeType->second.find(XML_w);
+ if (it == itNodeType->second.end())
+ {
+ aShapeWidths[i] = fShapeWidth;
+ continue;
+ }
+
+ aShapeWidths[i] = it->second;
+ }
+
+ bool bSpaceFromConstraints = fSpaceFromConstraint != 1.0;
+
+ const sal_Int32 nDir = maMap.count(XML_grDir) ? maMap.find(XML_grDir)->second : XML_tL;
+ sal_Int32 nIncX = 1;
+ sal_Int32 nIncY = 1;
+ bool bHorizontal = true;
+ switch (nDir)
+ {
+ case XML_tL: nIncX = 1; nIncY = 1; break;
+ case XML_tR: nIncX = -1; nIncY = 1; break;
+ case XML_bL: nIncX = 1; nIncY = -1; bHorizontal = false; break;
+ case XML_bR: nIncX = -1; nIncY = -1; bHorizontal = false; break;
+ }
+
+ sal_Int32 nCount = rShape->getChildren().size();
+ // Defaults in case not provided by constraints.
+ double fSpace = bSpaceFromConstraints ? fSpaceFromConstraint : 0.3;
+ double fAspectRatio = 0.54; // diagram should not spill outside, earlier it was 0.6
+
+ sal_Int32 nCol = 1;
+ sal_Int32 nRow = 1;
+ sal_Int32 nMaxRowWidth = 0;
+ if (nCount <= fChildAspectRatio)
+ // Child aspect ratio request (width/height) is N, and we have at most N shapes.
+ // This means we don't need multiple columns.
+ nRow = nCount;
+ else
+ {
+ for ( ; nRow<nCount; nRow++)
+ {
+ nCol = std::ceil(static_cast<double>(nCount) / nRow);
+ sal_Int32 nRowWidth = 0;
+ for (sal_Int32 i = 0; i < nCol; ++i)
+ {
+ if (i >= nCount)
+ {
+ break;
+ }
+
+ nRowWidth += aShapeWidths[i];
+ }
+ if ((fShapeHeight * nRow) / nRowWidth >= fAspectRatio)
+ {
+ if (nRowWidth > nMaxRowWidth)
+ {
+ nMaxRowWidth = nRowWidth;
+ }
+ break;
+ }
+ }
+ }
+
+ SAL_INFO("oox.drawingml", "Snake layout grid: " << nCol << "x" << nRow);
+
+ sal_Int32 nWidth = rShape->getSize().Width / (nCol + (nCol-1)*fSpace);
+ awt::Size aChildSize(nWidth, nWidth * fAspectRatio);
+ if (nCol == 1 && nRow > 1)
+ {
+ // We have a single column, so count the height based on the parent height, not
+ // based on width.
+ // Space occurs inside children; also double amount of space is needed outside (on
+ // both sides), if the factor comes from a constraint.
+ sal_Int32 nNumSpaces = -1;
+ if (bSpaceFromConstraints)
+ nNumSpaces += 4;
+ sal_Int32 nHeight
+ = rShape->getSize().Height / (nRow + (nRow + nNumSpaces) * fSpace);
+
+ if (fChildAspectRatio > 1)
+ {
+ // Shrink width if the aspect ratio requires it.
+ nWidth = std::min(rShape->getSize().Width,
+ static_cast<sal_Int32>(nHeight * fChildAspectRatio));
+ aChildSize = awt::Size(nWidth, nHeight);
+ }
+
+ bHorizontal = false;
+ }
+
+ 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;
+ else if (bSpaceFromConstraints)
+ {
+ if (!bHorizontal)
+ {
+ // Initial vertical offset to have upper spacing (outside, so double amount).
+ aCurrPos.Y = aChildSize.Height * fSpace * 2;
+ }
+ }
+
+ sal_Int32 nStartX = aCurrPos.X;
+ sal_Int32 nColIdx = 0,index = 0;
+
+ const sal_Int32 aContDir = maMap.count(XML_contDir) ? maMap.find(XML_contDir)->second : XML_sameDir;
+
+ switch(aContDir)
+ {
+ case XML_sameDir:
+ {
+ sal_Int32 nRowHeight = 0;
+ for (auto & aCurrShape : rShape->getChildren())
+ {
+ aCurrShape->setPosition(aCurrPos);
+ awt::Size aCurrSize(aChildSize);
+ // aShapeWidths items are a portion of nMaxRowWidth. We want the same ratio,
+ // based on the original parent width, ignoring the aspect ratio request.
+ double fWidthFactor = static_cast<double>(aShapeWidths[index]) / nMaxRowWidth;
+ bool bWidthsFromConstraints = nCount >= 2 && rShape->getChildren()[1]->getDataNodeType() == XML_sibTrans;
+ if (bWidthsFromConstraints)
+ {
+ // We can only work from constraints if spacing is represented by a real
+ // child shape.
+ aCurrSize.Width = rShape->getSize().Width * fWidthFactor;
+ }
+ if (fChildAspectRatio)
+ {
+ aCurrSize.Height = aCurrSize.Width / fChildAspectRatio;
+
+ // Child shapes are not allowed to leave their parent.
+ aCurrSize.Height = std::min<sal_Int32>(aCurrSize.Height, rShape->getSize().Height / (nRow + (nRow-1)*fSpace));
+ }
+ if (aCurrSize.Height > nRowHeight)
+ {
+ nRowHeight = aCurrSize.Height;
+ }
+ aCurrShape->setSize(aCurrSize);
+ aCurrShape->setChildSize(aCurrSize);
+
+ index++; // counts index of child, helpful for positioning.
+
+ if(index%nCol==0 || ((index/nCol)+1)!=nRow)
+ aCurrPos.X += nIncX * (aCurrSize.Width + fSpace*aCurrSize.Width);
+
+ if(++nColIdx == nCol) // condition for next row
+ {
+ // if last row, then position children according to number of shapes.
+ if((index+1)%nCol!=0 && (index+1)>=3 && ((index+1)/nCol+1)==nRow && nCount!=nRow*nCol)
+ {
+ // position first child of last row
+ if (bWidthsFromConstraints)
+ {
+ aCurrPos.X = nStartX;
+ }
+ else
+ {
+ // Can assume that all child shape has the same width.
+ aCurrPos.X = nStartX + (nIncX * (aCurrSize.Width + fSpace*aCurrSize.Width))/2;
+ }
+ }
+ else
+ // if not last row, positions first child of that row
+ aCurrPos.X = nStartX;
+ aCurrPos.Y += nIncY * (nRowHeight + fSpace*nRowHeight);
+ nColIdx = 0;
+ nRowHeight = 0;
+ }
+
+ // positions children in the last row.
+ if(index%nCol!=0 && index>=3 && ((index/nCol)+1)==nRow)
+ aCurrPos.X += (nIncX * (aCurrSize.Width + fSpace*aCurrSize.Width));
+ }
+ break;
+ }
+ case XML_revDir:
+ for (auto & aCurrShape : rShape->getChildren())
+ {
+ aCurrShape->setPosition(aCurrPos);
+ aCurrShape->setSize(aChildSize);
+ aCurrShape->setChildSize(aChildSize);
+
+ index++; // counts index of child, helpful for positioning.
+
+ /*
+ index%col -> tests node is at last column
+ ((index/nCol)+1)!=nRow) -> tests node is at last row or not
+ ((index/nCol)+1)%2!=0 -> tests node is at row which is multiple of 2, important for revDir
+ num!=nRow*nCol -> tests how last row nodes should be spread.
+ */
+
+ if((index%nCol==0 || ((index/nCol)+1)!=nRow) && ((index/nCol)+1)%2!=0)
+ aCurrPos.X += (aChildSize.Width + fSpace*aChildSize.Width);
+ else if( index%nCol!=0 && ((index/nCol)+1)!=nRow) // child other than placed at last column
+ aCurrPos.X -= (aChildSize.Width + fSpace*aChildSize.Width);
+
+ if(++nColIdx == nCol) // condition for next row
+ {
+ // if last row, then position children according to number of shapes.
+ if((index+1)%nCol!=0 && (index+1)>=4 && ((index+1)/nCol+1)==nRow && nCount!=nRow*nCol && ((index/nCol)+1)%2==0)
+ // position first child of last row
+ aCurrPos.X -= aChildSize.Width*3/2;
+ else if((index+1)%nCol!=0 && (index+1)>=4 && ((index+1)/nCol+1)==nRow && nCount!=nRow*nCol && ((index/nCol)+1)%2!=0)
+ aCurrPos.X = nStartX + (nIncX * (aChildSize.Width + fSpace*aChildSize.Width))/2;
+ else if(((index/nCol)+1)%2!=0)
+ aCurrPos.X = nStartX;
+
+ aCurrPos.Y += nIncY * (aChildSize.Height + fSpace*aChildSize.Height);
+ nColIdx = 0;
+ }
+
+ // positions children in the last row.
+ if(index%nCol!=0 && index>=3 && ((index/nCol)+1)==nRow && ((index/nCol)+1)%2==0)
+ //if row%2=0 then start from left else
+ aCurrPos.X -= (nIncX * (aChildSize.Width + fSpace*aChildSize.Width));
+ else if(index%nCol!=0 && index>=3 && ((index/nCol)+1)==nRow && ((index/nCol)+1)%2!=0)
+ // start from right
+ aCurrPos.X += (nIncX * (aChildSize.Width + fSpace*aChildSize.Width));
+ }
+ break;
+ }
+ break;
+ }
+
+ case XML_sp:
+ {
+ // HACK: Handled one level higher. Or rather, planned to
+ // HACK: text should appear only in tx node; we're assigning it earlier, so let's remove it here
+ rShape->setTextBody(TextBodyPtr());
+ break;
+ }
+
+ case XML_tx:
+ {
+ // adjust text alignment
+
+ // Parse constraints, only self margins as a start.
+ double fFontSize = 0;
+ for (const auto& rConstr : rConstraints)
+ {
+ if (rConstr.mnRefType == XML_w)
+ {
+ if (!rConstr.msForName.isEmpty())
+ continue;
+
+ sal_Int32 nProperty = getPropertyFromConstraint(rConstr.mnType);
+ if (!nProperty)
+ continue;
+
+ // PowerPoint takes size as points, but gives margin as MMs.
+ double fFactor = convertPointToMms(rConstr.mfFactor);
+
+ // DrawingML works in EMUs, UNO API works in MM100s.
+ sal_Int32 nValue = rShape->getSize().Width * fFactor / EMU_PER_HMM;
+
+ rShape->getShapeProperties().setProperty(nProperty, nValue);
+ }
+ if (rConstr.mnType == XML_primFontSz)
+ fFontSize = rConstr.mfValue;
+ }
+
+ TextBodyPtr pTextBody = rShape->getTextBody();
+ if (!pTextBody || pTextBody->isEmpty())
+ break;
+
+ // adjust text size to fit shape
+ if (fFontSize != 0)
+ {
+ for (auto& aParagraph : pTextBody->getParagraphs())
+ for (auto& aRun : aParagraph->getRuns())
+ if (!aRun->getTextCharacterProperties().moHeight.has())
+ aRun->getTextCharacterProperties().moHeight = fFontSize * 100;
+ }
+
+ if (!HasCustomText(rShape, getLayoutNode()))
+ {
+ // No customized text properties: enable autofit.
+ pTextBody->getTextProperties().maPropertyMap.setProperty(
+ PROP_TextFitToSize, drawing::TextFitToSizeType_AUTOFIT);
+ }
+
+ // ECMA-376-1:2016 21.4.7.5 ST_AutoTextRotation (Auto Text Rotation)
+ const sal_Int32 nautoTxRot = maMap.count(XML_autoTxRot) ? maMap.find(XML_autoTxRot)->second : XML_upr;
+ sal_Int32 nShapeRot = rShape->getRotation();
+ while (nShapeRot < 0)
+ nShapeRot += 360 * PER_DEGREE;
+ while (nShapeRot > 360 * PER_DEGREE)
+ nShapeRot -= 360 * PER_DEGREE;
+
+ switch(nautoTxRot)
+ {
+ case XML_upr:
+ {
+ int n90x = 0;
+ if (nShapeRot >= 315 * PER_DEGREE)
+ /* keep 0 */;
+ else if (nShapeRot > 225 * PER_DEGREE)
+ n90x = -3;
+ else if (nShapeRot >= 135 * PER_DEGREE)
+ n90x = -2;
+ else if (nShapeRot > 45 * PER_DEGREE)
+ n90x = -1;
+ pTextBody->getTextProperties().moRotation = n90x * 90 * PER_DEGREE;
+ }
+ break;
+ case XML_grav:
+ {
+ if (nShapeRot > (90 * PER_DEGREE) && nShapeRot < (270 * PER_DEGREE))
+ pTextBody->getTextProperties().moRotation = -180 * PER_DEGREE;
+ }
+ break;
+ case XML_none:
+ break;
+ }
+
+ const sal_Int32 atxAnchorVert = maMap.count(XML_txAnchorVert) ? maMap.find(XML_txAnchorVert)->second : XML_mid;
+
+ switch(atxAnchorVert)
+ {
+ case XML_t:
+ pTextBody->getTextProperties().meVA = css::drawing::TextVerticalAdjust_TOP;
+ break;
+ case XML_b:
+ pTextBody->getTextProperties().meVA = css::drawing::TextVerticalAdjust_BOTTOM;
+ break;
+ case XML_mid:
+ // text centered vertically by default
+ default:
+ pTextBody->getTextProperties().meVA = css::drawing::TextVerticalAdjust_CENTER;
+ break;
+ }
+
+ pTextBody->getTextProperties().maPropertyMap.setProperty(PROP_TextVerticalAdjust, pTextBody->getTextProperties().meVA);
+
+ // normalize list level
+ sal_Int32 nBaseLevel = pTextBody->getParagraphs().front()->getProperties().getLevel();
+ for (auto & aParagraph : pTextBody->getParagraphs())
+ {
+ if (aParagraph->getProperties().getLevel() < nBaseLevel)
+ nBaseLevel = aParagraph->getProperties().getLevel();
+ }
+
+ // Start bullets at:
+ // 1 - top level
+ // 2 - with children (default)
+ int nStartBulletsAtLevel = 2;
+ ParamMap::const_iterator aBulletLvl = maMap.find(XML_stBulletLvl);
+ if (aBulletLvl != maMap.end())
+ nStartBulletsAtLevel = aBulletLvl->second;
+ nStartBulletsAtLevel--;
+
+ bool isBulletList = false;
+ for (auto & aParagraph : pTextBody->getParagraphs())
+ {
+ sal_Int32 nLevel = aParagraph->getProperties().getLevel() - nBaseLevel;
+ aParagraph->getProperties().setLevel(nLevel);
+ if (nLevel >= nStartBulletsAtLevel)
+ {
+ if (!aParagraph->getProperties().getParaLeftMargin().has_value())
+ {
+ sal_Int32 nLeftMargin = 285750 * (nLevel - nStartBulletsAtLevel + 1) / EMU_PER_HMM;
+ aParagraph->getProperties().getParaLeftMargin() = nLeftMargin;
+ }
+
+ if (!aParagraph->getProperties().getFirstLineIndentation().has_value())
+ aParagraph->getProperties().getFirstLineIndentation() = -285750 / EMU_PER_HMM;
+
+ // It is not possible to change the bullet style for text.
+ OUString aBulletChar = OUString::fromUtf8(u8"•");
+ aParagraph->getProperties().getBulletList().setBulletChar(aBulletChar);
+ aParagraph->getProperties().getBulletList().setSuffixNone();
+ isBulletList = true;
+ }
+ }
+
+ // explicit alignment
+ ParamMap::const_iterator aDir = maMap.find(XML_parTxLTRAlign);
+ // TODO: XML_parTxRTLAlign
+ if (aDir != maMap.end())
+ {
+ css::style::ParagraphAdjust aAlignment = GetParaAdjust(aDir->second);
+ for (auto & aParagraph : pTextBody->getParagraphs())
+ aParagraph->getProperties().setParaAdjust(aAlignment);
+ }
+ else if (!isBulletList)
+ {
+ // if not list use default alignment - centered
+ for (auto & aParagraph : pTextBody->getParagraphs())
+ aParagraph->getProperties().setParaAdjust(css::style::ParagraphAdjust::ParagraphAdjust_CENTER);
+ }
+ break;
+ }
+
+ default:
+ break;
+ }
+
+ SAL_INFO(
+ "oox.drawingml",
+ "Layouting shape " << rShape->getInternalName() << ", alg type: " << mnType << ", ("
+ << rShape->getPosition().X << "," << rShape->getPosition().Y << ","
+ << rShape->getSize().Width << "," << rShape->getSize().Height << ")");
+}
+
+void LayoutNode::accept( LayoutAtomVisitor& rVisitor )
+{
+ rVisitor.visit(*this);
+}
+
+bool LayoutNode::setupShape( const ShapePtr& rShape, const dgm::Point* pPresNode, sal_Int32 nCurrIdx ) const
+{
+ SAL_INFO(
+ "oox.drawingml",
+ "Filling content from layout node named \"" << msName
+ << "\", modelId \"" << pPresNode->msModelId << "\"");
+
+ // have the presentation node - now, need the actual data node:
+ const DiagramData::StringMap::const_iterator aNodeName = mrDgm.getData()->getPresOfNameMap().find(
+ pPresNode->msModelId);
+ if( aNodeName != mrDgm.getData()->getPresOfNameMap().end() )
+ {
+ // Calculate the depth of what is effectively the topmost element.
+ sal_Int32 nMinDepth = std::numeric_limits<sal_Int32>::max();
+ for (const auto& rPair : aNodeName->second)
+ {
+ if (rPair.second.mnDepth < nMinDepth)
+ nMinDepth = rPair.second.mnDepth;
+ }
+
+ for (const auto& rPair : aNodeName->second)
+ {
+ const DiagramData::SourceIdAndDepth& rItem = rPair.second;
+ DiagramData::PointNameMap& rMap = mrDgm.getData()->getPointNameMap();
+ // pPresNode is the presentation node of the aDataNode2 data node.
+ DiagramData::PointNameMap::const_iterator aDataNode2 = rMap.find(rItem.msSourceId);
+ if (aDataNode2 == rMap.end())
+ {
+ //busted, skip it
+ continue;
+ }
+
+ if (!aDataNode2->second->mpShape)
+ {
+ //busted, skip it
+ continue;
+ }
+
+ rShape->setDataNodeType(aDataNode2->second->mnType);
+
+ if (rItem.mnDepth == 0)
+ {
+ // grab shape attr from topmost element(s)
+ rShape->getShapeProperties() = aDataNode2->second->mpShape->getShapeProperties();
+ rShape->getLineProperties() = aDataNode2->second->mpShape->getLineProperties();
+ rShape->getFillProperties() = aDataNode2->second->mpShape->getFillProperties();
+ rShape->getCustomShapeProperties() = aDataNode2->second->mpShape->getCustomShapeProperties();
+ rShape->setMasterTextListStyle( aDataNode2->second->mpShape->getMasterTextListStyle() );
+
+ SAL_INFO(
+ "oox.drawingml",
+ "Custom shape with preset type "
+ << (rShape->getCustomShapeProperties()
+ ->getShapePresetType())
+ << " added for layout node named \"" << msName
+ << "\"");
+ }
+ else if (rItem.mnDepth == nMinDepth)
+ {
+ // If no real topmost element, then take properties from the one that's the closest
+ // to topmost.
+ rShape->getLineProperties() = aDataNode2->second->mpShape->getLineProperties();
+ rShape->getFillProperties() = aDataNode2->second->mpShape->getFillProperties();
+ }
+
+ // append text with right outline level
+ if( aDataNode2->second->mpShape->getTextBody() &&
+ !aDataNode2->second->mpShape->getTextBody()->getParagraphs().empty() &&
+ !aDataNode2->second->mpShape->getTextBody()->getParagraphs().front()->getRuns().empty() )
+ {
+ TextBodyPtr pTextBody=rShape->getTextBody();
+ if( !pTextBody )
+ {
+ pTextBody = std::make_shared<TextBody>();
+
+ // also copy text attrs
+ pTextBody->getTextListStyle() =
+ aDataNode2->second->mpShape->getTextBody()->getTextListStyle();
+ pTextBody->getTextProperties() =
+ aDataNode2->second->mpShape->getTextBody()->getTextProperties();
+
+ rShape->setTextBody(pTextBody);
+ }
+
+ const TextParagraphVector& rSourceParagraphs
+ = aDataNode2->second->mpShape->getTextBody()->getParagraphs();
+ for (const auto& pSourceParagraph : rSourceParagraphs)
+ {
+ TextParagraph& rPara = pTextBody->addParagraph();
+ if (rItem.mnDepth != -1)
+ rPara.getProperties().setLevel(rItem.mnDepth);
+
+ for (const auto& pRun : pSourceParagraph->getRuns())
+ rPara.addRun(pRun);
+ const TextBodyPtr& rBody = aDataNode2->second->mpShape->getTextBody();
+ rPara.getProperties().apply(rBody->getParagraphs().front()->getProperties());
+ }
+ }
+ }
+ }
+ else
+ {
+ SAL_INFO(
+ "oox.drawingml",
+ "ShapeCreationVisitor::visit: no data node name found while"
+ " processing shape type "
+ << rShape->getCustomShapeProperties()->getShapePresetType()
+ << " for layout node named \"" << msName << "\"");
+ if (pPresNode->mpShape)
+ rShape->getFillProperties().assignUsed(pPresNode->mpShape->getFillProperties());
+ }
+
+ // TODO(Q1): apply styling & coloring - take presentation
+ // point's presStyleLbl for both style & color
+ // if not found use layout node's styleLbl
+ // however, docs are a bit unclear on this
+ OUString aStyleLabel = pPresNode->msPresentationLayoutStyleLabel;
+ if (aStyleLabel.isEmpty())
+ aStyleLabel = msStyleLabel;
+ if( !aStyleLabel.isEmpty() )
+ {
+ const DiagramQStyleMap::const_iterator aStyle = mrDgm.getStyles().find(aStyleLabel);
+ if( aStyle != mrDgm.getStyles().end() )
+ {
+ const DiagramStyle& rStyle = aStyle->second;
+ rShape->getShapeStyleRefs()[XML_fillRef] = rStyle.maFillStyle;
+ rShape->getShapeStyleRefs()[XML_lnRef] = rStyle.maLineStyle;
+ rShape->getShapeStyleRefs()[XML_effectRef] = rStyle.maEffectStyle;
+ rShape->getShapeStyleRefs()[XML_fontRef] = rStyle.maTextStyle;
+ }
+ else
+ {
+ SAL_WARN("oox.drawingml", "Style " << aStyleLabel << " not found");
+ }
+
+ const DiagramColorMap::const_iterator aColor = mrDgm.getColors().find(aStyleLabel);
+ if( aColor != mrDgm.getColors().end() )
+ {
+ // Take the nth color from the color list in case we are the nth shape in a
+ // <dgm:forEach> loop.
+ const DiagramColor& rColor=aColor->second;
+ if( !rColor.maFillColors.empty() )
+ rShape->getShapeStyleRefs()[XML_fillRef].maPhClr = DiagramColor::getColorByIndex(rColor.maFillColors, nCurrIdx);
+ if( !rColor.maLineColors.empty() )
+ rShape->getShapeStyleRefs()[XML_lnRef].maPhClr = DiagramColor::getColorByIndex(rColor.maLineColors, nCurrIdx);
+ if( !rColor.maEffectColors.empty() )
+ rShape->getShapeStyleRefs()[XML_effectRef].maPhClr = DiagramColor::getColorByIndex(rColor.maEffectColors, nCurrIdx);
+ if( !rColor.maTextFillColors.empty() )
+ rShape->getShapeStyleRefs()[XML_fontRef].maPhClr = DiagramColor::getColorByIndex(rColor.maTextFillColors, nCurrIdx);
+ }
+ }
+
+ // even if no data node found, successful anyway. it's
+ // contained at the layoutnode
+ 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);
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/oox/source/drawingml/diagram/diagramlayoutatoms.hxx b/oox/source/drawingml/diagram/diagramlayoutatoms.hxx
new file mode 100644
index 000000000..81b96c220
--- /dev/null
+++ b/oox/source/drawingml/diagram/diagramlayoutatoms.hxx
@@ -0,0 +1,325 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_OOX_SOURCE_DRAWINGML_DIAGRAM_DIAGRAMLAYOUTATOMS_HXX
+#define INCLUDED_OOX_SOURCE_DRAWINGML_DIAGRAM_DIAGRAMLAYOUTATOMS_HXX
+
+#include <map>
+#include <memory>
+
+#include <com/sun/star/xml/sax/XFastAttributeList.hpp>
+
+#include "diagram.hxx"
+
+namespace oox::drawingml {
+
+class DiagramLayout;
+typedef std::shared_ptr< DiagramLayout > DiagramLayoutPtr;
+
+// AG_IteratorAttributes
+struct IteratorAttr
+{
+ IteratorAttr();
+
+ // not sure this belong here, but wth
+ void loadFromXAttr( const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttributes );
+
+ std::vector<sal_Int32> maAxis;
+ sal_Int32 mnCnt;
+ bool mbHideLastTrans;
+ sal_Int32 mnPtType;
+ sal_Int32 mnSt;
+ sal_Int32 mnStep;
+};
+
+struct ConditionAttr
+{
+ ConditionAttr();
+
+ // not sure this belong here, but wth
+ void loadFromXAttr( const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttributes );
+
+ sal_Int32 mnFunc;
+ sal_Int32 mnArg;
+ sal_Int32 mnOp;
+ OUString msVal;
+ sal_Int32 mnVal;
+};
+
+/// Constraints allow you to specify an ideal (or starting point) size for each shape.
+struct Constraint
+{
+ sal_Int32 mnFor;
+ OUString msForName;
+ sal_Int32 mnPointType;
+ sal_Int32 mnType;
+ sal_Int32 mnRefFor;
+ OUString msRefForName;
+ sal_Int32 mnRefType;
+ sal_Int32 mnRefPointType;
+ double mfFactor;
+ double mfValue;
+ sal_Int32 mnOperator;
+};
+
+/// Rules allow you to specify what to do when constraints can't be fully satisfied.
+struct Rule
+{
+ OUString msForName;
+};
+
+typedef std::map<sal_Int32, sal_Int32> LayoutProperty;
+typedef std::map<OUString, LayoutProperty> LayoutPropertyMap;
+
+struct LayoutAtomVisitor;
+class LayoutAtom;
+class LayoutNode;
+
+typedef std::shared_ptr< LayoutAtom > LayoutAtomPtr;
+
+/** abstract Atom for the layout */
+class LayoutAtom
+{
+public:
+ LayoutAtom(LayoutNode& rLayoutNode) : mrLayoutNode(rLayoutNode) {}
+ virtual ~LayoutAtom() { }
+
+ LayoutNode& getLayoutNode()
+ { return mrLayoutNode; }
+
+ /** visitor acceptance
+ */
+ virtual void accept( LayoutAtomVisitor& ) = 0;
+
+ void setName( const OUString& sName )
+ { msName = sName; }
+ const OUString& getName() const
+ { return msName; }
+
+private:
+ void addChild( const LayoutAtomPtr & pNode )
+ { mpChildNodes.push_back( pNode ); }
+ void setParent(const LayoutAtomPtr& pParent) { mpParent = pParent; }
+
+public:
+ 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:
+ LayoutNode& mrLayoutNode;
+ std::vector< LayoutAtomPtr > mpChildNodes;
+ std::weak_ptr<LayoutAtom> mpParent;
+ OUString msName;
+};
+
+class ConstraintAtom
+ : public LayoutAtom
+{
+public:
+ ConstraintAtom(LayoutNode& rLayoutNode) : LayoutAtom(rLayoutNode) {}
+ virtual void accept( LayoutAtomVisitor& ) override;
+ Constraint& getConstraint()
+ { return maConstraint; }
+ void parseConstraint(std::vector<Constraint>& rConstraints, bool bRequireForName) const;
+private:
+ Constraint maConstraint;
+};
+
+/// Represents one <dgm:rule> element.
+class RuleAtom
+ : public LayoutAtom
+{
+public:
+ RuleAtom(LayoutNode& rLayoutNode) : LayoutAtom(rLayoutNode) {}
+ virtual void accept( LayoutAtomVisitor& ) override;
+ Rule& getRule()
+ { return maRule; }
+ void parseRule(std::vector<Rule>& rRules) const;
+private:
+ Rule maRule;
+};
+
+class AlgAtom
+ : public LayoutAtom
+{
+public:
+ AlgAtom(LayoutNode& rLayoutNode) : LayoutAtom(rLayoutNode), mnType(0), maMap() {}
+
+ typedef std::map<sal_Int32,sal_Int32> ParamMap;
+
+ virtual void accept( LayoutAtomVisitor& ) override;
+
+ void setType( sal_Int32 nToken )
+ { 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,
+ const std::vector<Rule>& rRules );
+
+ void setAspectRatio(double fAspectRatio) { mfAspectRatio = fAspectRatio; }
+
+ double getAspectRatio() const { return mfAspectRatio; }
+
+private:
+ sal_Int32 mnType;
+ ParamMap maMap;
+ /// Aspect ratio is not integer, so not part of maMap.
+ double mfAspectRatio = 0;
+
+ /// Determines the connector shape type for conn algorithm
+ sal_Int32 getConnectorType();
+};
+
+typedef std::shared_ptr< AlgAtom > AlgAtomPtr;
+
+class ForEachAtom
+ : public LayoutAtom
+{
+public:
+ explicit ForEachAtom(LayoutNode& rLayoutNode, const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttributes);
+
+ IteratorAttr & iterator()
+ { return maIter; }
+ void setRef(const OUString& rsRef)
+ { msRef = rsRef; }
+ const OUString& getRef() const
+ { return msRef; }
+ virtual void accept( LayoutAtomVisitor& ) override;
+ LayoutAtomPtr getRefAtom();
+
+private:
+ IteratorAttr maIter;
+ OUString msRef;
+};
+
+typedef std::shared_ptr< ForEachAtom > ForEachAtomPtr;
+
+class ConditionAtom
+ : public LayoutAtom
+{
+public:
+ explicit ConditionAtom(LayoutNode& rLayoutNode, bool isElse, const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttributes);
+ virtual void accept( LayoutAtomVisitor& ) override;
+ bool getDecision(const dgm::Point* pPresPoint) const;
+
+private:
+ static bool compareResult(sal_Int32 nOperator, sal_Int32 nFirst, sal_Int32 nSecond);
+ sal_Int32 getNodeCount(const dgm::Point* pPresPoint) const;
+
+ bool mIsElse;
+ IteratorAttr maIter;
+ ConditionAttr maCond;
+};
+
+typedef std::shared_ptr< ConditionAtom > ConditionAtomPtr;
+
+/** "choose" statements. Atoms will be tested in order. */
+class ChooseAtom
+ : public LayoutAtom
+{
+public:
+ ChooseAtom(LayoutNode& rLayoutNode)
+ : LayoutAtom(rLayoutNode)
+ {}
+ virtual void accept( LayoutAtomVisitor& ) override;
+};
+
+class LayoutNode
+ : public LayoutAtom
+{
+public:
+ typedef std::map<sal_Int32, OUString> VarMap;
+
+ LayoutNode(Diagram& rDgm)
+ : LayoutAtom(*this)
+ , mrDgm(rDgm)
+ , mnChildOrder(0)
+ {
+ }
+ Diagram& getDiagram() { return mrDgm; }
+ virtual void accept( LayoutAtomVisitor& ) override;
+ VarMap & variables()
+ { return mVariables; }
+ void setMoveWith( const OUString & sName )
+ { msMoveWith = sName; }
+ void setStyleLabel( const OUString & sLabel )
+ { msStyleLabel = sLabel; }
+ void setChildOrder( sal_Int32 nOrder )
+ { mnChildOrder = nOrder; }
+ sal_Int32 getChildOrder() const { return mnChildOrder; }
+ void setExistingShape( const ShapePtr& pShape )
+ { mpExistingShape = pShape; }
+ const ShapePtr& getExistingShape() const
+ { return mpExistingShape; }
+ const std::vector<ShapePtr> & getNodeShapes() const
+ { return mpNodeShapes; }
+ void addNodeShape(const ShapePtr& pShape)
+ { mpNodeShapes.push_back(pShape); }
+
+ bool setupShape( const ShapePtr& rShape,
+ const dgm::Point* pPresNode,
+ sal_Int32 nCurrIdx ) const;
+
+ const LayoutNode* getParentLayoutNode() const;
+
+private:
+ Diagram& mrDgm;
+ VarMap mVariables;
+ OUString msMoveWith;
+ OUString msStyleLabel;
+ ShapePtr mpExistingShape;
+ std::vector<ShapePtr> mpNodeShapes;
+ sal_Int32 mnChildOrder;
+};
+
+typedef std::shared_ptr< LayoutNode > LayoutNodePtr;
+
+class ShapeAtom
+ : public LayoutAtom
+{
+public:
+ ShapeAtom(LayoutNode& rLayoutNode, const ShapePtr& pShape) : LayoutAtom(rLayoutNode), mpShapeTemplate(pShape) {}
+ virtual void accept( LayoutAtomVisitor& ) override;
+ const ShapePtr& getShapeTemplate() const
+ { return mpShapeTemplate; }
+
+private:
+ ShapePtr mpShapeTemplate;
+};
+
+typedef std::shared_ptr< ShapeAtom > ShapeAtomPtr;
+
+}
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/oox/source/drawingml/diagram/layoutatomvisitorbase.cxx b/oox/source/drawingml/diagram/layoutatomvisitorbase.cxx
new file mode 100644
index 000000000..9b7ddaf8c
--- /dev/null
+++ b/oox/source/drawingml/diagram/layoutatomvisitorbase.cxx
@@ -0,0 +1,176 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "layoutatomvisitorbase.hxx"
+
+#include <sal/log.hxx>
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::uno;
+
+namespace oox::drawingml {
+
+void LayoutAtomVisitorBase::defaultVisit(LayoutAtom const& rAtom)
+{
+ for (const auto& pAtom : rAtom.getChildren())
+ pAtom->accept(*this);
+}
+
+void LayoutAtomVisitorBase::visit(ChooseAtom& rAtom)
+{
+ for (const auto& pChild : rAtom.getChildren())
+ {
+ const ConditionAtomPtr pCond = std::dynamic_pointer_cast<ConditionAtom>(pChild);
+ if (pCond && pCond->getDecision(mpCurrentNode))
+ {
+ SAL_INFO("oox.drawingml", "Entering if node: " << pCond->getName());
+ pCond->accept(*this);
+ break;
+ }
+ }
+}
+
+void LayoutAtomVisitorBase::visit(ConditionAtom& rAtom)
+{
+ defaultVisit(rAtom);
+}
+
+void LayoutAtomVisitorBase::visit(ForEachAtom& rAtom)
+{
+ if (!rAtom.getRef().isEmpty())
+ {
+ if (LayoutAtomPtr pRefAtom = rAtom.getRefAtom())
+ pRefAtom->accept(*this);
+ return;
+ }
+
+ if (rAtom.iterator().mbHideLastTrans && !rAtom.iterator().maAxis.empty() && rAtom.iterator().maAxis[0] == XML_followSib)
+ {
+ // If last transition is hidden and the axis is the follow sibling,
+ // then the last atom should not be visited.
+ if (mnCurrIdx + mnCurrStep >= mnCurrCnt)
+ return;
+ }
+
+ sal_Int32 nChildren = 1;
+ // Approximate the non-assistant type with the node type.
+ if (rAtom.iterator().mnPtType == XML_node || rAtom.iterator().mnPtType == XML_nonAsst)
+ {
+ // count child data nodes - check all child Atoms for "name"
+ // attribute that is contained in diagram's
+ // getPointsPresNameMap()
+ ShallowPresNameVisitor aVisitor(mrDgm, mpCurrentNode);
+ for (const auto& pAtom : rAtom.getChildren())
+ pAtom->accept(aVisitor);
+ nChildren = aVisitor.getCount();
+ }
+
+ const sal_Int32 nCnt = std::min(
+ nChildren,
+ 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
+ for (const auto& pAtom : rAtom.getChildren())
+ pAtom->accept(*this);
+ }
+
+ // and restore idx
+ mnCurrIdx = nOldIdx;
+ mnCurrStep = nOldStep;
+ mnCurrCnt = nOldCnt;
+}
+
+void LayoutAtomVisitorBase::visit(LayoutNode& rAtom)
+{
+ // TODO: deduplicate code in descendants
+
+ // stop processing if it's not a child of previous LayoutNode
+
+ const DiagramData::PointsNameMap::const_iterator aDataNode
+ = mrDgm.getData()->getPointsPresNameMap().find(rAtom.getName());
+ if (aDataNode == mrDgm.getData()->getPointsPresNameMap().end()
+ || mnCurrIdx >= static_cast<sal_Int32>(aDataNode->second.size()))
+ return;
+
+ const dgm::Point* pNewNode = aDataNode->second.at(mnCurrIdx);
+ if (!mpCurrentNode || !pNewNode)
+ return;
+
+ bool bIsChild = false;
+ for (const auto& aConnection : mrDgm.getData()->getConnections())
+ if (aConnection.msSourceId == mpCurrentNode->msModelId
+ && aConnection.msDestId == pNewNode->msModelId)
+ bIsChild = true;
+
+ if (!bIsChild)
+ return;
+
+ const dgm::Point* pPreviousNode = mpCurrentNode;
+ mpCurrentNode = pNewNode;
+
+ defaultVisit(rAtom);
+
+ mpCurrentNode = pPreviousNode;
+}
+
+void ShallowPresNameVisitor::visit(ConstraintAtom& /*rAtom*/)
+{
+ // stop processing
+}
+
+void ShallowPresNameVisitor::visit(RuleAtom& /*rAtom*/)
+{
+ // stop processing
+}
+
+void ShallowPresNameVisitor::visit(AlgAtom& /*rAtom*/)
+{
+ // stop processing
+}
+
+void ShallowPresNameVisitor::visit(ForEachAtom& rAtom)
+{
+ defaultVisit(rAtom);
+}
+
+void ShallowPresNameVisitor::visit(LayoutNode& rAtom)
+{
+ DiagramData::PointsNameMap::const_iterator aDataNode =
+ mrDgm.getData()->getPointsPresNameMap().find(rAtom.getName());
+ if( aDataNode != mrDgm.getData()->getPointsPresNameMap().end() )
+ mnCnt = std::max(mnCnt,
+ aDataNode->second.size());
+}
+
+void ShallowPresNameVisitor::visit(ShapeAtom& /*rAtom*/)
+{
+ // stop processing
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/oox/source/drawingml/diagram/layoutatomvisitorbase.hxx b/oox/source/drawingml/diagram/layoutatomvisitorbase.hxx
new file mode 100644
index 000000000..0f97c578c
--- /dev/null
+++ b/oox/source/drawingml/diagram/layoutatomvisitorbase.hxx
@@ -0,0 +1,98 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_OOX_SOURCE_DRAWINGML_DIAGRAM_LAYOUTATOMVISITORBASE_HXX
+#define INCLUDED_OOX_SOURCE_DRAWINGML_DIAGRAM_LAYOUTATOMVISITORBASE_HXX
+
+#include "diagram.hxx"
+#include "diagramlayoutatoms.hxx"
+
+namespace oox::drawingml {
+
+struct LayoutAtomVisitor
+{
+ virtual ~LayoutAtomVisitor() {}
+ virtual void visit(ConstraintAtom& rAtom) = 0;
+ virtual void visit(RuleAtom& rAtom) = 0;
+ virtual void visit(AlgAtom& rAtom) = 0;
+ virtual void visit(ForEachAtom& rAtom) = 0;
+ virtual void visit(ConditionAtom& rAtom) = 0;
+ virtual void visit(ChooseAtom& rAtom) = 0;
+ virtual void visit(LayoutNode& rAtom) = 0;
+ virtual void visit(ShapeAtom& rAtom) = 0;
+};
+
+// basic visitor implementation that follows if/else and for-each nodes
+// and keeps track of current position in data tree
+class LayoutAtomVisitorBase : public LayoutAtomVisitor
+{
+public:
+ LayoutAtomVisitorBase(const Diagram& rDgm, const dgm::Point* pRootPoint) :
+ mrDgm(rDgm),
+ mpCurrentNode(pRootPoint),
+ mnCurrIdx(0),
+ mnCurrStep(0),
+ mnCurrCnt(0),
+ meLookFor(LAYOUT_NODE)
+ {}
+
+ void defaultVisit(LayoutAtom const& rAtom);
+
+ using LayoutAtomVisitor::visit;
+ virtual void visit(ForEachAtom& rAtom) override;
+ virtual void visit(ConditionAtom& rAtom) override;
+ virtual void visit(ChooseAtom& rAtom) override;
+ virtual void visit(LayoutNode& rAtom) override;
+
+protected:
+ const Diagram& mrDgm;
+ const dgm::Point* mpCurrentNode;
+ sal_Int32 mnCurrIdx;
+ sal_Int32 mnCurrStep;
+ sal_Int32 mnCurrCnt;
+ enum {LAYOUT_NODE, CONSTRAINT, ALGORITHM, RULE} meLookFor;
+};
+
+class ShallowPresNameVisitor : public LayoutAtomVisitorBase
+{
+public:
+ explicit ShallowPresNameVisitor(const Diagram& rDgm, const dgm::Point* pRootPoint) :
+ LayoutAtomVisitorBase(rDgm, pRootPoint),
+ mnCnt(0)
+ {}
+
+ using LayoutAtomVisitorBase::visit;
+ virtual void visit(ConstraintAtom& rAtom) override;
+ virtual void visit(RuleAtom& rAtom) override;
+ virtual void visit(AlgAtom& rAtom) override;
+ virtual void visit(ForEachAtom& rAtom) override;
+ virtual void visit(LayoutNode& rAtom) override;
+ virtual void visit(ShapeAtom& rAtom) override;
+
+ size_t getCount() const { return mnCnt; }
+
+private:
+ size_t mnCnt;
+};
+
+}
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/oox/source/drawingml/diagram/layoutatomvisitors.cxx b/oox/source/drawingml/diagram/layoutatomvisitors.cxx
new file mode 100644
index 000000000..5d31fd21c
--- /dev/null
+++ b/oox/source/drawingml/diagram/layoutatomvisitors.cxx
@@ -0,0 +1,260 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "layoutatomvisitors.hxx"
+
+#include <drawingml/customshapeproperties.hxx>
+
+#include <sal/log.hxx>
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::xml::sax;
+using namespace ::oox::core;
+
+namespace oox::drawingml {
+
+void ShapeCreationVisitor::visit(ConstraintAtom& /*rAtom*/)
+{
+ // stop processing
+}
+
+void ShapeCreationVisitor::visit(RuleAtom& /*rAtom*/)
+{
+ // stop processing
+}
+
+void ShapeCreationVisitor::visit(AlgAtom& rAtom)
+{
+ 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());
+ if (aDataNode == mrDgm.getData()->getPointsPresNameMap().end() || mnCurrIdx >= static_cast<sal_Int32>(aDataNode->second.size()))
+ return;
+
+ const dgm::Point* pNewNode = aDataNode->second.at(mnCurrIdx);
+ if (!mpCurrentNode || !pNewNode)
+ return;
+
+ bool bIsChild = false;
+ for (const auto & aConnection : mrDgm.getData()->getConnections())
+ if (aConnection.msSourceId == mpCurrentNode->msModelId && aConnection.msDestId == pNewNode->msModelId)
+ bIsChild = true;
+
+ if (!bIsChild)
+ return;
+
+ ShapePtr pCurrParent(mpParentShape);
+
+ if (rAtom.getExistingShape())
+ {
+ // reuse existing shape
+ ShapePtr pShape = rAtom.getExistingShape();
+ if (rAtom.setupShape(pShape, pNewNode, mnCurrIdx))
+ {
+ pShape->setInternalName(rAtom.getName());
+ rAtom.addNodeShape(pShape);
+ mrDgm.getLayout()->getPresPointShapeMap()[pNewNode] = pShape;
+ }
+ }
+ else
+ {
+ ShapeTemplateVisitor aTemplateVisitor(mrDgm, pNewNode);
+ aTemplateVisitor.defaultVisit(rAtom);
+ ShapePtr pShape = aTemplateVisitor.getShapeCopy();
+
+ if (pShape)
+ {
+ SAL_INFO(
+ "oox.drawingml",
+ "processing shape type " << (pShape->getCustomShapeProperties()->getShapePresetType()));
+
+ if (rAtom.setupShape(pShape, pNewNode, mnCurrIdx))
+ {
+ pShape->setInternalName(rAtom.getName());
+ pCurrParent->addChild(pShape);
+ pCurrParent = pShape;
+ rAtom.addNodeShape(pShape);
+ mrDgm.getLayout()->getPresPointShapeMap()[pNewNode] = pShape;
+ }
+ }
+ else
+ {
+ SAL_WARN("oox.drawingml", "ShapeCreationVisitor::visit: no shape set while processing layoutnode named " << rAtom.getName());
+ }
+ }
+
+ const dgm::Point* pPreviousNode = mpCurrentNode;
+ mpCurrentNode = pNewNode;
+
+ // set new parent for children
+ ShapePtr pPreviousParent(mpParentShape);
+ mpParentShape=pCurrParent;
+
+ // process children
+ meLookFor = LAYOUT_NODE;
+ defaultVisit(rAtom);
+
+ meLookFor = ALGORITHM;
+ defaultVisit(rAtom);
+ meLookFor = LAYOUT_NODE;
+
+ // restore parent
+ mpParentShape=pPreviousParent;
+ mpCurrentNode = pPreviousNode;
+}
+
+void ShapeCreationVisitor::visit(ShapeAtom& /*rAtom*/)
+{
+ // stop processing
+}
+
+void ShapeTemplateVisitor::visit(ConstraintAtom& /*rAtom*/)
+{
+ // stop processing
+}
+
+void ShapeTemplateVisitor::visit(RuleAtom& /*rAtom*/)
+{
+ // stop processing
+}
+
+void ShapeTemplateVisitor::visit(AlgAtom& /*rAtom*/)
+{
+ // stop processing
+}
+
+void ShapeTemplateVisitor::visit(ForEachAtom& /*rAtom*/)
+{
+ // stop processing
+}
+
+void ShapeTemplateVisitor::visit(LayoutNode& /*rAtom*/)
+{
+ // stop processing - only traverse Condition/Choose atoms
+}
+
+void ShapeTemplateVisitor::visit(ShapeAtom& rAtom)
+{
+ if (mpShape)
+ {
+ SAL_WARN("oox.drawingml", "multiple shapes encountered inside LayoutNode");
+ return;
+ }
+
+ const ShapePtr& pCurrShape(rAtom.getShapeTemplate());
+
+ // TODO(F3): cloned shape shares all properties by reference,
+ // don't change them!
+ mpShape = std::make_shared<Shape>(pCurrShape);
+ // Fill properties have to be changed as sometimes only the presentation node contains the blip
+ // fill, unshare those.
+ mpShape->cloneFillProperties();
+}
+
+void ShapeLayoutingVisitor::visit(ConstraintAtom& rAtom)
+{
+ if (meLookFor == CONSTRAINT)
+ rAtom.parseConstraint(maConstraints, /*bRequireForName=*/true);
+}
+
+void ShapeLayoutingVisitor::visit(RuleAtom& rAtom)
+{
+ if (meLookFor == RULE)
+ rAtom.parseRule(maRules);
+}
+
+void ShapeLayoutingVisitor::visit(AlgAtom& rAtom)
+{
+ if (meLookFor == ALGORITHM)
+ {
+ const PresPointShapeMap aMap = rAtom.getLayoutNode().getDiagram().getLayout()->getPresPointShapeMap();
+ auto pShape = aMap.find(mpCurrentNode);
+ if (pShape != aMap.end())
+ rAtom.layoutShape(pShape->second, maConstraints, maRules);
+ }
+}
+
+void ShapeLayoutingVisitor::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());
+ if (aDataNode == mrDgm.getData()->getPointsPresNameMap().end()
+ || mnCurrIdx >= static_cast<sal_Int32>(aDataNode->second.size()))
+ return;
+
+ const dgm::Point* pNewNode = aDataNode->second.at(mnCurrIdx);
+ if (!mpCurrentNode || !pNewNode)
+ return;
+
+ bool bIsChild = false;
+ for (const auto& aConnection : mrDgm.getData()->getConnections())
+ if (aConnection.msSourceId == mpCurrentNode->msModelId
+ && aConnection.msDestId == pNewNode->msModelId)
+ bIsChild = true;
+
+ if (!bIsChild)
+ return;
+
+ size_t nParentConstraintsNumber = maConstraints.size();
+
+ const dgm::Point* pPreviousNode = mpCurrentNode;
+ mpCurrentNode = pNewNode;
+
+ // process alg atoms first, nested layout nodes afterwards
+ meLookFor = CONSTRAINT;
+ defaultVisit(rAtom);
+ meLookFor = RULE;
+ defaultVisit(rAtom);
+ meLookFor = ALGORITHM;
+ defaultVisit(rAtom);
+ meLookFor = LAYOUT_NODE;
+ defaultVisit(rAtom);
+
+ mpCurrentNode = pPreviousNode;
+
+ // delete added constraints, keep parent constraints
+ maConstraints.erase(maConstraints.begin() + nParentConstraintsNumber, maConstraints.end());
+}
+
+void ShapeLayoutingVisitor::visit(ShapeAtom& /*rAtom*/)
+{
+ // stop processing
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/oox/source/drawingml/diagram/layoutatomvisitors.hxx b/oox/source/drawingml/diagram/layoutatomvisitors.hxx
new file mode 100644
index 000000000..b8c060b0d
--- /dev/null
+++ b/oox/source/drawingml/diagram/layoutatomvisitors.hxx
@@ -0,0 +1,96 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_OOX_SOURCE_DRAWINGML_DIAGRAM_LAYOUTATOMVISITORS_HXX
+#define INCLUDED_OOX_SOURCE_DRAWINGML_DIAGRAM_LAYOUTATOMVISITORS_HXX
+
+#include <oox/drawingml/drawingmltypes.hxx>
+#include "diagram.hxx"
+#include "diagramlayoutatoms.hxx"
+#include "layoutatomvisitorbase.hxx"
+
+namespace oox::drawingml {
+
+class ShapeCreationVisitor : public LayoutAtomVisitorBase
+{
+public:
+ ShapeCreationVisitor(const Diagram& rDgm,
+ const dgm::Point* pRootPoint,
+ const ShapePtr& rParentShape) :
+ LayoutAtomVisitorBase(rDgm, pRootPoint),
+ mpParentShape(rParentShape)
+ {}
+
+ using LayoutAtomVisitorBase::visit;
+ virtual void visit(ConstraintAtom& rAtom) override;
+ virtual void visit(RuleAtom& rAtom) override;
+ virtual void visit(AlgAtom& rAtom) override;
+ virtual void visit(LayoutNode& rAtom) override;
+ virtual void visit(ShapeAtom& rAtom) override;
+
+private:
+ ShapePtr mpParentShape;
+};
+
+class ShapeTemplateVisitor : public LayoutAtomVisitorBase
+{
+public:
+ ShapeTemplateVisitor(const Diagram& rDgm, const dgm::Point* pRootPoint)
+ : LayoutAtomVisitorBase(rDgm, pRootPoint)
+ {}
+
+ using LayoutAtomVisitorBase::visit;
+ virtual void visit(ConstraintAtom& rAtom) override;
+ virtual void visit(RuleAtom& rAtom) override;
+ virtual void visit(AlgAtom& rAtom) override;
+ virtual void visit(ForEachAtom& rAtom) override;
+ virtual void visit(LayoutNode& rAtom) override;
+ virtual void visit(ShapeAtom& rAtom) override;
+
+ ShapePtr const & getShapeCopy() const
+ { return mpShape; }
+
+private:
+ ShapePtr mpShape;
+};
+
+class ShapeLayoutingVisitor : public LayoutAtomVisitorBase
+{
+public:
+ ShapeLayoutingVisitor(const Diagram& rDgm, const dgm::Point* pRootPoint) :
+ LayoutAtomVisitorBase(rDgm, pRootPoint)
+ {}
+
+ using LayoutAtomVisitorBase::visit;
+ virtual void visit(ConstraintAtom& rAtom) override;
+ virtual void visit(RuleAtom& rAtom) override;
+ virtual void visit(AlgAtom& rAtom) override;
+ virtual void visit(LayoutNode& rAtom) override;
+ virtual void visit(ShapeAtom& rAtom) override;
+
+private:
+ std::vector<Constraint> maConstraints;
+ std::vector<Rule> maRules;
+};
+
+}
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/oox/source/drawingml/diagram/layoutnodecontext.cxx b/oox/source/drawingml/diagram/layoutnodecontext.cxx
new file mode 100644
index 000000000..93f927531
--- /dev/null
+++ b/oox/source/drawingml/diagram/layoutnodecontext.cxx
@@ -0,0 +1,309 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "layoutnodecontext.hxx"
+
+#include <oox/helper/attributelist.hxx>
+#include <oox/drawingml/shapecontext.hxx>
+#include <drawingml/customshapeproperties.hxx>
+#include "constraintlistcontext.hxx"
+#include "rulelistcontext.hxx"
+#include <oox/token/namespaces.hxx>
+#include <oox/token/tokens.hxx>
+#include <sal/log.hxx>
+
+using namespace ::oox::core;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::xml::sax;
+
+namespace oox::drawingml {
+
+namespace {
+
+class IfContext
+ : public LayoutNodeContext
+{
+public:
+ IfContext( ContextHandler2Helper const & rParent,
+ const AttributeList& rAttribs,
+ const ConditionAtomPtr& pAtom )
+ : LayoutNodeContext( rParent, rAttribs, pAtom )
+ {}
+};
+
+class AlgorithmContext
+ : public ContextHandler2
+{
+public:
+ AlgorithmContext( ContextHandler2Helper const & rParent, const AttributeList& rAttribs, const AlgAtomPtr & pNode )
+ : ContextHandler2( rParent )
+ , mnRevision( 0 )
+ , mpNode( pNode )
+ {
+ mnRevision = rAttribs.getInteger( XML_rev, 0 );
+ pNode->setType(rAttribs.getToken(XML_type, 0));
+ }
+
+ virtual ContextHandlerRef
+ onCreateContext( ::sal_Int32 aElement,
+ const AttributeList& rAttribs ) override
+ {
+ switch( aElement )
+ {
+ case DGM_TOKEN( param ):
+ {
+ sal_Int32 nType = rAttribs.getToken(XML_type, 0);
+ switch (nType)
+ {
+ case XML_ar:
+ mpNode->setAspectRatio(rAttribs.getDouble(XML_val, 0));
+ break;
+ default:
+ const sal_Int32 nValTok = rAttribs.getToken(XML_val, 0);
+ mpNode->addParam(nType, nValTok > 0 ? nValTok
+ : rAttribs.getInteger(XML_val, 0));
+ break;
+ }
+ break;
+ }
+ default:
+ break;
+ }
+
+ return this;
+ }
+
+private:
+ sal_Int32 mnRevision;
+ AlgAtomPtr mpNode;
+};
+
+class ChooseContext
+ : public ContextHandler2
+{
+public:
+ ChooseContext( ContextHandler2Helper const & rParent, const AttributeList& rAttribs, const LayoutAtomPtr & pNode )
+ : ContextHandler2( rParent )
+ , mpNode( pNode )
+ {
+ msName = rAttribs.getString( XML_name ).get();
+ }
+
+ virtual ContextHandlerRef
+ onCreateContext( ::sal_Int32 aElement,
+ const AttributeList& rAttribs ) override
+ {
+ switch( aElement )
+ {
+ case DGM_TOKEN( if ):
+ {
+ // CT_When
+ ConditionAtomPtr pNode = std::make_shared<ConditionAtom>(mpNode->getLayoutNode(), false, rAttribs.getFastAttributeList());
+ LayoutAtom::connect(mpNode, pNode);
+ return new IfContext( *this, rAttribs, pNode );
+ }
+ case DGM_TOKEN( else ):
+ {
+ // CT_Otherwise
+ ConditionAtomPtr pNode = std::make_shared<ConditionAtom>(mpNode->getLayoutNode(), true, rAttribs.getFastAttributeList());
+ LayoutAtom::connect(mpNode, pNode);
+ return new IfContext( *this, rAttribs, pNode );
+ }
+ default:
+ break;
+ }
+
+ return this;
+ }
+private:
+ OUString msName;
+ LayoutAtomPtr mpNode;
+};
+
+class ForEachContext
+ : public LayoutNodeContext
+{
+public:
+ ForEachContext( ContextHandler2Helper const & rParent, const AttributeList& rAttribs, const ForEachAtomPtr& pAtom )
+ : LayoutNodeContext( rParent, rAttribs, pAtom )
+ {
+ pAtom->setRef(rAttribs.getString(XML_ref).get());
+ pAtom->iterator().loadFromXAttr( rAttribs.getFastAttributeList() );
+
+ LayoutAtomMap& rLayoutAtomMap = pAtom->getLayoutNode().getDiagram().getLayout()->getLayoutAtomMap();
+ rLayoutAtomMap[pAtom->getName()] = pAtom;
+ }
+};
+
+// CT_LayoutVariablePropertySet
+class LayoutVariablePropertySetContext
+ : public ContextHandler2
+{
+public:
+ LayoutVariablePropertySetContext( ContextHandler2Helper const & rParent, LayoutNode::VarMap & aVar )
+ : ContextHandler2( rParent )
+ , mVariables( aVar )
+ {
+ }
+
+ virtual ContextHandlerRef onCreateContext( ::sal_Int32 aElement, const AttributeList& rAttribs ) override
+ {
+ mVariables[ getBaseToken(aElement) ] = rAttribs.getString( XML_val ).get();
+ return this;
+ }
+private:
+ LayoutNode::VarMap & mVariables;
+};
+
+}
+
+// CT_LayoutNode
+LayoutNodeContext::LayoutNodeContext( ContextHandler2Helper const & rParent,
+ const AttributeList& rAttribs,
+ const LayoutAtomPtr& pAtom )
+ : ContextHandler2( rParent )
+ , mpNode( pAtom )
+{
+ assert( pAtom && "Node must NOT be NULL" );
+ mpNode->setName( rAttribs.getString( XML_name ).get() );
+}
+
+LayoutNodeContext::~LayoutNodeContext()
+{
+}
+
+ContextHandlerRef
+LayoutNodeContext::onCreateContext( ::sal_Int32 aElement,
+ const AttributeList& rAttribs )
+{
+ switch( aElement )
+ {
+ case DGM_TOKEN( layoutNode ):
+ {
+ LayoutNodePtr pNode = std::make_shared<LayoutNode>(mpNode->getLayoutNode().getDiagram());
+ LayoutAtom::connect(mpNode, pNode);
+
+ if (rAttribs.hasAttribute(XML_chOrder))
+ {
+ pNode->setChildOrder(rAttribs.getToken(XML_chOrder, XML_b));
+ }
+ else
+ {
+ for (LayoutAtomPtr pAtom = mpNode; pAtom; pAtom = pAtom->getParent())
+ {
+ auto pLayoutNode = dynamic_cast<LayoutNode*>(pAtom.get());
+ if (pLayoutNode)
+ {
+ pNode->setChildOrder(pLayoutNode->getChildOrder());
+ break;
+ }
+ }
+ }
+
+ pNode->setMoveWith( rAttribs.getString( XML_moveWith ).get() );
+ pNode->setStyleLabel( rAttribs.getString( XML_styleLbl ).get() );
+ return new LayoutNodeContext( *this, rAttribs, pNode );
+ }
+ case DGM_TOKEN( shape ):
+ {
+ ShapePtr pShape;
+
+ if( rAttribs.hasAttribute( XML_type ) )
+ {
+ pShape = std::make_shared<Shape>("com.sun.star.drawing.CustomShape");
+ if (!rAttribs.getBool(XML_hideGeom, false))
+ {
+ const sal_Int32 nType(rAttribs.getToken( XML_type, XML_obj ));
+ pShape->setSubType( nType );
+ pShape->getCustomShapeProperties()->setShapePresetType( nType );
+ }
+ }
+ else
+ {
+ pShape = std::make_shared<Shape>("com.sun.star.drawing.GroupShape");
+ }
+
+ pShape->setDiagramRotation(rAttribs.getInteger(XML_rot, 0) * PER_DEGREE);
+
+ pShape->setZOrderOff(rAttribs.getInteger(XML_zOrderOff, 0));
+
+ ShapeAtomPtr pAtom = std::make_shared<ShapeAtom>(mpNode->getLayoutNode(), pShape);
+ LayoutAtom::connect(mpNode, pAtom);
+ return new ShapeContext( *this, ShapePtr(), pShape );
+ }
+ case DGM_TOKEN( extLst ):
+ return nullptr;
+ case DGM_TOKEN( alg ):
+ {
+ // CT_Algorithm
+ AlgAtomPtr pAtom = std::make_shared<AlgAtom>(mpNode->getLayoutNode());
+ LayoutAtom::connect(mpNode, pAtom);
+ return new AlgorithmContext( *this, rAttribs, pAtom );
+ }
+ case DGM_TOKEN( choose ):
+ {
+ // CT_Choose
+ LayoutAtomPtr pAtom = std::make_shared<ChooseAtom>(mpNode->getLayoutNode());
+ LayoutAtom::connect(mpNode, pAtom);
+ return new ChooseContext( *this, rAttribs, pAtom );
+ }
+ case DGM_TOKEN( forEach ):
+ {
+ // CT_ForEach
+ ForEachAtomPtr pAtom = std::make_shared<ForEachAtom>(mpNode->getLayoutNode(), rAttribs.getFastAttributeList());
+ LayoutAtom::connect(mpNode, pAtom);
+ return new ForEachContext( *this, rAttribs, pAtom );
+ }
+ case DGM_TOKEN( constrLst ):
+ // CT_Constraints
+ return new ConstraintListContext( *this, mpNode );
+ case DGM_TOKEN( presOf ):
+ {
+ // CT_PresentationOf
+ // TODO
+ IteratorAttr aIterator;
+ aIterator.loadFromXAttr(rAttribs.getFastAttributeList());
+ break;
+ }
+ case DGM_TOKEN( ruleLst ):
+ // CT_Rules
+ return new RuleListContext( *this, mpNode );
+ case DGM_TOKEN( varLst ):
+ {
+ LayoutNodePtr pNode(std::dynamic_pointer_cast<LayoutNode>(mpNode));
+ if( pNode )
+ {
+ return new LayoutVariablePropertySetContext( *this, pNode->variables() );
+ }
+ else
+ {
+ SAL_WARN("oox", "OOX: encountered a varLst in a non layoutNode context" );
+ }
+ break;
+ }
+ default:
+ break;
+ }
+
+ return this;
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/oox/source/drawingml/diagram/layoutnodecontext.hxx b/oox/source/drawingml/diagram/layoutnodecontext.hxx
new file mode 100644
index 000000000..3499f5704
--- /dev/null
+++ b/oox/source/drawingml/diagram/layoutnodecontext.hxx
@@ -0,0 +1,43 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_OOX_SOURCE_DRAWINGML_DIAGRAM_LAYOUTNODECONTEXT_HXX
+#define INCLUDED_OOX_SOURCE_DRAWINGML_DIAGRAM_LAYOUTNODECONTEXT_HXX
+
+#include <oox/core/contexthandler2.hxx>
+#include "diagramlayoutatoms.hxx"
+
+namespace oox::drawingml {
+
+class LayoutNodeContext : public ::oox::core::ContextHandler2
+{
+public:
+ LayoutNodeContext( ::oox::core::ContextHandler2Helper const & rParent, const ::oox::AttributeList& rAttributes, const LayoutAtomPtr &pNode );
+ virtual ~LayoutNodeContext() override;
+
+ virtual ::oox::core::ContextHandlerRef onCreateContext( ::sal_Int32 Element, const ::oox::AttributeList& rAttribs ) override;
+private:
+ LayoutAtomPtr mpNode;
+};
+
+}
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/oox/source/drawingml/diagram/rulelistcontext.cxx b/oox/source/drawingml/diagram/rulelistcontext.cxx
new file mode 100644
index 000000000..69e930cdc
--- /dev/null
+++ b/oox/source/drawingml/diagram/rulelistcontext.cxx
@@ -0,0 +1,58 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "rulelistcontext.hxx"
+#include <oox/helper/attributelist.hxx>
+#include <oox/token/namespaces.hxx>
+#include <oox/token/tokens.hxx>
+
+namespace oox::drawingml
+{
+RuleListContext::RuleListContext(ContextHandler2Helper const& rParent, const LayoutAtomPtr& pNode)
+ : ContextHandler2(rParent)
+ , mpNode(pNode)
+{
+ assert(pNode);
+}
+
+RuleListContext::~RuleListContext() {}
+
+core::ContextHandlerRef RuleListContext::onCreateContext(sal_Int32 nElement,
+ const AttributeList& rAttribs)
+{
+ switch (nElement)
+ {
+ case DGM_TOKEN(rule):
+ {
+ auto pNode = std::make_shared<RuleAtom>(mpNode->getLayoutNode());
+ LayoutAtom::connect(mpNode, pNode);
+
+ Rule& rRule = pNode->getRule();
+ rRule.msForName = rAttribs.getString(XML_forName, "");
+ break;
+ }
+ default:
+ break;
+ }
+
+ return this;
+}
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/oox/source/drawingml/diagram/rulelistcontext.hxx b/oox/source/drawingml/diagram/rulelistcontext.hxx
new file mode 100644
index 000000000..43098ce71
--- /dev/null
+++ b/oox/source/drawingml/diagram/rulelistcontext.hxx
@@ -0,0 +1,42 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <oox/core/contexthandler2.hxx>
+#include "diagramlayoutatoms.hxx"
+
+namespace oox::drawingml
+{
+/// Handles one <dgm:ruleLst> element.
+class RuleListContext : public oox::core::ContextHandler2
+{
+public:
+ RuleListContext(ContextHandler2Helper const& rParent, const LayoutAtomPtr& pNode);
+ virtual ~RuleListContext() override;
+
+ virtual oox::core::ContextHandlerRef onCreateContext(sal_Int32 nElement,
+ const AttributeList& rAttribs) override;
+
+private:
+ LayoutAtomPtr mpNode;
+};
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */