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 .
21 #include <boost/bind.hpp>
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"
38 #include "diagramlayoutatoms.hxx"
39 #include "diagramfragmenthandler.hxx"
44 using namespace ::com::sun::star
;
46 namespace oox
{ namespace drawingml
{
50 void Connection::dump()
54 "cnx modelId " << msModelId
<< ", srcId " << msSourceId
<< ", dstId "
55 << msDestId
<< ", parTransId " << msParTransId
<< ", presId "
56 << msPresId
<< ", sibTransId " << msSibTransId
<< ", srcOrd "
57 << mnSourceOrder
<< ", dstOrd " << mnDestOrder
);
64 "pt text " << mpShape
.get() << ", cnxId " << msCnxId
<< ", modelId "
65 << msModelId
<< ", type " << mnType
);
70 DiagramData::DiagramData()
71 : mpFillProperties( new FillProperties
)
75 void DiagramData::dump()
77 OSL_TRACE("Dgm: DiagramData # of cnx: %zu", maConnections
.size() );
78 std::for_each( maConnections
.begin(), maConnections
.end(),
79 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 boost::bind( &dgm::Point::dump
, _1
) );
85 void Diagram::setData( const DiagramDataPtr
& pData
)
90 void Diagram::setLayout( const DiagramLayoutPtr
& pLayout
)
95 #if OSL_DEBUG_LEVEL > 1
96 OString
normalizeDotName( const OUString
& rStr
)
101 const sal_Int32
nLen(rStr
.getLength());
102 sal_Int32
nCurrIndex(0);
103 while( nCurrIndex
< nLen
)
105 const sal_Int32 aChar
=rStr
.iterateCodePoints(&nCurrIndex
);
106 if( aChar
!= '-' && aChar
!= '{' && aChar
!= '}' )
107 aBuf
.append((sal_Unicode
)aChar
);
110 return OUStringToOString(aBuf
.makeStringAndClear(),
111 RTL_TEXTENCODING_UTF8
);
115 static sal_Int32
calcDepth( const OUString
& rNodeName
,
116 const dgm::Connections
& rCnx
)
118 // find length of longest path in 'isChild' graph, ending with rNodeName
119 dgm::Connections::const_iterator
aCurrCxn( rCnx
.begin() );
120 const dgm::Connections::const_iterator
aEndCxn( rCnx
.end() );
121 while( aCurrCxn
!= aEndCxn
)
123 if( !aCurrCxn
->msParTransId
.isEmpty() &&
124 !aCurrCxn
->msSibTransId
.isEmpty() &&
125 !aCurrCxn
->msSourceId
.isEmpty() &&
126 !aCurrCxn
->msDestId
.isEmpty() &&
127 aCurrCxn
->mnType
!= XML_presOf
&&
128 aCurrCxn
->mnType
!= XML_presParOf
&&
129 rNodeName
== aCurrCxn
->msDestId
)
131 return calcDepth(aCurrCxn
->msSourceId
,
140 void Diagram::build( )
142 // build name-object maps
144 #if OSL_DEBUG_LEVEL > 1
145 std::ofstream
output("/tmp/tree.dot");
147 output
<< "digraph datatree {" << std::endl
;
150 dgm::Points
& rPoints
= getData()->getPoints();
151 dgm::Points::iterator
aCurrPoint(rPoints
.begin());
152 dgm::Points::iterator
aEndPoint(rPoints
.end());
153 while( aCurrPoint
!= aEndPoint
)
155 #if OSL_DEBUG_LEVEL > 1
157 << normalizeDotName(aCurrPoint
->msModelId
).getStr()
160 if( !aCurrPoint
->msPresentationLayoutName
.isEmpty() )
162 << OUStringToOString(
163 aCurrPoint
->msPresentationLayoutName
,
164 RTL_TEXTENCODING_UTF8
).getStr() << "\", ";
167 << OUStringToOString(
168 aCurrPoint
->msModelId
,
169 RTL_TEXTENCODING_UTF8
).getStr() << "\", ";
171 switch( aCurrPoint
->mnType
)
173 case XML_doc
: output
<< "style=filled, color=red"; break;
174 case XML_asst
: output
<< "style=filled, color=green"; break;
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;
182 output
<< "];" << std::endl
;
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() )
190 static sal_Int32 nCount
=0;
193 << "textNode" << nCount
196 << OUStringToOString(
197 aCurrPoint
->mpShape
->getTextBody()->getParagraphs().front()->getRuns().front()->getText(),
198 RTL_TEXTENCODING_UTF8
).getStr()
199 << "\"" << "];" << std::endl
;
201 << normalizeDotName(aCurrPoint
->msModelId
).getStr()
203 << "textNode" << nCount
++
208 const bool bInserted1
=getData()->getPointNameMap().insert(
209 std::make_pair(aCurrPoint
->msModelId
,&(*aCurrPoint
))).second
;
212 OSL_ENSURE(bInserted1
,"Diagram::build(): non-unique point model id");
214 if( !aCurrPoint
->msPresentationLayoutName
.isEmpty() )
216 DiagramData::PointsNameMap::value_type::second_type
& rVec
=
217 getData()->getPointsPresNameMap()[aCurrPoint
->msPresentationLayoutName
];
218 rVec
.push_back(&(*aCurrPoint
));
223 const dgm::Connections
& rConnections
= getData()->getConnections();
224 dgm::Connections::const_iterator
aCurrCxn(rConnections
.begin());
225 const dgm::Connections::const_iterator
aEndCxn(rConnections
.end());
226 while( aCurrCxn
!= aEndCxn
)
228 #if OSL_DEBUG_LEVEL > 1
229 if( !aCurrCxn
->msParTransId
.isEmpty() ||
230 !aCurrCxn
->msSibTransId
.isEmpty() )
232 if( !aCurrCxn
->msSourceId
.isEmpty() ||
233 !aCurrCxn
->msDestId
.isEmpty() )
236 << normalizeDotName(aCurrCxn
->msSourceId
).getStr()
238 << normalizeDotName(aCurrCxn
->msParTransId
).getStr()
240 << normalizeDotName(aCurrCxn
->msSibTransId
).getStr()
242 << normalizeDotName(aCurrCxn
->msDestId
).getStr()
244 << ((aCurrCxn
->mnType
== XML_presOf
) ? " color=red, " : ((aCurrCxn
->mnType
== XML_presParOf
) ? " color=green, " : " "))
246 << OUStringToOString(aCurrCxn
->msModelId
,
247 RTL_TEXTENCODING_UTF8
).getStr()
248 << "\"];" << std::endl
;
253 << normalizeDotName(aCurrCxn
->msParTransId
).getStr()
255 << normalizeDotName(aCurrCxn
->msSibTransId
).getStr()
257 << ((aCurrCxn
->mnType
== XML_presOf
) ? " color=red, " : ((aCurrCxn
->mnType
== XML_presParOf
) ? " color=green, " : " "))
259 << OUStringToOString(aCurrCxn
->msModelId
,
260 RTL_TEXTENCODING_UTF8
).getStr()
261 << "\"];" << std::endl
;
264 else if( !aCurrCxn
->msSourceId
.isEmpty() ||
265 !aCurrCxn
->msDestId
.isEmpty() )
267 << normalizeDotName(aCurrCxn
->msSourceId
).getStr()
269 << normalizeDotName(aCurrCxn
->msDestId
).getStr()
271 << OUStringToOString(aCurrCxn
->msModelId
,
272 RTL_TEXTENCODING_UTF8
).getStr()
273 << ((aCurrCxn
->mnType
== XML_presOf
) ? "\", color=red]" : ((aCurrCxn
->mnType
== XML_presParOf
) ? "\", color=green]" : "\"]"))
277 const bool bInserted1
=getData()->getConnectionNameMap().insert(
278 std::make_pair(aCurrCxn
->msModelId
,&(*aCurrCxn
))).second
;
281 OSL_ENSURE(bInserted1
,"Diagram::build(): non-unique connection model id");
283 if( aCurrCxn
->mnType
== XML_presOf
)
285 DiagramData::StringMap::value_type::second_type
& rVec
=getData()->getPresOfNameMap()[aCurrCxn
->msDestId
];
288 aCurrCxn
->msSourceId
,sal_Int32(0)));
294 // assign outline levels
295 DiagramData::StringMap
& rStringMap
= getData()->getPresOfNameMap();
296 DiagramData::StringMap::iterator aPresOfIter
=rStringMap
.begin();
297 const DiagramData::StringMap::iterator aPresOfEnd
=rStringMap
.end();
298 while( aPresOfIter
!= aPresOfEnd
)
300 DiagramData::StringMap::value_type::second_type::iterator aPresOfNodeIterCalcLevel
=aPresOfIter
->second
.begin();
301 const DiagramData::StringMap::value_type::second_type::iterator aPresOfNodeEnd
=aPresOfIter
->second
.end();
302 while(aPresOfNodeIterCalcLevel
!= aPresOfNodeEnd
)
304 const sal_Int32 nDepth
=calcDepth(aPresOfNodeIterCalcLevel
->first
,
305 getData()->getConnections());
306 aPresOfNodeIterCalcLevel
->second
= nDepth
!= 0 ? nDepth
: -1;
307 ++aPresOfNodeIterCalcLevel
;
313 #if OSL_DEBUG_LEVEL > 1
314 output
<< "}" << std::endl
;
318 void Diagram::addTo( const ShapePtr
& pParentShape
)
320 // collect data, init maps
323 // create Shape hierarchy
324 ShapeCreationVisitor
aCreationVisitor(pParentShape
, *this);
325 if( mpLayout
->getNode() )
326 mpLayout
->getNode()->accept( aCreationVisitor
);
328 pParentShape
->setDiagramDoms( getDomsAsPropertyValues() );
331 uno::Sequence
<beans::PropertyValue
> Diagram::getDomsAsPropertyValues() const
333 sal_Int32 length
= maMainDomMap
.size();
335 if ( 0 < maDataRelsMap
.getLength() )
338 uno::Sequence
<beans::PropertyValue
> aValue(length
);
339 beans::PropertyValue
* pValue
= aValue
.getArray();
340 for (DiagramDomMap::const_iterator i
= maMainDomMap
.begin();
341 i
!= maMainDomMap
.end();
344 pValue
[0].Name
= i
->first
;
345 pValue
[0].Value
= uno::makeAny(i
->second
);
349 if ( 0 < maDataRelsMap
.getLength() )
351 pValue
[0].Name
= "OOXDiagramDataRels";
352 pValue
[0].Value
= uno::makeAny ( maDataRelsMap
);
359 uno::Reference
<xml::dom::XDocument
> loadFragment(
360 core::XmlFilterBase
& rFilter
,
361 const OUString
& rFragmentPath
)
363 // load diagramming fragments into DOM representation, that later
364 // gets serialized back to SAX events and parsed
365 return rFilter
.importFragment( rFragmentPath
);
368 uno::Reference
<xml::dom::XDocument
> loadFragment(
369 core::XmlFilterBase
& rFilter
,
370 const rtl::Reference
< core::FragmentHandler
>& rxHandler
)
372 return loadFragment( rFilter
, rxHandler
->getFragmentPath() );
375 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
)
381 DiagramDomMap
& rMainDomMap
= pDiagram
->getDomMap();
382 rMainDomMap
[OUString::createFromAscii(pDocName
)] = rXDom
;
384 uno::Reference
<xml::sax::XFastSAXSerializable
> xSerializer(
385 rXDom
, uno::UNO_QUERY_THROW
);
387 // now serialize DOM tree into internal data structures
388 rFilter
.importFragment( rxHandler
, xSerializer
);
391 void loadDiagram( ShapePtr
& pShape
,
392 core::XmlFilterBase
& rFilter
,
393 const OUString
& rDataModelPath
,
394 const OUString
& rLayoutPath
,
395 const OUString
& rQStylePath
,
396 const OUString
& rColorStylePath
)
398 DiagramPtr
pDiagram( new Diagram() );
400 DiagramDataPtr
pData( new DiagramData() );
401 pDiagram
->setData( pData
);
403 DiagramLayoutPtr
pLayout( new DiagramLayout() );
404 pDiagram
->setLayout( pLayout
);
407 if( !rDataModelPath
.isEmpty() )
409 rtl::Reference
< core::FragmentHandler
> xRefDataModel(
410 new DiagramDataFragmentHandler( rFilter
, rDataModelPath
, pData
));
412 importFragment(rFilter
,
413 loadFragment(rFilter
,xRefDataModel
),
418 pDiagram
->getDataRelsMap() = pShape
->resolveRelationshipsOfTypeFromOfficeDoc( rFilter
,
419 xRefDataModel
->getFragmentPath(), "image" );
421 // Pass the info to pShape
422 for( ::std::vector
<OUString
>::const_iterator aIt
= pData
->getExtDrawings().begin(), aEnd
= pData
->getExtDrawings().end();
424 pShape
->addExtDrawingRelId( *aIt
);
427 // extLst is present, lets bet on that and ignore the rest of the data from here
428 if( pData
->getExtDrawings().empty() )
431 if( !rLayoutPath
.isEmpty() )
433 rtl::Reference
< core::FragmentHandler
> xRefLayout(
434 new DiagramLayoutFragmentHandler( rFilter
, rLayoutPath
, pLayout
));
436 importFragment(rFilter
,
437 loadFragment(rFilter
,xRefLayout
),
444 if( !rQStylePath
.isEmpty() )
446 rtl::Reference
< core::FragmentHandler
> xRefQStyle(
447 new DiagramQStylesFragmentHandler( rFilter
, rQStylePath
, pDiagram
->getStyles() ));
449 importFragment(rFilter
,
450 loadFragment(rFilter
,xRefQStyle
),
456 // We still want to add the XDocuments to the DiagramDomMap
457 DiagramDomMap
& rMainDomMap
= pDiagram
->getDomMap();
458 rMainDomMap
[OUString("OOXLayout")] = loadFragment(rFilter
,rLayoutPath
);
459 rMainDomMap
[OUString("OOXStyle")] = loadFragment(rFilter
,rQStylePath
);
463 if( !rColorStylePath
.isEmpty() )
465 rtl::Reference
< core::FragmentHandler
> xRefColorStyle(
466 new ColorFragmentHandler( rFilter
, rColorStylePath
, pDiagram
->getColors() ));
468 importFragment(rFilter
,
469 loadFragment(rFilter
,xRefColorStyle
),
475 if( !pData
->getExtDrawings().empty() )
477 const DiagramColorMap::const_iterator aColor
= pDiagram
->getColors().find("node0");
478 if( aColor
!= pDiagram
->getColors().end() )
480 pShape
->setFontRefColorForNodes(aColor
->second
.maTextFillColor
);
484 // diagram loaded. now lump together & attach to shape
485 pDiagram
->addTo(pShape
);
488 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
)
495 DiagramPtr
pDiagram( new Diagram() );
497 DiagramDataPtr
pData( new DiagramData() );
498 pDiagram
->setData( pData
);
500 DiagramLayoutPtr
pLayout( new DiagramLayout() );
501 pDiagram
->setLayout( pLayout
);
506 if( rXDataModelDom
.is() )
507 importFragment(rFilter
,
511 new DiagramDataFragmentHandler( rFilter
, aEmpty
, pData
));
514 if( rXLayoutDom
.is() )
515 importFragment(rFilter
,
519 new DiagramLayoutFragmentHandler( rFilter
, aEmpty
, pLayout
));
522 if( rXQStyleDom
.is() )
523 importFragment(rFilter
,
527 new DiagramQStylesFragmentHandler( rFilter
, aEmpty
, pDiagram
->getStyles() ));
530 if( rXColorStyleDom
.is() )
531 importFragment(rFilter
,
535 new ColorFragmentHandler( rFilter
, aEmpty
, pDiagram
->getColors() ));
537 // diagram loaded. now lump together & attach to shape
538 pDiagram
->addTo(pShape
);
543 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */