include/oox/export/drawingml.hxx         |    9 +--
 include/svx/diagram/DomMapFlag.hxx       |   13 ++---
 oox/source/drawingml/diagram/diagram.cxx |   15 ++++-
 oox/source/drawingml/shape.cxx           |   78 ++++++++++++++++++++++---------
 oox/source/export/drawingml.cxx          |   60 +++++++++++++++++++++--
 5 files changed, 133 insertions(+), 42 deletions(-)

New commits:
commit 58aaf1b33a35885b595e23a97611ca157c015e7d
Author:     Karthik Godha <[email protected]>
AuthorDate: Tue Dec 23 14:15:00 2025 +0530
Commit:     Michael Stahl <[email protected]>
CommitDate: Tue Jan 27 14:37:37 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]>
    (cherry picked from commit 8a049c8a1482260f0ffe85b6da8c5e8834c20652)
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/198182
    Tested-by: Jenkins

diff --git a/include/oox/export/drawingml.hxx b/include/oox/export/drawingml.hxx
index cb39c05bf2c5..36378c9dbdb4 100644
--- a/include/oox/export/drawingml.hxx
+++ b/include/oox/export/drawingml.hxx
@@ -521,10 +521,11 @@ public:
     OOX_DLLPUBLIC bool PrepareToWriteAsDiagram(const 
css::uno::Reference<css::drawing::XShape>& rXRootShape);
     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/include/svx/diagram/DomMapFlag.hxx 
b/include/svx/diagram/DomMapFlag.hxx
index f698da928cc0..0ceb4186f7f3 100644
--- a/include/svx/diagram/DomMapFlag.hxx
+++ b/include/svx/diagram/DomMapFlag.hxx
@@ -28,12 +28,13 @@ namespace svx::diagram
 enum class SVXCORE_DLLPUBLIC DomMapFlag : sal_uInt16
 {
     OOXData = 0,
-    OOXDataRels = 1,
-    OOXLayout = 2,
-    OOXStyle = 3,
-    OOXColor = 4,
-    OOXDrawing = 5,
-    OOXDrawingRels = 6
+    OOXDataImageRels = 1,
+    OOXDataHlinkRels = 2,
+    OOXLayout = 3,
+    OOXStyle = 4,
+    OOXColor = 5,
+    OOXDrawing = 6,
+    OOXDrawingRels = 7
 };
 
 typedef std::vector<DomMapFlag> DomMapFlags;
diff --git a/oox/source/drawingml/diagram/diagram.cxx 
b/oox/source/drawingml/diagram/diagram.cxx
index 75f84c3b7340..b567a48e0890 100644
--- a/oox/source/drawingml/diagram/diagram.cxx
+++ b/oox/source/drawingml/diagram/diagram.cxx
@@ -410,10 +410,17 @@ void loadDiagram( ShapePtr const & pShape,
                            pDiagram,
                            xRefDataModel);
 
-            uno::Sequence< uno::Sequence< uno::Any > > aDataRelsMap(
-                pShape->resolveRelationshipsOfTypeFromOfficeDoc( rFilter, 
xRefDataModel->getFragmentPath(), u"image" ));
-
-            pDiagram->setOOXDomValue(svx::diagram::DomMapFlag::OOXDataRels, 
uno::Any(aDataRelsMap));
+            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 : pData->getExtDrawings())
diff --git a/oox/source/drawingml/shape.cxx b/oox/source/drawingml/shape.cxx
index 59c3ce0c93a3..324bdb913e10 100644
--- a/oox/source/drawingml/shape.cxx
+++ b/oox/source/drawingml/shape.cxx
@@ -2876,36 +2876,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 3544d9b4aa9c..a986078dc1f1 100644
--- a/oox/source/export/drawingml.cxx
+++ b/oox/source/export/drawingml.cxx
@@ -6837,8 +6837,13 @@ void DrawingML::WriteDiagram(const 
css::uno::Reference<css::drawing::XShape>& rX
     uno::Reference<xml::dom::XDocument> drawingDom;
     rIDiagramHelper->getOOXDomValue(svx::diagram::DomMapFlag::OOXDrawing) >>= 
drawingDom;
 
-    uno::Sequence<uno::Sequence<uno::Any>> xDataRelSeq;
-    rIDiagramHelper->getOOXDomValue(svx::diagram::DomMapFlag::OOXDataRels) >>= 
xDataRelSeq;
+    uno::Sequence<uno::Sequence<uno::Any>> xDataImageRelSeq;
+    rIDiagramHelper->getOOXDomValue(svx::diagram::DomMapFlag::OOXDataImageRels)
+        >>= xDataImageRelSeq;
+
+    uno::Sequence<uno::Sequence<uno::Any>> xDataHlinkRelSeq;
+    rIDiagramHelper->getOOXDomValue(svx::diagram::DomMapFlag::OOXDataHlinkRels)
+        >>= xDataHlinkRelSeq;
 
     // generate a unique id
     rtl::Reference<sax_fastparser::FastAttributeList> pDocPrAttrList
@@ -6977,7 +6982,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);
@@ -7018,12 +7024,13 @@ 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;
     rIDiagramHelper->getOOXDomValue(svx::diagram::DomMapFlag::OOXDrawingRels) 
>>= 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));
@@ -7081,6 +7088,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)
 {

Reply via email to