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 .
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"
43 using namespace ::com::sun::star
;
45 namespace oox
{ namespace drawingml
{
49 void Connection::dump()
53 "cnx modelId " << msModelId
<< ", srcId " << msSourceId
<< ", dstId "
54 << msDestId
<< ", parTransId " << msParTransId
<< ", presId "
55 << msPresId
<< ", sibTransId " << msSibTransId
<< ", srcOrd "
56 << mnSourceOrder
<< ", dstOrd " << mnDestOrder
);
63 "pt text " << mpShape
.get() << ", cnxId " << msCnxId
<< ", modelId "
64 << msModelId
<< ", type " << mnType
);
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
)
89 void Diagram::setLayout( const DiagramLayoutPtr
& pLayout
)
94 #ifdef DEBUG_OOX_DIAGRAM
95 OString
normalizeDotName( const OUString
& rStr
)
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
);
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
,
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
;
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
154 << normalizeDotName(aCurrPoint
->msModelId
).getStr()
157 if( !aCurrPoint
->msPresentationLayoutName
.isEmpty() )
159 << OUStringToOString(
160 aCurrPoint
->msPresentationLayoutName
,
161 RTL_TEXTENCODING_UTF8
).getStr() << "\", ";
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;
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
;
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;
191 << "textNode" << nCount
194 << OUStringToOString(
195 aCurrPoint
->mpShape
->getTextBody()->getParagraphs().front()->getRuns().front()->getText(),
196 RTL_TEXTENCODING_UTF8
).getStr()
197 << "\"" << "];" << std::endl
;
199 << normalizeDotName(aCurrPoint
->msModelId
).getStr()
201 << "textNode" << nCount
++
206 const bool bInserted1
=getData()->getPointNameMap().insert(
207 std::make_pair(aCurrPoint
->msModelId
,&(*aCurrPoint
))).second
;
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
));
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() )
234 << normalizeDotName(aCurrCxn
->msSourceId
).getStr()
236 << normalizeDotName(aCurrCxn
->msParTransId
).getStr()
238 << normalizeDotName(aCurrCxn
->msSibTransId
).getStr()
240 << normalizeDotName(aCurrCxn
->msDestId
).getStr()
242 << ((aCurrCxn
->mnType
== XML_presOf
) ? " color=red, " : ((aCurrCxn
->mnType
== XML_presParOf
) ? " color=green, " : " "))
244 << OUStringToOString(aCurrCxn
->msModelId
,
245 RTL_TEXTENCODING_UTF8
).getStr()
246 << "\"];" << std::endl
;
251 << normalizeDotName(aCurrCxn
->msParTransId
).getStr()
253 << normalizeDotName(aCurrCxn
->msSibTransId
).getStr()
255 << ((aCurrCxn
->mnType
== XML_presOf
) ? " color=red, " : ((aCurrCxn
->mnType
== XML_presParOf
) ? " color=green, " : " "))
257 << OUStringToOString(aCurrCxn
->msModelId
,
258 RTL_TEXTENCODING_UTF8
).getStr()
259 << "\"];" << std::endl
;
262 else if( !aCurrCxn
->msSourceId
.isEmpty() ||
263 !aCurrCxn
->msDestId
.isEmpty() )
265 << normalizeDotName(aCurrCxn
->msSourceId
).getStr()
267 << normalizeDotName(aCurrCxn
->msDestId
).getStr()
269 << OUStringToOString(aCurrCxn
->msModelId
,
270 RTL_TEXTENCODING_UTF8
).getStr()
271 << ((aCurrCxn
->mnType
== XML_presOf
) ? "\", color=red]" : ((aCurrCxn
->mnType
== XML_presParOf
) ? "\", color=green]" : "\"]"))
275 const bool bInserted1
=getData()->getConnectionNameMap().insert(
276 std::make_pair(aCurrCxn
->msModelId
,&(*aCurrCxn
))).second
;
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
];
286 aCurrCxn
->msSourceId
,sal_Int32(0)));
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
;
310 #ifdef DEBUG_OOX_DIAGRAM
311 output
<< "}" << std::endl
;
315 void Diagram::addTo( const ShapePtr
& pParentShape
)
317 // collect data, init maps
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() )
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();
341 pValue
[0].Name
= i
->first
;
342 pValue
[0].Value
<<= i
->second
;
346 if ( 0 < maDataRelsMap
.getLength() )
348 pValue
[0].Name
= "OOXDiagramDataRels";
349 pValue
[0].Value
<<= maDataRelsMap
;
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
);
404 if( !rDataModelPath
.isEmpty() )
406 rtl::Reference
< core::FragmentHandler
> xRefDataModel(
407 new DiagramDataFragmentHandler( rFilter
, rDataModelPath
, pData
));
409 importFragment(rFilter
,
410 loadFragment(rFilter
,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();
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() )
428 if( !rLayoutPath
.isEmpty() )
430 rtl::Reference
< core::FragmentHandler
> xRefLayout(
431 new DiagramLayoutFragmentHandler( rFilter
, rLayoutPath
, pLayout
));
433 importFragment(rFilter
,
434 loadFragment(rFilter
,xRefLayout
),
441 if( !rQStylePath
.isEmpty() )
443 rtl::Reference
< core::FragmentHandler
> xRefQStyle(
444 new DiagramQStylesFragmentHandler( rFilter
, rQStylePath
, pDiagram
->getStyles() ));
446 importFragment(rFilter
,
447 loadFragment(rFilter
,xRefQStyle
),
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
);
460 if( !rColorStylePath
.isEmpty() )
462 rtl::Reference
< core::FragmentHandler
> xRefColorStyle(
463 new ColorFragmentHandler( rFilter
, rColorStylePath
, pDiagram
->getColors() ));
465 importFragment(rFilter
,
466 loadFragment(rFilter
,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
);
501 if( rXDataModelDom
.is() )
502 importFragment(rFilter
,
506 new DiagramDataFragmentHandler( rFilter
, "", pData
));
509 if( rXLayoutDom
.is() )
510 importFragment(rFilter
,
514 new DiagramLayoutFragmentHandler( rFilter
, "", pLayout
));
517 if( rXQStyleDom
.is() )
518 importFragment(rFilter
,
522 new DiagramQStylesFragmentHandler( rFilter
, "", pDiagram
->getStyles() ));
525 if( rXColorStyleDom
.is() )
526 importFragment(rFilter
,
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: */