/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
* This file is part of the LibreOffice project.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
* This file incorporates work covered by the following license notice:
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed
* with this work for additional information regarding copyright
* ownership. The ASF licenses this file to you under the Apache
* License, Version 2.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of
* the License at http://www.apache.org/licenses/LICENSE-2.0 .
*/
#include <com/sun/star/awt/Point.hpp>
#include <com/sun/star/awt/Size.hpp>
#include <com/sun/star/xml/dom/XDocument.hpp>
#include <com/sun/star/xml/sax/XFastSAXSerializable.hpp>
#include <rtl/ustrbuf.hxx>
#include <sal/log.hxx>
#include <editeng/unoprnms.hxx>
#include <drawingml/textbody.hxx>
#include <drawingml/textparagraph.hxx>
#include <drawingml/textrun.hxx>
#include <drawingml/diagram/diagram.hxx>
#include <drawingml/fillproperties.hxx>
#include <oox/ppt/pptshapegroupcontext.hxx>
#include <oox/ppt/pptshape.hxx>
#include "diagramlayoutatoms.hxx"
#include "layoutatomvisitors.hxx"
#include "diagramfragmenthandler.hxx"
#include <iostream>
#include <fstream>
using namespace ::com::sun::star;
namespace oox { namespace drawingml {
namespace dgm {
void Connection::dump() const
{
SAL_INFO(
"oox.drawingml",
"cnx modelId " << msModelId << ", srcId " << msSourceId << ", dstId "
<< msDestId << ", parTransId " << msParTransId << ", presId "
<< msPresId << ", sibTransId " << msSibTransId << ", srcOrd "
<< mnSourceOrder << ", dstOrd " << mnDestOrder);
}
void Point::dump() const
{
SAL_INFO(
"oox.drawingml",
"pt text " << mpShape.get() << ", cnxId " << msCnxId << ", modelId "
<< msModelId << ", type " << mnType);
}
} // dgm namespace
DiagramData::DiagramData() :
mpFillProperties( new FillProperties ),
mnMaxDepth(0)
{
}
const dgm::Point* DiagramData::getRootPoint() const
{
for (const auto & aCurrPoint : maPoints)
if (aCurrPoint.mnType == XML_doc)
return &aCurrPoint;
SAL_WARN("oox.drawingml", "No root point");
return nullptr;
}
void DiagramData::dump() const
{
SAL_INFO("oox.drawingml", "Dgm: DiagramData # of cnx: " << maConnections.size() );
for (const auto& rConnection : maConnections)
rConnection.dump();
SAL_INFO("oox.drawingml", "Dgm: DiagramData # of pt: " << maPoints.size() );
for (const auto& rPoint : maPoints)
rPoint.dump();
}
#ifdef DEBUG_OOX_DIAGRAM
OString normalizeDotName( const OUString& rStr )
{
OUStringBuffer aBuf;
aBuf.append('N');
const sal_Int32 nLen(rStr.getLength());
sal_Int32 nCurrIndex(0);
while( nCurrIndex < nLen )
{
const sal_Int32 aChar=rStr.iterateCodePoints(&nCurrIndex);
if( aChar != '-' && aChar != '{' && aChar != '}' )
aBuf.append((sal_Unicode)aChar);
}
return OUStringToOString(aBuf.makeStringAndClear(),
RTL_TEXTENCODING_UTF8);
}
#endif
static sal_Int32 calcDepth( const OUString& rNodeName,
const dgm::Connections& rCnx )
{
// find length of longest path in 'isChild' graph, ending with rNodeName
for (auto const& elem : rCnx)
{
if( !elem.msParTransId.isEmpty() &&
!elem.msSibTransId.isEmpty() &&
!elem.msSourceId.isEmpty() &&
!elem.msDestId.isEmpty() &&
elem.mnType == XML_parOf &&
rNodeName == elem.msDestId )
{
return calcDepth(elem.msSourceId, rCnx) + 1;
}
}
return 0;
}
void Diagram::build( )
{
// build name-object maps
#ifdef DEBUG_OOX_DIAGRAM
std::ofstream output("/tmp/tree.dot");
output << "digraph datatree {" << std::endl;
#endif
dgm::Points& rPoints = getData()->getPoints();
for (auto & point : rPoints)
{
#ifdef DEBUG_OOX_DIAGRAM
output << "\t"
<< normalizeDotName(point.msModelId).getStr()
<< "[";
if( !point.msPresentationLayoutName.isEmpty() )
output << "label=\""
<< OUStringToOString(
point.msPresentationLayoutName,
RTL_TEXTENCODING_UTF8).getStr() << "\", ";
else
output << "label=\""
<< OUStringToOString(
point.msModelId,
RTL_TEXTENCODING_UTF8).getStr() << "\", ";
switch( point.mnType )
{
case XML_doc: output << "style=filled, color=red"; break;
case XML_asst: output << "style=filled, color=green"; break;
default:
case XML_node: output << "style=filled, color=blue"; break;
case XML_pres: output << "style=filled, color=yellow"; break;
case XML_parTrans: output << "color=grey"; break;
case XML_sibTrans: output << " "; break;
}
output << "];" << std::endl;
#endif
// does currpoint have any text set?
if( point.mpShape &&
point.mpShape->getTextBody() &&
!point.mpShape->getTextBody()->getParagraphs().empty() &&
!point.mpShape->getTextBody()->getParagraphs().front()->getRuns().empty() )
{
#ifdef DEBUG_OOX_DIAGRAM
static sal_Int32 nCount=0;
output << "\t"
<< "textNode" << nCount
<< " ["
<< "label=\""
<< OUStringToOString(
point.mpShape->getTextBody()->getParagraphs().front()->getRuns().front()->getText(),
RTL_TEXTENCODING_UTF8).getStr()
<< "\"" << "];" << std::endl;
output << "\t"
<< normalizeDotName(point.msModelId).getStr()
<< " -> "
<< "textNode" << nCount++
<< ";" << std::endl;
#endif
}
const bool bInserted1=getData()->getPointNameMap().insert(
std::make_pair(point.msModelId,&point)).second;
SAL_WARN_IF(!bInserted1, "oox.drawingml", "Diagram::build(): non-unique point model id");
if( !point.msPresentationLayoutName.isEmpty() )
{
DiagramData::PointsNameMap::value_type::second_type& rVec=
getData()->getPointsPresNameMap()[point.msPresentationLayoutName];
rVec.push_back(&point);
}
}
const dgm::Connections& rConnections = getData()->getConnections();
for (auto const& connection : rConnections)
{
#ifdef DEBUG_OOX_DIAGRAM
if( !connection.msParTransId.isEmpty() ||
!connection.msSibTransId.isEmpty() )
{
if( !connection.msSourceId.isEmpty() ||
!connection.msDestId.isEmpty() )
{
output << "\t"
<< normalizeDotName(connection.msSourceId).getStr()
<< " -> "
<< normalizeDotName(connection.msParTransId).getStr()
<< " -> "
<< normalizeDotName(connection.msSibTransId).getStr()
<< " -> "
<< normalizeDotName(connection.msDestId).getStr()
<< " [style=dotted,"
<< ((connection.mnType == XML_presOf) ? " color=red, " : ((connection.mnType == XML_presParOf) ? " color=green, " : " "))
<< "label=\""
<< OUStringToOString(connection.msModelId,
RTL_TEXTENCODING_UTF8 ).getStr()
<< "\"];" << std::endl;
}
else
{
output << "\t"
<< normalizeDotName(connection.msParTransId).getStr()
<< " -> "
<< normalizeDotName(connection.msSibTransId).getStr()
<< " ["
<< ((connection.mnType == XML_presOf) ? " color=red, " : ((connection.mnType == XML_presParOf) ? " color=green, " : " "))
<< "label=\""
<< OUStringToOString(connection.msModelId,
RTL_TEXTENCODING_UTF8 ).getStr()
<< "\"];" << std::endl;
}
}
else if( !connection.msSourceId.isEmpty() ||
!connection.msDestId.isEmpty() )
output << "\t"
<< normalizeDotName(connection.msSourceId).getStr()
<< " -> "
<< normalizeDotName(connection.msDestId).getStr()
<< " [label=\""
<< OUStringToOString(connection.msModelId,
RTL_TEXTENCODING_UTF8 ).getStr()
<< ((connection.mnType == XML_presOf) ? "\", color=red]" : ((connection.mnType == XML_presParOf) ? "\", color=green]" : "\"]"))
<< ";" << std::endl;
#endif
const bool bInserted1=getData()->getConnectionNameMap().insert(
std::make_pair(connection.msModelId,&connection)).second;
SAL_WARN_IF(!bInserted1, "oox.drawingml", "Diagram::build(): non-unique connection model id");
if( connection.mnType == XML_presOf )
{
DiagramData::StringMap::value_type::second_type& rVec=getData()->getPresOfNameMap()[connection.msDestId];
rVec.emplace_back(
connection.msSourceId,sal_Int32(0));
}
}
// assign outline levels
DiagramData::StringMap& rStringMap = getData()->getPresOfNameMap();
for (auto & elemPresOf : rStringMap)
{
for (auto & elem : elemPresOf.second)
{
const sal_Int32 nDepth=calcDepth(elem.first,
getData()->getConnections());
elem.second = nDepth != 0 ? nDepth : -1;
if (nDepth > getData()->getMaxDepth())
getData()->setMaxDepth(nDepth);
}
}
#ifdef DEBUG_OOX_DIAGRAM
output << "}" << std::endl;
#endif
}
void Diagram::addTo( const ShapePtr & pParentShape )
{
// collect data, init maps
build( );
if (pParentShape->getSize().Width == 0 || pParentShape->getSize().Height == 0)
SAL_WARN("oox.drawingml", "Diagram cannot be correctly laid out. Size: "
<< pParentShape->getSize().Width << "x" << pParentShape->getSize().Height);
pParentShape->setChildSize(pParentShape->getSize());
if( mpLayout->getNode() )
{
// create Shape hierarchy
ShapeCreationVisitor aCreationVisitor(pParentShape, *this);
mpLayout->getNode()->setExistingShape(pParentShape);
mpLayout->getNode()->accept(aCreationVisitor);
// layout shapes - now all shapes are created
ShapeLayoutingVisitor aLayoutingVisitor;
mpLayout->getNode()->accept(aLayoutingVisitor);
}
pParentShape->setDiagramDoms( getDomsAsPropertyValues() );
}
uno::Sequence<beans::PropertyValue> Diagram::getDomsAsPropertyValues() const
{
sal_Int32 length = maMainDomMap.size();
if (maDataRelsMap.hasElements())
++length;
uno::Sequence<beans::PropertyValue> aValue(length);
beans::PropertyValue* pValue = aValue.getArray();
for (auto const& mainDom : maMainDomMap)
{
pValue->Name = mainDom.first;
pValue->Value <<= mainDom.second;
++pValue;
}
if (maDataRelsMap.hasElements())
{
pValue->Name = "OOXDiagramDataRels";
pValue->Value <<= maDataRelsMap;
++pValue;
}
return aValue;
}
uno::Reference<xml::dom::XDocument> loadFragment(
core::XmlFilterBase& rFilter,
const OUString& rFragmentPath )
{
// load diagramming fragments into DOM representation, that later
// gets serialized back to SAX events and parsed
return rFilter.importFragment( rFragmentPath );
}
uno::Reference<xml::dom::XDocument> loadFragment(
core::XmlFilterBase& rFilter,
const rtl::Reference< core::FragmentHandler >& rxHandler )
{
return loadFragment( rFilter, rxHandler->getFragmentPath() );
}
void importFragment( core::XmlFilterBase& rFilter,
const uno::Reference<xml::dom::XDocument>& rXDom,
const char* pDocName,
const DiagramPtr& pDiagram,
const rtl::Reference< core::FragmentHandler >& rxHandler )
{
DiagramDomMap& rMainDomMap = pDiagram->getDomMap();
rMainDomMap[OUString::createFromAscii(pDocName)] = rXDom;
uno::Reference<xml::sax::XFastSAXSerializable> xSerializer(
rXDom, uno::UNO_QUERY_THROW);
// now serialize DOM tree into internal data structures
rFilter.importFragment( rxHandler, xSerializer );
}
void loadDiagram( ShapePtr const & pShape,
core::XmlFilterBase& rFilter,
const OUString& rDataModelPath,
const OUString& rLayoutPath,
const OUString& rQStylePath,
const OUString& rColorStylePath )
{
DiagramPtr pDiagram( new Diagram );
DiagramDataPtr pData( new DiagramData() );
pDiagram->setData( pData );
DiagramLayoutPtr pLayout( new DiagramLayout(*pDiagram) );
pDiagram->setLayout( pLayout );
// data
if( !rDataModelPath.isEmpty() )
{
rtl::Reference< core::FragmentHandler > xRefDataModel(
new DiagramDataFragmentHandler( rFilter, rDataModelPath, pData ));
importFragment(rFilter,
loadFragment(rFilter,xRefDataModel),
"OOXData",
pDiagram,
xRefDataModel);
pDiagram->getDataRelsMap() = pShape->resolveRelationshipsOfTypeFromOfficeDoc( rFilter,
xRefDataModel->getFragmentPath(), "image" );
// Pass the info to pShape
for (auto const& extDrawing : pData->getExtDrawings())
pShape->addExtDrawingRelId(extDrawing);
}
// extLst is present, lets bet on that and ignore the rest of the data from here
if( pData->getExtDrawings().empty() )
{
// layout
if( !rLayoutPath.isEmpty() )
{
rtl::Reference< core::FragmentHandler > xRefLayout(
new DiagramLayoutFragmentHandler( rFilter, rLayoutPath, pLayout ));
importFragment(rFilter,
loadFragment(rFilter,xRefLayout),
"OOXLayout",
pDiagram,
xRefLayout);
}
// style
if( !rQStylePath.isEmpty() )
{
rtl::Reference< core::FragmentHandler > xRefQStyle(
new DiagramQStylesFragmentHandler( rFilter, rQStylePath, pDiagram->getStyles() ));
importFragment(rFilter,
loadFragment(rFilter,xRefQStyle),
"OOXStyle",
pDiagram,
xRefQStyle);
}
}
else
{
// We still want to add the XDocuments to the DiagramDomMap
DiagramDomMap& rMainDomMap = pDiagram->getDomMap();
rMainDomMap[OUString("OOXLayout")] = loadFragment(rFilter,rLayoutPath);
rMainDomMap[OUString("OOXStyle")] = loadFragment(rFilter,rQStylePath);
}
// colors
if( !rColorStylePath.isEmpty() )
{
rtl::Reference< core::FragmentHandler > xRefColorStyle(
new ColorFragmentHandler( rFilter, rColorStylePath, pDiagram->getColors() ));
importFragment(rFilter,
loadFragment(rFilter,xRefColorStyle),
"OOXColor",
pDiagram,
xRefColorStyle);
}
if( !pData->getExtDrawings().empty() )
{
const DiagramColorMap::const_iterator aColor = pDiagram->getColors().find("node0");
if( aColor != pDiagram->getColors().end() )
{
pShape->setFontRefColorForNodes(aColor->second.maTextFillColor);
}
}
// diagram loaded. now lump together & attach to shape
pDiagram->addTo(pShape);
}
void loadDiagram( const ShapePtr& pShape,
core::XmlFilterBase& rFilter,
const uno::Reference<xml::dom::XDocument>& rXDataModelDom,
const uno::Reference<xml::dom::XDocument>& rXLayoutDom,
const uno::Reference<xml::dom::XDocument>& rXQStyleDom,
const uno::Reference<xml::dom::XDocument>& rXColorStyleDom )
{
DiagramPtr pDiagram( new Diagram );
DiagramDataPtr pData( new DiagramData() );
pDiagram->setData( pData );
DiagramLayoutPtr pLayout( new DiagramLayout(*pDiagram) );
pDiagram->setLayout( pLayout );
// data
if( rXDataModelDom.is() )
importFragment(rFilter,
rXDataModelDom,
"OOXData",
pDiagram,
new DiagramDataFragmentHandler( rFilter, "", pData ));
// layout
if( rXLayoutDom.is() )
importFragment(rFilter,
rXLayoutDom,
"OOXLayout",
pDiagram,
new DiagramLayoutFragmentHandler( rFilter, "", pLayout ));
// style
if( rXQStyleDom.is() )
importFragment(rFilter,
rXQStyleDom,
"OOXStyle",
pDiagram,
new DiagramQStylesFragmentHandler( rFilter, "", pDiagram->getStyles() ));
// colors
if( rXColorStyleDom.is() )
importFragment(rFilter,
rXColorStyleDom,
"OOXColor",
pDiagram,
new ColorFragmentHandler( rFilter, "", pDiagram->getColors() ));
// diagram loaded. now lump together & attach to shape
pDiagram->addTo(pShape);
}
} }
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
↑ V758 The 'rVec' reference becomes invalid when temporary object returned by a function is destroyed.
↑ V758 The 'rVec' reference becomes invalid when temporary object returned by a function is destroyed.