/src/libreoffice/oox/source/drawingml/diagram/diagram.cxx
Line | Count | Source |
1 | | /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ |
2 | | /* |
3 | | * This file is part of the LibreOffice project. |
4 | | * |
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/. |
8 | | * |
9 | | * This file incorporates work covered by the following license notice: |
10 | | * |
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 . |
18 | | */ |
19 | | |
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 <com/sun/star/xml/dom/XDocumentBuilder.hpp> |
30 | | #include <com/sun/star/xml/dom/DocumentBuilder.hpp> |
31 | | #include <sal/log.hxx> |
32 | | #include <editeng/unoprnms.hxx> |
33 | | #include <drawingml/fillproperties.hxx> |
34 | | #include <drawingml/customshapeproperties.hxx> |
35 | | #include <o3tl/unit_conversion.hxx> |
36 | | #include <oox/drawingml/theme.hxx> |
37 | | #include <oox/token/namespaces.hxx> |
38 | | #include <basegfx/matrix/b2dhommatrix.hxx> |
39 | | #include <svx/svdpage.hxx> |
40 | | #include <oox/ppt/pptimport.hxx> |
41 | | #include <comphelper/xmltools.hxx> |
42 | | #include "diagramlayoutatoms.hxx" |
43 | | #include "layoutatomvisitors.hxx" |
44 | | #include "diagramfragmenthandler.hxx" |
45 | | #include <comphelper/processfactory.hxx> |
46 | | #include <com/sun/star/io/TempFile.hpp> |
47 | | #include <oox/export/drawingml.hxx> |
48 | | |
49 | | #include <com/sun/star/xml/sax/XSAXSerializable.hpp> |
50 | | #include <com/sun/star/xml/sax/Writer.hpp> |
51 | | #include <oox/core/fastparser.hxx> |
52 | | #include <unotools/streamwrap.hxx> |
53 | | #include <tools/stream.hxx> |
54 | | |
55 | | #ifdef DBG_UTIL |
56 | | #include <osl/file.hxx> |
57 | | #include <o3tl/environment.hxx> |
58 | | #include <comphelper/storagehelper.hxx> |
59 | | #include <com/sun/star/embed/XRelationshipAccess.hpp> |
60 | | #endif |
61 | | |
62 | | using namespace ::com::sun::star; |
63 | | |
64 | | namespace oox::drawingml { |
65 | | |
66 | | static void sortChildrenByZOrder(const ShapePtr& pShape) |
67 | 290 | { |
68 | 290 | std::vector<ShapePtr>& rChildren = pShape->getChildren(); |
69 | | |
70 | | // Offset the children from their default z-order stacking, if necessary. |
71 | 544 | for (size_t i = 0; i < rChildren.size(); ++i) |
72 | 254 | rChildren[i]->setZOrder(i); |
73 | | |
74 | 544 | for (size_t i = 0; i < rChildren.size(); ++i) |
75 | 254 | { |
76 | 254 | const ShapePtr& pChild = rChildren[i]; |
77 | 254 | sal_Int32 nZOrderOff = pChild->getZOrderOff(); |
78 | 254 | if (nZOrderOff <= 0) |
79 | 254 | continue; |
80 | | |
81 | | // Increase my ZOrder by nZOrderOff. |
82 | 0 | pChild->setZOrder(pChild->getZOrder() + nZOrderOff); |
83 | 0 | pChild->setZOrderOff(0); |
84 | |
|
85 | 0 | for (sal_Int32 j = 0; j < nZOrderOff; ++j) |
86 | 0 | { |
87 | 0 | size_t nIndex = i + j + 1; |
88 | 0 | if (nIndex >= rChildren.size()) |
89 | 0 | break; |
90 | | |
91 | | // Decrease the ZOrder of the next nZOrderOff elements by one. |
92 | 0 | const ShapePtr& pNext = rChildren[nIndex]; |
93 | 0 | pNext->setZOrder(pNext->getZOrder() - 1); |
94 | 0 | } |
95 | 0 | } |
96 | | |
97 | | // Now that the ZOrders are adjusted, sort the children. |
98 | 290 | std::sort(rChildren.begin(), rChildren.end(), |
99 | 290 | [](const ShapePtr& a, const ShapePtr& b) { return a->getZOrder() < b->getZOrder(); }); |
100 | | |
101 | | // Apply also for children. |
102 | 290 | for (const auto& rChild : rChildren) |
103 | 254 | sortChildrenByZOrder(rChild); |
104 | 290 | } |
105 | | |
106 | | /// Removes empty group shapes, now that their spacing influenced the layout. |
107 | | static void removeUnneededGroupShapes(const ShapePtr& pShape) |
108 | 290 | { |
109 | 290 | std::vector<ShapePtr>& rChildren = pShape->getChildren(); |
110 | | |
111 | 290 | std::erase_if(rChildren, |
112 | 290 | [](const ShapePtr& aChild) { |
113 | 254 | return aChild->getServiceName() |
114 | 254 | == "com.sun.star.drawing.GroupShape" |
115 | 30 | && aChild->getChildren().empty(); |
116 | 254 | }); |
117 | | |
118 | 290 | for (const auto& pChild : rChildren) |
119 | 254 | { |
120 | 254 | removeUnneededGroupShapes(pChild); |
121 | 254 | } |
122 | 290 | } |
123 | | |
124 | | void SmartArtDiagram::createShapeHierarchyFromModel( const ShapePtr & pParentShape, bool bCreate ) |
125 | 107 | { |
126 | 107 | if (pParentShape->getSize().Width == 0 || pParentShape->getSize().Height == 0) |
127 | 107 | SAL_WARN("oox.drawingml", "SmartArtDiagram cannot be correctly laid out. Size: " |
128 | 107 | << pParentShape->getSize().Width << "x" << pParentShape->getSize().Height); |
129 | | |
130 | 107 | pParentShape->setChildSize(pParentShape->getSize()); |
131 | | |
132 | 107 | const svx::diagram::Point* pRootPoint = mpData->getRootPoint(); |
133 | 107 | if (bCreate && mpLayout->getNode() && pRootPoint) |
134 | 36 | { |
135 | | // create Shape hierarchy |
136 | 36 | ShapeCreationVisitor aCreationVisitor(*this, pRootPoint, pParentShape); |
137 | 36 | mpLayout->getNode()->setExistingShape(pParentShape); |
138 | 36 | mpLayout->getNode()->accept(aCreationVisitor); |
139 | | |
140 | | // layout shapes - now all shapes are created |
141 | 36 | ShapeLayoutingVisitor aLayoutingVisitor(*this, pRootPoint); |
142 | 36 | mpLayout->getNode()->accept(aLayoutingVisitor); |
143 | | |
144 | 36 | sortChildrenByZOrder(pParentShape); |
145 | 36 | removeUnneededGroupShapes(pParentShape); |
146 | 36 | } |
147 | | |
148 | 107 | ShapePtr pBackground = std::make_shared<Shape>("com.sun.star.drawing.CustomShape"); |
149 | 107 | pBackground->setSubType(XML_rect); |
150 | 107 | pBackground->getCustomShapeProperties()->setShapePresetType(XML_rect); |
151 | 107 | pBackground->setSize(pParentShape->getSize()); |
152 | 107 | if (mpData->getBackgroundShapeFillProperties()) |
153 | 107 | pBackground->getFillProperties() = *mpData->getBackgroundShapeFillProperties(); |
154 | | |
155 | | // create and set ModelID for BackgroundShape to allow later association |
156 | 107 | getData()->setBackgroundShapeModelID(OStringToOUString(comphelper::xml::generateGUIDString(), RTL_TEXTENCODING_UTF8)); |
157 | 107 | pBackground->setDiagramDataModelID(getData()->getBackgroundShapeModelID()); |
158 | | |
159 | 107 | auto& aChildren = pParentShape->getChildren(); |
160 | 107 | aChildren.insert(aChildren.begin(), pBackground); |
161 | 107 | } |
162 | | |
163 | | uno::Reference<xml::dom::XDocument> SmartArtDiagram::convertAndSet(std::u16string_view rDOM, svx::diagram::DomMapFlag aDomMapFlag) |
164 | 0 | { |
165 | | // construct MemoryStream and OStreamWrapper |
166 | 0 | const OString sUtf8(OUStringToOString(rDOM, RTL_TEXTENCODING_UTF8)); |
167 | 0 | SvMemoryStream aStream(const_cast<char*>(sUtf8.getStr()), sUtf8.getLength(), StreamMode::READ); |
168 | 0 | rtl::Reference<utl::OStreamWrapper> pStreamWrapper = new utl::OStreamWrapper(aStream); |
169 | | |
170 | | // create the dom parser & create DomTree |
171 | 0 | uno::Reference<xml::dom::XDocumentBuilder> xDomBuilder(xml::dom::DocumentBuilder::create(comphelper::getProcessComponentContext())); |
172 | 0 | uno::Reference<xml::dom::XDocument> aDomTree(xDomBuilder->parse(pStreamWrapper->getInputStream())); |
173 | | |
174 | | // set DomTree locally |
175 | 0 | setOOXDomValue(aDomMapFlag, uno::Any(aDomTree)); |
176 | 0 | return aDomTree; |
177 | 0 | } |
178 | | |
179 | | SmartArtDiagram::SmartArtDiagram() |
180 | 370 | : maDiagramFontHeights() |
181 | 370 | , mpData(std::make_shared<DiagramData_oox>()) |
182 | 370 | , mpLayout(std::make_shared<DiagramLayout>(*this)) |
183 | 370 | , maStyles() |
184 | 370 | , maColors() |
185 | 370 | , maDiagramPRDomMap() |
186 | 370 | { |
187 | 370 | } |
188 | | |
189 | | SmartArtDiagram::SmartArtDiagram(SmartArtDiagram const& rSource) |
190 | 0 | : maDiagramFontHeights() |
191 | 0 | , mpData(rSource.mpData ? new DiagramData_oox(*rSource.mpData) : nullptr) |
192 | 0 | , mpLayout(rSource.mpLayout) |
193 | 0 | , maStyles(rSource.maStyles) |
194 | 0 | , maColors(rSource.maColors) |
195 | 0 | , maDiagramPRDomMap(rSource.maDiagramPRDomMap) |
196 | 0 | { |
197 | 0 | } |
198 | | |
199 | | SmartArtDiagram::SmartArtDiagram(const boost::property_tree::ptree& rDiagramModel) |
200 | 0 | : maDiagramFontHeights() |
201 | 0 | , mpData(std::make_shared<DiagramData_oox>(rDiagramModel)) |
202 | 0 | , mpLayout(std::make_shared<DiagramLayout>(*this)) |
203 | 0 | , maStyles() |
204 | 0 | , maColors() |
205 | 0 | , maDiagramPRDomMap() |
206 | 0 | { |
207 | | #ifdef DBG_UTIL |
208 | | mpData->dump(); |
209 | | #endif |
210 | 0 | const OUString aOOXLayoutDOM(OUString::fromUtf8(rDiagramModel.get("OOXLayout", ""))); |
211 | 0 | const OUString aOOXStyleDOM(OUString::fromUtf8(rDiagramModel.get("OOXStyle", ""))); |
212 | 0 | const OUString aOOXColorDOM(OUString::fromUtf8(rDiagramModel.get("OOXColor", ""))); |
213 | |
|
214 | 0 | if (!aOOXLayoutDOM.isEmpty() || !aOOXStyleDOM.isEmpty() || !aOOXColorDOM.isEmpty()) |
215 | 0 | { |
216 | | // we need a PowerPointImport for the FragmentHandlers, so create a single |
217 | | // temporary one. Use this for all possible DomTrees |
218 | 0 | rtl::Reference<oox::ppt::PowerPointImport> xPPTImport(new oox::ppt::PowerPointImport(comphelper::getProcessComponentContext())); |
219 | |
|
220 | 0 | if (!aOOXLayoutDOM.isEmpty()) |
221 | 0 | { |
222 | | // create and set DomTree locally |
223 | 0 | uno::Reference<xml::dom::XDocument> xDom(convertAndSet(aOOXLayoutDOM, svx::diagram::DomMapFlag::OOXLayout)); |
224 | | |
225 | | // import DomTree to mpLayout |
226 | 0 | uno::Reference<xml::sax::XFastSAXSerializable> xSerializer(xDom, uno::UNO_QUERY_THROW); |
227 | 0 | rtl::Reference< core::FragmentHandler > xRefLayout(new DiagramLayoutFragmentHandler(*this, *xPPTImport, "internal", mpLayout)); |
228 | 0 | xPPTImport->importFragment(xRefLayout, xSerializer); |
229 | 0 | } |
230 | |
|
231 | 0 | if (!aOOXStyleDOM.isEmpty()) |
232 | 0 | { |
233 | | // create and set DomTree locally |
234 | 0 | uno::Reference<xml::dom::XDocument> xDom(convertAndSet(aOOXStyleDOM, svx::diagram::DomMapFlag::OOXStyle)); |
235 | | |
236 | | // import DomTree to maStyles |
237 | 0 | uno::Reference<xml::sax::XFastSAXSerializable> xSerializer(xDom, uno::UNO_QUERY_THROW); |
238 | 0 | rtl::Reference< core::FragmentHandler > xRefLayout(new DiagramQStylesFragmentHandler(*xPPTImport, "internal", maStyles)); |
239 | 0 | xPPTImport->importFragment(xRefLayout, xSerializer); |
240 | 0 | } |
241 | |
|
242 | 0 | if (!aOOXColorDOM.isEmpty()) |
243 | 0 | { |
244 | | // create and set DomTree locally |
245 | 0 | uno::Reference<xml::dom::XDocument> xDom(convertAndSet(aOOXColorDOM, svx::diagram::DomMapFlag::OOXColor)); |
246 | | |
247 | | // import DomTree to maColors |
248 | 0 | uno::Reference<xml::sax::XFastSAXSerializable> xSerializer(xDom, uno::UNO_QUERY_THROW); |
249 | 0 | rtl::Reference< core::FragmentHandler > xRefLayout(new ColorFragmentHandler(*xPPTImport, "internal", maColors)); |
250 | 0 | xPPTImport->importFragment(xRefLayout, xSerializer); |
251 | 0 | } |
252 | 0 | } |
253 | 0 | } |
254 | | |
255 | | SmartArtDiagram::~SmartArtDiagram() |
256 | 370 | { |
257 | 370 | } |
258 | | |
259 | | uno::Any SmartArtDiagram::getOOXDomValue(svx::diagram::DomMapFlag aDomMapFlag) const |
260 | 0 | { |
261 | 0 | const DiagramPRDomMap::const_iterator aHit = maDiagramPRDomMap.find(aDomMapFlag); |
262 | |
|
263 | 0 | if (aHit != maDiagramPRDomMap.end()) |
264 | 0 | return aHit->second; |
265 | | |
266 | 0 | return uno::Any(); |
267 | 0 | } |
268 | | |
269 | | void SmartArtDiagram::setOOXDomValue(svx::diagram::DomMapFlag aDomMapFlag, const uno::Any& rValue) |
270 | 1.77k | { |
271 | 1.77k | maDiagramPRDomMap[aDomMapFlag] = rValue; |
272 | 1.77k | } |
273 | | |
274 | | void SmartArtDiagram::resetOOXDomValues(svx::diagram::DomMapFlags aDomMapFlags) |
275 | 0 | { |
276 | 0 | for (const auto& rEntry : aDomMapFlags) |
277 | 0 | { |
278 | 0 | maDiagramPRDomMap.erase(rEntry); |
279 | |
|
280 | 0 | if (maDiagramPRDomMap.empty()) |
281 | 0 | return; |
282 | 0 | } |
283 | 0 | } |
284 | | |
285 | | bool SmartArtDiagram::checkMinimalDataDoms() const |
286 | 0 | { |
287 | | // check if re-creation is activated |
288 | 0 | static bool bActivateAdvancedDiagramFeatures(nullptr != std::getenv("ACTIVATE_ADVANCED_DIAGRAM_FEATURES")); |
289 | |
|
290 | 0 | if (!bActivateAdvancedDiagramFeatures && maDiagramPRDomMap.end() == maDiagramPRDomMap.find(svx::diagram::DomMapFlag::OOXData)) |
291 | 0 | return false; |
292 | | |
293 | 0 | if (maDiagramPRDomMap.end() == maDiagramPRDomMap.find(svx::diagram::DomMapFlag::OOXLayout)) |
294 | 0 | return false; |
295 | | |
296 | 0 | if (maDiagramPRDomMap.end() == maDiagramPRDomMap.find(svx::diagram::DomMapFlag::OOXStyle)) |
297 | 0 | return false; |
298 | | |
299 | 0 | if (maDiagramPRDomMap.end() == maDiagramPRDomMap.find(svx::diagram::DomMapFlag::OOXColor)) |
300 | 0 | return false; |
301 | | |
302 | 0 | return true; |
303 | 0 | } |
304 | | |
305 | | void SmartArtDiagram::writeDiagramOOXData(DrawingML& rOriginalDrawingML, uno::Reference<io::XOutputStream>& xOutputStream, std::u16string_view rDrawingRelId) const |
306 | 0 | { |
307 | 0 | if (!xOutputStream) |
308 | 0 | return; |
309 | | |
310 | | // re-create OOXData DomFile from model data |
311 | 0 | sax_fastparser::FSHelperPtr aFS = std::make_shared<sax_fastparser::FastSerializerHelper>(xOutputStream, true); |
312 | 0 | getData()->writeDiagramData(rOriginalDrawingML, aFS, rDrawingRelId); |
313 | | |
314 | | // this call is *important*, without it xDocBuilder->parse below fails and some strange |
315 | | // and wrong assertion gets thrown in ~FastSerializerHelper that shall get called |
316 | 0 | xOutputStream->closeOutput(); |
317 | |
|
318 | | #ifdef DBG_UTIL |
319 | | uno::Reference< embed::XRelationshipAccess > xRelations( xOutputStream, uno::UNO_QUERY ); |
320 | | if( xRelations.is() ) |
321 | | { |
322 | | const uno::Sequence<uno::Sequence<beans::StringPair>> aSeqs = xRelations->getAllRelationships(); |
323 | | for (const uno::Sequence<beans::StringPair>& aSeq : aSeqs) |
324 | | { |
325 | | SAL_INFO("oox", "RelationData:"); |
326 | | for (const beans::StringPair& aPair : aSeq) |
327 | | SAL_INFO("oox", " Key: " << aPair.First << ", Value: " << aPair.Second); |
328 | | } |
329 | | } |
330 | | |
331 | | const OUString env(o3tl::getEnvironment(u"DIAGRAM_DUMP_PATH"_ustr)); |
332 | | if(!env.isEmpty()) |
333 | | { |
334 | | OUString url; |
335 | | ::osl::FileBase::getFileURLFromSystemPath(env, url); |
336 | | SvFileStream aOutStream(url + "data_T.xml", StreamMode::WRITE|StreamMode::TRUNC); |
337 | | uno::Reference<io::XStream> xOutStream(new utl::OStreamWrapper(aOutStream)); |
338 | | uno::Reference<io::XStream> xInStream(xOutputStream, uno::UNO_QUERY); |
339 | | comphelper::OStorageHelper::CopyInputToOutput(xInStream->getInputStream(), xOutStream->getOutputStream()); |
340 | | } |
341 | | #endif |
342 | 0 | } |
343 | | |
344 | | void SmartArtDiagram::writeDiagramOOXDrawing(DrawingML& rOriginalDrawingML, uno::Reference<io::XOutputStream>& xOutputStream) const |
345 | 0 | { |
346 | 0 | if (!xOutputStream) |
347 | 0 | return; |
348 | | |
349 | | // re-create OOXDrawing DomFile from model data |
350 | 0 | SAL_INFO("oox", "DiagramReCreate: creating DomMapFlag::OOXDrawing"); |
351 | 0 | sax_fastparser::FSHelperPtr aFS = std::make_shared<sax_fastparser::FastSerializerHelper>(xOutputStream, true); |
352 | 0 | getData()->writeDiagramReplacement(rOriginalDrawingML, aFS); |
353 | | |
354 | | // this call is *important*, without it xDocBuilder->parse below fails and some strange |
355 | | // and wrong assertion gets thrown in ~FastSerializerHelper that shall get called |
356 | 0 | xOutputStream->closeOutput(); |
357 | |
|
358 | | #ifdef DBG_UTIL |
359 | | uno::Reference< embed::XRelationshipAccess > xRelations( xOutputStream, uno::UNO_QUERY ); |
360 | | if( xRelations.is() ) |
361 | | { |
362 | | const uno::Sequence<uno::Sequence<beans::StringPair>> aSeqs = xRelations->getAllRelationships(); |
363 | | for (const uno::Sequence<beans::StringPair>& aSeq : aSeqs) |
364 | | { |
365 | | SAL_INFO("oox", "RelationDrawing:"); |
366 | | for (const beans::StringPair& aPair : aSeq) |
367 | | SAL_INFO("oox", " Key: " << aPair.First << ", Value: " << aPair.Second); |
368 | | } |
369 | | } |
370 | | |
371 | | const OUString env(o3tl::getEnvironment(u"DIAGRAM_DUMP_PATH"_ustr)); |
372 | | if(!env.isEmpty()) |
373 | | { |
374 | | OUString url; |
375 | | ::osl::FileBase::getFileURLFromSystemPath(env, url); |
376 | | SvFileStream aOutStream(url + "drawing_T.xml", StreamMode::WRITE|StreamMode::TRUNC); |
377 | | uno::Reference<io::XStream> xOutStream(new utl::OStreamWrapper(aOutStream)); |
378 | | uno::Reference<io::XStream> xInStream(xOutputStream, uno::UNO_QUERY); |
379 | | comphelper::OStorageHelper::CopyInputToOutput(xInStream->getInputStream(), xOutStream->getOutputStream()); |
380 | | } |
381 | | #endif |
382 | 0 | } |
383 | | |
384 | | void SmartArtDiagram::addDomTreeToModelData(svx::diagram::DomMapFlag aId, std::u16string_view aName, boost::property_tree::ptree& rTarget) const |
385 | 0 | { |
386 | 0 | uno::Reference<xml::dom::XDocument> aDomTree; |
387 | 0 | getOOXDomValue(aId) >>= aDomTree; |
388 | |
|
389 | 0 | if (aDomTree) |
390 | 0 | { |
391 | | // serialize DomTree to a MemoryStream |
392 | 0 | SvMemoryStream aStream( 1024, 1024 ); |
393 | 0 | rtl::Reference<utl::OStreamWrapper> pStreamWrapper = new utl::OStreamWrapper( aStream ); |
394 | 0 | uno::Reference<xml::sax::XSAXSerializable> serializer; |
395 | 0 | uno::Reference<xml::sax::XWriter> writer = xml::sax::Writer::create(comphelper::getProcessComponentContext()); |
396 | 0 | serializer.set(aDomTree, uno::UNO_QUERY); |
397 | 0 | writer->setOutputStream(pStreamWrapper->getOutputStream()); |
398 | 0 | serializer->serialize(uno::Reference<xml::sax::XDocumentHandler>(writer, uno::UNO_QUERY_THROW), uno::Sequence<beans::StringPair>()); |
399 | | |
400 | | // put into string |
401 | 0 | const OUString aContent(static_cast<const char*>(aStream.GetData()), aStream.TellEnd(), RTL_TEXTENCODING_UTF8); |
402 | | |
403 | | // add to ModelData |
404 | 0 | const OString sUtf8(OUStringToOString(aName, RTL_TEXTENCODING_UTF8)); |
405 | 0 | rTarget.put(sUtf8.getStr(), aContent); |
406 | 0 | } |
407 | 0 | } |
408 | | |
409 | | void SmartArtDiagram::addDiagramModelData(boost::property_tree::ptree& rTarget) const |
410 | 0 | { |
411 | | // add Point and Connection data |
412 | 0 | getData()->addDiagramModelData(rTarget); |
413 | | |
414 | | // What DomMaps are needed? |
415 | | // |
416 | | // With the above OOXData is covered. OOXDataImageRels/OOXDataHlinkRels also, |
417 | | // these may/will be re-created when OOX export and a new OOXDataDomTree |
418 | | // needs to be created. |
419 | | // Similar with OOXDrawing: This contains parts of ModelData, e.g. Text and |
420 | | // Attributes represented by the XShapes/Sdrobjects, so for internal formats |
421 | | // this is not needed to be saved. This also true for OOXDrawingImageRels |
422 | | // and OOXDrawingHlinkRels. |
423 | | // We *do* import OOXLayoutDomTree/ModelInfo and this is used in the layouting |
424 | | // mechanism (reLayout), but it is not changed. We could add an export of that |
425 | | // for internal formats, but since it's not changed it just needs to be |
426 | | // preserved, either for internal use or export to OOX formats. |
427 | | // OOXStyle and OOXColor are imported only on oox import side, partially held |
428 | | // for initial import at Diagram classes. Also never changed, but maybe needed |
429 | | // for export to OOX formats. Not sure about that since Style and Color is |
430 | | // part of XShape/SdrObject Model Hierarchy, so exports to OOX should be possible |
431 | | // without these, but maybe MSO wants that data... |
432 | | // |
433 | | // OOXLayout = 3, |
434 | | // OOXStyle = 4, |
435 | | // OOXColor = 5, |
436 | 0 | addDomTreeToModelData(svx::diagram::DomMapFlag::OOXLayout, u"OOXLayout", rTarget); |
437 | 0 | addDomTreeToModelData(svx::diagram::DomMapFlag::OOXStyle, u"OOXStyle", rTarget); |
438 | 0 | addDomTreeToModelData(svx::diagram::DomMapFlag::OOXColor, u"OOXColor", rTarget); |
439 | 0 | } |
440 | | |
441 | | using ShapePairs |
442 | | = std::map<std::shared_ptr<drawingml::Shape>, uno::Reference<drawing::XShape>>; |
443 | | |
444 | | void SmartArtDiagram::syncDiagramFontHeights() |
445 | 107 | { |
446 | | // Each name represents a group of shapes, for which the font height should have the same |
447 | | // scaling. |
448 | 107 | for (const auto& rNameAndPairs : maDiagramFontHeights) |
449 | 7 | { |
450 | | // Find out the minimum scale within this group. |
451 | 7 | const ShapePairs& rShapePairs = rNameAndPairs.second; |
452 | 7 | double fMinFontScale = 100.0; |
453 | 7 | double fMinSpacingScale = 100.0; |
454 | 7 | for (const auto& rShapePair : rShapePairs) |
455 | 0 | { |
456 | 0 | uno::Reference<beans::XPropertySet> xPropertySet(rShapePair.second, uno::UNO_QUERY); |
457 | 0 | if (xPropertySet.is()) |
458 | 0 | { |
459 | 0 | double fFontScale = 0.0; |
460 | 0 | double fSpacingScale = 0.0; |
461 | 0 | xPropertySet->getPropertyValue(u"TextFitToSizeFontScale"_ustr) >>= fFontScale; |
462 | 0 | xPropertySet->getPropertyValue(u"TextFitToSizeSpacingScale"_ustr) >>= fSpacingScale; |
463 | |
|
464 | 0 | if (fFontScale > 0 && fSpacingScale > 0 |
465 | 0 | && (fFontScale < fMinFontScale || (fFontScale == fMinFontScale && fSpacingScale < fMinSpacingScale))) |
466 | 0 | { |
467 | 0 | fMinFontScale = fFontScale; |
468 | 0 | fMinSpacingScale = fSpacingScale; |
469 | 0 | } |
470 | 0 | } |
471 | 0 | } |
472 | | |
473 | | // Set that minimum scale for all members of the group. |
474 | 7 | if (fMinFontScale < 100.0 || fMinSpacingScale < 100.0) |
475 | 0 | { |
476 | 0 | for (const auto& rShapePair : rShapePairs) |
477 | 0 | { |
478 | 0 | uno::Reference<beans::XPropertySet> xPropertySet(rShapePair.second, uno::UNO_QUERY); |
479 | 0 | if (xPropertySet.is()) |
480 | 0 | { |
481 | 0 | xPropertySet->setPropertyValue(u"TextFitToSizeFontScale"_ustr, uno::Any(fMinFontScale)); |
482 | 0 | xPropertySet->setPropertyValue(u"TextFitToSizeSpacingScale"_ustr, uno::Any(fMinSpacingScale)); |
483 | 0 | } |
484 | 0 | } |
485 | 0 | } |
486 | 7 | } |
487 | | |
488 | | // no longer needed after processing |
489 | 107 | maDiagramFontHeights.clear(); |
490 | 107 | } |
491 | | |
492 | | static uno::Reference<xml::dom::XDocument> loadFragment( |
493 | | core::XmlFilterBase& rFilter, |
494 | | const OUString& rFragmentPath ) |
495 | 1.05k | { |
496 | | // load diagramming fragments into DOM representation, that later |
497 | | // gets serialized back to SAX events and parsed |
498 | 1.05k | return rFilter.importFragment( rFragmentPath ); |
499 | 1.05k | } |
500 | | |
501 | | static uno::Reference<xml::dom::XDocument> loadFragment( |
502 | | core::XmlFilterBase& rFilter, |
503 | | const rtl::Reference< core::FragmentHandler >& rxHandler ) |
504 | 1.05k | { |
505 | 1.05k | return loadFragment( rFilter, rxHandler->getFragmentPath() ); |
506 | 1.05k | } |
507 | | |
508 | | static void importFragment( core::XmlFilterBase& rFilter, |
509 | | const uno::Reference<xml::dom::XDocument>& rXDom, |
510 | | svx::diagram::DomMapFlag aDomMapFlag, |
511 | | const DiagramPtr& pDiagram, |
512 | | const rtl::Reference< core::FragmentHandler >& rxHandler ) |
513 | 1.05k | { |
514 | 1.05k | pDiagram->setOOXDomValue(aDomMapFlag, uno::Any(rXDom)); |
515 | | |
516 | 1.05k | uno::Reference<xml::sax::XFastSAXSerializable> xSerializer( |
517 | 1.05k | rXDom, uno::UNO_QUERY_THROW); |
518 | | |
519 | | // now serialize DOM tree into internal data structures |
520 | 1.05k | rFilter.importFragment( rxHandler, xSerializer ); |
521 | 1.05k | } |
522 | | |
523 | | namespace |
524 | | { |
525 | | /** |
526 | | * A fragment handler that just counts the number of <dsp:sp> elements in a |
527 | | * fragment. |
528 | | */ |
529 | | class DiagramShapeCounter : public oox::core::FragmentHandler2 |
530 | | { |
531 | | public: |
532 | | DiagramShapeCounter(oox::core::XmlFilterBase& rFilter, const OUString& rFragmentPath, |
533 | | sal_Int32& nCounter); |
534 | | oox::core::ContextHandlerRef onCreateContext(sal_Int32 nElement, |
535 | | const AttributeList& rAttribs) override; |
536 | | |
537 | | private: |
538 | | sal_Int32& m_nCounter; |
539 | | }; |
540 | | |
541 | | DiagramShapeCounter::DiagramShapeCounter(oox::core::XmlFilterBase& rFilter, |
542 | | const OUString& rFragmentPath, sal_Int32& nCounter) |
543 | 286 | : FragmentHandler2(rFilter, rFragmentPath) |
544 | 286 | , m_nCounter(nCounter) |
545 | 286 | { |
546 | 286 | } |
547 | | |
548 | | oox::core::ContextHandlerRef DiagramShapeCounter::onCreateContext(sal_Int32 nElement, |
549 | | const AttributeList& /*rAttribs*/) |
550 | 1.13k | { |
551 | 1.13k | switch (nElement) |
552 | 1.13k | { |
553 | 131 | case DSP_TOKEN(drawing): |
554 | 262 | case DSP_TOKEN(spTree): |
555 | 262 | return this; |
556 | 643 | case DSP_TOKEN(sp): |
557 | 643 | ++m_nCounter; |
558 | 643 | break; |
559 | 232 | default: |
560 | 232 | break; |
561 | 1.13k | } |
562 | | |
563 | 875 | return nullptr; |
564 | 1.13k | } |
565 | | } |
566 | | |
567 | | void loadDiagram( ShapePtr const & pShape, |
568 | | core::XmlFilterBase& rFilter, |
569 | | const OUString& rDataModelPath, |
570 | | const OUString& rLayoutPath, |
571 | | const OUString& rQStylePath, |
572 | | const OUString& rColorStylePath, |
573 | | const oox::core::Relations& rRelations ) |
574 | 370 | { |
575 | 370 | DiagramPtr pDiagram = std::make_shared<SmartArtDiagram>(); |
576 | | |
577 | 370 | try |
578 | 370 | { |
579 | | // set DiagramFontHeights at filter |
580 | 370 | rFilter.setDiagramFontHeights(&pDiagram->getDiagramFontHeights()); |
581 | | |
582 | | // data |
583 | 370 | if( !rDataModelPath.isEmpty() ) |
584 | 366 | { |
585 | 366 | rtl::Reference< core::FragmentHandler > xRefDataModel( |
586 | 366 | new DiagramDataFragmentHandler( rFilter, rDataModelPath, pDiagram->getData() )); |
587 | | |
588 | 366 | importFragment(rFilter, |
589 | 366 | loadFragment(rFilter,xRefDataModel), |
590 | 366 | svx::diagram::DomMapFlag::OOXData, |
591 | 366 | pDiagram, |
592 | 366 | xRefDataModel); |
593 | | |
594 | 366 | uno::Sequence<uno::Sequence<uno::Any>> aDataImageRelsMap( |
595 | 366 | pShape->resolveRelationshipsOfTypeFromOfficeDoc( |
596 | 366 | rFilter, xRefDataModel->getFragmentPath(), u"image")); |
597 | 366 | uno::Sequence<uno::Sequence<uno::Any>> aDataHlinkRelsMap( |
598 | 366 | pShape->resolveRelationshipsOfTypeFromOfficeDoc( |
599 | 366 | rFilter, xRefDataModel->getFragmentPath(), u"hlink")); |
600 | | |
601 | 366 | pDiagram->setOOXDomValue(svx::diagram::DomMapFlag::OOXDataImageRels, |
602 | 366 | uno::Any(aDataImageRelsMap)); |
603 | 366 | pDiagram->setOOXDomValue(svx::diagram::DomMapFlag::OOXDataHlinkRels, |
604 | 366 | uno::Any(aDataHlinkRelsMap)); |
605 | | |
606 | | // Pass the info to pShape |
607 | 366 | for (auto const& extDrawing : pDiagram->getData()->getExtDrawings()) |
608 | 286 | { |
609 | 286 | OUString aFragmentPath = rRelations.getFragmentPathFromRelId(extDrawing); |
610 | | // Ignore RelIds which don't resolve to a fragment path. |
611 | 286 | if (aFragmentPath.isEmpty()) |
612 | 0 | continue; |
613 | | |
614 | 286 | sal_Int32 nCounter = 0; |
615 | 286 | rtl::Reference<core::FragmentHandler> xCounter( |
616 | 286 | new DiagramShapeCounter(rFilter, aFragmentPath, nCounter)); |
617 | 286 | rFilter.importFragment(xCounter); |
618 | | // Ignore ext drawings which don't actually have any shapes. |
619 | 286 | if (nCounter == 0) |
620 | 155 | continue; |
621 | | |
622 | 131 | pShape->addExtDrawingRelId(extDrawing); |
623 | 131 | } |
624 | 366 | } |
625 | | |
626 | | // Layout: always import to allow editing in the future. It's needed for |
627 | | // DiagramHelper_oox::reLayout to re-create the oox::Shape(s) for the |
628 | | // model. Without importing these the diagram model will be not complete. |
629 | | // NOTE: This also adds the DomMaps to rMainDomMap, so the lines |
630 | | // DiagramDomMap& rMainDomMap = pDiagram->getDomMap(); |
631 | | // rMainDomMap[u"OOXLayout"_ustr] = loadFragment(rFilter,rLayoutPath); |
632 | | // rMainDomMap[u"OOXStyle"_ustr] = loadFragment(rFilter,rQStylePath); |
633 | | // which were used before if !pShape->getExtDrawings().empty() are not |
634 | | // needed |
635 | 370 | if (!rLayoutPath.isEmpty()) |
636 | 313 | { |
637 | 313 | rtl::Reference< core::FragmentHandler > xRefLayout( |
638 | 313 | new DiagramLayoutFragmentHandler( *pDiagram, rFilter, rLayoutPath, pDiagram->getLayout())); |
639 | | |
640 | 313 | importFragment(rFilter, |
641 | 313 | loadFragment(rFilter,xRefLayout), |
642 | 313 | svx::diagram::DomMapFlag::OOXLayout, |
643 | 313 | pDiagram, |
644 | 313 | xRefLayout); |
645 | 313 | } |
646 | | |
647 | | // Style: same as for Layout (above) |
648 | 370 | if( !rQStylePath.isEmpty() ) |
649 | 236 | { |
650 | 236 | rtl::Reference< core::FragmentHandler > xRefQStyle( |
651 | 236 | new DiagramQStylesFragmentHandler( rFilter, rQStylePath, pDiagram->getStyles() )); |
652 | | |
653 | 236 | importFragment(rFilter, |
654 | 236 | loadFragment(rFilter,xRefQStyle), |
655 | 236 | svx::diagram::DomMapFlag::OOXStyle, |
656 | 236 | pDiagram, |
657 | 236 | xRefQStyle); |
658 | 236 | } |
659 | | |
660 | | // colors |
661 | 370 | if( !rColorStylePath.isEmpty() ) |
662 | 141 | { |
663 | 141 | rtl::Reference< core::FragmentHandler > xRefColorStyle( |
664 | 141 | new ColorFragmentHandler( rFilter, rColorStylePath, pDiagram->getColors() )); |
665 | | |
666 | 141 | importFragment(rFilter, |
667 | 141 | loadFragment(rFilter,xRefColorStyle), |
668 | 141 | svx::diagram::DomMapFlag::OOXColor, |
669 | 141 | pDiagram, |
670 | 141 | xRefColorStyle); |
671 | 141 | } |
672 | | |
673 | 370 | if( !pDiagram->getData()->getExtDrawings().empty() ) |
674 | 98 | { |
675 | 98 | const DiagramColorMap::const_iterator aColor = pDiagram->getColors().find(u"node0"_ustr); |
676 | 98 | if( aColor != pDiagram->getColors().end() && !aColor->second.maTextFillColors.empty()) |
677 | 0 | { |
678 | | // TODO(F1): well, actually, there might be *several* color |
679 | | // definitions in it, after all it's called list. |
680 | 0 | pShape->setFontRefColorForNodes(DiagramColor::getColorByIndex(aColor->second.maTextFillColors, -1)); |
681 | 0 | } |
682 | 98 | } |
683 | | |
684 | | // collect data, init maps |
685 | | // for Diagram import, do - for now - NOT clear all oox::drawingml::Shape |
686 | 370 | pDiagram->getData()->buildDiagramDataModel(false); |
687 | | #ifdef DBG_UTIL |
688 | | pDiagram->getData()->dump(); |
689 | | #endif |
690 | | |
691 | | // diagram loaded. now lump together & attach to shape |
692 | | // create own geometry if extLst is not present (no geometric |
693 | | // representation is available in file). This will - if false - |
694 | | // just create the BackgroundShape. |
695 | | // NOTE: Need to use pShape->getExtDrawings() here, this is the |
696 | | // already *filtered* version, see usage of DiagramShapeCounter |
697 | | // above. Moving to local bool, there might more conditions show |
698 | | // up |
699 | 370 | static bool bIgnoreExtDrawings(nullptr != std::getenv("DIAGRAM_IGNORE_EXTDRAWINGS")); |
700 | 370 | const bool bCreate(bIgnoreExtDrawings || pShape->getExtDrawings().empty()); |
701 | 370 | pDiagram->createShapeHierarchyFromModel(pShape, bCreate); |
702 | | |
703 | | // Get the oox::Theme definition and - if available - move/secure the |
704 | | // original ImportData directly to the Diagram ModelData |
705 | 370 | std::shared_ptr<::oox::drawingml::Theme> aTheme(rFilter.getCurrentThemePtr()); |
706 | 370 | if(aTheme) |
707 | 14 | pDiagram->getData()->setThemeDocument(aTheme->getFragment()); |
708 | | |
709 | | // Prepare support for the advanced DiagramHelper using Diagram & Theme data |
710 | | // This is where pDiagram is moved to where it will stay, else it will get |
711 | | // cleaned up (what is intended) |
712 | 370 | pShape->prepareDiagramHelper(pDiagram, rFilter.getCurrentThemePtr()); |
713 | 370 | } |
714 | 370 | catch (...) |
715 | 370 | { |
716 | | // unset DiagramFontHeights at filter if there was a failure |
717 | | // to avoid dangling pointer |
718 | 263 | rFilter.setDiagramFontHeights(nullptr); |
719 | 263 | throw; |
720 | 263 | } |
721 | 370 | } |
722 | | |
723 | | const oox::drawingml::Color& |
724 | | DiagramColor::getColorByIndex(const std::vector<oox::drawingml::Color>& rColors, sal_Int32 nIndex) |
725 | 488 | { |
726 | 488 | assert(!rColors.empty()); |
727 | 488 | if (nIndex == -1) |
728 | 0 | { |
729 | 0 | return rColors[rColors.size() - 1]; |
730 | 0 | } |
731 | | |
732 | 488 | return rColors[nIndex % rColors.size()]; |
733 | 488 | } |
734 | | } |
735 | | |
736 | | /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |