Version 5.4.3.2, tag libreoffice-5.4.3.2
[LibreOffice.git] / oox / source / drawingml / diagram / diagram.cxx
bloba72988627602ba4f14904a6c7e27eb1c66e1dc06
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 <functional>
22 #include <com/sun/star/awt/Point.hpp>
23 #include <com/sun/star/awt/Size.hpp>
24 #include <com/sun/star/xml/dom/XDocument.hpp>
25 #include <com/sun/star/xml/sax/XFastSAXSerializable.hpp>
26 #include <rtl/ustrbuf.hxx>
27 #include <osl/diagnose.h>
28 #include <editeng/unoprnms.hxx>
29 #include "drawingml/textbody.hxx"
30 #include "drawingml/textparagraph.hxx"
31 #include "drawingml/textrun.hxx"
32 #include "drawingml/diagram/diagram.hxx"
33 #include "drawingml/fillproperties.hxx"
34 #include "oox/ppt/pptshapegroupcontext.hxx"
35 #include "oox/ppt/pptshape.hxx"
37 #include "diagramlayoutatoms.hxx"
38 #include "diagramfragmenthandler.hxx"
40 #include <iostream>
41 #include <fstream>
43 using namespace ::com::sun::star;
45 namespace oox { namespace drawingml {
47 namespace dgm {
49 void Connection::dump()
51 SAL_INFO(
52 "oox.drawingml",
53 "cnx modelId " << msModelId << ", srcId " << msSourceId << ", dstId "
54 << msDestId << ", parTransId " << msParTransId << ", presId "
55 << msPresId << ", sibTransId " << msSibTransId << ", srcOrd "
56 << mnSourceOrder << ", dstOrd " << mnDestOrder);
59 void Point::dump()
61 SAL_INFO(
62 "oox.drawingml",
63 "pt text " << mpShape.get() << ", cnxId " << msCnxId << ", modelId "
64 << msModelId << ", type " << mnType);
67 } // dgm namespace
69 DiagramData::DiagramData()
70 : mpFillProperties( new FillProperties )
74 void DiagramData::dump()
76 SAL_INFO("oox.drawingml", "Dgm: DiagramData # of cnx: " << maConnections.size() );
77 std::for_each( maConnections.begin(), maConnections.end(),
78 [] (dgm::Connection & rConnection) { rConnection.dump(); } );
79 SAL_INFO("oox.drawingml", "Dgm: DiagramData # of pt: " << maPoints.size() );
80 std::for_each( maPoints.begin(), maPoints.end(),
81 [] (dgm::Point & rPoint) { rPoint.dump(); } );
84 void Diagram::setData( const DiagramDataPtr & pData)
86 mpData = pData;
89 void Diagram::setLayout( const DiagramLayoutPtr & pLayout)
91 mpLayout = pLayout;
94 #ifdef DEBUG_OOX_DIAGRAM
95 OString normalizeDotName( const OUString& rStr )
97 OUStringBuffer aBuf;
98 aBuf.append('N');
100 const sal_Int32 nLen(rStr.getLength());
101 sal_Int32 nCurrIndex(0);
102 while( nCurrIndex < nLen )
104 const sal_Int32 aChar=rStr.iterateCodePoints(&nCurrIndex);
105 if( aChar != '-' && aChar != '{' && aChar != '}' )
106 aBuf.append((sal_Unicode)aChar);
109 return OUStringToOString(aBuf.makeStringAndClear(),
110 RTL_TEXTENCODING_UTF8);
112 #endif
114 static sal_Int32 calcDepth( const OUString& rNodeName,
115 const dgm::Connections& rCnx )
117 // find length of longest path in 'isChild' graph, ending with rNodeName
118 dgm::Connections::const_iterator aCurrCxn( rCnx.begin() );
119 const dgm::Connections::const_iterator aEndCxn( rCnx.end() );
120 while( aCurrCxn != aEndCxn )
122 if( !aCurrCxn->msParTransId.isEmpty() &&
123 !aCurrCxn->msSibTransId.isEmpty() &&
124 !aCurrCxn->msSourceId.isEmpty() &&
125 !aCurrCxn->msDestId.isEmpty() &&
126 aCurrCxn->mnType != XML_presOf &&
127 aCurrCxn->mnType != XML_presParOf &&
128 rNodeName == aCurrCxn->msDestId )
130 return calcDepth(aCurrCxn->msSourceId,
131 rCnx) + 1;
133 ++aCurrCxn;
136 return 0;
139 void Diagram::build( )
141 // build name-object maps
142 #ifdef DEBUG_OOX_DIAGRAM
143 std::ofstream output("/tmp/tree.dot");
145 output << "digraph datatree {" << std::endl;
146 #endif
147 dgm::Points& rPoints = getData()->getPoints();
148 dgm::Points::iterator aCurrPoint(rPoints.begin());
149 dgm::Points::iterator aEndPoint(rPoints.end());
150 while( aCurrPoint != aEndPoint )
152 #ifdef DEBUG_OOX_DIAGRAM
153 output << "\t"
154 << normalizeDotName(aCurrPoint->msModelId).getStr()
155 << "[";
157 if( !aCurrPoint->msPresentationLayoutName.isEmpty() )
158 output << "label=\""
159 << OUStringToOString(
160 aCurrPoint->msPresentationLayoutName,
161 RTL_TEXTENCODING_UTF8).getStr() << "\", ";
162 else
163 output << "label=\""
164 << OUStringToOString(
165 aCurrPoint->msModelId,
166 RTL_TEXTENCODING_UTF8).getStr() << "\", ";
168 switch( aCurrPoint->mnType )
170 case XML_doc: output << "style=filled, color=red"; break;
171 case XML_asst: output << "style=filled, color=green"; break;
172 default:
173 case XML_node: output << "style=filled, color=blue"; break;
174 case XML_pres: output << "style=filled, color=yellow"; break;
175 case XML_parTrans: output << "color=grey"; break;
176 case XML_sibTrans: output << " "; break;
179 output << "];" << std::endl;
180 #endif
182 // does currpoint have any text set?
183 if( aCurrPoint->mpShape &&
184 aCurrPoint->mpShape->getTextBody() &&
185 !aCurrPoint->mpShape->getTextBody()->getParagraphs().empty() &&
186 !aCurrPoint->mpShape->getTextBody()->getParagraphs().front()->getRuns().empty() )
188 #ifdef DEBUG_OOX_DIAGRAM
189 static sal_Int32 nCount=0;
190 output << "\t"
191 << "textNode" << nCount
192 << " ["
193 << "label=\""
194 << OUStringToOString(
195 aCurrPoint->mpShape->getTextBody()->getParagraphs().front()->getRuns().front()->getText(),
196 RTL_TEXTENCODING_UTF8).getStr()
197 << "\"" << "];" << std::endl;
198 output << "\t"
199 << normalizeDotName(aCurrPoint->msModelId).getStr()
200 << " -> "
201 << "textNode" << nCount++
202 << ";" << std::endl;
203 #endif
206 const bool bInserted1=getData()->getPointNameMap().insert(
207 std::make_pair(aCurrPoint->msModelId,&(*aCurrPoint))).second;
208 (void)bInserted1;
210 OSL_ENSURE(bInserted1,"Diagram::build(): non-unique point model id");
212 if( !aCurrPoint->msPresentationLayoutName.isEmpty() )
214 DiagramData::PointsNameMap::value_type::second_type& rVec=
215 getData()->getPointsPresNameMap()[aCurrPoint->msPresentationLayoutName];
216 rVec.push_back(&(*aCurrPoint));
218 ++aCurrPoint;
221 const dgm::Connections& rConnections = getData()->getConnections();
222 dgm::Connections::const_iterator aCurrCxn(rConnections.begin());
223 const dgm::Connections::const_iterator aEndCxn(rConnections.end());
224 while( aCurrCxn != aEndCxn )
226 #ifdef DEBUG_OOX_DIAGRAM
227 if( !aCurrCxn->msParTransId.isEmpty() ||
228 !aCurrCxn->msSibTransId.isEmpty() )
230 if( !aCurrCxn->msSourceId.isEmpty() ||
231 !aCurrCxn->msDestId.isEmpty() )
233 output << "\t"
234 << normalizeDotName(aCurrCxn->msSourceId).getStr()
235 << " -> "
236 << normalizeDotName(aCurrCxn->msParTransId).getStr()
237 << " -> "
238 << normalizeDotName(aCurrCxn->msSibTransId).getStr()
239 << " -> "
240 << normalizeDotName(aCurrCxn->msDestId).getStr()
241 << " [style=dotted,"
242 << ((aCurrCxn->mnType == XML_presOf) ? " color=red, " : ((aCurrCxn->mnType == XML_presParOf) ? " color=green, " : " "))
243 << "label=\""
244 << OUStringToOString(aCurrCxn->msModelId,
245 RTL_TEXTENCODING_UTF8 ).getStr()
246 << "\"];" << std::endl;
248 else
250 output << "\t"
251 << normalizeDotName(aCurrCxn->msParTransId).getStr()
252 << " -> "
253 << normalizeDotName(aCurrCxn->msSibTransId).getStr()
254 << " ["
255 << ((aCurrCxn->mnType == XML_presOf) ? " color=red, " : ((aCurrCxn->mnType == XML_presParOf) ? " color=green, " : " "))
256 << "label=\""
257 << OUStringToOString(aCurrCxn->msModelId,
258 RTL_TEXTENCODING_UTF8 ).getStr()
259 << "\"];" << std::endl;
262 else if( !aCurrCxn->msSourceId.isEmpty() ||
263 !aCurrCxn->msDestId.isEmpty() )
264 output << "\t"
265 << normalizeDotName(aCurrCxn->msSourceId).getStr()
266 << " -> "
267 << normalizeDotName(aCurrCxn->msDestId).getStr()
268 << " [label=\""
269 << OUStringToOString(aCurrCxn->msModelId,
270 RTL_TEXTENCODING_UTF8 ).getStr()
271 << ((aCurrCxn->mnType == XML_presOf) ? "\", color=red]" : ((aCurrCxn->mnType == XML_presParOf) ? "\", color=green]" : "\"]"))
272 << ";" << std::endl;
273 #endif
275 const bool bInserted1=getData()->getConnectionNameMap().insert(
276 std::make_pair(aCurrCxn->msModelId,&(*aCurrCxn))).second;
277 (void)bInserted1;
279 OSL_ENSURE(bInserted1,"Diagram::build(): non-unique connection model id");
281 if( aCurrCxn->mnType == XML_presOf )
283 DiagramData::StringMap::value_type::second_type& rVec=getData()->getPresOfNameMap()[aCurrCxn->msDestId];
284 rVec.push_back(
285 std::make_pair(
286 aCurrCxn->msSourceId,sal_Int32(0)));
289 ++aCurrCxn;
292 // assign outline levels
293 DiagramData::StringMap& rStringMap = getData()->getPresOfNameMap();
294 DiagramData::StringMap::iterator aPresOfIter=rStringMap.begin();
295 const DiagramData::StringMap::iterator aPresOfEnd=rStringMap.end();
296 while( aPresOfIter != aPresOfEnd )
298 DiagramData::StringMap::value_type::second_type::iterator aPresOfNodeIterCalcLevel=aPresOfIter->second.begin();
299 const DiagramData::StringMap::value_type::second_type::iterator aPresOfNodeEnd=aPresOfIter->second.end();
300 while(aPresOfNodeIterCalcLevel != aPresOfNodeEnd)
302 const sal_Int32 nDepth=calcDepth(aPresOfNodeIterCalcLevel->first,
303 getData()->getConnections());
304 aPresOfNodeIterCalcLevel->second = nDepth != 0 ? nDepth : -1;
305 ++aPresOfNodeIterCalcLevel;
308 ++aPresOfIter;
310 #ifdef DEBUG_OOX_DIAGRAM
311 output << "}" << std::endl;
312 #endif
315 void Diagram::addTo( const ShapePtr & pParentShape )
317 // collect data, init maps
318 build( );
320 // create Shape hierarchy
321 ShapeCreationVisitor aCreationVisitor(pParentShape, *this);
322 if( mpLayout->getNode() )
323 mpLayout->getNode()->accept( aCreationVisitor );
325 pParentShape->setDiagramDoms( getDomsAsPropertyValues() );
328 uno::Sequence<beans::PropertyValue> Diagram::getDomsAsPropertyValues() const
330 sal_Int32 length = maMainDomMap.size();
332 if ( 0 < maDataRelsMap.getLength() )
333 ++length;
335 uno::Sequence<beans::PropertyValue> aValue(length);
336 beans::PropertyValue* pValue = aValue.getArray();
337 for (DiagramDomMap::const_iterator i = maMainDomMap.begin();
338 i != maMainDomMap.end();
339 ++i)
341 pValue[0].Name = i->first;
342 pValue[0].Value <<= i->second;
343 ++pValue;
346 if ( 0 < maDataRelsMap.getLength() )
348 pValue[0].Name = "OOXDiagramDataRels";
349 pValue[0].Value <<= maDataRelsMap;
350 ++pValue;
353 return aValue;
356 uno::Reference<xml::dom::XDocument> loadFragment(
357 core::XmlFilterBase& rFilter,
358 const OUString& rFragmentPath )
360 // load diagramming fragments into DOM representation, that later
361 // gets serialized back to SAX events and parsed
362 return rFilter.importFragment( rFragmentPath );
365 uno::Reference<xml::dom::XDocument> loadFragment(
366 core::XmlFilterBase& rFilter,
367 const rtl::Reference< core::FragmentHandler >& rxHandler )
369 return loadFragment( rFilter, rxHandler->getFragmentPath() );
372 void importFragment( core::XmlFilterBase& rFilter,
373 const uno::Reference<xml::dom::XDocument>& rXDom,
374 const char* pDocName,
375 const DiagramPtr& pDiagram,
376 const rtl::Reference< core::FragmentHandler >& rxHandler )
378 DiagramDomMap& rMainDomMap = pDiagram->getDomMap();
379 rMainDomMap[OUString::createFromAscii(pDocName)] = rXDom;
381 uno::Reference<xml::sax::XFastSAXSerializable> xSerializer(
382 rXDom, uno::UNO_QUERY_THROW);
384 // now serialize DOM tree into internal data structures
385 rFilter.importFragment( rxHandler, xSerializer );
388 void loadDiagram( ShapePtr& pShape,
389 core::XmlFilterBase& rFilter,
390 const OUString& rDataModelPath,
391 const OUString& rLayoutPath,
392 const OUString& rQStylePath,
393 const OUString& rColorStylePath )
395 DiagramPtr pDiagram( new Diagram );
397 DiagramDataPtr pData( new DiagramData() );
398 pDiagram->setData( pData );
400 DiagramLayoutPtr pLayout( new DiagramLayout );
401 pDiagram->setLayout( pLayout );
403 // data
404 if( !rDataModelPath.isEmpty() )
406 rtl::Reference< core::FragmentHandler > xRefDataModel(
407 new DiagramDataFragmentHandler( rFilter, rDataModelPath, pData ));
409 importFragment(rFilter,
410 loadFragment(rFilter,xRefDataModel),
411 "OOXData",
412 pDiagram,
413 xRefDataModel);
415 pDiagram->getDataRelsMap() = pShape->resolveRelationshipsOfTypeFromOfficeDoc( rFilter,
416 xRefDataModel->getFragmentPath(), "image" );
418 // Pass the info to pShape
419 for( ::std::vector<OUString>::const_iterator aIt = pData->getExtDrawings().begin(), aEnd = pData->getExtDrawings().end();
420 aIt != aEnd; ++aIt )
421 pShape->addExtDrawingRelId( *aIt );
424 // extLst is present, lets bet on that and ignore the rest of the data from here
425 if( pData->getExtDrawings().empty() )
427 // layout
428 if( !rLayoutPath.isEmpty() )
430 rtl::Reference< core::FragmentHandler > xRefLayout(
431 new DiagramLayoutFragmentHandler( rFilter, rLayoutPath, pLayout ));
433 importFragment(rFilter,
434 loadFragment(rFilter,xRefLayout),
435 "OOXLayout",
436 pDiagram,
437 xRefLayout);
440 // style
441 if( !rQStylePath.isEmpty() )
443 rtl::Reference< core::FragmentHandler > xRefQStyle(
444 new DiagramQStylesFragmentHandler( rFilter, rQStylePath, pDiagram->getStyles() ));
446 importFragment(rFilter,
447 loadFragment(rFilter,xRefQStyle),
448 "OOXStyle",
449 pDiagram,
450 xRefQStyle);
452 } else {
453 // We still want to add the XDocuments to the DiagramDomMap
454 DiagramDomMap& rMainDomMap = pDiagram->getDomMap();
455 rMainDomMap[OUString("OOXLayout")] = loadFragment(rFilter,rLayoutPath);
456 rMainDomMap[OUString("OOXStyle")] = loadFragment(rFilter,rQStylePath);
459 // colors
460 if( !rColorStylePath.isEmpty() )
462 rtl::Reference< core::FragmentHandler > xRefColorStyle(
463 new ColorFragmentHandler( rFilter, rColorStylePath, pDiagram->getColors() ));
465 importFragment(rFilter,
466 loadFragment(rFilter,xRefColorStyle),
467 "OOXColor",
468 pDiagram,
469 xRefColorStyle);
472 if( !pData->getExtDrawings().empty() )
474 const DiagramColorMap::const_iterator aColor = pDiagram->getColors().find("node0");
475 if( aColor != pDiagram->getColors().end() )
477 pShape->setFontRefColorForNodes(aColor->second.maTextFillColor);
481 // diagram loaded. now lump together & attach to shape
482 pDiagram->addTo(pShape);
485 void loadDiagram( const ShapePtr& pShape,
486 core::XmlFilterBase& rFilter,
487 const uno::Reference<xml::dom::XDocument>& rXDataModelDom,
488 const uno::Reference<xml::dom::XDocument>& rXLayoutDom,
489 const uno::Reference<xml::dom::XDocument>& rXQStyleDom,
490 const uno::Reference<xml::dom::XDocument>& rXColorStyleDom )
492 DiagramPtr pDiagram( new Diagram );
494 DiagramDataPtr pData( new DiagramData() );
495 pDiagram->setData( pData );
497 DiagramLayoutPtr pLayout( new DiagramLayout );
498 pDiagram->setLayout( pLayout );
500 // data
501 if( rXDataModelDom.is() )
502 importFragment(rFilter,
503 rXDataModelDom,
504 "OOXData",
505 pDiagram,
506 new DiagramDataFragmentHandler( rFilter, "", pData ));
508 // layout
509 if( rXLayoutDom.is() )
510 importFragment(rFilter,
511 rXLayoutDom,
512 "OOXLayout",
513 pDiagram,
514 new DiagramLayoutFragmentHandler( rFilter, "", pLayout ));
516 // style
517 if( rXQStyleDom.is() )
518 importFragment(rFilter,
519 rXQStyleDom,
520 "OOXStyle",
521 pDiagram,
522 new DiagramQStylesFragmentHandler( rFilter, "", pDiagram->getStyles() ));
524 // colors
525 if( rXColorStyleDom.is() )
526 importFragment(rFilter,
527 rXColorStyleDom,
528 "OOXColor",
529 pDiagram,
530 new ColorFragmentHandler( rFilter, "", pDiagram->getColors() ));
532 // diagram loaded. now lump together & attach to shape
533 pDiagram->addTo(pShape);
538 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */