Line data Source code
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 <functional>
21 : #include <boost/bind.hpp>
22 :
23 : #include <com/sun/star/awt/Point.hpp>
24 : #include <com/sun/star/awt/Size.hpp>
25 : #include <com/sun/star/xml/dom/XDocument.hpp>
26 : #include <com/sun/star/xml/sax/XFastSAXSerializable.hpp>
27 : #include <rtl/ustrbuf.hxx>
28 : #include <osl/diagnose.h>
29 : #include <editeng/unoprnms.hxx>
30 : #include "drawingml/textbody.hxx"
31 : #include "drawingml/textparagraph.hxx"
32 : #include "drawingml/textrun.hxx"
33 : #include "drawingml/diagram/diagram.hxx"
34 : #include "oox/drawingml/fillproperties.hxx"
35 : #include "oox/ppt/pptshapegroupcontext.hxx"
36 : #include "oox/ppt/pptshape.hxx"
37 :
38 : #include "diagramlayoutatoms.hxx"
39 : #include "diagramfragmenthandler.hxx"
40 :
41 : #include <iostream>
42 : #include <fstream>
43 :
44 : using namespace ::com::sun::star;
45 :
46 : namespace oox { namespace drawingml {
47 :
48 : namespace dgm {
49 :
50 1687 : void Connection::dump()
51 : {
52 : SAL_INFO(
53 : "oox.drawingml",
54 : "cnx modelId " << msModelId << ", srcId " << msSourceId << ", dstId "
55 : << msDestId << ", parTransId " << msParTransId << ", presId "
56 : << msPresId << ", sibTransId " << msSibTransId << ", srcOrd "
57 : << mnSourceOrder << ", dstOrd " << mnDestOrder);
58 1687 : }
59 :
60 1837 : void Point::dump()
61 : {
62 : SAL_INFO(
63 : "oox.drawingml",
64 : "pt text " << mpShape.get() << ", cnxId " << msCnxId << ", modelId "
65 : << msModelId << ", type " << mnType);
66 1837 : }
67 :
68 : } // dgm namespace
69 :
70 67 : DiagramData::DiagramData()
71 67 : : mpFillProperties( new FillProperties )
72 : {
73 67 : }
74 :
75 67 : void DiagramData::dump()
76 : {
77 : OSL_TRACE("Dgm: DiagramData # of cnx: %zu", maConnections.size() );
78 : std::for_each( maConnections.begin(), maConnections.end(),
79 67 : boost::bind( &dgm::Connection::dump, _1 ) );
80 : OSL_TRACE("Dgm: DiagramData # of pt: %zu", maPoints.size() );
81 : std::for_each( maPoints.begin(), maPoints.end(),
82 67 : boost::bind( &dgm::Point::dump, _1 ) );
83 67 : }
84 :
85 47 : void Diagram::setData( const DiagramDataPtr & pData)
86 : {
87 47 : mpData = pData;
88 47 : }
89 :
90 47 : void Diagram::setLayout( const DiagramLayoutPtr & pLayout)
91 : {
92 47 : mpLayout = pLayout;
93 47 : }
94 :
95 : #if OSL_DEBUG_LEVEL > 1
96 : OString normalizeDotName( const OUString& rStr )
97 : {
98 : OUStringBuffer aBuf;
99 : aBuf.append('N');
100 :
101 : const sal_Int32 nLen(rStr.getLength());
102 : sal_Int32 nCurrIndex(0);
103 : while( nCurrIndex < nLen )
104 : {
105 : const sal_Int32 aChar=rStr.iterateCodePoints(&nCurrIndex);
106 : if( aChar != '-' && aChar != '{' && aChar != '}' )
107 : aBuf.append((sal_Unicode)aChar);
108 : }
109 :
110 : return OUStringToOString(aBuf.makeStringAndClear(),
111 : RTL_TEXTENCODING_UTF8);
112 : }
113 : #endif
114 :
115 994 : static sal_Int32 calcDepth( const OUString& rNodeName,
116 : const dgm::Connections& rCnx )
117 : {
118 : // find length of longest path in 'isChild' graph, ending with rNodeName
119 994 : dgm::Connections::const_iterator aCurrCxn( rCnx.begin() );
120 994 : const dgm::Connections::const_iterator aEndCxn( rCnx.end() );
121 144297 : while( aCurrCxn != aEndCxn )
122 : {
123 304413 : if( !aCurrCxn->msParTransId.isEmpty() &&
124 37370 : !aCurrCxn->msSibTransId.isEmpty() &&
125 37370 : !aCurrCxn->msSourceId.isEmpty() &&
126 37370 : !aCurrCxn->msDestId.isEmpty() &&
127 37370 : aCurrCxn->mnType != XML_presOf &&
128 180234 : aCurrCxn->mnType != XML_presParOf &&
129 18685 : rNodeName == aCurrCxn->msDestId )
130 : {
131 555 : return calcDepth(aCurrCxn->msSourceId,
132 555 : rCnx) + 1;
133 : }
134 142309 : ++aCurrCxn;
135 : }
136 :
137 439 : return 0;
138 : }
139 :
140 47 : void Diagram::build( )
141 : {
142 : // build name-object maps
143 :
144 : #if OSL_DEBUG_LEVEL > 1
145 : std::ofstream output("/tmp/tree.dot");
146 :
147 : output << "digraph datatree {" << std::endl;
148 : #endif
149 :
150 47 : dgm::Points& rPoints = getData()->getPoints();
151 47 : dgm::Points::iterator aCurrPoint(rPoints.begin());
152 47 : dgm::Points::iterator aEndPoint(rPoints.end());
153 1859 : while( aCurrPoint != aEndPoint )
154 : {
155 : #if OSL_DEBUG_LEVEL > 1
156 : output << "\t"
157 : << normalizeDotName(aCurrPoint->msModelId).getStr()
158 : << "[";
159 :
160 : if( !aCurrPoint->msPresentationLayoutName.isEmpty() )
161 : output << "label=\""
162 : << OUStringToOString(
163 : aCurrPoint->msPresentationLayoutName,
164 : RTL_TEXTENCODING_UTF8).getStr() << "\", ";
165 : else
166 : output << "label=\""
167 : << OUStringToOString(
168 : aCurrPoint->msModelId,
169 : RTL_TEXTENCODING_UTF8).getStr() << "\", ";
170 :
171 : switch( aCurrPoint->mnType )
172 : {
173 : case XML_doc: output << "style=filled, color=red"; break;
174 : case XML_asst: output << "style=filled, color=green"; break;
175 : default:
176 : case XML_node: output << "style=filled, color=blue"; break;
177 : case XML_pres: output << "style=filled, color=yellow"; break;
178 : case XML_parTrans: output << "color=grey"; break;
179 : case XML_sibTrans: output << " "; break;
180 : }
181 :
182 : output << "];" << std::endl;
183 :
184 : // does currpoint have any text set?
185 : if( aCurrPoint->mpShape &&
186 : aCurrPoint->mpShape->getTextBody() &&
187 : !aCurrPoint->mpShape->getTextBody()->getParagraphs().empty() &&
188 : !aCurrPoint->mpShape->getTextBody()->getParagraphs().front()->getRuns().empty() )
189 : {
190 : static sal_Int32 nCount=0;
191 :
192 : output << "\t"
193 : << "textNode" << nCount
194 : << " ["
195 : << "label=\""
196 : << OUStringToOString(
197 : aCurrPoint->mpShape->getTextBody()->getParagraphs().front()->getRuns().front()->getText(),
198 : RTL_TEXTENCODING_UTF8).getStr()
199 : << "\"" << "];" << std::endl;
200 : output << "\t"
201 : << normalizeDotName(aCurrPoint->msModelId).getStr()
202 : << " -> "
203 : << "textNode" << nCount++
204 : << ";" << std::endl;
205 : }
206 : #endif
207 :
208 3530 : const bool bInserted1=getData()->getPointNameMap().insert(
209 5295 : std::make_pair(aCurrPoint->msModelId,&(*aCurrPoint))).second;
210 : (void)bInserted1;
211 :
212 : OSL_ENSURE(bInserted1,"Diagram::build(): non-unique point model id");
213 :
214 1765 : if( !aCurrPoint->msPresentationLayoutName.isEmpty() )
215 : {
216 : DiagramData::PointsNameMap::value_type::second_type& rVec=
217 1001 : getData()->getPointsPresNameMap()[aCurrPoint->msPresentationLayoutName];
218 1001 : rVec.push_back(&(*aCurrPoint));
219 : }
220 1765 : ++aCurrPoint;
221 : }
222 :
223 47 : const dgm::Connections& rConnections = getData()->getConnections();
224 47 : dgm::Connections::const_iterator aCurrCxn(rConnections.begin());
225 47 : const dgm::Connections::const_iterator aEndCxn(rConnections.end());
226 1726 : while( aCurrCxn != aEndCxn )
227 : {
228 : #if OSL_DEBUG_LEVEL > 1
229 : if( !aCurrCxn->msParTransId.isEmpty() ||
230 : !aCurrCxn->msSibTransId.isEmpty() )
231 : {
232 : if( !aCurrCxn->msSourceId.isEmpty() ||
233 : !aCurrCxn->msDestId.isEmpty() )
234 : {
235 : output << "\t"
236 : << normalizeDotName(aCurrCxn->msSourceId).getStr()
237 : << " -> "
238 : << normalizeDotName(aCurrCxn->msParTransId).getStr()
239 : << " -> "
240 : << normalizeDotName(aCurrCxn->msSibTransId).getStr()
241 : << " -> "
242 : << normalizeDotName(aCurrCxn->msDestId).getStr()
243 : << " [style=dotted,"
244 : << ((aCurrCxn->mnType == XML_presOf) ? " color=red, " : ((aCurrCxn->mnType == XML_presParOf) ? " color=green, " : " "))
245 : << "label=\""
246 : << OUStringToOString(aCurrCxn->msModelId,
247 : RTL_TEXTENCODING_UTF8 ).getStr()
248 : << "\"];" << std::endl;
249 : }
250 : else
251 : {
252 : output << "\t"
253 : << normalizeDotName(aCurrCxn->msParTransId).getStr()
254 : << " -> "
255 : << normalizeDotName(aCurrCxn->msSibTransId).getStr()
256 : << " ["
257 : << ((aCurrCxn->mnType == XML_presOf) ? " color=red, " : ((aCurrCxn->mnType == XML_presParOf) ? " color=green, " : " "))
258 : << "label=\""
259 : << OUStringToOString(aCurrCxn->msModelId,
260 : RTL_TEXTENCODING_UTF8 ).getStr()
261 : << "\"];" << std::endl;
262 : }
263 : }
264 : else if( !aCurrCxn->msSourceId.isEmpty() ||
265 : !aCurrCxn->msDestId.isEmpty() )
266 : output << "\t"
267 : << normalizeDotName(aCurrCxn->msSourceId).getStr()
268 : << " -> "
269 : << normalizeDotName(aCurrCxn->msDestId).getStr()
270 : << " [label=\""
271 : << OUStringToOString(aCurrCxn->msModelId,
272 : RTL_TEXTENCODING_UTF8 ).getStr()
273 : << ((aCurrCxn->mnType == XML_presOf) ? "\", color=red]" : ((aCurrCxn->mnType == XML_presParOf) ? "\", color=green]" : "\"]"))
274 : << ";" << std::endl;
275 : #endif
276 :
277 3264 : const bool bInserted1=getData()->getConnectionNameMap().insert(
278 4896 : std::make_pair(aCurrCxn->msModelId,&(*aCurrCxn))).second;
279 : (void)bInserted1;
280 :
281 : OSL_ENSURE(bInserted1,"Diagram::build(): non-unique connection model id");
282 :
283 1632 : if( aCurrCxn->mnType == XML_presOf )
284 : {
285 439 : DiagramData::StringMap::value_type::second_type& rVec=getData()->getPresOfNameMap()[aCurrCxn->msDestId];
286 : rVec.push_back(
287 : std::make_pair(
288 439 : aCurrCxn->msSourceId,sal_Int32(0)));
289 : }
290 :
291 1632 : ++aCurrCxn;
292 : }
293 :
294 : // assign outline levels
295 47 : DiagramData::StringMap& rStringMap = getData()->getPresOfNameMap();
296 47 : DiagramData::StringMap::iterator aPresOfIter=rStringMap.begin();
297 47 : const DiagramData::StringMap::iterator aPresOfEnd=rStringMap.end();
298 522 : while( aPresOfIter != aPresOfEnd )
299 : {
300 428 : DiagramData::StringMap::value_type::second_type::iterator aPresOfNodeIterCalcLevel=aPresOfIter->second.begin();
301 428 : const DiagramData::StringMap::value_type::second_type::iterator aPresOfNodeEnd=aPresOfIter->second.end();
302 1295 : while(aPresOfNodeIterCalcLevel != aPresOfNodeEnd)
303 : {
304 439 : const sal_Int32 nDepth=calcDepth(aPresOfNodeIterCalcLevel->first,
305 878 : getData()->getConnections());
306 439 : aPresOfNodeIterCalcLevel->second = nDepth != 0 ? nDepth : -1;
307 439 : ++aPresOfNodeIterCalcLevel;
308 : }
309 :
310 428 : ++aPresOfIter;
311 : }
312 :
313 : #if OSL_DEBUG_LEVEL > 1
314 : output << "}" << std::endl;
315 : #endif
316 47 : }
317 :
318 47 : void Diagram::addTo( const ShapePtr & pParentShape )
319 : {
320 : // collect data, init maps
321 47 : build( );
322 :
323 : // create Shape hierarchy
324 47 : ShapeCreationVisitor aCreationVisitor(pParentShape, *this);
325 47 : if( mpLayout->getNode() )
326 10 : mpLayout->getNode()->accept( aCreationVisitor );
327 :
328 47 : pParentShape->setDiagramDoms( getDomsAsPropertyValues() );
329 47 : }
330 :
331 47 : uno::Sequence<beans::PropertyValue> Diagram::getDomsAsPropertyValues() const
332 : {
333 47 : sal_Int32 length = maMainDomMap.size();
334 :
335 47 : if ( 0 < maDataRelsMap.getLength() )
336 12 : ++length;
337 :
338 47 : uno::Sequence<beans::PropertyValue> aValue(length);
339 47 : beans::PropertyValue* pValue = aValue.getArray();
340 705 : for (DiagramDomMap::const_iterator i = maMainDomMap.begin();
341 470 : i != maMainDomMap.end();
342 : ++i)
343 : {
344 188 : pValue[0].Name = i->first;
345 188 : pValue[0].Value = uno::makeAny(i->second);
346 188 : ++pValue;
347 : }
348 :
349 47 : if ( 0 < maDataRelsMap.getLength() )
350 : {
351 12 : pValue[0].Name = "OOXDiagramDataRels";
352 12 : pValue[0].Value = uno::makeAny ( maDataRelsMap );
353 12 : ++pValue;
354 : }
355 :
356 47 : return aValue;
357 : }
358 :
359 188 : uno::Reference<xml::dom::XDocument> loadFragment(
360 : core::XmlFilterBase& rFilter,
361 : const OUString& rFragmentPath )
362 : {
363 : // load diagramming fragments into DOM representation, that later
364 : // gets serialized back to SAX events and parsed
365 188 : return rFilter.importFragment( rFragmentPath );
366 : }
367 :
368 114 : uno::Reference<xml::dom::XDocument> loadFragment(
369 : core::XmlFilterBase& rFilter,
370 : const rtl::Reference< core::FragmentHandler >& rxHandler )
371 : {
372 114 : return loadFragment( rFilter, rxHandler->getFragmentPath() );
373 : }
374 :
375 114 : void importFragment( core::XmlFilterBase& rFilter,
376 : const uno::Reference<xml::dom::XDocument>& rXDom,
377 : const char* pDocName,
378 : const DiagramPtr& pDiagram,
379 : const rtl::Reference< core::FragmentHandler >& rxHandler )
380 : {
381 114 : DiagramDomMap& rMainDomMap = pDiagram->getDomMap();
382 114 : rMainDomMap[OUString::createFromAscii(pDocName)] = rXDom;
383 :
384 : uno::Reference<xml::sax::XFastSAXSerializable> xSerializer(
385 114 : rXDom, uno::UNO_QUERY_THROW);
386 :
387 : // now serialize DOM tree into internal data structures
388 114 : rFilter.importFragment( rxHandler, xSerializer );
389 114 : }
390 :
391 47 : void loadDiagram( ShapePtr& pShape,
392 : core::XmlFilterBase& rFilter,
393 : const OUString& rDataModelPath,
394 : const OUString& rLayoutPath,
395 : const OUString& rQStylePath,
396 : const OUString& rColorStylePath )
397 : {
398 47 : DiagramPtr pDiagram( new Diagram() );
399 :
400 94 : DiagramDataPtr pData( new DiagramData() );
401 47 : pDiagram->setData( pData );
402 :
403 94 : DiagramLayoutPtr pLayout( new DiagramLayout() );
404 47 : pDiagram->setLayout( pLayout );
405 :
406 : // data
407 47 : if( !rDataModelPath.isEmpty() )
408 : {
409 : rtl::Reference< core::FragmentHandler > xRefDataModel(
410 47 : new DiagramDataFragmentHandler( rFilter, rDataModelPath, pData ));
411 :
412 : importFragment(rFilter,
413 : loadFragment(rFilter,xRefDataModel),
414 : "OOXData",
415 : pDiagram,
416 47 : xRefDataModel);
417 :
418 141 : pDiagram->getDataRelsMap() = pShape->resolveRelationshipsOfTypeFromOfficeDoc( rFilter,
419 94 : xRefDataModel->getFragmentPath(), "image" );
420 :
421 : // Pass the info to pShape
422 84 : for( ::std::vector<OUString>::const_iterator aIt = pData->getExtDrawings().begin(), aEnd = pData->getExtDrawings().end();
423 : aIt != aEnd; ++aIt )
424 84 : pShape->addExtDrawingRelId( *aIt );
425 : }
426 :
427 : // extLst is present, lets bet on that and ignore the rest of the data from here
428 47 : if( pData->getExtDrawings().empty() )
429 : {
430 : // layout
431 10 : if( !rLayoutPath.isEmpty() )
432 : {
433 : rtl::Reference< core::FragmentHandler > xRefLayout(
434 10 : new DiagramLayoutFragmentHandler( rFilter, rLayoutPath, pLayout ));
435 :
436 : importFragment(rFilter,
437 : loadFragment(rFilter,xRefLayout),
438 : "OOXLayout",
439 : pDiagram,
440 10 : xRefLayout);
441 : }
442 :
443 : // style
444 10 : if( !rQStylePath.isEmpty() )
445 : {
446 : rtl::Reference< core::FragmentHandler > xRefQStyle(
447 10 : new DiagramQStylesFragmentHandler( rFilter, rQStylePath, pDiagram->getStyles() ));
448 :
449 : importFragment(rFilter,
450 : loadFragment(rFilter,xRefQStyle),
451 : "OOXStyle",
452 : pDiagram,
453 10 : xRefQStyle);
454 : }
455 : } else {
456 : // We still want to add the XDocuments to the DiagramDomMap
457 37 : DiagramDomMap& rMainDomMap = pDiagram->getDomMap();
458 37 : rMainDomMap[OUString("OOXLayout")] = loadFragment(rFilter,rLayoutPath);
459 37 : rMainDomMap[OUString("OOXStyle")] = loadFragment(rFilter,rQStylePath);
460 : }
461 :
462 : // colors
463 47 : if( !rColorStylePath.isEmpty() )
464 : {
465 : rtl::Reference< core::FragmentHandler > xRefColorStyle(
466 47 : new ColorFragmentHandler( rFilter, rColorStylePath, pDiagram->getColors() ));
467 :
468 : importFragment(rFilter,
469 : loadFragment(rFilter,xRefColorStyle),
470 : "OOXColor",
471 : pDiagram,
472 47 : xRefColorStyle);
473 : }
474 :
475 47 : if( !pData->getExtDrawings().empty() )
476 : {
477 37 : const DiagramColorMap::const_iterator aColor = pDiagram->getColors().find("node0");
478 37 : if( aColor != pDiagram->getColors().end() )
479 : {
480 37 : pShape->setFontRefColorForNodes(aColor->second.maTextFillColor);
481 : }
482 : }
483 :
484 : // diagram loaded. now lump together & attach to shape
485 94 : pDiagram->addTo(pShape);
486 47 : }
487 :
488 0 : void loadDiagram( const ShapePtr& pShape,
489 : core::XmlFilterBase& rFilter,
490 : const uno::Reference<xml::dom::XDocument>& rXDataModelDom,
491 : const uno::Reference<xml::dom::XDocument>& rXLayoutDom,
492 : const uno::Reference<xml::dom::XDocument>& rXQStyleDom,
493 : const uno::Reference<xml::dom::XDocument>& rXColorStyleDom )
494 : {
495 0 : DiagramPtr pDiagram( new Diagram() );
496 :
497 0 : DiagramDataPtr pData( new DiagramData() );
498 0 : pDiagram->setData( pData );
499 :
500 0 : DiagramLayoutPtr pLayout( new DiagramLayout() );
501 0 : pDiagram->setLayout( pLayout );
502 :
503 0 : OUString aEmpty;
504 :
505 : // data
506 0 : if( rXDataModelDom.is() )
507 : importFragment(rFilter,
508 : rXDataModelDom,
509 : "OOXData",
510 : pDiagram,
511 0 : new DiagramDataFragmentHandler( rFilter, aEmpty, pData ));
512 :
513 : // layout
514 0 : if( rXLayoutDom.is() )
515 : importFragment(rFilter,
516 : rXLayoutDom,
517 : "OOXLayout",
518 : pDiagram,
519 0 : new DiagramLayoutFragmentHandler( rFilter, aEmpty, pLayout ));
520 :
521 : // style
522 0 : if( rXQStyleDom.is() )
523 : importFragment(rFilter,
524 : rXQStyleDom,
525 : "OOXStyle",
526 : pDiagram,
527 0 : new DiagramQStylesFragmentHandler( rFilter, aEmpty, pDiagram->getStyles() ));
528 :
529 : // colors
530 0 : if( rXColorStyleDom.is() )
531 : importFragment(rFilter,
532 : rXColorStyleDom,
533 : "OOXColor",
534 : pDiagram,
535 0 : new ColorFragmentHandler( rFilter, aEmpty, pDiagram->getColors() ));
536 :
537 : // diagram loaded. now lump together & attach to shape
538 0 : pDiagram->addTo(pShape);
539 0 : }
540 :
541 246 : } }
542 :
543 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|