include/oox/export/drawingml.hxx | 8 +-- oox/source/drawingml/diagram/diagram.cxx | 22 ++++++-- oox/source/drawingml/diagram/diagram.hxx | 10 +++ oox/source/drawingml/shape.cxx | 78 ++++++++++++++++++++++--------- oox/source/export/drawingml.cxx | 59 ++++++++++++++++++++--- 5 files changed, 137 insertions(+), 40 deletions(-)
New commits: commit 8a049c8a1482260f0ffe85b6da8c5e8834c20652 Author: Karthik Godha <[email protected]> AuthorDate: Tue Dec 23 14:15:00 2025 +0530 Commit: Michael Stahl <[email protected]> CommitDate: Thu Jan 8 20:22:40 2026 +0100 tdf#170094: PPTX->PPTX export SmartArt hyperlinks SmartArt hyperlinks are not preserved after roundtrip. This leads to XML having relationship IDs which are invalid. Change-Id: I3fa3d6f79b8eb8d086b1061d26c30568e3b5793f Reviewed-on: https://gerrit.libreoffice.org/c/core/+/196152 Reviewed-by: Michael Stahl <[email protected]> Tested-by: Jenkins CollaboraOffice <[email protected]> diff --git a/include/oox/export/drawingml.hxx b/include/oox/export/drawingml.hxx index 0cfe1cc34763..6f94bad3f883 100644 --- a/include/oox/export/drawingml.hxx +++ b/include/oox/export/drawingml.hxx @@ -545,9 +545,11 @@ public: OString WriteWdpPicture( const OUString& rFileId, const css::uno::Sequence< sal_Int8 >& rPictureData ); OOX_DLLPUBLIC void WriteDiagram(const css::uno::Reference<css::drawing::XShape>& rXShape, sal_Int32 nDiagramId, sal_Int32 nShapeId = -1); - void writeDiagramRels(const css::uno::Sequence<css::uno::Sequence<css::uno::Any>>& xRelSeq, - const css::uno::Reference<css::io::XOutputStream>& xOutStream, - std::u16string_view sGrabBagProperyName, int nDiagramId); + void writeDiagramImageRels(const css::uno::Sequence<css::uno::Sequence<css::uno::Any>>& xRelSeq, + const css::uno::Reference<css::io::XOutputStream>& xOutStream, + std::u16string_view sGrabBagProperyName, int nDiagramId); + void writeDiagramHlinkRels(const css::uno::Sequence<css::uno::Sequence<css::uno::Any>>& xRelSeq, + const css::uno::Reference<css::io::XOutputStream>& xOutStream); static void WriteFromTo(const css::uno::Reference<css::drawing::XShape>& rXShape, const css::awt::Size& aPageSize, const sax_fastparser::FSHelperPtr& pDrawing); diff --git a/oox/source/drawingml/diagram/diagram.cxx b/oox/source/drawingml/diagram/diagram.cxx index 3928e82767ed..e552e1c6e55b 100644 --- a/oox/source/drawingml/diagram/diagram.cxx +++ b/oox/source/drawingml/diagram/diagram.cxx @@ -152,7 +152,9 @@ uno::Sequence<beans::PropertyValue> Diagram::getDomsAsPropertyValues() const { sal_Int32 length = maMainDomMap.size(); - if (maDataRelsMap.hasElements()) + if (getDataImageRelsSeq().hasElements()) + ++length; + if (getDataHlinkRelsSeq().hasElements()) ++length; uno::Sequence<beans::PropertyValue> aValue(length); @@ -164,10 +166,16 @@ uno::Sequence<beans::PropertyValue> Diagram::getDomsAsPropertyValues() const ++pValue; } - if (maDataRelsMap.hasElements()) + if (getDataImageRelsSeq().hasElements()) + { + pValue->Name = "OOXDiagramDataImageRels"; + pValue->Value <<= getDataImageRelsSeq(); + ++pValue; + } + if (getDataHlinkRelsSeq().hasElements()) { - pValue->Name = "OOXDiagramDataRels"; - pValue->Value <<= maDataRelsMap; + pValue->Name = "OOXDiagramDataHlinkRels"; + pValue->Value <<= getDataHlinkRelsSeq(); ++pValue; } @@ -334,8 +342,10 @@ void loadDiagram( ShapePtr const & pShape, pDiagram, xRefDataModel); - pDiagram->getDataRelsMap() = pShape->resolveRelationshipsOfTypeFromOfficeDoc( rFilter, - xRefDataModel->getFragmentPath(), u"image" ); + pDiagram->getDataImageRelsSeq() = pShape->resolveRelationshipsOfTypeFromOfficeDoc( + rFilter, xRefDataModel->getFragmentPath(), u"image"); + pDiagram->getDataHlinkRelsSeq() = pShape->resolveRelationshipsOfTypeFromOfficeDoc( + rFilter, xRefDataModel->getFragmentPath(), u"hlink"); // Pass the info to pShape for (auto const& extDrawing : pData->getExtDrawings()) diff --git a/oox/source/drawingml/diagram/diagram.hxx b/oox/source/drawingml/diagram/diagram.hxx index 5eb82069b91b..cc5ab12b8024 100644 --- a/oox/source/drawingml/diagram/diagram.hxx +++ b/oox/source/drawingml/diagram/diagram.hxx @@ -143,7 +143,12 @@ public: DiagramColorMap& getColors() { return maColors; } const DiagramColorMap& getColors() const { return maColors; } DiagramDomMap & getDomMap() { return maMainDomMap; } - css::uno::Sequence< css::uno::Sequence< css::uno::Any > > & getDataRelsMap() { return maDataRelsMap; } + + css::uno::Sequence<css::uno::Sequence<css::uno::Any>>& getDataImageRelsSeq() { return maImageRelsSeq; } + css::uno::Sequence<css::uno::Sequence<css::uno::Any>>& getDataHlinkRelsSeq() { return maHlinkRelsSeq; } + const css::uno::Sequence<css::uno::Sequence<css::uno::Any>>& getDataImageRelsSeq() const { return maImageRelsSeq; } + const css::uno::Sequence<css::uno::Sequence<css::uno::Any>>& getDataHlinkRelsSeq() const { return maHlinkRelsSeq; } + void addTo( const ShapePtr & pShape, bool bCreate ); css::uno::Sequence<css::beans::PropertyValue> getDomsAsPropertyValues() const; @@ -159,7 +164,8 @@ private: DiagramQStyleMap maStyles; DiagramColorMap maColors; DiagramDomMap maMainDomMap; - css::uno::Sequence< css::uno::Sequence< css::uno::Any > > maDataRelsMap; + css::uno::Sequence<css::uno::Sequence<css::uno::Any>> maImageRelsSeq; + css::uno::Sequence<css::uno::Sequence<css::uno::Any>> maHlinkRelsSeq; }; typedef std::shared_ptr< Diagram > DiagramPtr; diff --git a/oox/source/drawingml/shape.cxx b/oox/source/drawingml/shape.cxx index c05f7bf8d0e9..55f0eef8afe4 100644 --- a/oox/source/drawingml/shape.cxx +++ b/oox/source/drawingml/shape.cxx @@ -2882,36 +2882,72 @@ uno::Sequence< uno::Sequence< uno::Any > > Shape::resolveRelationshipsOfTypeFro core::RelationsRef xRels = rFilter.importRelations( sFragment ); if ( xRels ) { - core::RelationsRef xImageRels = xRels->getRelationsFromTypeFromOfficeDoc( sType ); - if ( xImageRels ) + if (sType == u"image") { - xRelListTemp.realloc( xImageRels->size() ); - auto pxRelListTemp = xRelListTemp.getArray(); - for (auto const& imageRel : *xImageRels) + core::RelationsRef xImageRels = xRels->getRelationsFromTypeFromOfficeDoc(sType); + if (xImageRels) { - uno::Sequence< uno::Any > diagramRelTuple (3); - auto pdiagramRelTuple = diagramRelTuple.getArray(); - // [0] => RID, [1] => InputStream [2] => extension - OUString sRelId = imageRel.second.maId; + xRelListTemp.realloc(xImageRels->size()); + auto pxRelListTemp = xRelListTemp.getArray(); + for (auto const& imageRel : *xImageRels) + { + uno::Sequence<uno::Any> diagramRelTuple(3); + auto pdiagramRelTuple = diagramRelTuple.getArray(); + // [0] => RID, [1] => InputStream [2] => extension + OUString sRelId = imageRel.second.maId; + + pdiagramRelTuple[0] <<= sRelId; + OUString sTarget = xImageRels->getFragmentPathFromRelId(sRelId); + + uno::Reference<io::XInputStream> xImageInputStrm( + rFilter.openInputStream(sTarget), uno::UNO_SET_THROW); + StreamDataSequence dataSeq; + if (rFilter.importBinaryData(dataSeq, sTarget)) + { + pdiagramRelTuple[1] <<= dataSeq; + } - pdiagramRelTuple[0] <<= sRelId; - OUString sTarget = xImageRels->getFragmentPathFromRelId( sRelId ); + pdiagramRelTuple[2] <<= sTarget.copy(sTarget.lastIndexOf(".")); - uno::Reference< io::XInputStream > xImageInputStrm( rFilter.openInputStream( sTarget ), uno::UNO_SET_THROW ); - StreamDataSequence dataSeq; - if ( rFilter.importBinaryData( dataSeq, sTarget ) ) - { - pdiagramRelTuple[1] <<= dataSeq; + pxRelListTemp[counter] = std::move(diagramRelTuple); + ++counter; } + } + } + else if (sType == u"hlink") + { + // Hyperlink can be internal or external + core::RelationsRef xSlideRels = xRels->getRelationsFromTypeFromOfficeDoc(u"slide"); + core::RelationsRef xHlinkRels = xRels->getRelationsFromTypeFromOfficeDoc(u"hyperlink"); - pdiagramRelTuple[2] <<= sTarget.copy( sTarget.lastIndexOf(".") ); + sal_Int32 totalSize + = (xSlideRels ? xSlideRels->size() : 0) + (xHlinkRels ? xHlinkRels->size() : 0); + xRelListTemp.realloc(totalSize); + auto pxRelListTemp = xRelListTemp.getArray(); - pxRelListTemp[counter] = std::move(diagramRelTuple); - ++counter; - } - xRelListTemp.realloc(counter); + // Helper to create relation tuple + auto addRelation = [&](const auto& rel, const OUString& relType) + { + uno::Sequence<uno::Any> tuple(3); + auto pTuple = tuple.getArray(); + pTuple[0] <<= rel.second.maId; + pTuple[1] <<= rel.second.maTarget; + pTuple[2] <<= relType; + pxRelListTemp[counter++] = std::move(tuple); + }; + if (xSlideRels) + { + for (auto const& slideRel : *xSlideRels) + addRelation(slideRel, "slide"); + } + if (xHlinkRels) + { + for (auto const& hlinkRel : *xHlinkRels) + addRelation(hlinkRel, "hyperlink"); + } } + xRelListTemp.realloc(counter); } return xRelListTemp; } diff --git a/oox/source/export/drawingml.cxx b/oox/source/export/drawingml.cxx index 15497622cabf..8b7e18d2ec66 100644 --- a/oox/source/export/drawingml.cxx +++ b/oox/source/export/drawingml.cxx @@ -6723,7 +6723,8 @@ void DrawingML::WriteDiagram(const css::uno::Reference<css::drawing::XShape>& rX uno::Reference<xml::dom::XDocument> styleDom; uno::Reference<xml::dom::XDocument> colorDom; uno::Reference<xml::dom::XDocument> drawingDom; - uno::Sequence<uno::Sequence<uno::Any>> xDataRelSeq; + uno::Sequence<uno::Sequence<uno::Any>> xDataImageRelSeq; + uno::Sequence<uno::Sequence<uno::Any>> xDataHlinkRelSeq; uno::Sequence<uno::Any> diagramDrawing; // retrieve the doms from the GrabBag @@ -6746,8 +6747,10 @@ void DrawingML::WriteDiagram(const css::uno::Reference<css::drawing::XShape>& rX diagramDrawing[0] >>= drawingDom; // if there is OOXDrawing property then set drawingDom here only. } - else if (propName == "OOXDiagramDataRels") - rProp.Value >>= xDataRelSeq; + else if (propName == "OOXDiagramDataImageRels") + rProp.Value >>= xDataImageRelSeq; + else if (propName == "OOXDiagramDataHlinkRels") + rProp.Value >>= xDataHlinkRelSeq; } // check that we have the 4 mandatory XDocuments @@ -6892,7 +6895,8 @@ void DrawingML::WriteDiagram(const css::uno::Reference<css::drawing::XShape>& rX uno::Sequence<beans::StringPair>()); // write the associated Images and rels for data file - writeDiagramRels(xDataRelSeq, xDataOutputStream, u"OOXDiagramDataRels", nDiagramId); + writeDiagramImageRels(xDataImageRelSeq, xDataOutputStream, u"OOXDiagramDataRels", nDiagramId); + writeDiagramHlinkRels(xDataHlinkRelSeq, xDataOutputStream); // write layout file serializer.set(layoutDom, uno::UNO_QUERY); @@ -6933,12 +6937,12 @@ void DrawingML::WriteDiagram(const css::uno::Reference<css::drawing::XShape>& rX // write the associated Images and rels for drawing file uno::Sequence<uno::Sequence<uno::Any>> xDrawingRelSeq; diagramDrawing[1] >>= xDrawingRelSeq; - writeDiagramRels(xDrawingRelSeq, xDrawingOutputStream, u"OOXDiagramDrawingRels", nDiagramId); + writeDiagramImageRels(xDrawingRelSeq, xDrawingOutputStream, u"OOXDiagramDrawingRels", nDiagramId); } -void DrawingML::writeDiagramRels(const uno::Sequence<uno::Sequence<uno::Any>>& xRelSeq, - const uno::Reference<io::XOutputStream>& xOutStream, - std::u16string_view sGrabBagProperyName, int nDiagramId) +void DrawingML::writeDiagramImageRels(const uno::Sequence<uno::Sequence<uno::Any>>& xRelSeq, + const uno::Reference<io::XOutputStream>& xOutStream, + std::u16string_view sGrabBagProperyName, int nDiagramId) { // add image relationships of OOXData, OOXDiagram OUString sType(oox::getRelationship(Relationship::IMAGE)); @@ -6996,6 +7000,45 @@ void DrawingML::writeDiagramRels(const uno::Sequence<uno::Sequence<uno::Any>>& x } } +void DrawingML::writeDiagramHlinkRels(const uno::Sequence<uno::Sequence<uno::Any>>& xRelSeq, + const uno::Reference<io::XOutputStream>& xOutStream) +{ + uno::Reference<xml::sax::XWriter> xWriter + = xml::sax::Writer::create(comphelper::getProcessComponentContext()); + xWriter->setOutputStream(xOutStream); + + // retrieve the relationships from Sequence + for (sal_Int32 j = 0; j < xRelSeq.getLength(); j++) + { + // diagramDataRelTuple[0] => RID, + // diagramDataRelTuple[1] => Target + // diagramDataRelTuple[2] => Type + const uno::Sequence<uno::Any>& diagramDataRelTuple = xRelSeq[j]; + + OUString sRelId; + OUString sTarget; + OUString sType; + diagramDataRelTuple[0] >>= sRelId; + sRelId = sRelId.copy(3); + diagramDataRelTuple[1] >>= sTarget; + diagramDataRelTuple[2] >>= sType; + + OUString sRelType; + bool bExtURL = true; + if (sType == u"slide") + { + sRelType = oox::getRelationship(Relationship::SLIDE); + bExtURL = false; + } + else + sRelType = oox::getRelationship(Relationship::HYPERLINK); + + PropertySet aProps(xOutStream); + aProps.setAnyProperty(PROP_RelId, uno::Any(sRelId.toInt32())); + mpFB->addRelation(xOutStream, sRelType, sTarget, bExtURL); + } +} + void DrawingML::WriteFromTo(const uno::Reference<css::drawing::XShape>& rXShape, const awt::Size& aPageSize, const FSHelperPtr& pDrawing) {
