Version 6.4.0.0.beta1, tag libreoffice-6.4.0.0.beta1
[LibreOffice.git] / oox / source / drawingml / diagram / diagram.cxx
blobb2f3373ad113bf3b62909308c8027ed45c830495
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
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();
57 if (nZOrderOff <= 0)
58 continue;
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())
68 break;
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())
123 ++length;
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;
131 ++pValue;
134 if (maDataRelsMap.hasElements())
136 pValue->Name = "OOXDiagramDataRels";
137 pValue->Value <<= maDataRelsMap;
138 ++pValue;
141 return aValue;
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 );
176 namespace
179 * A fragment handler that just counts the number of <dsp:sp> elements in a
180 * fragment.
182 class DiagramShapeCounter : public oox::core::FragmentHandler2
184 public:
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;
190 private:
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*/)
204 switch (nElement)
206 case DSP_TOKEN(drawing):
207 case DSP_TOKEN(spTree):
208 return this;
209 case DSP_TOKEN(sp):
210 ++m_nCounter;
211 break;
212 default:
213 break;
216 return nullptr;
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 );
236 // data
237 if( !rDataModelPath.isEmpty() )
239 rtl::Reference< core::FragmentHandler > xRefDataModel(
240 new DiagramDataFragmentHandler( rFilter, rDataModelPath, pData ));
242 importFragment(rFilter,
243 loadFragment(rFilter,xRefDataModel),
244 "OOXData",
245 pDiagram,
246 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())
257 continue;
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.
264 if (nCounter == 0)
265 continue;
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() )
274 // layout
275 if( !rLayoutPath.isEmpty() )
277 rtl::Reference< core::FragmentHandler > xRefLayout(
278 new DiagramLayoutFragmentHandler( rFilter, rLayoutPath, pLayout ));
280 importFragment(rFilter,
281 loadFragment(rFilter,xRefLayout),
282 "OOXLayout",
283 pDiagram,
284 xRefLayout);
287 // style
288 if( !rQStylePath.isEmpty() )
290 rtl::Reference< core::FragmentHandler > xRefQStyle(
291 new DiagramQStylesFragmentHandler( rFilter, rQStylePath, pDiagram->getStyles() ));
293 importFragment(rFilter,
294 loadFragment(rFilter,xRefQStyle),
295 "OOXStyle",
296 pDiagram,
297 xRefQStyle);
300 else
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);
308 // colors
309 if( !rColorStylePath.isEmpty() )
311 rtl::Reference< core::FragmentHandler > xRefColorStyle(
312 new ColorFragmentHandler( rFilter, rColorStylePath, pDiagram->getColors() ));
314 importFragment(rFilter,
315 loadFragment(rFilter,xRefColorStyle),
316 "OOXColor",
317 pDiagram,
318 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
331 pData->build();
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);
353 // layout
354 if (layoutDom.is())
356 rtl::Reference<core::FragmentHandler> xRefLayout(
357 new DiagramLayoutFragmentHandler(rFilter, OUString(), pLayout));
359 importFragment(rFilter, layoutDom, "OOXLayout", pDiagram, xRefLayout);
362 // style
363 if (styleDom.is())
365 rtl::Reference<core::FragmentHandler> xRefQStyle(
366 new DiagramQStylesFragmentHandler(rFilter, OUString(), pDiagram->getStyles()));
368 importFragment(rFilter, styleDom, "OOXStyle", pDiagram, xRefQStyle);
371 // colors
372 if (colorDom.is())
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());
387 if (!pDiagramData)
388 return;
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: */