Change-Id: I5b6d62104e46c6eb5d142e7e11666806383eed09 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/210566 Tested-by: Jenkins Reviewed-by: Julien Nabet <serval2412@yahoo.fr>
738 lines
29 KiB
C++
738 lines
29 KiB
C++
/* -*- 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 <oox/drawingml/diagram/diagram.hxx>
|
|
#include "diagram.hxx"
|
|
#include <com/sun/star/awt/Point.hpp>
|
|
#include <com/sun/star/awt/Size.hpp>
|
|
#include <com/sun/star/beans/XPropertySet.hpp>
|
|
#include <com/sun/star/drawing/XShape.hpp>
|
|
#include <com/sun/star/drawing/XShapes.hpp>
|
|
#include <com/sun/star/xml/dom/XDocument.hpp>
|
|
#include <com/sun/star/xml/sax/XFastSAXSerializable.hpp>
|
|
#include <com/sun/star/xml/dom/XDocumentBuilder.hpp>
|
|
#include <com/sun/star/xml/dom/DocumentBuilder.hpp>
|
|
#include <sal/log.hxx>
|
|
#include <editeng/unoprnms.hxx>
|
|
#include <drawingml/fillproperties.hxx>
|
|
#include <drawingml/lineproperties.hxx>
|
|
#include <drawingml/customshapeproperties.hxx>
|
|
#include <o3tl/unit_conversion.hxx>
|
|
#include <oox/drawingml/theme.hxx>
|
|
#include <oox/token/namespaces.hxx>
|
|
#include <basegfx/matrix/b2dhommatrix.hxx>
|
|
#include <svx/svdpage.hxx>
|
|
#include <oox/ppt/pptimport.hxx>
|
|
#include <comphelper/xmltools.hxx>
|
|
#include "diagramlayoutatoms.hxx"
|
|
#include "layoutatomvisitors.hxx"
|
|
#include "diagramfragmenthandler.hxx"
|
|
#include <comphelper/processfactory.hxx>
|
|
#include <com/sun/star/io/TempFile.hpp>
|
|
#include <oox/export/drawingml.hxx>
|
|
|
|
#include <com/sun/star/xml/sax/XSAXSerializable.hpp>
|
|
#include <com/sun/star/xml/sax/Writer.hpp>
|
|
#include <oox/core/fastparser.hxx>
|
|
#include <unotools/streamwrap.hxx>
|
|
#include <tools/stream.hxx>
|
|
|
|
#ifdef DBG_UTIL
|
|
#include <osl/file.hxx>
|
|
#include <o3tl/environment.hxx>
|
|
#include <comphelper/storagehelper.hxx>
|
|
#include <com/sun/star/embed/XRelationshipAccess.hpp>
|
|
#endif
|
|
|
|
using namespace ::com::sun::star;
|
|
|
|
namespace oox::drawingml {
|
|
|
|
static void sortChildrenByZOrder(const ShapePtr& pShape)
|
|
{
|
|
std::vector<ShapePtr>& rChildren = pShape->getChildren();
|
|
|
|
// Offset the children from their default z-order stacking, if necessary.
|
|
for (size_t i = 0; i < rChildren.size(); ++i)
|
|
rChildren[i]->setZOrder(i);
|
|
|
|
for (size_t i = 0; i < rChildren.size(); ++i)
|
|
{
|
|
const ShapePtr& pChild = rChildren[i];
|
|
sal_Int32 nZOrderOff = pChild->getZOrderOff();
|
|
if (nZOrderOff <= 0)
|
|
continue;
|
|
|
|
// Increase my ZOrder by nZOrderOff.
|
|
pChild->setZOrder(pChild->getZOrder() + nZOrderOff);
|
|
pChild->setZOrderOff(0);
|
|
|
|
for (sal_Int32 j = 0; j < nZOrderOff; ++j)
|
|
{
|
|
size_t nIndex = i + j + 1;
|
|
if (nIndex >= rChildren.size())
|
|
break;
|
|
|
|
// Decrease the ZOrder of the next nZOrderOff elements by one.
|
|
const ShapePtr& pNext = rChildren[nIndex];
|
|
pNext->setZOrder(pNext->getZOrder() - 1);
|
|
}
|
|
}
|
|
|
|
// Now that the ZOrders are adjusted, sort the children.
|
|
std::sort(rChildren.begin(), rChildren.end(),
|
|
[](const ShapePtr& a, const ShapePtr& b) { return a->getZOrder() < b->getZOrder(); });
|
|
|
|
// Apply also for children.
|
|
for (const auto& rChild : rChildren)
|
|
sortChildrenByZOrder(rChild);
|
|
}
|
|
|
|
/// Removes empty group shapes, now that their spacing influenced the layout.
|
|
static void removeUnneededGroupShapes(const ShapePtr& pShape)
|
|
{
|
|
std::vector<ShapePtr>& rChildren = pShape->getChildren();
|
|
|
|
std::erase_if(rChildren,
|
|
[](const ShapePtr& aChild) {
|
|
return aChild->getServiceName()
|
|
== "com.sun.star.drawing.GroupShape"
|
|
&& aChild->getChildren().empty();
|
|
});
|
|
|
|
for (const auto& pChild : rChildren)
|
|
{
|
|
removeUnneededGroupShapes(pChild);
|
|
}
|
|
}
|
|
|
|
void SmartArtDiagram::createShapeHierarchyFromModel( const ShapePtr & pParentShape, bool bCreate )
|
|
{
|
|
if (pParentShape->getSize().Width == 0 || pParentShape->getSize().Height == 0)
|
|
SAL_WARN("oox.drawingml", "SmartArtDiagram cannot be correctly laid out. Size: "
|
|
<< pParentShape->getSize().Width << "x" << pParentShape->getSize().Height);
|
|
|
|
pParentShape->setChildSize(pParentShape->getSize());
|
|
|
|
const svx::diagram::Point* pRootPoint = mpData->getRootPoint();
|
|
if (bCreate && mpLayout->getNode() && pRootPoint)
|
|
{
|
|
// create Shape hierarchy
|
|
ShapeCreationVisitor aCreationVisitor(*this, pRootPoint, pParentShape);
|
|
mpLayout->getNode()->setExistingShape(pParentShape);
|
|
mpLayout->getNode()->accept(aCreationVisitor);
|
|
|
|
// layout shapes - now all shapes are created
|
|
ShapeLayoutingVisitor aLayoutingVisitor(*this, pRootPoint);
|
|
mpLayout->getNode()->accept(aLayoutingVisitor);
|
|
|
|
sortChildrenByZOrder(pParentShape);
|
|
removeUnneededGroupShapes(pParentShape);
|
|
}
|
|
|
|
ShapePtr pBackground = std::make_shared<Shape>("com.sun.star.drawing.CustomShape");
|
|
pBackground->setSubType(XML_rect);
|
|
pBackground->getCustomShapeProperties()->setShapePresetType(XML_rect);
|
|
pBackground->setSize(pParentShape->getSize());
|
|
if (mpData->getBackgroundShapeFillProperties())
|
|
pBackground->getFillProperties() = *mpData->getBackgroundShapeFillProperties();
|
|
if (mpData->getBackgroundShapeLineProperties())
|
|
pBackground->getLineProperties() = *mpData->getBackgroundShapeLineProperties();
|
|
|
|
// create and set ModelID for BackgroundShape to allow later association
|
|
getData()->setBackgroundShapeModelID(OStringToOUString(comphelper::xml::generateGUIDString(), RTL_TEXTENCODING_UTF8));
|
|
pBackground->setDiagramDataModelID(getData()->getBackgroundShapeModelID());
|
|
|
|
auto& aChildren = pParentShape->getChildren();
|
|
aChildren.insert(aChildren.begin(), pBackground);
|
|
}
|
|
|
|
uno::Reference<xml::dom::XDocument> SmartArtDiagram::convertAndSet(std::u16string_view rDOM, svx::diagram::DomMapFlag aDomMapFlag)
|
|
{
|
|
// construct MemoryStream and OStreamWrapper
|
|
const OString sUtf8(OUStringToOString(rDOM, RTL_TEXTENCODING_UTF8));
|
|
SvMemoryStream aStream(const_cast<char*>(sUtf8.getStr()), sUtf8.getLength(), StreamMode::READ);
|
|
rtl::Reference<utl::OStreamWrapper> pStreamWrapper = new utl::OStreamWrapper(aStream);
|
|
|
|
// create the dom parser & create DomTree
|
|
uno::Reference<xml::dom::XDocumentBuilder> xDomBuilder(xml::dom::DocumentBuilder::create(comphelper::getProcessComponentContext()));
|
|
uno::Reference<xml::dom::XDocument> aDomTree(xDomBuilder->parse(pStreamWrapper->getInputStream()));
|
|
|
|
// set DomTree locally
|
|
setOOXDomValue(aDomMapFlag, uno::Any(aDomTree));
|
|
return aDomTree;
|
|
}
|
|
|
|
SmartArtDiagram::SmartArtDiagram()
|
|
: maDiagramFontHeights()
|
|
, mpData(std::make_shared<DiagramData_oox>())
|
|
, mpLayout(std::make_shared<DiagramLayout>(*this))
|
|
, maStyles()
|
|
, maColors()
|
|
, maDiagramPRDomMap()
|
|
{
|
|
}
|
|
|
|
SmartArtDiagram::SmartArtDiagram(SmartArtDiagram const& rSource)
|
|
: maDiagramFontHeights()
|
|
, mpData(rSource.mpData ? new DiagramData_oox(*rSource.mpData) : nullptr)
|
|
, mpLayout(rSource.mpLayout)
|
|
, maStyles(rSource.maStyles)
|
|
, maColors(rSource.maColors)
|
|
, maDiagramPRDomMap(rSource.maDiagramPRDomMap)
|
|
{
|
|
}
|
|
|
|
SmartArtDiagram::SmartArtDiagram(const boost::property_tree::ptree& rDiagramModel)
|
|
: maDiagramFontHeights()
|
|
, mpData(std::make_shared<DiagramData_oox>(rDiagramModel))
|
|
, mpLayout(std::make_shared<DiagramLayout>(*this))
|
|
, maStyles()
|
|
, maColors()
|
|
, maDiagramPRDomMap()
|
|
{
|
|
#ifdef DBG_UTIL
|
|
mpData->dump();
|
|
#endif
|
|
const OUString aOOXLayoutDOM(OUString::fromUtf8(rDiagramModel.get("OOXLayout", "")));
|
|
const OUString aOOXStyleDOM(OUString::fromUtf8(rDiagramModel.get("OOXStyle", "")));
|
|
const OUString aOOXColorDOM(OUString::fromUtf8(rDiagramModel.get("OOXColor", "")));
|
|
|
|
if (!aOOXLayoutDOM.isEmpty() || !aOOXStyleDOM.isEmpty() || !aOOXColorDOM.isEmpty())
|
|
{
|
|
// we need a PowerPointImport for the FragmentHandlers, so create a single
|
|
// temporary one. Use this for all possible DomTrees
|
|
rtl::Reference<oox::ppt::PowerPointImport> xPPTImport(new oox::ppt::PowerPointImport(comphelper::getProcessComponentContext()));
|
|
|
|
if (!aOOXLayoutDOM.isEmpty())
|
|
{
|
|
// create and set DomTree locally
|
|
uno::Reference<xml::dom::XDocument> xDom(convertAndSet(aOOXLayoutDOM, svx::diagram::DomMapFlag::OOXLayout));
|
|
|
|
// import DomTree to mpLayout
|
|
uno::Reference<xml::sax::XFastSAXSerializable> xSerializer(xDom, uno::UNO_QUERY_THROW);
|
|
rtl::Reference< core::FragmentHandler > xRefLayout(new DiagramLayoutFragmentHandler(*this, *xPPTImport, "internal", mpLayout));
|
|
xPPTImport->importFragment(xRefLayout, xSerializer);
|
|
}
|
|
|
|
if (!aOOXStyleDOM.isEmpty())
|
|
{
|
|
// create and set DomTree locally
|
|
uno::Reference<xml::dom::XDocument> xDom(convertAndSet(aOOXStyleDOM, svx::diagram::DomMapFlag::OOXStyle));
|
|
|
|
// import DomTree to maStyles
|
|
uno::Reference<xml::sax::XFastSAXSerializable> xSerializer(xDom, uno::UNO_QUERY_THROW);
|
|
rtl::Reference< core::FragmentHandler > xRefLayout(new DiagramQStylesFragmentHandler(*xPPTImport, "internal", maStyles));
|
|
xPPTImport->importFragment(xRefLayout, xSerializer);
|
|
}
|
|
|
|
if (!aOOXColorDOM.isEmpty())
|
|
{
|
|
// create and set DomTree locally
|
|
uno::Reference<xml::dom::XDocument> xDom(convertAndSet(aOOXColorDOM, svx::diagram::DomMapFlag::OOXColor));
|
|
|
|
// import DomTree to maColors
|
|
uno::Reference<xml::sax::XFastSAXSerializable> xSerializer(xDom, uno::UNO_QUERY_THROW);
|
|
rtl::Reference< core::FragmentHandler > xRefLayout(new ColorFragmentHandler(*xPPTImport, "internal", maColors));
|
|
xPPTImport->importFragment(xRefLayout, xSerializer);
|
|
}
|
|
}
|
|
}
|
|
|
|
SmartArtDiagram::~SmartArtDiagram()
|
|
{
|
|
}
|
|
|
|
uno::Any SmartArtDiagram::getOOXDomValue(svx::diagram::DomMapFlag aDomMapFlag) const
|
|
{
|
|
const DiagramPRDomMap::const_iterator aHit = maDiagramPRDomMap.find(aDomMapFlag);
|
|
|
|
if (aHit != maDiagramPRDomMap.end())
|
|
return aHit->second;
|
|
|
|
return uno::Any();
|
|
}
|
|
|
|
void SmartArtDiagram::setOOXDomValue(svx::diagram::DomMapFlag aDomMapFlag, const uno::Any& rValue)
|
|
{
|
|
maDiagramPRDomMap[aDomMapFlag] = rValue;
|
|
}
|
|
|
|
void SmartArtDiagram::resetOOXDomValues(svx::diagram::DomMapFlags aDomMapFlags)
|
|
{
|
|
for (const auto& rEntry : aDomMapFlags)
|
|
{
|
|
maDiagramPRDomMap.erase(rEntry);
|
|
|
|
if (maDiagramPRDomMap.empty())
|
|
return;
|
|
}
|
|
}
|
|
|
|
bool SmartArtDiagram::checkMinimalDataDoms() const
|
|
{
|
|
// check if re-creation is activated
|
|
if (!SdrObject::useAdvancedDiagramFeatures() && maDiagramPRDomMap.end() == maDiagramPRDomMap.find(svx::diagram::DomMapFlag::OOXData))
|
|
return false;
|
|
|
|
if (maDiagramPRDomMap.end() == maDiagramPRDomMap.find(svx::diagram::DomMapFlag::OOXLayout))
|
|
return false;
|
|
|
|
if (maDiagramPRDomMap.end() == maDiagramPRDomMap.find(svx::diagram::DomMapFlag::OOXStyle))
|
|
return false;
|
|
|
|
if (maDiagramPRDomMap.end() == maDiagramPRDomMap.find(svx::diagram::DomMapFlag::OOXColor))
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
void SmartArtDiagram::writeDiagramOOXData(DrawingML& rOriginalDrawingML, uno::Reference<io::XOutputStream>& xOutputStream, std::u16string_view rDrawingRelId) const
|
|
{
|
|
if (!xOutputStream)
|
|
return;
|
|
|
|
// re-create OOXData DomFile from model data
|
|
sax_fastparser::FSHelperPtr aFS = std::make_shared<sax_fastparser::FastSerializerHelper>(xOutputStream, true);
|
|
getData()->writeDiagramData(rOriginalDrawingML, aFS, rDrawingRelId);
|
|
|
|
// this call is *important*, without it xDocBuilder->parse below fails and some strange
|
|
// and wrong assertion gets thrown in ~FastSerializerHelper that shall get called
|
|
xOutputStream->closeOutput();
|
|
|
|
#ifdef DBG_UTIL
|
|
uno::Reference< embed::XRelationshipAccess > xRelations( xOutputStream, uno::UNO_QUERY );
|
|
if( xRelations.is() )
|
|
{
|
|
const uno::Sequence<uno::Sequence<beans::StringPair>> aSeqs = xRelations->getAllRelationships();
|
|
for (const uno::Sequence<beans::StringPair>& aSeq : aSeqs)
|
|
{
|
|
SAL_INFO("oox", "RelationData:");
|
|
for (const beans::StringPair& aPair : aSeq)
|
|
SAL_INFO("oox", " Key: " << aPair.First << ", Value: " << aPair.Second);
|
|
}
|
|
}
|
|
|
|
const OUString env(o3tl::getEnvironment(u"DIAGRAM_DUMP_PATH"_ustr));
|
|
if(!env.isEmpty())
|
|
{
|
|
OUString url;
|
|
::osl::FileBase::getFileURLFromSystemPath(env, url);
|
|
SvFileStream aOutStream(url + "data_T.xml", StreamMode::WRITE|StreamMode::TRUNC);
|
|
uno::Reference<io::XStream> xOutStream(new utl::OStreamWrapper(aOutStream));
|
|
uno::Reference<io::XStream> xInStream(xOutputStream, uno::UNO_QUERY);
|
|
comphelper::OStorageHelper::CopyInputToOutput(xInStream->getInputStream(), xOutStream->getOutputStream());
|
|
}
|
|
#endif
|
|
}
|
|
|
|
void SmartArtDiagram::writeDiagramOOXDrawing(DrawingML& rOriginalDrawingML, uno::Reference<io::XOutputStream>& xOutputStream) const
|
|
{
|
|
if (!xOutputStream)
|
|
return;
|
|
|
|
// re-create OOXDrawing DomFile from model data
|
|
SAL_INFO("oox", "DiagramReCreate: creating DomMapFlag::OOXDrawing");
|
|
sax_fastparser::FSHelperPtr aFS = std::make_shared<sax_fastparser::FastSerializerHelper>(xOutputStream, true);
|
|
getData()->writeDiagramReplacement(rOriginalDrawingML, aFS);
|
|
|
|
// this call is *important*, without it xDocBuilder->parse below fails and some strange
|
|
// and wrong assertion gets thrown in ~FastSerializerHelper that shall get called
|
|
xOutputStream->closeOutput();
|
|
|
|
#ifdef DBG_UTIL
|
|
uno::Reference< embed::XRelationshipAccess > xRelations( xOutputStream, uno::UNO_QUERY );
|
|
if( xRelations.is() )
|
|
{
|
|
const uno::Sequence<uno::Sequence<beans::StringPair>> aSeqs = xRelations->getAllRelationships();
|
|
for (const uno::Sequence<beans::StringPair>& aSeq : aSeqs)
|
|
{
|
|
SAL_INFO("oox", "RelationDrawing:");
|
|
for (const beans::StringPair& aPair : aSeq)
|
|
SAL_INFO("oox", " Key: " << aPair.First << ", Value: " << aPair.Second);
|
|
}
|
|
}
|
|
|
|
const OUString env(o3tl::getEnvironment(u"DIAGRAM_DUMP_PATH"_ustr));
|
|
if(!env.isEmpty())
|
|
{
|
|
OUString url;
|
|
::osl::FileBase::getFileURLFromSystemPath(env, url);
|
|
SvFileStream aOutStream(url + "drawing_T.xml", StreamMode::WRITE|StreamMode::TRUNC);
|
|
uno::Reference<io::XStream> xOutStream(new utl::OStreamWrapper(aOutStream));
|
|
uno::Reference<io::XStream> xInStream(xOutputStream, uno::UNO_QUERY);
|
|
comphelper::OStorageHelper::CopyInputToOutput(xInStream->getInputStream(), xOutStream->getOutputStream());
|
|
}
|
|
#endif
|
|
}
|
|
|
|
void SmartArtDiagram::addDomTreeToModelData(svx::diagram::DomMapFlag aId, std::u16string_view aName, boost::property_tree::ptree& rTarget) const
|
|
{
|
|
uno::Reference<xml::dom::XDocument> aDomTree;
|
|
getOOXDomValue(aId) >>= aDomTree;
|
|
|
|
if (aDomTree)
|
|
{
|
|
// serialize DomTree to a MemoryStream
|
|
SvMemoryStream aStream( 1024, 1024 );
|
|
rtl::Reference<utl::OStreamWrapper> pStreamWrapper = new utl::OStreamWrapper( aStream );
|
|
uno::Reference<xml::sax::XSAXSerializable> serializer;
|
|
uno::Reference<xml::sax::XWriter> writer = xml::sax::Writer::create(comphelper::getProcessComponentContext());
|
|
serializer.set(aDomTree, uno::UNO_QUERY);
|
|
writer->setOutputStream(pStreamWrapper->getOutputStream());
|
|
serializer->serialize(uno::Reference<xml::sax::XDocumentHandler>(writer, uno::UNO_QUERY_THROW), uno::Sequence<beans::StringPair>());
|
|
|
|
// put into string
|
|
const OUString aContent(static_cast<const char*>(aStream.GetData()), aStream.TellEnd(), RTL_TEXTENCODING_UTF8);
|
|
|
|
// add to ModelData
|
|
const OString sUtf8(OUStringToOString(aName, RTL_TEXTENCODING_UTF8));
|
|
rTarget.put(sUtf8.getStr(), aContent);
|
|
}
|
|
}
|
|
|
|
void SmartArtDiagram::addDiagramModelData(boost::property_tree::ptree& rTarget) const
|
|
{
|
|
// add Point and Connection data
|
|
getData()->addDiagramModelData(rTarget);
|
|
|
|
// What DomMaps are needed?
|
|
//
|
|
// With the above OOXData is covered. OOXDataImageRels/OOXDataHlinkRels also,
|
|
// these may/will be re-created when OOX export and a new OOXDataDomTree
|
|
// needs to be created.
|
|
// Similar with OOXDrawing: This contains parts of ModelData, e.g. Text and
|
|
// Attributes represented by the XShapes/Sdrobjects, so for internal formats
|
|
// this is not needed to be saved. This is also true for OOXDrawingImageRels
|
|
// and OOXDrawingHlinkRels.
|
|
// We *do* import OOXLayoutDomTree/ModelInfo and this is used in the layouting
|
|
// mechanism (reLayout), but it is not changed. We could add an export of that
|
|
// for internal formats, but since it's not changed it just needs to be
|
|
// preserved, either for internal use or export to OOX formats.
|
|
// OOXStyle and OOXColor are imported only on oox import side, partially held
|
|
// for initial import at Diagram classes. Also never changed, but maybe needed
|
|
// for export to OOX formats. Not sure about that since Style and Color is
|
|
// part of XShape/SdrObject Model Hierarchy, so exports to OOX should be possible
|
|
// without these, but maybe MSO wants that data...
|
|
//
|
|
// OOXLayout = 3,
|
|
// OOXStyle = 4,
|
|
// OOXColor = 5,
|
|
addDomTreeToModelData(svx::diagram::DomMapFlag::OOXLayout, u"OOXLayout", rTarget);
|
|
addDomTreeToModelData(svx::diagram::DomMapFlag::OOXStyle, u"OOXStyle", rTarget);
|
|
addDomTreeToModelData(svx::diagram::DomMapFlag::OOXColor, u"OOXColor", rTarget);
|
|
}
|
|
|
|
using ShapePairs
|
|
= std::unordered_map<std::shared_ptr<drawingml::Shape>, uno::Reference<drawing::XShape>>;
|
|
|
|
void SmartArtDiagram::syncDiagramFontHeights()
|
|
{
|
|
// Each name represents a group of shapes, for which the font height should have the same
|
|
// scaling.
|
|
for (const auto& rNameAndPairs : maDiagramFontHeights)
|
|
{
|
|
// Find out the minimum scale within this group.
|
|
const ShapePairs& rShapePairs = rNameAndPairs.second;
|
|
double fMinFontScale = 100.0;
|
|
double fMinSpacingScale = 100.0;
|
|
for (const auto& rShapePair : rShapePairs)
|
|
{
|
|
uno::Reference<beans::XPropertySet> xPropertySet(rShapePair.second, uno::UNO_QUERY);
|
|
if (xPropertySet.is())
|
|
{
|
|
double fFontScale = 0.0;
|
|
double fSpacingScale = 0.0;
|
|
xPropertySet->getPropertyValue(u"TextFitToSizeFontScale"_ustr) >>= fFontScale;
|
|
xPropertySet->getPropertyValue(u"TextFitToSizeSpacingScale"_ustr) >>= fSpacingScale;
|
|
|
|
if (fFontScale > 0 && fSpacingScale > 0
|
|
&& (fFontScale < fMinFontScale || (fFontScale == fMinFontScale && fSpacingScale < fMinSpacingScale)))
|
|
{
|
|
fMinFontScale = fFontScale;
|
|
fMinSpacingScale = fSpacingScale;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Set that minimum scale for all members of the group.
|
|
if (fMinFontScale < 100.0 || fMinSpacingScale < 100.0)
|
|
{
|
|
for (const auto& rShapePair : rShapePairs)
|
|
{
|
|
uno::Reference<beans::XPropertySet> xPropertySet(rShapePair.second, uno::UNO_QUERY);
|
|
if (xPropertySet.is())
|
|
{
|
|
xPropertySet->setPropertyValue(u"TextFitToSizeFontScale"_ustr, uno::Any(fMinFontScale));
|
|
xPropertySet->setPropertyValue(u"TextFitToSizeSpacingScale"_ustr, uno::Any(fMinSpacingScale));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// no longer needed after processing
|
|
maDiagramFontHeights.clear();
|
|
}
|
|
|
|
static 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 );
|
|
}
|
|
|
|
static uno::Reference<xml::dom::XDocument> loadFragment(
|
|
core::XmlFilterBase& rFilter,
|
|
const rtl::Reference< core::FragmentHandler >& rxHandler )
|
|
{
|
|
return loadFragment( rFilter, rxHandler->getFragmentPath() );
|
|
}
|
|
|
|
static void importFragment( core::XmlFilterBase& rFilter,
|
|
const uno::Reference<xml::dom::XDocument>& rXDom,
|
|
svx::diagram::DomMapFlag aDomMapFlag,
|
|
const DiagramPtr& pDiagram,
|
|
const rtl::Reference< core::FragmentHandler >& rxHandler )
|
|
{
|
|
pDiagram->setOOXDomValue(aDomMapFlag, uno::Any(rXDom));
|
|
|
|
uno::Reference<xml::sax::XFastSAXSerializable> xSerializer(
|
|
rXDom, uno::UNO_QUERY_THROW);
|
|
|
|
// now serialize DOM tree into internal data structures
|
|
rFilter.importFragment( rxHandler, xSerializer );
|
|
}
|
|
|
|
namespace
|
|
{
|
|
/**
|
|
* A fragment handler that just counts the number of <dsp:sp> elements in a
|
|
* fragment.
|
|
*/
|
|
class DiagramShapeCounter : public oox::core::FragmentHandler2
|
|
{
|
|
public:
|
|
DiagramShapeCounter(oox::core::XmlFilterBase& rFilter, const OUString& rFragmentPath,
|
|
sal_Int32& nCounter);
|
|
oox::core::ContextHandlerRef onCreateContext(sal_Int32 nElement,
|
|
const AttributeList& rAttribs) override;
|
|
|
|
private:
|
|
sal_Int32& m_nCounter;
|
|
};
|
|
|
|
DiagramShapeCounter::DiagramShapeCounter(oox::core::XmlFilterBase& rFilter,
|
|
const OUString& rFragmentPath, sal_Int32& nCounter)
|
|
: FragmentHandler2(rFilter, rFragmentPath)
|
|
, m_nCounter(nCounter)
|
|
{
|
|
}
|
|
|
|
oox::core::ContextHandlerRef DiagramShapeCounter::onCreateContext(sal_Int32 nElement,
|
|
const AttributeList& /*rAttribs*/)
|
|
{
|
|
switch (nElement)
|
|
{
|
|
case DSP_TOKEN(drawing):
|
|
case DSP_TOKEN(spTree):
|
|
return this;
|
|
case DSP_TOKEN(sp):
|
|
++m_nCounter;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return nullptr;
|
|
}
|
|
}
|
|
|
|
void loadDiagram( ShapePtr const & pShape,
|
|
core::XmlFilterBase& rFilter,
|
|
const OUString& rDataModelPath,
|
|
const OUString& rLayoutPath,
|
|
const OUString& rQStylePath,
|
|
const OUString& rColorStylePath,
|
|
const oox::core::Relations& rRelations )
|
|
{
|
|
DiagramPtr pDiagram = std::make_shared<SmartArtDiagram>();
|
|
|
|
try
|
|
{
|
|
// set DiagramFontHeights at filter
|
|
rFilter.setDiagramFontHeights(&pDiagram->getDiagramFontHeights());
|
|
|
|
// data
|
|
if( !rDataModelPath.isEmpty() )
|
|
{
|
|
rtl::Reference< core::FragmentHandler > xRefDataModel(
|
|
new DiagramDataFragmentHandler( rFilter, rDataModelPath, pDiagram->getData() ));
|
|
|
|
importFragment(rFilter,
|
|
loadFragment(rFilter,xRefDataModel),
|
|
svx::diagram::DomMapFlag::OOXData,
|
|
pDiagram,
|
|
xRefDataModel);
|
|
|
|
uno::Sequence<uno::Sequence<uno::Any>> aDataImageRelsMap(
|
|
pShape->resolveRelationshipsOfTypeFromOfficeDoc(
|
|
rFilter, xRefDataModel->getFragmentPath(), u"image"));
|
|
uno::Sequence<uno::Sequence<uno::Any>> aDataHlinkRelsMap(
|
|
pShape->resolveRelationshipsOfTypeFromOfficeDoc(
|
|
rFilter, xRefDataModel->getFragmentPath(), u"hlink"));
|
|
|
|
pDiagram->setOOXDomValue(svx::diagram::DomMapFlag::OOXDataImageRels,
|
|
uno::Any(aDataImageRelsMap));
|
|
pDiagram->setOOXDomValue(svx::diagram::DomMapFlag::OOXDataHlinkRels,
|
|
uno::Any(aDataHlinkRelsMap));
|
|
|
|
// Pass the info to pShape
|
|
for (auto const& extDrawing : pDiagram->getData()->getExtDrawings())
|
|
{
|
|
OUString aFragmentPath = rRelations.getFragmentPathFromRelId(extDrawing);
|
|
// Ignore RelIds which don't resolve to a fragment path.
|
|
if (aFragmentPath.isEmpty())
|
|
continue;
|
|
|
|
sal_Int32 nCounter = 0;
|
|
rtl::Reference<core::FragmentHandler> xCounter(
|
|
new DiagramShapeCounter(rFilter, aFragmentPath, nCounter));
|
|
rFilter.importFragment(xCounter);
|
|
// Ignore ext drawings which don't actually have any shapes.
|
|
if (nCounter == 0)
|
|
continue;
|
|
|
|
pShape->addExtDrawingRelId(extDrawing);
|
|
}
|
|
}
|
|
|
|
// Layout: always import to allow editing in the future. It's needed for
|
|
// DiagramHelper_oox::reLayout to re-create the oox::Shape(s) for the
|
|
// model. Without importing these the diagram model will be not complete.
|
|
// NOTE: This also adds the DomMaps to rMainDomMap, so the lines
|
|
// DiagramDomMap& rMainDomMap = pDiagram->getDomMap();
|
|
// rMainDomMap[u"OOXLayout"_ustr] = loadFragment(rFilter,rLayoutPath);
|
|
// rMainDomMap[u"OOXStyle"_ustr] = loadFragment(rFilter,rQStylePath);
|
|
// which were used before if !pShape->getExtDrawings().empty() are not
|
|
// needed
|
|
if (!rLayoutPath.isEmpty())
|
|
{
|
|
rtl::Reference< core::FragmentHandler > xRefLayout(
|
|
new DiagramLayoutFragmentHandler( *pDiagram, rFilter, rLayoutPath, pDiagram->getLayout()));
|
|
|
|
importFragment(rFilter,
|
|
loadFragment(rFilter,xRefLayout),
|
|
svx::diagram::DomMapFlag::OOXLayout,
|
|
pDiagram,
|
|
xRefLayout);
|
|
}
|
|
|
|
// Style: same as for Layout (above)
|
|
if( !rQStylePath.isEmpty() )
|
|
{
|
|
rtl::Reference< core::FragmentHandler > xRefQStyle(
|
|
new DiagramQStylesFragmentHandler( rFilter, rQStylePath, pDiagram->getStyles() ));
|
|
|
|
importFragment(rFilter,
|
|
loadFragment(rFilter,xRefQStyle),
|
|
svx::diagram::DomMapFlag::OOXStyle,
|
|
pDiagram,
|
|
xRefQStyle);
|
|
}
|
|
|
|
// colors
|
|
if( !rColorStylePath.isEmpty() )
|
|
{
|
|
rtl::Reference< core::FragmentHandler > xRefColorStyle(
|
|
new ColorFragmentHandler( rFilter, rColorStylePath, pDiagram->getColors() ));
|
|
|
|
importFragment(rFilter,
|
|
loadFragment(rFilter,xRefColorStyle),
|
|
svx::diagram::DomMapFlag::OOXColor,
|
|
pDiagram,
|
|
xRefColorStyle);
|
|
}
|
|
|
|
if( !pDiagram->getData()->getExtDrawings().empty() )
|
|
{
|
|
const DiagramColorMap::const_iterator aColor = pDiagram->getColors().find(u"node0"_ustr);
|
|
if( aColor != pDiagram->getColors().end() && !aColor->second.maTextFillColors.empty())
|
|
{
|
|
// TODO(F1): well, actually, there might be *several* color
|
|
// definitions in it, after all it's called list.
|
|
pShape->setFontRefColorForNodes(DiagramColor::getColorByIndex(aColor->second.maTextFillColors, -1));
|
|
}
|
|
}
|
|
|
|
// collect data, init maps
|
|
// for Diagram import, do - for now - NOT clear all oox::drawingml::Shape
|
|
pDiagram->getData()->buildDiagramDataModel(false);
|
|
#ifdef DBG_UTIL
|
|
pDiagram->getData()->dump();
|
|
#endif
|
|
|
|
// diagram loaded. now lump together & attach to shape
|
|
// create own geometry if extLst is not present (no geometric
|
|
// representation is available in file). This will - if false -
|
|
// just create the BackgroundShape.
|
|
// NOTE: Need to use pShape->getExtDrawings() here, this is the
|
|
// already *filtered* version, see usage of DiagramShapeCounter
|
|
// above. Moving to local bool, there might more conditions show
|
|
// up
|
|
static bool bIgnoreExtDrawings(nullptr != std::getenv("DIAGRAM_IGNORE_EXTDRAWINGS"));
|
|
const bool bCreate(bIgnoreExtDrawings || pShape->getExtDrawings().empty());
|
|
pDiagram->createShapeHierarchyFromModel(pShape, bCreate);
|
|
|
|
// Get the oox::Theme definition and - if available - move/secure the
|
|
// original ImportData directly to the Diagram ModelData
|
|
std::shared_ptr<::oox::drawingml::Theme> aTheme(rFilter.getCurrentThemePtr());
|
|
if(aTheme)
|
|
pDiagram->getData()->setThemeDocument(aTheme->getFragment());
|
|
|
|
// Prepare support for the advanced DiagramHelper using Diagram & Theme data
|
|
// This is where pDiagram is moved to where it will stay, else it will get
|
|
// cleaned up (what is intended)
|
|
pShape->prepareDiagramHelper(pDiagram, rFilter.getCurrentThemePtr());
|
|
}
|
|
catch (...)
|
|
{
|
|
// unset DiagramFontHeights at filter if there was a failure
|
|
// to avoid dangling pointer
|
|
rFilter.setDiagramFontHeights(nullptr);
|
|
throw;
|
|
}
|
|
}
|
|
|
|
const oox::drawingml::Color&
|
|
DiagramColor::getColorByIndex(const std::vector<oox::drawingml::Color>& rColors, sal_Int32 nIndex)
|
|
{
|
|
assert(!rColors.empty());
|
|
if (nIndex == -1)
|
|
{
|
|
return rColors[rColors.size() - 1];
|
|
}
|
|
|
|
return rColors[nIndex % rColors.size()];
|
|
}
|
|
}
|
|
|
|
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|