SmartArt: Added export of OOXDiagram

This change adds writing the OOXDiagram SmartArt replacement
graphic. This makes stuff working better, but still stuff
missing. With this we have now two of the 4-7 (optional)
DomTrees used for the Diagram data representation.
Still deactivated, to use for experiments you will need to
set ACTIVATE_RECREATE_DIAGRAM_DATADOMS.

Change-Id: Ic6c1108d6cf0037302085b6f22433b199d40ee53
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/198435
Tested-by: Jenkins
Reviewed-by: Armin Le Grand <Armin.Le.Grand@me.com>
This commit is contained in:
Armin Le Grand (collabora)
2026-01-30 17:49:29 +01:00
committed by Armin Le Grand
parent d7c2f3dfe7
commit f2d455d03f
11 changed files with 159 additions and 22 deletions
@@ -28,6 +28,7 @@
namespace oox::drawingml {
class Diagram;
class DrawingML;
// Advanced DiagramHelper
//
@@ -101,7 +102,7 @@ public:
// check if mandatory DiagramDomS exist (or can be created)
bool checkMinimalDataDoms() const;
void tryToCreateMissingDataDoms(oox::core::XmlFilterBase& rFB);
void tryToCreateMissingDataDoms(DrawingML& rOriginalDrawingML);
};
}
+7
View File
@@ -316,6 +316,9 @@ protected:
/// True when exporting presentation placeholder shape.
bool mbPlaceholder;
/// True when DiagramReplacementVisualization is exported
bool mbDiagaramExport;
bool mbEmbedFonts = false;
bool GetProperty( const css::uno::Reference< css::beans::XPropertySet >& rXPropSet, const OUString& aName );
@@ -376,6 +379,10 @@ public:
/// The application-specific text exporter callback, if there is one.
DMLTextExport* GetTextExport() { return mpTextExport; }
/// get/set mbDiagaramExport
void setDiagaramExport(bool bNew) { mbDiagaramExport = bNew; }
bool isDiagaramExport() const { return mbDiagaramExport;}
void SetBackgroundDark(bool bIsDark) { mbIsBackgroundDark = bIsDark; }
/// If bRelPathToMedia is true add "../" to image folder path while adding the image relationship
OOX_DLLPUBLIC OUString writeGraphicToStorage(const Graphic &rGraphic , bool bRelPathToMedia = false, GraphicExport::TypeHint eHint = GraphicExport::TypeHint::Detect);
@@ -121,6 +121,9 @@ public:
// access to PropertyValues
virtual css::uno::Any getOOXDomValue(svx::diagram::DomMapFlag aDomMapFlag) const = 0;
// access to RootShape - the GroupObject used to host this Diagram
css::uno::Reference< css::drawing::XShape >& getRootShape() { return accessRootShape(); }
};
}} // end of namespace
+56 -6
View File
@@ -34,6 +34,7 @@
#include <oox/token/namespaces.hxx>
#include <oox/export/drawingml.hxx>
#include <sax/fastattribs.hxx>
#include <oox/export/shapes.hxx>
#include <unordered_set>
@@ -59,14 +60,63 @@ Shape* DiagramData_oox::getOrCreateAssociatedShape(const svx::diagram::Point& rP
return rShapePtr.get();
}
void DiagramData_oox::writeDiagramData(oox::core::XmlFilterBase& rFB, sax_fastparser::FSHelperPtr& rTarget)
void DiagramData_oox::writeDiagramReplacement(DrawingML& rOriginalDrawingML, sax_fastparser::FSHelperPtr& rTarget)
{
if (!rTarget)
return;
uno::Reference<drawing::XShapes> xShapes(accessRootShape(), uno::UNO_QUERY);
if (!xShapes.is())
return;
// create an own ShapeExport with the needed target and namespace, mark as DiagramExport
::oox::core::XmlFilterBase* pOriginalFB(rOriginalDrawingML.GetFB());
ShapeExport aShapeExport(XML_dsp, rTarget, nullptr, pOriginalFB, rOriginalDrawingML.GetDocumentType(), rOriginalDrawingML.GetTextExport(), true);
aShapeExport.setDiagaramExport(true);
const sal_Int32 nCount(xShapes->getCount());
// write header infos
const OUString aNsDmlDiagram(pOriginalFB->getNamespaceURL(OOX_NS(dmlDiagram)));
const OUString aNsDsp(pOriginalFB->getNamespaceURL(OOX_NS(dsp)));
const OUString aNsDml(pOriginalFB->getNamespaceURL(OOX_NS(dml)));
rTarget->startElementNS(XML_dsp, XML_drawing,
FSNS(XML_xmlns, XML_dgm), aNsDmlDiagram,
FSNS(XML_xmlns, XML_dsp), aNsDsp,
FSNS(XML_xmlns, XML_a), aNsDml);
rTarget->startElementNS(XML_dsp, XML_spTree);
for ( sal_Int32 i = 0; i < nCount; ++i )
{
uno::Reference< drawing::XShape > xShape;
if ( xShapes->getByIndex( i ) >>= xShape )
{
if (xShape)
{
// do *not* write BackgroundShape. MSO has BG infos just as fill properties
// as part of the OOXData DomTree
SdrObject* pTarget(SdrObject::getSdrObjectFromXShape(xShape));
if (nullptr == pTarget || getBackgroundShapeModelID() == pTarget->getDiagramDataModelID())
continue;
// write Shape & sub-shapes
aShapeExport.WriteShape(xShape);
}
}
}
rTarget->endElementNS(XML_dsp, XML_spTree);
rTarget->endElementNS(XML_dsp, XML_drawing);
rTarget->endDocument();
}
void DiagramData_oox::writeDiagramData(oox::core::XmlFilterBase& rOriginalFB, sax_fastparser::FSHelperPtr& rTarget)
{
if (!rTarget)
return;
// write header infos
const OUString aNsDmlDiagram(rFB.getNamespaceURL(OOX_NS(dmlDiagram)));
const OUString aNsDml(rFB.getNamespaceURL(OOX_NS(dml)));
const OUString aNsDmlDiagram(rOriginalFB.getNamespaceURL(OOX_NS(dmlDiagram)));
const OUString aNsDml(rOriginalFB.getNamespaceURL(OOX_NS(dml)));
rTarget->startElementNS(XML_dgm, XML_dataModel,
FSNS(XML_xmlns, XML_dgm), aNsDmlDiagram,
FSNS(XML_xmlns, XML_a), aNsDml);
@@ -90,7 +140,7 @@ void DiagramData_oox::writeDiagramData(oox::core::XmlFilterBase& rFB, sax_fastpa
if (xMasterText)
{
rTarget->startElementNS(XML_dgm, XML_t);
DrawingML aTempML(rTarget, &rFB);
DrawingML aTempML(rTarget, &rOriginalFB);
aTempML.WriteText(xMasterText, false, true, XML_a);
rTarget->endElementNS(XML_dgm, XML_t);
@@ -145,7 +195,7 @@ void DiagramData_oox::writeDiagramData(oox::core::XmlFilterBase& rFB, sax_fastpa
{
// if we have the BGShape as XShape, export using a temp DrawingML which uses
// the target file combined with the XmlFilterBase representing the ongoing Diagram export
DrawingML aTempML(rTarget, &rFB);
DrawingML aTempML(rTarget, &rOriginalFB);
uno::Reference<beans::XPropertySet> xProps(xBgShape, uno::UNO_QUERY);
aTempML.WriteFill( xProps, xBgShape->getSize());
}
@@ -159,7 +209,7 @@ void DiagramData_oox::writeDiagramData(oox::core::XmlFilterBase& rFB, sax_fastpa
// for this case where the only relevant data is the 'relId' entry I will allow
// to construct the XML statement by own string concatenation
rTarget->startElementNS(XML_dgm, XML_extLst);
const OUString rNsDsp(rFB.getNamespaceURL(OOX_NS(dsp)));
const OUString rNsDsp(rOriginalFB.getNamespaceURL(OOX_NS(dsp)));
rTarget->startElementNS(XML_a, XML_ext, XML_uri, rNsDsp);
OUString aDspLine("<dsp:dataModelExt xmlns:dsp=\"" + rNsDsp + "\" ");
if (!getExtDrawings().empty())
@@ -34,6 +34,8 @@
namespace oox::drawingml {
class DrawingML;
class DiagramData_oox : public svx::diagram::DiagramData_svx
{
public:
@@ -50,7 +52,8 @@ public:
Shape* getOrCreateAssociatedShape(const svx::diagram::Point& rPoint, bool bCreateOnDemand = false) const;
void writeDiagramData(oox::core::XmlFilterBase& rFB, sax_fastparser::FSHelperPtr& rTarget);
void writeDiagramReplacement(DrawingML& rOriginalDrawingML, sax_fastparser::FSHelperPtr& rTarget);
void writeDiagramData(oox::core::XmlFilterBase& rOriginalFB, sax_fastparser::FSHelperPtr& rTarget);
private:
// The model definition, the parts *only* available in oox. Also look for already
+54 -10
View File
@@ -44,10 +44,11 @@
#include "diagramfragmenthandler.hxx"
#include <comphelper/processfactory.hxx>
#include <com/sun/star/io/TempFile.hpp>
#include <oox/export/drawingml.hxx>
#ifdef DBG_UTIL
#include <osl/file.hxx>
#include <iostream>
#include <o3tl/environment.hxx>
#endif
using namespace ::com::sun::star;
@@ -200,25 +201,23 @@ bool Diagram::checkMinimalDataDoms() const
return true;
}
void Diagram::tryToCreateMissingDataDoms(oox::core::XmlFilterBase& rFB)
void Diagram::tryToCreateMissingDataDoms(DrawingML& rOriginalDrawingML)
{
// internal testing: allow to force to always recreate
static bool bForceAlwaysReCreate(false);
static bool bForceAlwaysReCreate(nullptr != std::getenv("FORCE_RECREATE_DIAGRAM_DATADOMS"));
// check if activated, return if not to stay compatible for now
static bool bReCreateDiagramDataDoms(nullptr != std::getenv("ACTIVATE_RECREATE_DIAGRAM_DATADOMS"));
#ifdef DBG_UTIL
std::cout << "DiagramReCreate: always==" << bForceAlwaysReCreate << ",bReCreate==" << bReCreateDiagramDataDoms << std::endl;
#endif
SAL_INFO("oox", "DiagramReCreate: always==" << bForceAlwaysReCreate << ",bReCreate==" << bReCreateDiagramDataDoms);
if (!bForceAlwaysReCreate && !bReCreateDiagramDataDoms)
return;
oox::core::XmlFilterBase& rFB(*rOriginalDrawingML.GetFB());
if (bForceAlwaysReCreate || maDiagramPRDomMap.end() == maDiagramPRDomMap.find(svx::diagram::DomMapFlag::OOXData))
{
// re-create OOXData DomFile from model data
#ifdef DBG_UTIL
std::cout << "DiagramReCreate: creating DomMapFlag::OOXData" << std::endl;
#endif
SAL_INFO("oox", "DiagramReCreate: creating DomMapFlag::OOXData");
uno::Reference< io::XTempFile > xTempFile = io::TempFile::create(comphelper::getProcessComponentContext());
uno::Reference< io::XOutputStream > xOutput = xTempFile->getOutputStream();
@@ -243,7 +242,52 @@ void Diagram::tryToCreateMissingDataDoms(oox::core::XmlFilterBase& rFB)
}
#ifdef DBG_UTIL
osl::File::move(xTempFile->getUri(), "file:///home/alg/Downloads/tonne/test.xml");
const OUString env(o3tl::getEnvironment(u"DIAGRAM_DUMP_PATH"_ustr));
if(!env.isEmpty())
{
OUString url;
::osl::FileBase::getFileURLFromSystemPath(env, url);
osl::File::move(xTempFile->getUri(), url + "data_T.xml");
}
#endif
}
}
if (bForceAlwaysReCreate || maDiagramPRDomMap.end() == maDiagramPRDomMap.find(svx::diagram::DomMapFlag::OOXDrawing))
{
// re-create OOXDrawing DomFile from model data
SAL_INFO("oox", "DiagramReCreate: creating DomMapFlag::OOXDrawing");
uno::Reference< io::XTempFile > xTempFile = io::TempFile::create(comphelper::getProcessComponentContext());
uno::Reference< io::XOutputStream > xOutput = xTempFile->getOutputStream();
if (xOutput)
{
sax_fastparser::FSHelperPtr aFS = std::make_shared<sax_fastparser::FastSerializerHelper>(xOutput, true);
getData()->writeDiagramReplacement(rOriginalDrawingML, aFS);
xOutput->flush();
// this call is *important*, without it xDocBuilder->parse below fails and some strange
// and wrong assertion gets thrown in ~FastSerializerHelper that shall get called
xOutput->closeOutput();
uno::Reference<xml::dom::XDocumentBuilder> xDocBuilder(xml::dom::DocumentBuilder::create(comphelper::getProcessComponentContext()));
if (xDocBuilder)
{
uno::Reference<xml::dom::XDocument> xInstance = xDocBuilder->parse(xTempFile->getInputStream());
if (xInstance)
{
maDiagramPRDomMap[svx::diagram::DomMapFlag::OOXDrawing] <<= xInstance;
}
}
#ifdef DBG_UTIL
const OUString env(o3tl::getEnvironment(u"DIAGRAM_DUMP_PATH"_ustr));
if(!env.isEmpty())
{
OUString url;
::osl::FileBase::getFileURLFromSystemPath(env, url);
osl::File::move(xTempFile->getUri(), url + "drawing_T.xml");
}
#endif
}
}
+1 -1
View File
@@ -153,7 +153,7 @@ public:
// check if mandatory DiagramDomS exist (or can be created)
bool checkMinimalDataDoms() const;
void tryToCreateMissingDataDoms(oox::core::XmlFilterBase& rFB);
void tryToCreateMissingDataDoms(DrawingML& rOriginalDrawingML);
private:
// This contains groups of shapes: automatic font size is the same in each group.
@@ -469,12 +469,12 @@ bool DiagramHelper_oox::checkMinimalDataDoms() const
return mpDiagramPtr->checkMinimalDataDoms();
}
void DiagramHelper_oox::tryToCreateMissingDataDoms(oox::core::XmlFilterBase& rFB)
void DiagramHelper_oox::tryToCreateMissingDataDoms(DrawingML& rOriginalDrawingML)
{
if (!mpDiagramPtr)
return;
mpDiagramPtr->tryToCreateMissingDataDoms(rFB);
mpDiagramPtr->tryToCreateMissingDataDoms(rOriginalDrawingML);
}
}
+21 -1
View File
@@ -265,6 +265,7 @@ DrawingML::DrawingML(::sax_fastparser::FSHelperPtr pFS, ::oox::core::XmlFilterBa
, mpFB(pFB)
, mbIsBackgroundDark(false)
, mbPlaceholder(false)
, mbDiagaramExport(false)
{
uno::Reference<beans::XPropertySet> xSettings(pFB->getModelFactory()->createInstance(u"com.sun.star.document.Settings"_ustr), uno::UNO_QUERY);
if (xSettings.is())
@@ -2327,6 +2328,25 @@ void DrawingML::WriteShapeTransformation( const Reference< XShape >& rXShape, sa
bFlipH = bFlipH && !bFlippedBeforeRotation;
bFlipV = bFlipV && !bFlippedBeforeRotation;
if (isDiagaramExport())
{
// need to offset from top-left of Diagram's GroupObject
SdrObject* pShape(SdrObject::getSdrObjectFromXShape(rXShape));
if (nullptr != pShape)
{
std::shared_ptr< svx::diagram::DiagramHelper_svx > pDiagramHelper(pShape->getDiagramHelperFromDiagramOrMember());
if (pDiagramHelper)
{
const Reference< XShape >& rRootShape(pDiagramHelper->getRootShape());
awt::Point aRootPos = rRootShape->getPosition();
aPos.X -= aRootPos.X;
aPos.Y -= aRootPos.Y;
}
}
}
if (GetDocumentType() == DOCUMENT_DOCX && m_xParent.is())
{
awt::Point aParentPos = m_xParent->getPosition();
@@ -6793,7 +6813,7 @@ bool DrawingML::PrepareToWriteAsDiagram(const css::uno::Reference<css::drawing::
return false;
// try to re-create (if needed is decided there)
pAdvancedDiagramHelper->tryToCreateMissingDataDoms(*GetFB());
pAdvancedDiagramHelper->tryToCreateMissingDataDoms(*this);
if (!pAdvancedDiagramHelper->checkMinimalDataDoms())
return false;
+8
View File
@@ -912,6 +912,14 @@ ShapeExport& ShapeExport::WriteCustomShape( const Reference< XShape >& xShape )
}
}
if (isDiagaramExport())
{
// add evtl. used DiagramDataModelID if DiagramExport
SdrObject* pTarget(SdrObject::getSdrObjectFromXShape(xShape));
if (nullptr != pTarget && !pTarget->getDiagramDataModelID().isEmpty())
pAttrListSp->add(XML_modelId, pTarget->getDiagramDataModelID());
}
// export <sp> element (with a namespace prefix)
mpFS->startElementNS(mnXmlNamespace, XML_sp, pAttrListSp);
}
+1
View File
@@ -2024,6 +2024,7 @@ dropLine
dropLines
dropauto
ds
dsp
dstNode
dstrike
dt