]> source.dussan.org Git - poi.git/commitdiff
[bug-63189] support hyperlink relationships. Thanks to Ohyoung Kwon. This closes...
authorPJ Fanning <fanningpj@apache.org>
Fri, 19 Apr 2024 09:23:54 +0000 (09:23 +0000)
committerPJ Fanning <fanningpj@apache.org>
Fri, 19 Apr 2024 09:23:54 +0000 (09:23 +0000)
git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1917134 13f79535-47bb-0310-9956-ffa450edef68

12 files changed:
poi-ooxml/src/main/java/org/apache/poi/ooxml/HyperlinkRelationship.java [new file with mode: 0644]
poi-ooxml/src/main/java/org/apache/poi/ooxml/POIXMLDocumentPart.java
poi-ooxml/src/main/java/org/apache/poi/ooxml/ReferenceRelationship.java [new file with mode: 0644]
poi-ooxml/src/main/java/org/apache/poi/openxml4j/opc/PackageRelationship.java
poi-ooxml/src/main/java/org/apache/poi/openxml4j/opc/internal/marshallers/ZipPartMarshaller.java
poi-ooxml/src/main/java/org/apache/poi/xssf/usermodel/XSSFWorkbook.java
poi-ooxml/src/test/java/org/apache/poi/openxml4j/opc/TestPackage.java
poi-ooxml/src/test/java/org/apache/poi/openxml4j/opc/TestRelationships.java
poi-ooxml/src/test/java/org/apache/poi/xslf/TestXSLFBugs.java
poi-ooxml/src/test/java/org/apache/poi/xssf/TestXSSFCloneSheet.java
poi-ooxml/src/test/java/org/apache/poi/xssf/usermodel/TestXSSFBugs.java
test-data/spreadsheet/bug63189.xlsx [new file with mode: 0644]

diff --git a/poi-ooxml/src/main/java/org/apache/poi/ooxml/HyperlinkRelationship.java b/poi-ooxml/src/main/java/org/apache/poi/ooxml/HyperlinkRelationship.java
new file mode 100644 (file)
index 0000000..e9649b3
--- /dev/null
@@ -0,0 +1,44 @@
+/* ====================================================================\r
+   Licensed to the Apache Software Foundation (ASF) under one or more\r
+   contributor license agreements.  See the NOTICE file distributed with\r
+   this work for additional information regarding copyright ownership.\r
+   The ASF licenses this file to You under the Apache License, Version 2.0\r
+   (the "License"); you may not use this file except in compliance with\r
+   the License.  You may obtain a copy of the License at\r
+\r
+       http://www.apache.org/licenses/LICENSE-2.0\r
+\r
+   Unless required by applicable law or agreed to in writing, software\r
+   distributed under the License is distributed on an "AS IS" BASIS,\r
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+   See the License for the specific language governing permissions and\r
+   limitations under the License.\r
+==================================================================== */\r
+package org.apache.poi.ooxml;\r
+\r
+import org.apache.poi.openxml4j.opc.PackageRelationshipTypes;\r
+\r
+import java.net.URI;\r
+\r
+/**\r
+ * Represents a hyperlink relationship.\r
+ *\r
+ * @since POI 5.2.6\r
+ */\r
+public class HyperlinkRelationship extends ReferenceRelationship {\r
+    /**\r
+     * Initializes a new instance of the HyperlinkRelationship.\r
+     *\r
+     * @param hyperlinkUri The target uri of the hyperlink relationship.\r
+     * @param isExternal Is the URI external.\r
+     * @param id The relationship ID.\r
+     */\r
+    protected HyperlinkRelationship(POIXMLDocumentPart container, URI hyperlinkUri, boolean isExternal, String id) {\r
+        super(container, hyperlinkUri, isExternal, PackageRelationshipTypes.HYPERLINK_PART, id);\r
+    }\r
+\r
+    @Override\r
+    public String getRelationshipType() {\r
+        return PackageRelationshipTypes.HYPERLINK_PART;\r
+    }\r
+}\r
index c83ccfcaaa87e4eb772354ae4c8da607b1299ac1..c7c920f4eb8fecb6dbf4a6cfaebdf029721bb318 100644 (file)
@@ -23,6 +23,7 @@ import java.util.Collections;
 import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.Objects;
 import java.util.Set;
 
 import org.apache.logging.log4j.LogManager;
@@ -38,7 +39,6 @@ import org.apache.poi.openxml4j.opc.PackageRelationshipCollection;
 import org.apache.poi.openxml4j.opc.PackageRelationshipTypes;
 import org.apache.poi.openxml4j.opc.PackagingURIHelper;
 import org.apache.poi.openxml4j.opc.TargetMode;
-import org.apache.poi.util.IOUtils;
 import org.apache.poi.util.Internal;
 import org.apache.poi.xddf.usermodel.chart.XDDFChart;
 import org.apache.poi.xssf.usermodel.XSSFRelation;
@@ -59,6 +59,7 @@ public class POIXMLDocumentPart {
     private PackagePart packagePart;
     private POIXMLDocumentPart parent;
     private final Map<String, RelationPart> relations = new LinkedHashMap<>();
+    private final Map<String, ReferenceRelationship> referenceRelationships = new LinkedHashMap<>();
     private boolean isCommitted = false;
 
     /**
@@ -640,38 +641,42 @@ public class POIXMLDocumentPart {
 
         // scan breadth-first, so parent-relations are hopefully the shallowest element
         for (PackageRelationship rel : rels) {
-            if (rel.getTargetMode() == TargetMode.INTERNAL) {
-                URI uri = rel.getTargetURI();
-
-                // check for internal references (e.g. '#Sheet1!A1')
-                PackagePartName relName;
-                if (uri.getRawFragment() != null) {
-                    relName = PackagingURIHelper.createPartName(uri.getPath());
-                } else {
-                    relName = PackagingURIHelper.createPartName(uri);
-                }
+            if (Objects.equals(rel.getRelationshipType(), HyperlinkRelationship.HYPERLINK_REL_TYPE)) {
+                referenceRelationships.put(rel.getId(), new HyperlinkRelationship(this, rel.getTargetURI(), rel.getTargetMode() == TargetMode.EXTERNAL, rel.getId()));
+            } else {
+                if (rel.getTargetMode() == TargetMode.INTERNAL) {
+                    URI uri = rel.getTargetURI();
+
+                    // check for internal references (e.g. '#Sheet1!A1')
+                    PackagePartName relName;
+                    if (uri.getRawFragment() != null) {
+                        relName = PackagingURIHelper.createPartName(uri.getPath());
+                    } else {
+                        relName = PackagingURIHelper.createPartName(uri);
+                    }
 
-                final PackagePart p = packagePart.getPackage().getPart(relName);
-                if (p == null) {
-                    LOG.atError().log("Skipped invalid entry {}", rel.getTargetURI());
-                    continue;
-                }
+                    final PackagePart p = packagePart.getPackage().getPart(relName);
+                    if (p == null) {
+                        LOG.atError().log("Skipped invalid entry {}", rel.getTargetURI());
+                        continue;
+                    }
 
-                POIXMLDocumentPart childPart = context.get(p);
-                if (childPart == null) {
-                    childPart = factory.createDocumentPart(this, p);
-                    //here we are checking if part if embedded and excel then set it to chart class
-                    //so that at the time to writing we can also write updated embedded part
-                    if (this instanceof XDDFChart && childPart instanceof XSSFWorkbook) {
-                        ((XDDFChart) this).setWorkbook((XSSFWorkbook) childPart);
+                    POIXMLDocumentPart childPart = context.get(p);
+                    if (childPart == null) {
+                        childPart = factory.createDocumentPart(this, p);
+                        //here we are checking if part if embedded and excel then set it to chart class
+                        //so that at the time to writing we can also write updated embedded part
+                        if (this instanceof XDDFChart && childPart instanceof XSSFWorkbook) {
+                            ((XDDFChart) this).setWorkbook((XSSFWorkbook) childPart);
+                        }
+                        childPart.parent = this;
+                        // already add child to context, so other children can reference it
+                        context.put(p, childPart);
+                        readLater.add(childPart);
                     }
-                    childPart.parent = this;
-                    // already add child to context, so other children can reference it
-                    context.put(p, childPart);
-                    readLater.add(childPart);
-                }
 
-                addRelation(rel, childPart);
+                    addRelation(rel, childPart);
+                }
             }
         }
 
@@ -767,4 +772,31 @@ public class POIXMLDocumentPart {
             throw new POIXMLException("OOXML file structure broken/invalid", e);
         }
     }
+
+    public boolean removeReferenceRelationship(String relId) {
+        ReferenceRelationship existing = referenceRelationships.remove(relId);
+        if (existing != null) {
+            packagePart.removeRelationship(relId);
+            return true;
+        }
+
+        return false;
+    }
+
+    public ReferenceRelationship getReferenceRelationship(String relId) {
+        return referenceRelationships.get(relId);
+    }
+
+    public HyperlinkRelationship createHyperlink(URI uri, boolean isExternal, String relId) {
+        PackageRelationship pr = packagePart.addRelationship(uri, isExternal ? TargetMode.EXTERNAL : TargetMode.INTERNAL,
+                HyperlinkRelationship.HYPERLINK_REL_TYPE, relId);
+        HyperlinkRelationship hyperlink = new HyperlinkRelationship(this, uri, isExternal, relId);
+        referenceRelationships.put(relId, hyperlink);
+        return hyperlink;
+    }
+
+    public List<ReferenceRelationship> getReferenceRelationships() {
+        List<ReferenceRelationship> list = new ArrayList<>(referenceRelationships.values());
+        return Collections.unmodifiableList(list);
+    }
 }
diff --git a/poi-ooxml/src/main/java/org/apache/poi/ooxml/ReferenceRelationship.java b/poi-ooxml/src/main/java/org/apache/poi/ooxml/ReferenceRelationship.java
new file mode 100644 (file)
index 0000000..12dce49
--- /dev/null
@@ -0,0 +1,79 @@
+/* ====================================================================\r
+   Licensed to the Apache Software Foundation (ASF) under one or more\r
+   contributor license agreements.  See the NOTICE file distributed with\r
+   this work for additional information regarding copyright ownership.\r
+   The ASF licenses this file to You under the Apache License, Version 2.0\r
+   (the "License"); you may not use this file except in compliance with\r
+   the License.  You may obtain a copy of the License at\r
+\r
+       http://www.apache.org/licenses/LICENSE-2.0\r
+\r
+   Unless required by applicable law or agreed to in writing, software\r
+   distributed under the License is distributed on an "AS IS" BASIS,\r
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+   See the License for the specific language governing permissions and\r
+   limitations under the License.\r
+==================================================================== */\r
+package org.apache.poi.ooxml;\r
+\r
+import org.apache.poi.openxml4j.opc.PackageRelationship;\r
+import org.apache.poi.openxml4j.opc.TargetMode;\r
+\r
+import java.net.URI;\r
+\r
+/**\r
+ * Defines a reference relationship. A reference relationship can be internal or external.\r
+ *\r
+ * @since POI 5.2.6\r
+ */\r
+public abstract class ReferenceRelationship {\r
+    private POIXMLDocumentPart container;\r
+    private final String relationshipType;\r
+    private final boolean external;\r
+    private final String id;\r
+    private final URI uri;\r
+\r
+    protected ReferenceRelationship(POIXMLDocumentPart container, PackageRelationship packageRelationship) {\r
+        if (packageRelationship == null) {\r
+            throw new IllegalArgumentException("packageRelationship");\r
+        }\r
+\r
+        this.container = container;\r
+        this.relationshipType = packageRelationship.getRelationshipType();\r
+        this.uri = packageRelationship.getTargetURI();\r
+        this.external = packageRelationship.getTargetMode() == TargetMode.EXTERNAL;\r
+        this.id = packageRelationship.getId();\r
+    }\r
+\r
+    protected ReferenceRelationship(POIXMLDocumentPart container, URI targetUri, boolean isExternal, String relationshipType, String id) {\r
+        if (targetUri == null) {\r
+            throw new IllegalArgumentException("targetUri");\r
+        }\r
+\r
+        this.container = container;\r
+        this.relationshipType = relationshipType;\r
+        this.uri = targetUri;\r
+        this.id = id;\r
+        this.external = isExternal;\r
+    }\r
+\r
+    public POIXMLDocumentPart getContainer() {\r
+        return container;\r
+    }\r
+\r
+    public String getRelationshipType() {\r
+        return relationshipType;\r
+    }\r
+\r
+    public boolean isExternal() {\r
+        return external;\r
+    }\r
+\r
+    public String getId() {\r
+        return id;\r
+    }\r
+\r
+    public URI getUri() {\r
+        return uri;\r
+    }\r
+}\r
index bb7717492e6381b5be3fa470ee6875508b6c29fd..417f3899d92e2eaf0bf671ba11a98eaa6fed4cb3 100644 (file)
@@ -186,6 +186,12 @@ public final class PackageRelationship {
             return targetUri;
         }
 
+        // If it's an internal hyperlink target, we don't
+        //  need to apply our normal validation rules
+        if (PackageRelationshipTypes.HYPERLINK_PART.equals(relationshipType)) {
+            return targetUri;
+        }
+
         // Internal target
         // If it isn't absolute, resolve it relative
         //  to ourselves
index 9519af2d35dc7c6c15f02e8eef04d7cbebf75fe0..acfe0ff2f8f168394621c69c80d284d237b1c0c9 100644 (file)
@@ -21,6 +21,7 @@ import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
 import java.net.URI;
+import java.util.Objects;
 
 import org.apache.commons.compress.archivers.zip.ZipArchiveEntry;
 import org.apache.commons.compress.archivers.zip.ZipArchiveOutputStream;
@@ -32,6 +33,7 @@ import org.apache.poi.openxml4j.opc.PackagePart;
 import org.apache.poi.openxml4j.opc.PackagePartName;
 import org.apache.poi.openxml4j.opc.PackageRelationship;
 import org.apache.poi.openxml4j.opc.PackageRelationshipCollection;
+import org.apache.poi.openxml4j.opc.PackageRelationshipTypes;
 import org.apache.poi.openxml4j.opc.PackagingURIHelper;
 import org.apache.poi.openxml4j.opc.StreamHelper;
 import org.apache.poi.openxml4j.opc.TargetMode;
@@ -154,7 +156,14 @@ public final class ZipPartMarshaller implements PartMarshaller {
             // the relationship Target
             String targetValue;
             URI uri = rel.getTargetURI();
-            if (rel.getTargetMode() == TargetMode.EXTERNAL) {
+            if (Objects.equals(rel.getRelationshipType(), PackageRelationshipTypes.HYPERLINK_PART)) {
+                // Save the target as-is - we don't need to validate it,
+                targetValue = uri.toString();
+                if (rel.getTargetMode() == TargetMode.EXTERNAL) {
+                    // add TargetMode attribute (as it is external link external)
+                    relElem.setAttribute(PackageRelationship.TARGET_MODE_ATTRIBUTE_NAME, "External");
+                }
+            } else if (rel.getTargetMode() == TargetMode.EXTERNAL) {
                 // Save the target as-is - we don't need to validate it,
                 //  alter it etc
                 targetValue = uri.toString();
index e43bba7321622d0a395a3fed8ccaf6ca9a3e52cf..7c03b27fa420072846c891c7948c8337c94e35d0 100644 (file)
@@ -47,10 +47,12 @@ import org.apache.commons.io.output.UnsynchronizedByteArrayOutputStream;
 import org.apache.logging.log4j.LogManager;
 import org.apache.logging.log4j.Logger;
 import org.apache.poi.hpsf.ClassIDPredefined;
+import org.apache.poi.ooxml.HyperlinkRelationship;
 import org.apache.poi.ooxml.POIXMLDocument;
 import org.apache.poi.ooxml.POIXMLDocumentPart;
 import org.apache.poi.ooxml.POIXMLException;
 import org.apache.poi.ooxml.POIXMLProperties;
+import org.apache.poi.ooxml.ReferenceRelationship;
 import org.apache.poi.ooxml.util.PackageHelper;
 import org.apache.poi.openxml4j.exceptions.InvalidFormatException;
 import org.apache.poi.openxml4j.exceptions.OpenXML4JException;
@@ -683,6 +685,14 @@ public class XSSFWorkbook extends POIXMLDocument implements Workbook, Date1904Su
             addRelation(rp, clonedSheet);
         }
 
+        // copy sheet's reference relations;
+        List<ReferenceRelationship> referenceRelationships = srcSheet.getReferenceRelationships();
+        for (ReferenceRelationship ref : referenceRelationships) {
+            if (ref instanceof HyperlinkRelationship) {
+                createHyperlink(ref.getUri(), ref.isExternal(), ref.getId());
+            }
+        }
+
         try {
             for(PackageRelationship pr : srcSheet.getPackagePart().getRelationships()) {
                 if (pr.getTargetMode() == TargetMode.EXTERNAL) {
@@ -742,6 +752,14 @@ public class XSSFWorkbook extends POIXMLDocument implements Workbook, Date1904Su
                         addRelation(rp, clonedDg);
                     }
                 }
+
+                // copy sheet's reference relations;
+                List<ReferenceRelationship> srcRefs = drawingPatriarch.getReferenceRelationships();
+                for (ReferenceRelationship ref : srcRefs) {
+                    if (ref instanceof HyperlinkRelationship) {
+                        clonedDg.createHyperlink(ref.getUri(), ref.isExternal(), ref.getId());
+                    }
+                }
             }
         }
         XSSFSheet.cloneTables(clonedSheet);
index d0d0514a80dc8ab45f06d71bf45acc296b6179a1..6973c0cbc5512f36626391872c298d53a2db5948 100644 (file)
@@ -284,7 +284,7 @@ public final class TestPackage {
             assertEquals(1, rels.size());
             PackageRelationship rel = rels.getRelationship(0);
             assertNotNull(rel);
-            assertEquals("Sheet1!A1", rel.getTargetURI().getRawFragment());
+            assertEquals("#Sheet1!A1", rel.getTargetURI().toString());
 
             assertMSCompatibility(pkg);
         }
index 6af8b2c1f37497eac70e3cd1cd14940e013be449..fe370a6fe51d7a1c82c7176d635fc9aefa6e0f76 100644 (file)
@@ -323,10 +323,11 @@ class TestRelationships {
 
         PackageRelationship rId1 = drawingPart.getRelationship("rId1");
         URI parent = drawingPart.getPartName().getURI();
-        URI rel1 = parent.relativize(rId1.getTargetURI());
-        URI rel11 = PackagingURIHelper.relativizeURI(drawingPart.getPartName().getURI(), rId1.getTargetURI());
-        assertEquals("'Another Sheet'!A1", rel1.getFragment());
-        assertEquals("'Another Sheet'!A1", rel11.getFragment());
+        // Hyperlink is not a target of relativize() because it is not resolved based on sourceURI in getTargetURI()
+//        URI rel1 = parent.relativize(rId1.getTargetURI());
+//        URI rel11 = PackagingURIHelper.relativizeURI(drawingPart.getPartName().getURI(), rId1.getTargetURI());
+//        assertEquals("'Another Sheet'!A1", rel1.getFragment());
+//        assertEquals("'Another Sheet'!A1", rel11.getFragment());
 
         PackageRelationship rId2 = drawingPart.getRelationship("rId2");
         URI rel2 = PackagingURIHelper.relativizeURI(drawingPart.getPartName().getURI(), rId2.getTargetURI());
index 8d7a37dd7ac794dd17bfb561afc4ff801598daeb..1b0f4684163ba7cb01f8f85145ac48c79a933270 100644 (file)
@@ -53,8 +53,10 @@ import org.apache.commons.io.output.NullPrintStream;
 import org.apache.poi.POIDataSamples;
 import org.apache.poi.common.usermodel.HyperlinkType;
 import org.apache.poi.extractor.ExtractorFactory;
+import org.apache.poi.ooxml.HyperlinkRelationship;
 import org.apache.poi.ooxml.POIXMLDocumentPart;
 import org.apache.poi.ooxml.POIXMLDocumentPart.RelationPart;
+import org.apache.poi.ooxml.ReferenceRelationship;
 import org.apache.poi.openxml4j.exceptions.InvalidFormatException;
 import org.apache.poi.openxml4j.opc.OPCPackage;
 import org.apache.poi.openxml4j.opc.PackagePartName;
@@ -384,23 +386,31 @@ class TestXSLFBugs {
 
             // Check the relations from this
             Collection<RelationPart> rels = slide.getRelationParts();
+            Collection<ReferenceRelationship> referenceRelationships = slide.getReferenceRelationships();
 
             // Should have 6 relations:
             //   1 external hyperlink (skipped from list)
             //   4 internal hyperlinks
             //   1 slide layout
-            assertEquals(5, rels.size());
+            assertEquals(1, rels.size());
+            assertEquals(5, referenceRelationships.size());
             int layouts = 0;
             int hyperlinks = 0;
+            int extHyperLinks = 0;
             for (RelationPart p : rels) {
-                if (p.getRelationship().getRelationshipType().equals(XSLFRelation.HYPERLINK.getRelation())) {
-                    hyperlinks++;
-                } else if (p.getDocumentPart() instanceof XSLFSlideLayout) {
+                if (p.getDocumentPart() instanceof XSLFSlideLayout) {
                     layouts++;
                 }
             }
+            for (ReferenceRelationship ref : referenceRelationships) {
+                if (ref instanceof HyperlinkRelationship) {
+                    if (ref.isExternal()) extHyperLinks++;
+                    else hyperlinks++;
+                }
+            }
             assertEquals(1, layouts);
             assertEquals(4, hyperlinks);
+            assertEquals(1, extHyperLinks);
 
             // Hyperlinks should all be to #_ftn1 or #ftnref1
             for (RelationPart p : rels) {
index 72fc676c6ecbb064e6cf3bb02ebda68578dfb3b1..e966520bedd3eb60fcacf712583fb5749b80cfd0 100644 (file)
 package org.apache.poi.xssf;
 
 import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertInstanceOf;
 import static org.junit.jupiter.api.Assertions.assertNotEquals;
 import static org.junit.jupiter.api.Assertions.assertNotNull;
 import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertTrue;
 
+import org.apache.poi.ooxml.ReferenceRelationship;
+import org.apache.poi.openxml4j.opc.PackageRelationship;
 import org.apache.poi.ss.usermodel.BaseTestCloneSheet;
 import org.apache.poi.ss.usermodel.Sheet;
 import org.apache.poi.ss.usermodel.Workbook;
 import org.apache.poi.xddf.usermodel.chart.XDDFDataSource;
 import org.apache.poi.xssf.usermodel.XSSFDrawing;
+import org.apache.poi.xssf.usermodel.XSSFPicture;
 import org.apache.poi.xssf.usermodel.XSSFSheet;
 import org.apache.poi.xssf.usermodel.XSSFWorkbook;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
+import org.openxmlformats.schemas.drawingml.x2006.main.CTBlip;
+import org.openxmlformats.schemas.drawingml.x2006.main.CTBlipFillProperties;
+import org.openxmlformats.schemas.drawingml.x2006.main.CTHyperlink;
+import org.openxmlformats.schemas.drawingml.x2006.main.CTNonVisualDrawingProps;
+import org.openxmlformats.schemas.drawingml.x2006.main.CTNonVisualPictureProperties;
+import org.openxmlformats.schemas.drawingml.x2006.spreadsheetDrawing.CTPicture;
+import org.openxmlformats.schemas.drawingml.x2006.spreadsheetDrawing.CTPictureNonVisual;
 
 import java.io.IOException;
 
@@ -127,4 +139,74 @@ class TestXSSFCloneSheet  extends BaseTestCloneSheet {
         }
     }
 
+    @Test
+    void testBug63189() throws IOException {
+        try (XSSFWorkbook workbook = XSSFTestDataSamples.openSampleWorkbook("bug63189.xlsx")) {
+            // given
+            String linkRelationType = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/hyperlink";
+            String linkTargetUrl = "#Sheet3!A1";
+            String imageRelationType = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/image";
+            String imageTargetUrl = "/xl/media/image1.png";
+
+            XSSFSheet srcSheet = workbook.getSheetAt(0);
+            assertEquals("CloneMe", srcSheet.getSheetName());
+            XSSFDrawing drawing = srcSheet.getDrawingPatriarch();
+            assertNotNull(drawing);
+            assertEquals(1, drawing.getShapes().size());
+            assertInstanceOf(XSSFPicture.class, drawing.getShapes().get(0));
+            XSSFPicture lPic = (XSSFPicture)drawing.getShapes().get(0);
+            CTPicture pic = lPic.getCTPicture();
+            CTPictureNonVisual nvPicPr = pic.getNvPicPr();
+            CTNonVisualDrawingProps cNvPr = nvPicPr.getCNvPr();
+            assertTrue(cNvPr.isSetHlinkClick());
+            CTHyperlink hlinkClick = cNvPr.getHlinkClick();
+            String linkRelId = hlinkClick.getId();
+
+            ReferenceRelationship linkRel = drawing.getReferenceRelationship(linkRelId);
+            assertEquals(linkRelationType, linkRel.getRelationshipType());
+            assertEquals(linkTargetUrl, linkRel.getUri().toString());
+
+            CTNonVisualPictureProperties cNvPicPr = nvPicPr.getCNvPicPr();
+            assertTrue(cNvPicPr.getPicLocks().getNoChangeAspect());
+
+            CTBlipFillProperties blipFill = pic.getBlipFill();
+            CTBlip blip = blipFill.getBlip();
+            String imageRelId = blip.getEmbed();
+
+            PackageRelationship imageRel = drawing.getRelationPartById(imageRelId).getRelationship();
+            assertEquals(imageRelationType, imageRel.getRelationshipType());
+            assertEquals(imageTargetUrl, imageRel.getTargetURI().toString());
+
+            // when
+            XSSFSheet clonedSheet = workbook.cloneSheet(0);
+
+            // then
+            XSSFDrawing drawing2 = clonedSheet.getDrawingPatriarch();
+            assertNotNull(drawing2);
+            assertEquals(1, drawing2.getShapes().size());
+            assertInstanceOf(XSSFPicture.class, drawing2.getShapes().get(0));
+            XSSFPicture lPic2 = (XSSFPicture)drawing2.getShapes().get(0);
+            CTPicture pic2 = lPic2.getCTPicture();
+            CTPictureNonVisual nvPicPr2 = pic2.getNvPicPr();
+            CTNonVisualDrawingProps cNvPr2 = nvPicPr2.getCNvPr();
+            assertTrue(cNvPr2.isSetHlinkClick());
+            CTHyperlink hlinkClick2 = cNvPr2.getHlinkClick();
+            String linkRelId2 = hlinkClick2.getId();
+
+            ReferenceRelationship linkRel2 = drawing2.getReferenceRelationship(linkRelId2);
+            assertEquals(linkRelationType, linkRel2.getRelationshipType());
+            assertEquals(linkTargetUrl, linkRel2.getUri().toString());
+
+            CTNonVisualPictureProperties cNvPicPr2 = nvPicPr2.getCNvPicPr();
+            assertTrue(cNvPicPr2.getPicLocks().getNoChangeAspect());
+
+            CTBlipFillProperties blipFill2 = pic2.getBlipFill();
+            CTBlip blip2 = blipFill2.getBlip();
+            String imageRelId2 = blip2.getEmbed();
+
+            PackageRelationship imageRel2 = drawing2.getRelationPartById(imageRelId2).getRelationship();
+            assertEquals(imageRelationType, imageRel2.getRelationshipType());
+            assertEquals(imageTargetUrl, imageRel2.getTargetURI().toString());
+        }
+    }
 }
index 37013d73bec8d9e862236bfca48ee6e3f002579b..09d2120c76f9ca8fdad23bcca3b0c47c1192f1ab 100644 (file)
@@ -60,6 +60,7 @@ import org.apache.poi.ooxml.POIXMLDocumentPart;
 import org.apache.poi.ooxml.POIXMLDocumentPart.RelationPart;
 import org.apache.poi.ooxml.POIXMLException;
 import org.apache.poi.ooxml.POIXMLProperties;
+import org.apache.poi.ooxml.ReferenceRelationship;
 import org.apache.poi.ooxml.util.DocumentHelper;
 import org.apache.poi.openxml4j.exceptions.InvalidFormatException;
 import org.apache.poi.openxml4j.exceptions.InvalidOperationException;
@@ -234,18 +235,18 @@ public final class TestXSSFBugs extends BaseTestBugzillaIssues {
             assertEquals(1, wb1.getNumberOfSheets());
             XSSFSheet sh = wb1.getSheetAt(0);
             XSSFDrawing drawing = sh.createDrawingPatriarch();
-            List<RelationPart> rels = drawing.getRelationParts();
-            assertEquals(1, rels.size());
-            assertEquals("Sheet1!A1", rels.get(0).getRelationship().getTargetURI().getFragment());
+            List<ReferenceRelationship> referenceRelationships = drawing.getReferenceRelationships();
+            assertEquals(1, referenceRelationships.size());
+            assertEquals("#Sheet1!A1", referenceRelationships.get(0).getUri().toString());
 
             // And again, just to be sure
             try (XSSFWorkbook wb2 = writeOutAndReadBack(wb1)) {
                 assertEquals(1, wb2.getNumberOfSheets());
                 sh = wb2.getSheetAt(0);
                 drawing = sh.createDrawingPatriarch();
-                rels = drawing.getRelationParts();
-                assertEquals(1, rels.size());
-                assertEquals("Sheet1!A1", rels.get(0).getRelationship().getTargetURI().getFragment());
+                referenceRelationships = drawing.getReferenceRelationships();
+                assertEquals(1, referenceRelationships.size());
+                assertEquals("#Sheet1!A1", referenceRelationships.get(0).getUri().toString());
             }
         }
     }
diff --git a/test-data/spreadsheet/bug63189.xlsx b/test-data/spreadsheet/bug63189.xlsx
new file mode 100644 (file)
index 0000000..cea94b1
Binary files /dev/null and b/test-data/spreadsheet/bug63189.xlsx differ