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.cxx468
-rw-r--r--oox/source/drawingml/diagram/datamodel.hxx81
-rw-r--r--oox/source/drawingml/diagram/datamodelcontext.cxx384
-rw-r--r--oox/source/drawingml/diagram/datamodelcontext.hxx45
-rw-r--r--oox/source/drawingml/diagram/diagram.cxx453
-rw-r--r--oox/source/drawingml/diagram/diagram.hxx171
-rw-r--r--oox/source/drawingml/diagram/diagramdefinitioncontext.cxx100
-rw-r--r--oox/source/drawingml/diagram/diagramdefinitioncontext.hxx44
-rw-r--r--oox/source/drawingml/diagram/diagramfragmenthandler.cxx235
-rw-r--r--oox/source/drawingml/diagram/diagramfragmenthandler.hxx103
-rw-r--r--oox/source/drawingml/diagram/diagramhelper.cxx271
-rw-r--r--oox/source/drawingml/diagram/diagramhelper.hxx94
-rw-r--r--oox/source/drawingml/diagram/diagramlayoutatoms.cxx2045
-rw-r--r--oox/source/drawingml/diagram/diagramlayoutatoms.hxx373
-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.cxx269
-rw-r--r--oox/source/drawingml/diagram/layoutatomvisitors.hxx97
-rw-r--r--oox/source/drawingml/diagram/layoutnodecontext.cxx310
-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
24 files changed, 6081 insertions, 0 deletions
diff --git a/oox/source/drawingml/diagram/constraintlistcontext.cxx b/oox/source/drawingml/diagram/constraintlistcontext.cxx
new file mode 100644
index 0000000000..9233c9235c
--- /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.getStringDefaulted( 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.getStringDefaulted( 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 0000000000..ba3b593555
--- /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 0000000000..97691af43f
--- /dev/null
+++ b/oox/source/drawingml/diagram/datamodel.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 "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 <com/sun/star/beans/XPropertyState.hpp>
+#include <com/sun/star/drawing/FillStyle.hpp>
+#include <com/sun/star/drawing/LineStyle.hpp>
+#include <editeng/unoprnms.hxx>
+
+#include <unordered_set>
+
+using namespace ::com::sun::star;
+
+namespace oox::drawingml {
+
+Shape* DiagramData::getOrCreateAssociatedShape(const svx::diagram::Point& rPoint, bool bCreateOnDemand) const
+{
+ if(maPointShapeMap.end() == maPointShapeMap.find(rPoint.msModelId))
+ {
+ const_cast<DiagramData*>(this)->maPointShapeMap[rPoint.msModelId] = ShapePtr();
+ }
+
+ const ShapePtr& rShapePtr = maPointShapeMap.find(rPoint.msModelId)->second;
+
+ if(!rShapePtr && bCreateOnDemand)
+ {
+ const_cast<ShapePtr&>(rShapePtr) = std::make_shared<Shape>();
+
+ // If we did create a new oox::drawingml::Shape, directly apply
+ // available data from the Diagram ModelData to it as preparation
+ restoreDataFromModelToShapeAfterReCreation(rPoint, *rShapePtr);
+ }
+
+ return rShapePtr.get();
+}
+
+void DiagramData::restoreDataFromModelToShapeAfterReCreation(const svx::diagram::Point& rPoint, Shape& rNewShape)
+{
+ // If we did create a new oox::drawingml::Shape, directly apply
+ // available data from the Diagram ModelData to it as preparation
+
+ // This is e.g. the Text, but may get more (styles?)
+ if(!rPoint.msTextBody->msText.isEmpty())
+ {
+ TextBodyPtr aNewTextBody(std::make_shared<TextBody>());
+ rNewShape.setTextBody(aNewTextBody);
+ TextRunPtr pTextRun = std::make_shared<TextRun>();
+ pTextRun->getText() = rPoint.msTextBody->msText;
+ aNewTextBody->addParagraph().addRun(pTextRun);
+
+ if(!rPoint.msTextBody->maTextProps.empty())
+ {
+ oox::PropertyMap& rTargetMap(aNewTextBody->getTextProperties().maPropertyMap);
+
+ for (auto const& prop : rPoint.msTextBody->maTextProps)
+ {
+ const sal_Int32 nPropId(oox::PropertyMap::getPropertyId(prop.first));
+ if(nPropId > 0)
+ rTargetMap.setAnyProperty(nPropId, prop.second);
+ }
+ }
+ }
+}
+
+static void addProperty(const OUString& rName,
+ const css::uno::Reference< css::beans::XPropertySetInfo >& xInfo,
+ std::vector< std::pair< OUString, css::uno::Any >>& rTarget,
+ const css::uno::Reference< css::beans::XPropertySet >& xPropSet )
+{
+ if(xInfo->hasPropertyByName(rName))
+ rTarget.push_back(std::pair(OUString(rName), xPropSet->getPropertyValue(rName)));
+}
+
+void DiagramData::secureStyleDataFromShapeToModel(::oox::drawingml::Shape& rShape)
+{
+ const std::vector< ShapePtr >& rChildren(rShape.getChildren());
+
+ if(!rChildren.empty())
+ {
+ // group shape
+ for (auto& child : rChildren)
+ {
+ secureStyleDataFromShapeToModel(*child);
+ }
+
+ // if group shape we are done. Do not secure properties for group shapes
+ return;
+ }
+
+ // we need a XShape
+ const css::uno::Reference< css::drawing::XShape > &rXShape(rShape.getXShape());
+ if(!rXShape)
+ return;
+
+ // we need a ModelID for association
+ if(rShape.getDiagramDataModelID().isEmpty())
+ return;
+
+ // define target to save to
+ svx::diagram::PointStyle* pTarget(nullptr);
+ const bool bIsBackgroundShape(rShape.getDiagramDataModelID() == msBackgroundShapeModelID);
+
+ if(bIsBackgroundShape)
+ {
+ // if BackgroundShape, create properties & set as target
+ if(!maBackgroundShapeStyle)
+ maBackgroundShapeStyle = std::make_shared< svx::diagram::PointStyle >();
+ pTarget = maBackgroundShapeStyle.get();
+ }
+ else
+ {
+ // if Shape, seek association
+ for (auto & point : maPoints)
+ {
+ if(point.msModelId == rShape.getDiagramDataModelID())
+ {
+ // found - create properties & set as target
+ pTarget = point.msPointStylePtr.get();
+
+ // we are done, there is no 2nd shape with the same ModelID by definition
+ break;
+ }
+ }
+ }
+
+ // no target -> nothing to do
+ if(nullptr == pTarget)
+ return;
+
+#ifdef DBG_UTIL
+ // to easier decide which additional properties may/should be preserved,
+ // create a full list of set properties to browse/decide (in debugger)
+ const css::uno::Reference< css::beans::XPropertyState > xAllPropStates(rXShape, css::uno::UNO_QUERY);
+ const css::uno::Reference< css::beans::XPropertySet > xAllPropSet( rXShape, css::uno::UNO_QUERY );
+ const css::uno::Sequence< css::beans::Property > allSequence(xAllPropSet->getPropertySetInfo()->getProperties());
+ std::vector< std::pair< OUString, css::uno::Any >> allSetProps;
+ for (auto& rProp : allSequence)
+ {
+ try
+ {
+ if (xAllPropStates->getPropertyState(rProp.Name) == css::beans::PropertyState::PropertyState_DIRECT_VALUE)
+ {
+ css::uno::Any aValue(xAllPropSet->getPropertyValue(rProp.Name));
+ if(aValue.hasValue())
+ allSetProps.push_back(std::pair(rProp.Name, aValue));
+ }
+ }
+ catch (...)
+ {
+ }
+ }
+#endif
+
+ const css::uno::Reference< css::beans::XPropertySet > xPropSet( rXShape, css::uno::UNO_QUERY );
+ if(!xPropSet)
+ return;
+
+ const css::uno::Reference< css::lang::XServiceInfo > xServiceInfo( rXShape, css::uno::UNO_QUERY );
+ if(!xServiceInfo)
+ return;
+
+ const css::uno::Reference< css::beans::XPropertySetInfo > xInfo(xPropSet->getPropertySetInfo());
+ if (!xInfo.is())
+ return;
+
+ // Note: The Text may also be secured here, so it may also be possible to
+ // secure/store it at PointStyle instead of at TextBody, same maybe evaluated
+ // for the text attributes - where when securing here the attributes would be
+ // in our UNO API format already.
+ // if(xServiceInfo->supportsService("com.sun.star.drawing.Text"))
+ // {
+ // css::uno::Reference< css::text::XText > xText(rXShape, css::uno::UNO_QUERY);
+ // const OUString aText(xText->getString());
+ //
+ // if(!aText.isEmpty())
+ // {
+ // }
+ // }
+
+ // Add all kinds of properties that are needed to re-create the XShape.
+ // For now this is a minimal example-selection, it will need to be extended
+ // over time for all kind of cases/properties
+
+ // text properties
+ if(!bIsBackgroundShape
+ && xServiceInfo->supportsService("com.sun.star.drawing.TextProperties"))
+ {
+ addProperty(UNO_NAME_CHAR_COLOR, xInfo, pTarget->maProperties, xPropSet);
+ addProperty(UNO_NAME_CHAR_HEIGHT, xInfo, pTarget->maProperties, xPropSet);
+ addProperty(UNO_NAME_CHAR_SHADOWED, xInfo, pTarget->maProperties, xPropSet);
+ addProperty(UNO_NAME_CHAR_WEIGHT, xInfo, pTarget->maProperties, xPropSet);
+ }
+
+ // fill properties
+ if(xServiceInfo->supportsService("com.sun.star.drawing.FillProperties"))
+ {
+ css::drawing::FillStyle eFillStyle(css::drawing::FillStyle_NONE);
+ if (xInfo->hasPropertyByName(UNO_NAME_FILLSTYLE))
+ xPropSet->getPropertyValue(UNO_NAME_FILLSTYLE) >>= eFillStyle;
+
+ if(css::drawing::FillStyle_NONE != eFillStyle)
+ {
+ addProperty(UNO_NAME_FILLSTYLE, xInfo, pTarget->maProperties, xPropSet);
+
+ switch(eFillStyle)
+ {
+ case css::drawing::FillStyle_SOLID:
+ {
+ addProperty(UNO_NAME_FILLCOLOR, xInfo, pTarget->maProperties, xPropSet);
+ break;
+ }
+ default:
+ case css::drawing::FillStyle_NONE:
+ case css::drawing::FillStyle_GRADIENT:
+ case css::drawing::FillStyle_HATCH:
+ case css::drawing::FillStyle_BITMAP:
+ break;
+ }
+ }
+ }
+
+ // line properties
+ if(!bIsBackgroundShape
+ && xServiceInfo->supportsService("com.sun.star.drawing.LineProperties"))
+ {
+ css::drawing::LineStyle eLineStyle(css::drawing::LineStyle_NONE);
+ if (xInfo->hasPropertyByName(UNO_NAME_LINESTYLE))
+ xPropSet->getPropertyValue(UNO_NAME_LINESTYLE) >>= eLineStyle;
+
+ if(css::drawing::LineStyle_NONE != eLineStyle)
+ {
+ addProperty(UNO_NAME_LINESTYLE, xInfo, pTarget->maProperties, xPropSet);
+ addProperty(UNO_NAME_LINECOLOR, xInfo, pTarget->maProperties, xPropSet);
+ addProperty(UNO_NAME_LINEWIDTH, xInfo, pTarget->maProperties, xPropSet);
+
+ switch(eLineStyle)
+ {
+ case css::drawing::LineStyle_SOLID:
+ break;
+ default:
+ case css::drawing::LineStyle_NONE:
+ case css::drawing::LineStyle_DASH:
+ break;
+ }
+ }
+ }
+}
+
+void DiagramData::secureDataFromShapeToModelAfterDiagramImport(::oox::drawingml::Shape& rRootShape)
+{
+ const std::vector< ShapePtr >& rChildren(rRootShape.getChildren());
+
+ for (auto& child : rChildren)
+ {
+ secureStyleDataFromShapeToModel(*child);
+ }
+
+ // After Diagram import, parts of the Diagram ModelData is at the
+ // oox::drawingml::Shape. Since these objects are temporary helpers,
+ // secure that data at the Diagram ModelData by copying.
+
+ // This is currently mainly the Text, but may get more (styles?)
+ for (auto & point : maPoints)
+ {
+ Shape* pShapeCandidate(getOrCreateAssociatedShape(point));
+
+ if(nullptr != pShapeCandidate)
+ {
+ if(pShapeCandidate->getTextBody() && !pShapeCandidate->getTextBody()->isEmpty())
+ {
+ point.msTextBody->msText = pShapeCandidate->getTextBody()->toString();
+
+ const uno::Sequence< beans::PropertyValue > aTextProps(
+ pShapeCandidate->getTextBody()->getTextProperties().maPropertyMap.makePropertyValueSequence());
+
+ for (auto const& prop : aTextProps)
+ point.msTextBody->maTextProps.push_back(std::pair(prop.Name, prop.Value));
+ }
+
+ // At this place a mechanism to find missing data should be added:
+ // Create a Shape from so-far secured data & compare it with the
+ // imported one. Report differences to allow extending the mechanism
+ // more easily.
+#ifdef DBG_UTIL
+ // The original is pShapeCandidate, re-create potential new oox::drawingml::Shape
+ // as aNew to be able to compare these
+ ShapePtr aNew(std::make_shared<Shape>());
+ restoreDataFromModelToShapeAfterReCreation(point, *aNew);
+
+ // Unfortunately oox::drawingml::Shape has no operator==. I tried to add
+ // one, but that is too expensive. I stopped at oox::drawingml::Color.
+ // To compare it is necessary to use the debugger, or for single aspects
+ // of the oox data it might be possible to call local dump() methods at
+ // both instances to compare them/their output
+
+ // bool bSame(aNew.get() == pShapeCandidate);
+#endif
+ }
+ }
+}
+
+void DiagramData::restoreStyleDataFromShapeToModel(::oox::drawingml::Shape& rShape)
+{
+ const std::vector< ShapePtr >& rChildren(rShape.getChildren());
+
+ if(!rChildren.empty())
+ {
+ // group shape
+ for (auto& child : rChildren)
+ {
+ restoreStyleDataFromShapeToModel(*child);
+ }
+
+ // if group shape we are done. Do not restore properties for group shapes
+ return;
+ }
+
+ // we need a XShape
+ const css::uno::Reference< css::drawing::XShape > &rXShape(rShape.getXShape());
+ if(!rXShape)
+ return;
+
+ // we need a ModelID for association
+ if(rShape.getDiagramDataModelID().isEmpty())
+ return;
+
+ // define source to save to
+ svx::diagram::PointStyle* pSource(nullptr);
+
+ if(rShape.getDiagramDataModelID() == msBackgroundShapeModelID)
+ {
+ // if BackgroundShape, set BackgroundShapeStyle as source
+ if(maBackgroundShapeStyle)
+ pSource = maBackgroundShapeStyle.get();
+ }
+ else
+ {
+ // if Shape, seek association
+ for (auto & point : maPoints)
+ {
+ if(point.msModelId == rShape.getDiagramDataModelID())
+ {
+ // found - create properties & set as source
+ pSource = point.msPointStylePtr.get();
+
+ // we are done, there is no 2nd shape with the same ModelID by definition
+ break;
+ }
+ }
+ }
+
+ // no source -> nothing to do
+ if(nullptr == pSource)
+ return;
+
+ // get target PropertySet of new XShape
+ css::uno::Reference<css::beans::XPropertySet> xPropSet(rXShape, css::uno::UNO_QUERY);
+ if(!xPropSet)
+ return;
+
+ // apply properties
+ for (auto const& prop : pSource->maProperties)
+ {
+ xPropSet->setPropertyValue(prop.first, prop.second);
+ }
+}
+
+void DiagramData::restoreDataFromShapeToModelAfterDiagramImport(::oox::drawingml::Shape& rRootShape)
+{
+ const std::vector< ShapePtr >& rChildren(rRootShape.getChildren());
+
+ for (auto& child : rChildren)
+ {
+ restoreStyleDataFromShapeToModel(*child);
+ }
+}
+
+DiagramData::DiagramData()
+: svx::diagram::DiagramData()
+, mpBackgroundShapeFillProperties( std::make_shared<FillProperties>() )
+{
+}
+
+DiagramData::~DiagramData()
+{
+}
+
+static void Connection_dump(const svx::diagram::Connection& rConnection)
+{
+ SAL_INFO(
+ "oox.drawingml",
+ "cnx modelId " << rConnection.msModelId << ", srcId " << rConnection.msSourceId << ", dstId "
+ << rConnection.msDestId << ", parTransId " << rConnection.msParTransId << ", presId "
+ << rConnection.msPresId << ", sibTransId " << rConnection.msSibTransId << ", srcOrd "
+ << rConnection.mnSourceOrder << ", dstOrd " << rConnection.mnDestOrder);
+}
+
+static void Point_dump(const svx::diagram::Point& rPoint, const Shape* pShape)
+{
+ SAL_INFO(
+ "oox.drawingml",
+ "pt text " << pShape << ", cnxId " << rPoint.msCnxId << ", modelId "
+ << rPoint.msModelId << ", type " << rPoint.mnXMLType);
+}
+
+void DiagramData::dump() const
+{
+ SAL_INFO("oox.drawingml", "Dgm: DiagramData # of cnx: " << maConnections.size() );
+ for (const auto& rConnection : maConnections)
+ Connection_dump(rConnection);
+
+ SAL_INFO("oox.drawingml", "Dgm: DiagramData # of pt: " << maPoints.size() );
+ for (const auto& rPoint : maPoints)
+ Point_dump(rPoint, getOrCreateAssociatedShape(rPoint));
+}
+
+void DiagramData::buildDiagramDataModel(bool bClearOoxShapes)
+{
+ if(bClearOoxShapes)
+ {
+ // Delete/remove all existing oox::drawingml::Shape
+ maPointShapeMap.clear();
+ }
+
+ // call parent
+ svx::diagram::DiagramData::buildDiagramDataModel(bClearOoxShapes);
+
+ if(bClearOoxShapes)
+ {
+ // re-create all existing oox::drawingml::Shape
+ svx::diagram::Points& rPoints = getPoints();
+
+ for (auto & point : rPoints)
+ {
+ // Create/get shape. Re-create here, that may also set needed
+ // and available data from the Diagram ModelData at the Shape
+ getOrCreateAssociatedShape(point, true);
+ }
+ }
+}
+
+}
+
+/* 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 0000000000..23104812ed
--- /dev/null
+++ b/oox/source/drawingml/diagram/datamodel.hxx
@@ -0,0 +1,81 @@
+/* -*- 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 <svx/diagram/datamodel.hxx>
+#include <oox/drawingml/drawingmltypes.hxx>
+#include <oox/helper/helper.hxx>
+#include <oox/token/tokens.hxx>
+
+namespace oox::drawingml {
+
+class DiagramData : public svx::diagram::DiagramData
+{
+public:
+ typedef std::map< OUString, ShapePtr > PointShapeMap;
+
+ DiagramData();
+ virtual ~DiagramData();
+
+ // creates temporary processing data from model data
+ virtual void buildDiagramDataModel(bool bClearOoxShapes);
+
+ FillPropertiesPtr& getBackgroundShapeFillProperties() { return mpBackgroundShapeFillProperties; }
+ virtual void dump() const;
+
+ Shape* getOrCreateAssociatedShape(const svx::diagram::Point& rPoint, bool bCreateOnDemand = false) const;
+
+ // get/set data between Diagram DataModel and oox::drawingml::Shape
+ void secureDataFromShapeToModelAfterDiagramImport(::oox::drawingml::Shape& rRootShape);
+ void restoreDataFromShapeToModelAfterDiagramImport(::oox::drawingml::Shape& rRootShape);
+ static void restoreDataFromModelToShapeAfterReCreation(const svx::diagram::Point& rPoint, Shape& rNewShape);
+
+protected:
+ void secureStyleDataFromShapeToModel(::oox::drawingml::Shape& rShape);
+ void restoreStyleDataFromShapeToModel(::oox::drawingml::Shape& rShape);
+
+private:
+ // The model definition, the parts *only* available in oox. Also look for already
+ // defined ModelData in svx::diagram::DiagramData
+
+ // - FillStyle for Diagram Background (empty constructed, may stay empty)
+ FillPropertiesPtr mpBackgroundShapeFillProperties;
+
+ // temporary processing data, deleted when using build(). Association
+ // map between oox::drawingml::Shape and svx::diagram::Point ModelData
+ PointShapeMap maPointShapeMap;
+};
+
+// Oox-local definition of DiagramData. Doing and using this on Oox
+// allows to do much less static_cast(s) - if at all from svx::diagram::DiagramData
+typedef std::shared_ptr< DiagramData > OoxDiagramDataPtr;
+
+}
+
+#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 0000000000..ba1826a94c
--- /dev/null
+++ b/oox/source/drawingml/diagram/datamodelcontext.cxx
@@ -0,0 +1,384 @@
+/* -*- 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,
+ svx::diagram::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( );
+ svx::diagram::Connection& rConnection=mrConnection.back();
+
+ rConnection.mnXMLType = static_cast<svx::diagram::TypeConstant>(rAttribs.getToken( XML_type, XML_parOf ));
+ rConnection.msModelId = rAttribs.getStringDefaulted( XML_modelId );
+ rConnection.msSourceId = rAttribs.getStringDefaulted( XML_srcId );
+ rConnection.msDestId = rAttribs.getStringDefaulted( XML_destId );
+ rConnection.msPresId = rAttribs.getStringDefaulted( XML_presId );
+ rConnection.msSibTransId = rAttribs.getStringDefaulted( XML_sibTransId );
+ rConnection.msParTransId = rAttribs.getStringDefaulted( XML_parTransId );
+ rConnection.mnSourceOrder = rAttribs.getInteger( XML_srcOrd, 0 );
+ rConnection.mnDestOrder = rAttribs.getInteger( XML_destOrd, 0 );
+
+ // skip CT_extLst
+ return nullptr;
+ }
+ default:
+ break;
+ }
+
+ return this;
+ }
+private:
+ svx::diagram::Connections& mrConnection;
+};
+
+// CT_presLayoutVars
+class PresLayoutVarsContext
+ : public ContextHandler2
+{
+public:
+ PresLayoutVarsContext( ContextHandler2Helper const & rParent,
+ svx::diagram::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 ):
+ {
+ // need to convert from oox::OptValue to std::optional since 1st is not available in svx
+ const std::optional< sal_Int32 > aOptVal(rAttribs.getToken( XML_val ));
+ if(aOptVal.has_value())
+ mrPoint.moHierarchyBranch = aOptVal.value();
+ 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:
+ svx::diagram::Point& mrPoint;
+};
+
+// CT_prSet
+class PropertiesContext
+ : public ContextHandler2
+{
+public:
+ PropertiesContext( ContextHandler2Helper const & rParent,
+ svx::diagram::Point & rPoint,
+ const AttributeList& rAttribs ) :
+ ContextHandler2( rParent ),
+ mrPoint( rPoint )
+ {
+ mrPoint.msColorTransformCategoryId = rAttribs.getStringDefaulted( XML_csCatId);
+ mrPoint.msColorTransformTypeId = rAttribs.getStringDefaulted( XML_csTypeId);
+ mrPoint.msLayoutCategoryId = rAttribs.getStringDefaulted( XML_loCatId);
+ mrPoint.msLayoutTypeId = rAttribs.getStringDefaulted( XML_loTypeId);
+ mrPoint.msPlaceholderText = rAttribs.getStringDefaulted( XML_phldrT);
+ mrPoint.msPresentationAssociationId = rAttribs.getStringDefaulted( XML_presAssocID);
+ mrPoint.msPresentationLayoutName = rAttribs.getStringDefaulted( XML_presName);
+ mrPoint.msPresentationLayoutStyleLabel = rAttribs.getStringDefaulted( XML_presStyleLbl);
+ mrPoint.msQuickStyleCategoryId = rAttribs.getStringDefaulted( XML_qsCatId);
+ mrPoint.msQuickStyleTypeId = rAttribs.getStringDefaulted( 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:
+ svx::diagram::Point& mrPoint;
+};
+
+// CL_Pt
+class PtContext
+ : public ContextHandler2
+{
+public:
+ PtContext( ContextHandler2Helper const& rParent,
+ const AttributeList& rAttribs,
+ svx::diagram::Point& rPoint,
+ DiagramData& rDiagramData):
+ ContextHandler2( rParent ),
+ mrPoint( rPoint ),
+ mrDiagramData( rDiagramData )
+ {
+ mrPoint.msModelId = rAttribs.getStringDefaulted( XML_modelId );
+
+ // the default type is XML_node
+ const sal_Int32 nType = rAttribs.getToken( XML_type, XML_node );
+ mrPoint.mnXMLType = static_cast<svx::diagram::TypeConstant>(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.getStringDefaulted( XML_cxnId );
+ }
+
+ 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 ):
+ {
+ Shape* pShape(mrDiagramData.getOrCreateAssociatedShape(mrPoint, true));
+ return new ShapePropertiesContext( *this, *pShape );
+ }
+ case DGM_TOKEN( t ):
+ {
+ Shape* pShape(mrDiagramData.getOrCreateAssociatedShape(mrPoint, true));
+ TextBodyPtr xTextBody = std::make_shared<TextBody>();
+ pShape->setTextBody( xTextBody );
+ return new TextBodyContext( *this, *xTextBody );
+ }
+ default:
+ break;
+ }
+ return this;
+ }
+
+private:
+ svx::diagram::Point& mrPoint;
+ DiagramData& mrDiagramData;
+};
+
+// CT_PtList
+class PtListContext
+ : public ContextHandler2
+{
+public:
+ PtListContext( ContextHandler2Helper const & rParent, svx::diagram::Points& rPoints, DiagramData& rDiagramData) :
+ ContextHandler2( rParent ),
+ mrPoints( rPoints ),
+ mrDiagramData( rDiagramData )
+ {}
+ 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(), mrDiagramData );
+ }
+ default:
+ break;
+ }
+ return this;
+ }
+
+private:
+ svx::diagram::Points& mrPoints;
+ DiagramData& mrDiagramData;
+};
+
+// CT_BackgroundFormatting
+class BackgroundFormattingContext
+ : public ContextHandler2
+{
+public:
+ BackgroundFormattingContext( ContextHandler2Helper const & rParent, OoxDiagramDataPtr 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->getBackgroundShapeFillProperties(), nullptr);
+ case A_TOKEN( effectDag ):
+ case A_TOKEN( effectLst ):
+ // TODO
+ // EG_EffectProperties
+ break;
+ default:
+ break;
+ }
+ return this;
+ }
+private:
+ OoxDiagramDataPtr mpDataModel;
+};
+
+}
+
+DataModelContext::DataModelContext( ContextHandler2Helper const& rParent,
+ const OoxDiagramDataPtr& 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(), *mpDataModel );
+ 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.getStringDefaulted( XML_relId ) );
+ 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 0000000000..9a5c323de8
--- /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 OoxDiagramDataPtr& pDataModelPtr );
+ virtual ~DataModelContext() override;
+
+ virtual ::oox::core::ContextHandlerRef onCreateContext( ::sal_Int32 Element, const ::oox::AttributeList& rAttribs ) override;
+
+private:
+ OoxDiagramDataPtr 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 0000000000..029c2c56e9
--- /dev/null
+++ b/oox/source/drawingml/diagram/diagram.cxx
@@ -0,0 +1,453 @@
+/* -*- 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 <o3tl/unit_conversion.hxx>
+#include <oox/token/namespaces.hxx>
+#include <basegfx/matrix/b2dhommatrix.hxx>
+#include <svx/svdpage.hxx>
+#include <oox/ppt/pptimport.hxx>
+#include <comphelper/xmltools.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();
+
+ std::erase_if(rChildren,
+ [](const ShapePtr& aChild) {
+ return aChild->getServiceName()
+ == "com.sun.star.drawing.GroupShape"
+ && aChild->getChildren().empty();
+ });
+
+ 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 svx::diagram::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->getBackgroundShapeFillProperties();
+ pBackground->setLocked(true);
+
+ // create and set ModelID for BackgroundShape to allow later association
+ getData()->setBackgroundShapeModelID(OStringToOUString(comphelper::xml::generateGUIDString(), RTL_TEXTENCODING_UTF8));
+ pBackground->setDiagramDataModelID(getData()->getBackgroundShapeModelID());
+
+ auto& aChildren = pParentShape->getChildren();
+ aChildren.insert(aChildren.begin(), pBackground);
+}
+
+Diagram::Diagram()
+: maDiagramFontHeights()
+{
+}
+
+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;
+}
+
+using ShapePairs
+ = std::map<std::shared_ptr<drawingml::Shape>, css::uno::Reference<css::drawing::XShape>>;
+
+void Diagram::syncDiagramFontHeights()
+{
+ // Each name represents a group of shapes, for which the font height should have the same
+ // scaling.
+ for (const auto& rNameAndPairs : maDiagramFontHeights)
+ {
+ // Find out the minimum scale within this group.
+ const ShapePairs& rShapePairs = rNameAndPairs.second;
+ double nMinScale = 100.0;
+ for (const auto& rShapePair : rShapePairs)
+ {
+ uno::Reference<beans::XPropertySet> xPropertySet(rShapePair.second, uno::UNO_QUERY);
+ if (xPropertySet.is())
+ {
+ double nTextFitToSizeScale = 0.0;
+ xPropertySet->getPropertyValue("TextFitToSizeScale") >>= nTextFitToSizeScale;
+ if (nTextFitToSizeScale > 0 && nTextFitToSizeScale < nMinScale)
+ {
+ nMinScale = nTextFitToSizeScale;
+ }
+ }
+ }
+
+ // Set that minimum scale for all members of the group.
+ if (nMinScale < 100.0)
+ {
+ for (const auto& rShapePair : rShapePairs)
+ {
+ uno::Reference<beans::XPropertySet> xPropertySet(rShapePair.second, uno::UNO_QUERY);
+ if (xPropertySet.is())
+ {
+ xPropertySet->setPropertyValue("TextFitToSizeScale", uno::Any(nMinScale));
+ }
+ }
+ }
+ }
+
+ // no longer needed after processing
+ maDiagramFontHeights.clear();
+}
+
+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>();
+
+ OoxDiagramDataPtr pData = std::make_shared<DiagramData>();
+ pDiagram->setData( pData );
+
+ DiagramLayoutPtr pLayout = std::make_shared<DiagramLayout>(*pDiagram);
+ pDiagram->setLayout( pLayout );
+
+ try
+ {
+ // set DiagramFontHeights at filter
+ rFilter.setDiagramFontHeights(&pDiagram->getDiagramFontHeights());
+
+ // 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(), u"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
+ // for Diagram import, do - for now - NOT clear all oox::drawingml::Shape
+ pData->buildDiagramDataModel(false);
+
+ // diagram loaded. now lump together & attach to shape
+ pDiagram->addTo(pShape);
+ pShape->setDiagramDoms(pDiagram->getDomsAsPropertyValues());
+
+ // Get the oox::Theme definition and - if available - move/secure the
+ // original ImportData directly to the Diagram ModelData
+ std::shared_ptr<::oox::drawingml::Theme> aTheme(rFilter.getCurrentThemePtr());
+ if(aTheme)
+ pData->setThemeDocument(aTheme->getFragment()); //getTempFile());
+
+ // Prepare support for the advanced DiagramHelper using Diagram & Theme data
+ pShape->prepareDiagramHelper(pDiagram, rFilter.getCurrentThemePtr());
+ }
+ catch (...)
+ {
+ // unset DiagramFontHeights at filter if there was a failure
+ // to avoid dangling pointer
+ rFilter.setDiagramFontHeights(nullptr);
+ throw;
+ }
+}
+
+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 0000000000..f58c762f6a
--- /dev/null
+++ b/oox/source/drawingml/diagram/diagram.hxx
@@ -0,0 +1,171 @@
+/* -*- 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 svx::diagram::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; }
+ OoxDiagramDataPtr& getSampData()
+ { return mpSampData; }
+ const OoxDiagramDataPtr& getSampData() const
+ { return mpSampData; }
+ OoxDiagramDataPtr& getStyleData()
+ { return mpStyleData; }
+ const OoxDiagramDataPtr& 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;
+ OoxDiagramDataPtr mpSampData;
+ OoxDiagramDataPtr 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();
+ void setData( OoxDiagramDataPtr& pData )
+ { mpData = pData; }
+ const OoxDiagramDataPtr& 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;
+ oox::core::NamedShapePairs& getDiagramFontHeights() { return maDiagramFontHeights; }
+ void syncDiagramFontHeights();
+
+private:
+ // This contains groups of shapes: automatic font size is the same in each group.
+ oox::core::NamedShapePairs maDiagramFontHeights;
+
+ OoxDiagramDataPtr 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 0000000000..42d78d1f3b
--- /dev/null
+++ b/oox/source/drawingml/diagram/diagramdefinitioncontext.cxx
@@ -0,0 +1,100 @@
+/* -*- 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>
+#include <utility>
+
+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,
+ DiagramLayoutPtr pLayout )
+ : ContextHandler2( rParent )
+ , mpLayout(std::move( pLayout ))
+{
+ mpLayout->setDefStyle( rAttributes.getStringDefaulted( XML_defStyle ) );
+ OUString sValue = rAttributes.getStringDefaulted( XML_minVer );
+ if( sValue.isEmpty() )
+ {
+ sValue = "http://schemas.openxmlformats.org/drawingml/2006/diagram";
+ }
+ mpLayout->setMinVer( sValue );
+ mpLayout->setUniqueId( rAttributes.getStringDefaulted( XML_uniqueId ) );
+}
+
+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.getStringDefaulted( XML_val ) );
+ break;
+ case DGM_TOKEN( desc ):
+ mpLayout->setDesc( rAttribs.getStringDefaulted( XML_val ) );
+ 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.getStringDefaulted( XML_moveWith ) );
+ pNode->setStyleLabel( rAttribs.getStringDefaulted( XML_styleLbl ) );
+ 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 0000000000..0222998527
--- /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, 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 0000000000..b65fca3f98
--- /dev/null
+++ b/oox/source/drawingml/diagram/diagramfragmenthandler.cxx
@@ -0,0 +1,235 @@
+/* -*- 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>
+#include <utility>
+
+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,
+ OoxDiagramDataPtr xDataPtr )
+ : FragmentHandler2( rFilter, rFragmentPath )
+ , mpDataPtr(std::move( xDataPtr ))
+{
+}
+
+DiagramDataFragmentHandler::~DiagramDataFragmentHandler( ) noexcept
+{
+
+}
+
+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,
+ DiagramLayoutPtr xDataPtr )
+ : FragmentHandler2( rFilter, rFragmentPath )
+ , mpDataPtr(std::move( xDataPtr ))
+{
+}
+
+DiagramLayoutFragmentHandler::~DiagramLayoutFragmentHandler( ) noexcept
+{
+
+}
+
+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 ),
+ 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.getStringDefaulted( XML_name);
+ 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),
+ 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.getStringDefaulted( XML_name);
+ 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 0000000000..caecb4886a
--- /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, OoxDiagramDataPtr xDataPtr);
+ virtual ~DiagramDataFragmentHandler() noexcept override;
+
+ virtual void SAL_CALL endDocument() override;
+ virtual ::oox::core::ContextHandlerRef onCreateContext( ::sal_Int32 Element, const ::oox::AttributeList& rAttribs ) override;
+
+private:
+
+ OoxDiagramDataPtr mpDataPtr;
+};
+
+class DiagramLayoutFragmentHandler : public ::oox::core::FragmentHandler2
+{
+public:
+ DiagramLayoutFragmentHandler(oox::core::XmlFilterBase& rFilter, const OUString& rFragmentPath, DiagramLayoutPtr xDataPtr);
+ virtual ~DiagramLayoutFragmentHandler() noexcept 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/diagramhelper.cxx b/oox/source/drawingml/diagram/diagramhelper.cxx
new file mode 100644
index 0000000000..3b25951bb9
--- /dev/null
+++ b/oox/source/drawingml/diagram/diagramhelper.cxx
@@ -0,0 +1,271 @@
+/* -*- 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 "diagramhelper.hxx"
+#include "diagram.hxx"
+
+#include <basegfx/matrix/b2dhommatrix.hxx>
+#include <oox/shape/ShapeFilterBase.hxx>
+#include <oox/ppt/pptimport.hxx>
+#include <drawingml/fillproperties.hxx>
+#include <svx/svdmodel.hxx>
+#include <comphelper/processfactory.hxx>
+#include <oox/drawingml/themefragmenthandler.hxx>
+#include <com/sun/star/xml/sax/XFastSAXSerializable.hpp>
+#include <utility>
+
+using namespace ::com::sun::star;
+
+namespace oox::drawingml {
+
+bool AdvancedDiagramHelper::hasDiagramData() const
+{
+ return mpDiagramPtr && mpDiagramPtr->getData();
+}
+
+AdvancedDiagramHelper::AdvancedDiagramHelper(
+ std::shared_ptr< Diagram > xDiagramPtr,
+ std::shared_ptr<::oox::drawingml::Theme> xTheme,
+ css::awt::Size aImportSize)
+: svx::diagram::IDiagramHelper()
+, mpDiagramPtr(std::move(xDiagramPtr))
+, mpThemePtr(std::move(xTheme))
+, maImportSize(aImportSize)
+{
+}
+
+AdvancedDiagramHelper::~AdvancedDiagramHelper()
+{
+}
+
+void AdvancedDiagramHelper::reLayout(SdrObjGroup& rTarget)
+{
+ if(!mpDiagramPtr)
+ {
+ return;
+ }
+
+ // Rescue/remember geometric transformation of existing Diagram
+ basegfx::B2DHomMatrix aTransformation;
+ basegfx::B2DPolyPolygon aPolyPolygon;
+ rTarget.TRGetBaseGeometry(aTransformation, aPolyPolygon);
+
+ // create temporary oox::Shape as target. No longer needed is to keep/remember
+ // the original oox::Shape to do that. Use original Size and Pos from initial import
+ // to get the same layout(s)
+ oox::drawingml::ShapePtr pShapePtr = std::make_shared<Shape>( "com.sun.star.drawing.GroupShape" );
+ pShapePtr->setDiagramType();
+ pShapePtr->setSize(maImportSize);
+
+ // Re-create the oox::Shapes for the diagram content
+ mpDiagramPtr->addTo(pShapePtr);
+
+ // Delete all existing shapes in that group to prepare re-creation
+ rTarget.getChildrenOfSdrObject()->ClearSdrObjList();
+
+ // For re-creation we need to use ::addShape functionality from the
+ // oox import filter since currently Shape import is very tightly
+ // coupled to Shape creation. It converts a oox::Shape representation
+ // combined with an oox::Theme to incarnated XShapes representing the
+ // Diagram.
+ // To use that functionality, we have to create a temporary filter
+ // (based on ShapeFilterBase). Problems are that this needs to know
+ // the oox:Theme and a ComponentModel from TargetDocument.
+ // The DiagramHelper holds/delivers the oox::Theme to use, so
+ // it does not need to be re-imported from oox repeatedly.
+ // The ComponentModel can be derived from the existing XShape/GroupShape
+ // when knowing where to get it from, making it independent from app.
+ //
+ // NOTE: Using another (buffered) oox::Theme would allow to re-create
+ // using another theming in the future.
+ // NOTE: The incarnation of import filter (ShapeFilterBase) is only
+ // used for XShape creation, no xml snippets/data gets imported
+ // here. XShape creation may be isolated in the future.
+ SdrModel& rModel(rTarget.getSdrModelFromSdrObject());
+ uno::Reference< uno::XInterface > const & rUnoModel(rModel.getUnoModel());
+ css::uno::Reference<css::uno::XComponentContext> xContext(comphelper::getProcessComponentContext());
+ rtl::Reference<oox::shape::ShapeFilterBase> xFilter(new oox::shape::ShapeFilterBase(xContext));
+
+ // set oox::Theme at Filter. All LineStyle/FillStyle/Colors/Attributes
+ // will be taken from there
+ if(UseDiagramThemeData())
+ xFilter->setCurrentTheme(getOrCreateThemePtr(xFilter));
+
+ css::uno::Reference< css::lang::XComponent > aComponentModel( rUnoModel, uno::UNO_QUERY );
+ xFilter->setTargetDocument(aComponentModel);
+
+ // set DiagramFontHeights
+ xFilter->setDiagramFontHeights(&mpDiagramPtr->getDiagramFontHeights());
+
+ // Prepare the target for the to-be-created XShapes
+ uno::Reference<drawing::XShapes> xShapes(rTarget.getUnoShape(), uno::UNO_QUERY_THROW);
+
+ for (auto const& child : pShapePtr->getChildren())
+ {
+ // Create all sub-shapes. This will recursively create needed geometry using
+ // filter-internal ::createShapes
+ child->addShape(
+ *xFilter,
+ xFilter->getCurrentTheme(),
+ xShapes,
+ aTransformation,
+ pShapePtr->getFillProperties());
+ }
+
+ // sync FontHeights
+ mpDiagramPtr->syncDiagramFontHeights();
+
+ // re-apply secured data from ModelData
+ if(UseDiagramModelData())
+ mpDiagramPtr->getData()->restoreDataFromShapeToModelAfterDiagramImport(*pShapePtr);
+
+ // Re-apply remembered geometry
+ rTarget.TRSetBaseGeometry(aTransformation, aPolyPolygon);
+}
+
+OUString AdvancedDiagramHelper::getString() const
+{
+ if(hasDiagramData())
+ {
+ return mpDiagramPtr->getData()->getString();
+ }
+
+ return OUString();
+}
+
+std::vector<std::pair<OUString, OUString>> AdvancedDiagramHelper::getChildren(const OUString& rParentId) const
+{
+ if(hasDiagramData())
+ {
+ return mpDiagramPtr->getData()->getChildren(rParentId);
+ }
+
+ return std::vector<std::pair<OUString, OUString>>();
+}
+
+OUString AdvancedDiagramHelper::addNode(const OUString& rText)
+{
+ OUString aRetval;
+
+ if(hasDiagramData())
+ {
+ aRetval = mpDiagramPtr->getData()->addNode(rText);
+
+ // reset temporary buffered ModelData association lists & rebuild them
+ // and the Diagram DataModel
+ mpDiagramPtr->getData()->buildDiagramDataModel(true);
+
+ // also reset temporary buffered layout data - that might
+ // still refer to changed oox::Shape data
+ mpDiagramPtr->getLayout()->getPresPointShapeMap().clear();
+ }
+
+ return aRetval;
+}
+
+bool AdvancedDiagramHelper::removeNode(const OUString& rNodeId)
+{
+ bool bRetval(false);
+
+ if(hasDiagramData())
+ {
+ bRetval = mpDiagramPtr->getData()->removeNode(rNodeId);
+
+ // reset temporary buffered ModelData association lists & rebuild them
+ // and the Diagram DataModel
+ mpDiagramPtr->getData()->buildDiagramDataModel(true);
+
+ // also reset temporary buffered layout data - that might
+ // still refer to changed oox::Shape data
+ mpDiagramPtr->getLayout()->getPresPointShapeMap().clear();
+ }
+
+ return bRetval;
+}
+
+svx::diagram::DiagramDataStatePtr AdvancedDiagramHelper::extractDiagramDataState() const
+{
+ if(!mpDiagramPtr)
+ {
+ return svx::diagram::DiagramDataStatePtr();
+ }
+
+ return mpDiagramPtr->getData()->extractDiagramDataState();
+}
+
+void AdvancedDiagramHelper::applyDiagramDataState(const svx::diagram::DiagramDataStatePtr& rState)
+{
+ if(!mpDiagramPtr)
+ {
+ return;
+ }
+
+ mpDiagramPtr->getData()->applyDiagramDataState(rState);
+}
+
+void AdvancedDiagramHelper::doAnchor(SdrObjGroup& rTarget, ::oox::drawingml::Shape& rRootShape)
+{
+ if(!mpDiagramPtr)
+ {
+ return;
+ }
+
+ mpDiagramPtr->syncDiagramFontHeights();
+
+ // After Diagram import, parts of the Diagram ModelData is at the
+ // oox::drawingml::Shape. Since these objects are temporary helpers,
+ // secure that data at the Diagram ModelData by copying.
+ mpDiagramPtr->getData()->secureDataFromShapeToModelAfterDiagramImport(rRootShape);
+
+ anchorToSdrObjGroup(rTarget);
+}
+
+const std::shared_ptr< ::oox::drawingml::Theme >& AdvancedDiagramHelper::getOrCreateThemePtr(
+ rtl::Reference< oox::shape::ShapeFilterBase >& rxFilter) const
+{
+ // (Re-)Use already existing Theme if existing/imported if possible.
+ // If not, re-import Theme if data is available and thus possible
+ if(hasDiagramData() && (ForceThemePtrRecreation() || !mpThemePtr))
+ {
+ // get the originally imported dom::XDocument
+ const uno::Reference< css::xml::dom::XDocument >& xThemeDocument(mpDiagramPtr->getData()->getThemeDocument());
+
+ if(xThemeDocument)
+ {
+ // reset local Theme ModelData *always* to get rid of former data that would
+ // else be added additionally
+ const_cast<AdvancedDiagramHelper*>(this)->mpThemePtr = std::make_shared<oox::drawingml::Theme>();
+ auto pTheme = std::make_shared<model::Theme>();
+ mpThemePtr->setTheme(pTheme);
+
+ // import Theme ModelData
+ rxFilter->importFragment(
+ new ThemeFragmentHandler(*rxFilter, OUString(), *mpThemePtr, *pTheme),
+ uno::Reference< css::xml::sax::XFastSAXSerializable >(
+ xThemeDocument,
+ uno::UNO_QUERY_THROW));
+ }
+ }
+
+ return mpThemePtr;
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/oox/source/drawingml/diagram/diagramhelper.hxx b/oox/source/drawingml/diagram/diagramhelper.hxx
new file mode 100644
index 0000000000..626d40382d
--- /dev/null
+++ b/oox/source/drawingml/diagram/diagramhelper.hxx
@@ -0,0 +1,94 @@
+/* -*- 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_DRAWINGML_DIAGRAM_DIAGRAMHELPER_HXX
+#define INCLUDED_OOX_DRAWINGML_DIAGRAM_DIAGRAMHELPER_HXX
+
+#include <rtl/ustring.hxx>
+#include <oox/drawingml/theme.hxx>
+#include <oox/shape/ShapeFilterBase.hxx>
+#include <svx/svdogrp.hxx>
+#include <svx/diagram/IDiagramHelper.hxx>
+
+namespace svx { namespace diagram {
+ class DiagramDataState;
+}}
+
+namespace oox::drawingml {
+
+class Diagram;
+
+// Advanced DiagramHelper
+//
+// This helper tries to hold all necessary data to re-layout
+// all XShapes/SdrObjects of an already imported Diagram. The
+// Diagram holds the SmarArt model data before it gets layouted,
+// while Theme holds the oox Fill/Line/Style definitions to
+// apply.
+// Re-Layouting (re-creating) is rather complex, for detailed
+// information see ::reLayout implementation.
+// This helper class may/should be extended to:
+// - deliver representative data from the Diagram-Model
+// - modify it eventually
+// - im/export Diagram model to other representations
+class AdvancedDiagramHelper final : public svx::diagram::IDiagramHelper
+{
+ const std::shared_ptr< Diagram > mpDiagramPtr;
+ std::shared_ptr<::oox::drawingml::Theme> mpThemePtr;
+
+ css::awt::Size maImportSize;
+
+ bool hasDiagramData() const;
+
+public:
+ AdvancedDiagramHelper(
+ std::shared_ptr< Diagram > xDiagramPtr,
+ std::shared_ptr<::oox::drawingml::Theme> xTheme,
+ css::awt::Size aImportSize);
+ virtual ~AdvancedDiagramHelper();
+
+ // re-create XShapes
+ virtual void reLayout(SdrObjGroup& rTarget) override;
+
+ // get text representation of data tree
+ virtual OUString getString() const override;
+
+ // get children of provided data node
+ // use empty string for top-level nodes
+ // returns vector of (id, text)
+ virtual std::vector<std::pair<OUString, OUString>> getChildren(const OUString& rParentId) const override;
+
+ // add/remove new top-level node to data model, returns its id
+ virtual OUString addNode(const OUString& rText) override;
+ virtual bool removeNode(const OUString& rNodeId) override;
+
+ // Undo/Redo helpers to extract/restore Diagram-defining data
+ virtual std::shared_ptr< svx::diagram::DiagramDataState > extractDiagramDataState() const override;
+ virtual void applyDiagramDataState(const std::shared_ptr< svx::diagram::DiagramDataState >& rState) override;
+
+ void doAnchor(SdrObjGroup& rTarget, ::oox::drawingml::Shape& rRootShape);
+ const std::shared_ptr< ::oox::drawingml::Theme >& getOrCreateThemePtr(
+ rtl::Reference< oox::shape::ShapeFilterBase>& rxFilter ) const;
+};
+
+}
+
+#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 0000000000..6ee4b88322
--- /dev/null
+++ b/oox/source/drawingml/diagram/diagramlayoutatoms.cxx
@@ -0,0 +1,2045 @@
+/* -*- 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 <o3tl/unit_conversion.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.
+std::optional<sal_Int32> findProperty(const oox::drawingml::LayoutPropertyMap& rProperties,
+ const OUString& rInternalName, sal_Int32 nProperty)
+{
+ std::optional<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 {
+void SnakeAlg::layoutShapeChildren(const AlgAtom& rAlg, const ShapePtr& rShape,
+ const std::vector<Constraint>& rConstraints)
+{
+ if (rShape->getChildren().empty() || rShape->getSize().Width == 0
+ || rShape->getSize().Height == 0)
+ return;
+
+ // 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 AlgAtom::ParamMap& rMap = rAlg.getMap();
+ const sal_Int32 nDir = rMap.count(XML_grDir) ? rMap.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];
+ }
+ double fTotalShapesHeight = fShapeHeight * nRow;
+ if (nRowWidth && fTotalShapesHeight / 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
+ = rMap.count(XML_contDir) ? rMap.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.
+ bool bWidthsFromConstraints
+ = nCount >= 2 && rShape->getChildren()[1]->getDataNodeType() == XML_sibTrans;
+ if (bWidthsFromConstraints && nMaxRowWidth)
+ {
+ double fWidthFactor = static_cast<double>(aShapeWidths[index]) / nMaxRowWidth;
+ // 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;
+ }
+}
+
+void PyraAlg::layoutShapeChildren(const ShapePtr& rShape)
+{
+ if (rShape->getChildren().empty() || rShape->getSize().Width == 0
+ || rShape->getSize().Height == 0)
+ return;
+
+ // 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);
+ if (nCount > 1)
+ {
+ aCurrPos.X -= aChildSize.Height / (nCount - 1);
+ }
+ aChildSize.Width += aChildSize.Height;
+ aCurrShape->setSize(aChildSize);
+ aCurrShape->setChildSize(aChildSize);
+ aCurrPos.Y += (aChildSize.Height);
+ }
+}
+
+bool CompositeAlg::inferFromLayoutProperty(const LayoutProperty& rMap, sal_Int32 nRefType,
+ sal_Int32& rValue)
+{
+ switch (nRefType)
+ {
+ case XML_r:
+ {
+ auto it = rMap.find(XML_l);
+ if (it == rMap.end())
+ {
+ return false;
+ }
+ sal_Int32 nLeft = it->second;
+ it = rMap.find(XML_w);
+ if (it == rMap.end())
+ {
+ return false;
+ }
+ rValue = nLeft + it->second;
+ return true;
+ }
+ default:
+ break;
+ }
+
+ return false;
+}
+
+void CompositeAlg::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())
+ return;
+
+ const LayoutProperty::const_iterator aRefType = aRef->second.find(rConstraint.mnRefType);
+ sal_Int32 nInferredValue = 0;
+ if (aRefType != aRef->second.end())
+ {
+ // Reference is found directly.
+ rProperties[rConstraint.msForName][rConstraint.mnType]
+ = aRefType->second * rConstraint.mfFactor;
+ }
+ else if (inferFromLayoutProperty(aRef->second, rConstraint.mnRefType, nInferredValue))
+ {
+ // Reference can be inferred.
+ rProperties[rConstraint.msForName][rConstraint.mnType]
+ = nInferredValue * rConstraint.mfFactor;
+ }
+ else
+ {
+ // Reference not found, assume a fixed value.
+ // Values are never in EMU, while oox::drawingml::Shape position and size are always in
+ // EMU.
+ const double fValue = o3tl::convert(rConstraint.mfValue,
+ isFontUnit(rConstraint.mnRefType) ? o3tl::Length::pt
+ : o3tl::Length::mm,
+ o3tl::Length::emu);
+ rProperties[rConstraint.msForName][rConstraint.mnType] = fValue;
+ }
+}
+
+void CompositeAlg::layoutShapeChildren(AlgAtom& rAlg, const ShapePtr& rShape,
+ const std::vector<Constraint>& rConstraints)
+{
+ 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 (rAlg.getAspectRatio() != 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 = rAlg.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
+ = rAlg.getLayoutNode().getDiagram().getDiagramFontHeights();
+ auto it = rDiagramFontHeights.find(aCurrShape->getInternalName());
+ if (it != rDiagramFontHeights.end())
+ {
+ // Internal name matches: put drawingml::Shape to the relevant group, for
+ // synchronized 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]))
+ return;
+
+ 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);
+ }
+ }
+}
+
+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, svx::diagram::TypeConstant nType, std::u16string_view rFrom,
+ bool bSourceToDestination)
+{
+ for (const auto& rConnection : rLayoutNode.getDiagram().getData()->getConnections())
+ {
+ if (rConnection.mnXMLType != nType)
+ continue;
+
+ if (bSourceToDestination)
+ {
+ if (rConnection.msSourceId == rFrom)
+ return rConnection.msDestId;
+ }
+ else
+ {
+ if (rConnection.msDestId == rFrom)
+ return rConnection.msSourceId;
+ }
+ }
+
+ return OUString();
+}
+
+sal_Int32 calcMaxDepth(std::u16string_view rNodeName, const svx::diagram::Connections& rConnections)
+{
+ sal_Int32 nMaxLength = 0;
+ for (auto const& aCxn : rConnections)
+ if (aCxn.mnXMLType == svx::diagram::TypeConstant::XML_parOf && aCxn.msSourceId == rNodeName)
+ nMaxLength = std::max(nMaxLength, calcMaxDepth(aCxn.msDestId, rConnections) + 1);
+
+ return nMaxLength;
+}
+}
+
+sal_Int32 ConditionAtom::getNodeCount(const svx::diagram::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, svx::diagram::TypeConstant::XML_parOf, sNodeId, /*bSourceToDestination*/ true);
+
+ if (!sNodeId.isEmpty())
+ {
+ for (const auto& aCxn : mrLayoutNode.getDiagram().getData()->getConnections())
+ if (aCxn.mnXMLType == svx::diagram::TypeConstant::XML_parOf && aCxn.msSourceId == sNodeId)
+ nCount++;
+ }
+
+ return nCount;
+}
+
+bool ConditionAtom::getDecision(const svx::diagram::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.value_or(XML_std);
+ if (!pPresPoint->moHierarchyBranch.has_value())
+ {
+ // If <dgm:hierBranch> is missing in the current presentation
+ // point, ask the parent.
+ OUString aParent = navigate(mrLayoutNode, svx::diagram::TypeConstant::XML_presParOf, pPresPoint->msModelId,
+ /*bSourceToDestination*/ false);
+ DiagramData::PointNameMap& rPointNameMap
+ = mrLayoutNode.getDiagram().getData()->getPointNameMap();
+ auto it = rPointNameMap.find(aParent);
+ if (it != rPointNameMap.end())
+ {
+ const svx::diagram::Point* pParent = it->second;
+ if (pParent->moHierarchyBranch.has_value())
+ nHierarchyBranch = pParent->moHierarchyBranch.value();
+ }
+ }
+ 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
+{
+ // Allowlist 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
+{
+/// 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 svx::diagram::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.
+ svx::diagram::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.
+ std::erase_if(
+ rShape->getChildren(),
+ [](const ShapePtr& aChild) {
+ return aChild->getServiceName() == "com.sun.star.drawing.GroupShape"
+ && aChild->getChildren().empty();
+ });
+ }
+
+ switch(mnType)
+ {
+ case XML_composite:
+ {
+ CompositeAlg::layoutShapeChildren(*this, rShape, rConstraints);
+ 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().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)
+ {
+ std::optional<sal_Int32> oWidth = findProperty(aProperties, aCurrShape->getInternalName(), XML_w);
+ bScaleDownEmptySpacing = oWidth.has_value() && oWidth.value() > 0;
+ }
+ if (!bScaleDownEmptySpacing && (nDir == XML_fromT || nDir == XML_fromB))
+ {
+ std::optional<sal_Int32> oHeight = findProperty(aProperties, aCurrShape->getInternalName(), XML_h);
+ bScaleDownEmptySpacing = oHeight.has_value() && oHeight.value() > 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.
+ std::erase_if(
+ rShape->getChildren(),
+ [](const ShapePtr& aChild) {
+ return aChild->getServiceName()
+ == "com.sun.star.drawing.GroupShape"
+ && aChild->getChildren().empty();
+ });
+ 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())
+ {
+ std::optional<sal_Int32> oWidth = findProperty(aProperties, aCurrShape->getInternalName(), XML_w);
+ std::optional<sal_Int32> oHeight = findProperty(aProperties, aCurrShape->getInternalName(), XML_h);
+ awt::Size aSize = aChildSize;
+ if (oWidth.has_value())
+ aSize.Width = oWidth.value();
+ if (oHeight.has_value())
+ aSize.Height = oHeight.value();
+ 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.
+ std::optional<sal_Int32> oWidth = findProperty(aProperties, aCurrShape->getInternalName(), XML_w);
+ std::optional<sal_Int32> oHeight = findProperty(aProperties, aCurrShape->getInternalName(), XML_h);
+
+ awt::Size aSize = aChildSize;
+ if (oWidth.has_value())
+ aSize.Width = oWidth.value();
+ if (oHeight.has_value())
+ aSize.Height = oHeight.value();
+ 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:
+ {
+ PyraAlg::layoutShapeChildren(rShape);
+ break;
+ }
+
+ case XML_snake:
+ {
+ SnakeAlg::layoutShapeChildren(*this, rShape, rConstraints);
+ 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 = o3tl::convert(rShape->getSize().Width * fFactor,
+ o3tl::Length::emu, o3tl::Length::mm100);
+
+ 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_value())
+ 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().moTextPreRotation = n90x * 90 * PER_DEGREE;
+ }
+ break;
+ case XML_grav:
+ {
+ if (nShapeRot > (90 * PER_DEGREE) && nShapeRot < (270 * PER_DEGREE))
+ pTextBody->getTextProperties().moTextPreRotation = -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
+ = o3tl::convert(285750 * (nLevel - nStartBulletsAtLevel + 1),
+ o3tl::Length::emu, o3tl::Length::mm100);
+ aParagraph->getProperties().getParaLeftMargin() = nLeftMargin;
+ }
+
+ if (!aParagraph->getProperties().getFirstLineIndentation().has_value())
+ aParagraph->getProperties().getFirstLineIndentation()
+ = o3tl::convert(-285750, o3tl::Length::emu, o3tl::Length::mm100);
+
+ // It is not possible to change the bullet style for text.
+ aParagraph->getProperties().getBulletList().setBulletChar(u"•"_ustr);
+ 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 svx::diagram::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;
+ }
+
+ Shape* pDataNode2Shape(mrDgm.getData()->getOrCreateAssociatedShape(*aDataNode2->second));
+ if (nullptr == pDataNode2Shape)
+ {
+ //busted, skip it
+ continue;
+ }
+
+ rShape->setDataNodeType(aDataNode2->second->mnXMLType);
+
+ if (rItem.mnDepth == 0)
+ {
+ // grab shape attr from topmost element(s)
+ rShape->getShapeProperties() = pDataNode2Shape->getShapeProperties();
+ rShape->getLineProperties() = pDataNode2Shape->getLineProperties();
+ rShape->getFillProperties() = pDataNode2Shape->getFillProperties();
+ rShape->getCustomShapeProperties() = pDataNode2Shape->getCustomShapeProperties();
+ rShape->setMasterTextListStyle( pDataNode2Shape->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() = pDataNode2Shape->getLineProperties();
+ rShape->getFillProperties() = pDataNode2Shape->getFillProperties();
+ }
+
+ // append text with right outline level
+ if( pDataNode2Shape->getTextBody() &&
+ !pDataNode2Shape->getTextBody()->getParagraphs().empty() &&
+ !pDataNode2Shape->getTextBody()->getParagraphs().front()->getRuns().empty() )
+ {
+ TextBodyPtr pTextBody=rShape->getTextBody();
+ if( !pTextBody )
+ {
+ pTextBody = std::make_shared<TextBody>();
+
+ // also copy text attrs
+ pTextBody->getTextListStyle() =
+ pDataNode2Shape->getTextBody()->getTextListStyle();
+ pTextBody->getTextProperties() =
+ pDataNode2Shape->getTextBody()->getTextProperties();
+
+ rShape->setTextBody(pTextBody);
+ }
+
+ const TextParagraphVector& rSourceParagraphs
+ = pDataNode2Shape->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 = pDataNode2Shape->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 << "\"");
+ Shape* pPresNodeShape(mrDgm.getData()->getOrCreateAssociatedShape(*pPresNode));
+ if (nullptr != pPresNodeShape)
+ rShape->getFillProperties().assignUsed(pPresNodeShape->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 0000000000..5458999029
--- /dev/null
+++ b/oox/source/drawingml/diagram/diagramlayoutatoms.hxx
@@ -0,0 +1,373 @@
+/* -*- 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 <utility>
+
+#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 );
+
+ OUString msVal;
+ sal_Int32 mnFunc;
+ sal_Int32 mnArg;
+ sal_Int32 mnOp;
+ sal_Int32 mnVal;
+};
+
+/// Constraints allow you to specify an ideal (or starting point) size for each shape.
+struct Constraint
+{
+ OUString msForName;
+ OUString msRefForName;
+ double mfFactor;
+ double mfValue;
+ sal_Int32 mnFor;
+ sal_Int32 mnPointType;
+ sal_Int32 mnType;
+ sal_Int32 mnRefFor;
+ sal_Int32 mnRefType;
+ sal_Int32 mnRefPointType;
+ 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; }
+ const ParamMap& getMap() const { return maMap; }
+ 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;
+
+/// Finds optimal grid to layout children that have fixed aspect ratio.
+class SnakeAlg
+{
+public:
+ static void layoutShapeChildren(const AlgAtom& rAlg, const ShapePtr& rShape,
+ const std::vector<Constraint>& rConstraints);
+};
+
+/**
+ * Lays out child layout nodes along a vertical path and works with the trapezoid shape to create a
+ * pyramid.
+ */
+class PyraAlg
+{
+public:
+ static void layoutShapeChildren(const ShapePtr& rShape);
+};
+
+/**
+ * Specifies the size and position for all child layout nodes.
+ */
+class CompositeAlg
+{
+public:
+ static void layoutShapeChildren(AlgAtom& rAlg, const ShapePtr& rShape,
+ const std::vector<Constraint>& rConstraints);
+
+private:
+ /**
+ * 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.
+ */
+ static void applyConstraintToLayout(const Constraint& rConstraint,
+ LayoutPropertyMap& rProperties);
+
+ /**
+ * Decides if a certain reference type (e.g. "right") can be inferred from the available properties
+ * in rMap (e.g. left and width). Returns true if rValue is written to.
+ */
+ static bool inferFromLayoutProperty(const LayoutProperty& rMap, sal_Int32 nRefType,
+ sal_Int32& rValue);
+};
+
+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 svx::diagram::Point* pPresPoint) const;
+
+private:
+ static bool compareResult(sal_Int32 nOperator, sal_Int32 nFirst, sal_Int32 nSecond);
+ sal_Int32 getNodeCount(const svx::diagram::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 svx::diagram::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, ShapePtr pShape) : LayoutAtom(rLayoutNode), mpShapeTemplate(std::move(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 0000000000..b7f5a59630
--- /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 svx::diagram::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 svx::diagram::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 0000000000..49c83f6745
--- /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 svx::diagram::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 svx::diagram::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 svx::diagram::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 0000000000..f34d93d98f
--- /dev/null
+++ b/oox/source/drawingml/diagram/layoutatomvisitors.cxx
@@ -0,0 +1,269 @@
+/* -*- 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 svx::diagram::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 svx::diagram::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();
+
+ // add/set ModelID from current node to allow later association
+ if (mpCurrentNode)
+ mpShape->setDiagramDataModelID(mpCurrentNode->msModelId);
+}
+
+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 svx::diagram::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 svx::diagram::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 0000000000..7c10fc436d
--- /dev/null
+++ b/oox/source/drawingml/diagram/layoutatomvisitors.hxx
@@ -0,0 +1,97 @@
+/* -*- 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 <utility>
+#include "diagram.hxx"
+#include "diagramlayoutatoms.hxx"
+#include "layoutatomvisitorbase.hxx"
+
+namespace oox::drawingml {
+
+class ShapeCreationVisitor : public LayoutAtomVisitorBase
+{
+public:
+ ShapeCreationVisitor(const Diagram& rDgm,
+ const svx::diagram::Point* pRootPoint,
+ ShapePtr xParentShape) :
+ LayoutAtomVisitorBase(rDgm, pRootPoint),
+ mpParentShape(std::move(xParentShape))
+ {}
+
+ 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 svx::diagram::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 svx::diagram::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 0000000000..45756e20bd
--- /dev/null
+++ b/oox/source/drawingml/diagram/layoutnodecontext.cxx
@@ -0,0 +1,310 @@
+/* -*- 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>
+#include <utility>
+
+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, LayoutAtomPtr pNode )
+ : ContextHandler2( rParent )
+ , mpNode(std::move( pNode ))
+ {
+ msName = rAttribs.getStringDefaulted( XML_name );
+ }
+
+ 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.getStringDefaulted(XML_ref));
+ 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.getStringDefaulted( XML_val );
+ 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.getStringDefaulted( XML_name ) );
+}
+
+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.getStringDefaulted( XML_moveWith ) );
+ pNode->setStyleLabel( rAttribs.getStringDefaulted( XML_styleLbl ) );
+ 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 0000000000..3499f57045
--- /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 0000000000..2a1c450d01
--- /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() = default;
+
+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.getStringDefaulted(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 0000000000..83a86c49e0
--- /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);
+ ~RuleListContext() override;
+
+ oox::core::ContextHandlerRef onCreateContext(sal_Int32 nElement,
+ const AttributeList& rAttribs) override;
+
+private:
+ LayoutAtomPtr mpNode;
+};
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */