include/oox/drawingml/diagram/diagramhelper_oox.hxx | 3 include/oox/export/drawingml.hxx | 7 ++ include/svx/diagram/DiagramHelper_svx.hxx | 3 oox/source/drawingml/diagram/datamodel_oox.cxx | 62 +++++++++++++++++-- oox/source/drawingml/diagram/datamodel_oox.hxx | 5 + oox/source/drawingml/diagram/diagram.cxx | 64 ++++++++++++++++---- oox/source/drawingml/diagram/diagram.hxx | 2 oox/source/drawingml/diagram/diagramhelper_oox.cxx | 4 - oox/source/export/drawingml.cxx | 22 ++++++ oox/source/export/shapes.cxx | 8 ++ oox/source/token/tokens.txt | 1 11 files changed, 159 insertions(+), 22 deletions(-)
New commits: commit f2d455d03fd980fa2c92607d66370de28d3d9e60 Author: Armin Le Grand (collabora) <[email protected]> AuthorDate: Fri Jan 30 15:11:36 2026 +0100 Commit: Armin Le Grand <[email protected]> CommitDate: Fri Jan 30 17:49:29 2026 +0100 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 <[email protected]> diff --git a/include/oox/drawingml/diagram/diagramhelper_oox.hxx b/include/oox/drawingml/diagram/diagramhelper_oox.hxx index f75a87752991..b3c9cb3f3cd2 100644 --- a/include/oox/drawingml/diagram/diagramhelper_oox.hxx +++ b/include/oox/drawingml/diagram/diagramhelper_oox.hxx @@ -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); }; } diff --git a/include/oox/export/drawingml.hxx b/include/oox/export/drawingml.hxx index 36378c9dbdb4..75a14e6fe8cd 100644 --- a/include/oox/export/drawingml.hxx +++ b/include/oox/export/drawingml.hxx @@ -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); diff --git a/include/svx/diagram/DiagramHelper_svx.hxx b/include/svx/diagram/DiagramHelper_svx.hxx index f6591f598236..517bc77699d5 100644 --- a/include/svx/diagram/DiagramHelper_svx.hxx +++ b/include/svx/diagram/DiagramHelper_svx.hxx @@ -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 diff --git a/oox/source/drawingml/diagram/datamodel_oox.cxx b/oox/source/drawingml/diagram/datamodel_oox.cxx index 2dafea9ff620..1a72c837ce18 100644 --- a/oox/source/drawingml/diagram/datamodel_oox.cxx +++ b/oox/source/drawingml/diagram/datamodel_oox.cxx @@ -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()) diff --git a/oox/source/drawingml/diagram/datamodel_oox.hxx b/oox/source/drawingml/diagram/datamodel_oox.hxx index 7845ffd5bec2..b3f6eb85fe8a 100644 --- a/oox/source/drawingml/diagram/datamodel_oox.hxx +++ b/oox/source/drawingml/diagram/datamodel_oox.hxx @@ -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 diff --git a/oox/source/drawingml/diagram/diagram.cxx b/oox/source/drawingml/diagram/diagram.cxx index 70eccc96c93c..704d0b030ed2 100644 --- a/oox/source/drawingml/diagram/diagram.cxx +++ b/oox/source/drawingml/diagram/diagram.cxx @@ -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 } } diff --git a/oox/source/drawingml/diagram/diagram.hxx b/oox/source/drawingml/diagram/diagram.hxx index c97c6699693a..8f79c852932e 100644 --- a/oox/source/drawingml/diagram/diagram.hxx +++ b/oox/source/drawingml/diagram/diagram.hxx @@ -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. diff --git a/oox/source/drawingml/diagram/diagramhelper_oox.cxx b/oox/source/drawingml/diagram/diagramhelper_oox.cxx index 42d4af5a9359..593a06a96160 100644 --- a/oox/source/drawingml/diagram/diagramhelper_oox.cxx +++ b/oox/source/drawingml/diagram/diagramhelper_oox.cxx @@ -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); } } diff --git a/oox/source/export/drawingml.cxx b/oox/source/export/drawingml.cxx index 8f673cfd49b1..aa1ea00bb173 100644 --- a/oox/source/export/drawingml.cxx +++ b/oox/source/export/drawingml.cxx @@ -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; diff --git a/oox/source/export/shapes.cxx b/oox/source/export/shapes.cxx index 29dba9826903..27947ec75e61 100644 --- a/oox/source/export/shapes.cxx +++ b/oox/source/export/shapes.cxx @@ -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); } diff --git a/oox/source/token/tokens.txt b/oox/source/token/tokens.txt index 1eb24e1b9a3b..3ffcbc9a3956 100644 --- a/oox/source/token/tokens.txt +++ b/oox/source/token/tokens.txt @@ -2024,6 +2024,7 @@ dropLine dropLines dropauto ds +dsp dstNode dstrike dt
