1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <oox/drawingml/diagram/diagram.hxx>
21 #include "diagram.hxx"
22 #include <com/sun/star/awt/Point.hpp>
23 #include <com/sun/star/awt/Size.hpp>
24 #include <com/sun/star/beans/XPropertySet.hpp>
25 #include <com/sun/star/drawing/XShape.hpp>
26 #include <com/sun/star/drawing/XShapes.hpp>
27 #include <com/sun/star/xml/dom/XDocument.hpp>
28 #include <com/sun/star/xml/sax/XFastSAXSerializable.hpp>
29 #include <sal/log.hxx>
30 #include <editeng/unoprnms.hxx>
31 #include <drawingml/fillproperties.hxx>
32 #include <drawingml/customshapeproperties.hxx>
33 #include <oox/token/namespaces.hxx>
34 #include <basegfx/matrix/b2dhommatrix.hxx>
35 #include <svx/svdpage.hxx>
37 #include "diagramlayoutatoms.hxx"
38 #include "layoutatomvisitors.hxx"
39 #include "diagramfragmenthandler.hxx"
41 using namespace ::com::sun::star
;
43 namespace oox
{ namespace drawingml
{
45 static void sortChildrenByZOrder(const ShapePtr
& pShape
)
47 std::vector
<ShapePtr
>& rChildren
= pShape
->getChildren();
49 // Offset the children from their default z-order stacking, if necessary.
50 for (size_t i
= 0; i
< rChildren
.size(); ++i
)
51 rChildren
[i
]->setZOrder(i
);
53 for (size_t i
= 0; i
< rChildren
.size(); ++i
)
55 const ShapePtr
& pChild
= rChildren
[i
];
56 sal_Int32 nZOrderOff
= pChild
->getZOrderOff();
60 // Increase my ZOrder by nZOrderOff.
61 pChild
->setZOrder(pChild
->getZOrder() + nZOrderOff
);
62 pChild
->setZOrderOff(0);
64 for (sal_Int32 j
= 0; j
< nZOrderOff
; ++j
)
66 size_t nIndex
= i
+ j
+ 1;
67 if (nIndex
>= rChildren
.size())
70 // Decrease the ZOrder of the next nZOrderOff elements by one.
71 const ShapePtr
& pNext
= rChildren
[nIndex
];
72 pNext
->setZOrder(pNext
->getZOrder() - 1);
76 // Now that the ZOrders are adjusted, sort the children.
77 std::sort(rChildren
.begin(), rChildren
.end(),
78 [](const ShapePtr
& a
, const ShapePtr
& b
) { return a
->getZOrder() < b
->getZOrder(); });
80 // Apply also for children.
81 for (const auto& rChild
: rChildren
)
82 sortChildrenByZOrder(rChild
);
85 void Diagram::addTo( const ShapePtr
& pParentShape
)
87 if (pParentShape
->getSize().Width
== 0 || pParentShape
->getSize().Height
== 0)
88 SAL_WARN("oox.drawingml", "Diagram cannot be correctly laid out. Size: "
89 << pParentShape
->getSize().Width
<< "x" << pParentShape
->getSize().Height
);
91 pParentShape
->setChildSize(pParentShape
->getSize());
93 const dgm::Point
* pRootPoint
= mpData
->getRootPoint();
94 if (mpLayout
->getNode() && pRootPoint
)
96 // create Shape hierarchy
97 ShapeCreationVisitor
aCreationVisitor(*this, pRootPoint
, pParentShape
);
98 mpLayout
->getNode()->setExistingShape(pParentShape
);
99 mpLayout
->getNode()->accept(aCreationVisitor
);
101 // layout shapes - now all shapes are created
102 ShapeLayoutingVisitor
aLayoutingVisitor(*this, pRootPoint
);
103 mpLayout
->getNode()->accept(aLayoutingVisitor
);
105 sortChildrenByZOrder(pParentShape
);
108 ShapePtr
pBackground(new Shape("com.sun.star.drawing.CustomShape"));
109 pBackground
->setSubType(XML_rect
);
110 pBackground
->getCustomShapeProperties()->setShapePresetType(XML_rect
);
111 pBackground
->setSize(pParentShape
->getSize());
112 pBackground
->getFillProperties() = *mpData
->getFillProperties();
113 pBackground
->setLocked(true);
114 auto& aChildren
= pParentShape
->getChildren();
115 aChildren
.insert(aChildren
.begin(), pBackground
);
118 uno::Sequence
<beans::PropertyValue
> Diagram::getDomsAsPropertyValues() const
120 sal_Int32 length
= maMainDomMap
.size();
122 if (maDataRelsMap
.hasElements())
125 uno::Sequence
<beans::PropertyValue
> aValue(length
);
126 beans::PropertyValue
* pValue
= aValue
.getArray();
127 for (auto const& mainDom
: maMainDomMap
)
129 pValue
->Name
= mainDom
.first
;
130 pValue
->Value
<<= mainDom
.second
;
134 if (maDataRelsMap
.hasElements())
136 pValue
->Name
= "OOXDiagramDataRels";
137 pValue
->Value
<<= maDataRelsMap
;
144 static uno::Reference
<xml::dom::XDocument
> loadFragment(
145 core::XmlFilterBase
& rFilter
,
146 const OUString
& rFragmentPath
)
148 // load diagramming fragments into DOM representation, that later
149 // gets serialized back to SAX events and parsed
150 return rFilter
.importFragment( rFragmentPath
);
153 static uno::Reference
<xml::dom::XDocument
> loadFragment(
154 core::XmlFilterBase
& rFilter
,
155 const rtl::Reference
< core::FragmentHandler
>& rxHandler
)
157 return loadFragment( rFilter
, rxHandler
->getFragmentPath() );
160 static void importFragment( core::XmlFilterBase
& rFilter
,
161 const uno::Reference
<xml::dom::XDocument
>& rXDom
,
162 const char* pDocName
,
163 const DiagramPtr
& pDiagram
,
164 const rtl::Reference
< core::FragmentHandler
>& rxHandler
)
166 DiagramDomMap
& rMainDomMap
= pDiagram
->getDomMap();
167 rMainDomMap
[OUString::createFromAscii(pDocName
)] = rXDom
;
169 uno::Reference
<xml::sax::XFastSAXSerializable
> xSerializer(
170 rXDom
, uno::UNO_QUERY_THROW
);
172 // now serialize DOM tree into internal data structures
173 rFilter
.importFragment( rxHandler
, xSerializer
);
179 * A fragment handler that just counts the number of <dsp:sp> elements in a
182 class DiagramShapeCounter
: public oox::core::FragmentHandler2
185 DiagramShapeCounter(oox::core::XmlFilterBase
& rFilter
, const OUString
& rFragmentPath
,
186 sal_Int32
& nCounter
);
187 oox::core::ContextHandlerRef
onCreateContext(sal_Int32 nElement
,
188 const AttributeList
& rAttribs
) override
;
191 sal_Int32
& m_nCounter
;
194 DiagramShapeCounter::DiagramShapeCounter(oox::core::XmlFilterBase
& rFilter
,
195 const OUString
& rFragmentPath
, sal_Int32
& nCounter
)
196 : FragmentHandler2(rFilter
, rFragmentPath
)
197 , m_nCounter(nCounter
)
201 oox::core::ContextHandlerRef
DiagramShapeCounter::onCreateContext(sal_Int32 nElement
,
202 const AttributeList
& /*rAttribs*/)
206 case DSP_TOKEN(drawing
):
207 case DSP_TOKEN(spTree
):
220 void loadDiagram( ShapePtr
const & pShape
,
221 core::XmlFilterBase
& rFilter
,
222 const OUString
& rDataModelPath
,
223 const OUString
& rLayoutPath
,
224 const OUString
& rQStylePath
,
225 const OUString
& rColorStylePath
,
226 const oox::core::Relations
& rRelations
)
228 DiagramPtr
pDiagram( new Diagram
);
230 DiagramDataPtr
pData( new DiagramData() );
231 pDiagram
->setData( pData
);
233 DiagramLayoutPtr
pLayout( new DiagramLayout(*pDiagram
) );
234 pDiagram
->setLayout( pLayout
);
237 if( !rDataModelPath
.isEmpty() )
239 rtl::Reference
< core::FragmentHandler
> xRefDataModel(
240 new DiagramDataFragmentHandler( rFilter
, rDataModelPath
, pData
));
242 importFragment(rFilter
,
243 loadFragment(rFilter
,xRefDataModel
),
248 pDiagram
->getDataRelsMap() = pShape
->resolveRelationshipsOfTypeFromOfficeDoc( rFilter
,
249 xRefDataModel
->getFragmentPath(), "image" );
251 // Pass the info to pShape
252 for (auto const& extDrawing
: pData
->getExtDrawings())
254 OUString aFragmentPath
= rRelations
.getFragmentPathFromRelId(extDrawing
);
255 // Ignore RelIds which don't resolve to a fragment path.
256 if (aFragmentPath
.isEmpty())
259 sal_Int32 nCounter
= 0;
260 rtl::Reference
<core::FragmentHandler
> xCounter(
261 new DiagramShapeCounter(rFilter
, aFragmentPath
, nCounter
));
262 rFilter
.importFragment(xCounter
);
263 // Ignore ext drawings which don't actually have any shapes.
267 pShape
->addExtDrawingRelId(extDrawing
);
271 // extLst is present, lets bet on that and ignore the rest of the data from here
272 if( pShape
->getExtDrawings().empty() )
275 if( !rLayoutPath
.isEmpty() )
277 rtl::Reference
< core::FragmentHandler
> xRefLayout(
278 new DiagramLayoutFragmentHandler( rFilter
, rLayoutPath
, pLayout
));
280 importFragment(rFilter
,
281 loadFragment(rFilter
,xRefLayout
),
288 if( !rQStylePath
.isEmpty() )
290 rtl::Reference
< core::FragmentHandler
> xRefQStyle(
291 new DiagramQStylesFragmentHandler( rFilter
, rQStylePath
, pDiagram
->getStyles() ));
293 importFragment(rFilter
,
294 loadFragment(rFilter
,xRefQStyle
),
302 // We still want to add the XDocuments to the DiagramDomMap
303 DiagramDomMap
& rMainDomMap
= pDiagram
->getDomMap();
304 rMainDomMap
[OUString("OOXLayout")] = loadFragment(rFilter
,rLayoutPath
);
305 rMainDomMap
[OUString("OOXStyle")] = loadFragment(rFilter
,rQStylePath
);
309 if( !rColorStylePath
.isEmpty() )
311 rtl::Reference
< core::FragmentHandler
> xRefColorStyle(
312 new ColorFragmentHandler( rFilter
, rColorStylePath
, pDiagram
->getColors() ));
314 importFragment(rFilter
,
315 loadFragment(rFilter
,xRefColorStyle
),
321 if( !pData
->getExtDrawings().empty() )
323 const DiagramColorMap::const_iterator aColor
= pDiagram
->getColors().find("node0");
324 if( aColor
!= pDiagram
->getColors().end() )
326 pShape
->setFontRefColorForNodes(aColor
->second
.maTextFillColor
);
330 // collect data, init maps
333 // diagram loaded. now lump together & attach to shape
334 pDiagram
->addTo(pShape
);
335 pShape
->setDiagramData(pData
);
336 pShape
->setDiagramDoms(pDiagram
->getDomsAsPropertyValues());
339 void loadDiagram(ShapePtr
const& pShape
,
340 DiagramDataPtr pDiagramData
,
341 const uno::Reference
<xml::dom::XDocument
>& layoutDom
,
342 const uno::Reference
<xml::dom::XDocument
>& styleDom
,
343 const uno::Reference
<xml::dom::XDocument
>& colorDom
,
344 core::XmlFilterBase
& rFilter
)
346 DiagramPtr
pDiagram(new Diagram
);
348 pDiagram
->setData(pDiagramData
);
350 DiagramLayoutPtr
pLayout(new DiagramLayout(*pDiagram
));
351 pDiagram
->setLayout(pLayout
);
356 rtl::Reference
<core::FragmentHandler
> xRefLayout(
357 new DiagramLayoutFragmentHandler(rFilter
, OUString(), pLayout
));
359 importFragment(rFilter
, layoutDom
, "OOXLayout", pDiagram
, xRefLayout
);
365 rtl::Reference
<core::FragmentHandler
> xRefQStyle(
366 new DiagramQStylesFragmentHandler(rFilter
, OUString(), pDiagram
->getStyles()));
368 importFragment(rFilter
, styleDom
, "OOXStyle", pDiagram
, xRefQStyle
);
374 rtl::Reference
<core::FragmentHandler
> xRefColorStyle(
375 new ColorFragmentHandler(rFilter
, OUString(), pDiagram
->getColors()));
377 importFragment(rFilter
, colorDom
, "OOXColor", pDiagram
, xRefColorStyle
);
380 // diagram loaded. now lump together & attach to shape
381 pDiagram
->addTo(pShape
);
384 void reloadDiagram(SdrObject
* pObj
, core::XmlFilterBase
& rFilter
)
386 DiagramDataPtr pDiagramData
= std::dynamic_pointer_cast
<DiagramData
>(pObj
->GetDiagramData());
390 pObj
->getChildrenOfSdrObject()->ClearSdrObjList();
392 uno::Reference
<css::drawing::XShape
> xShape(pObj
->getUnoShape(), uno::UNO_QUERY_THROW
);
393 uno::Reference
<beans::XPropertySet
> xPropSet(xShape
, uno::UNO_QUERY_THROW
);
395 uno::Reference
<xml::dom::XDocument
> layoutDom
;
396 uno::Reference
<xml::dom::XDocument
> styleDom
;
397 uno::Reference
<xml::dom::XDocument
> colorDom
;
399 // retrieve the doms from the GrabBag
400 uno::Sequence
<beans::PropertyValue
> propList
;
401 xPropSet
->getPropertyValue(UNO_NAME_MISC_OBJ_INTEROPGRABBAG
) >>= propList
;
402 for (const auto& rProp
: std::as_const(propList
))
404 OUString propName
= rProp
.Name
;
405 if (propName
== "OOXLayout")
406 rProp
.Value
>>= layoutDom
;
407 else if (propName
== "OOXStyle")
408 rProp
.Value
>>= styleDom
;
409 else if (propName
== "OOXColor")
410 rProp
.Value
>>= colorDom
;
413 ShapePtr
pShape(new Shape());
414 pShape
->setDiagramType();
415 pShape
->setSize(awt::Size(xShape
->getSize().Width
* EMU_PER_HMM
,
416 xShape
->getSize().Height
* EMU_PER_HMM
));
418 loadDiagram(pShape
, pDiagramData
, layoutDom
, styleDom
, colorDom
, rFilter
);
420 uno::Reference
<drawing::XShapes
> xShapes(xShape
, uno::UNO_QUERY_THROW
);
421 basegfx::B2DHomMatrix aTransformation
;
422 aTransformation
.translate(xShape
->getPosition().X
* EMU_PER_HMM
,
423 xShape
->getPosition().Y
* EMU_PER_HMM
);
424 for (auto const& child
: pShape
->getChildren())
425 child
->addShape(rFilter
, rFilter
.getCurrentTheme(), xShapes
, aTransformation
, pShape
->getFillProperties());
430 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */