aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPJ Fanning <fanningpj@apache.org>2024-04-19 09:23:54 +0000
committerPJ Fanning <fanningpj@apache.org>2024-04-19 09:23:54 +0000
commiteebb3717e04341aa041be14f8322ac4dd835c286 (patch)
tree1cfb0694ca5988f941017b733ec3de9b3834db5e
parentd525d1a5b106ed01b7036dcd4a30d34c5a919809 (diff)
downloadpoi-eebb3717e04341aa041be14f8322ac4dd835c286.tar.gz
poi-eebb3717e04341aa041be14f8322ac4dd835c286.zip
[bug-63189] support hyperlink relationships. Thanks to Ohyoung Kwon. This closes #617
git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1917134 13f79535-47bb-0310-9956-ffa450edef68
-rw-r--r--poi-ooxml/src/main/java/org/apache/poi/ooxml/HyperlinkRelationship.java44
-rw-r--r--poi-ooxml/src/main/java/org/apache/poi/ooxml/POIXMLDocumentPart.java90
-rw-r--r--poi-ooxml/src/main/java/org/apache/poi/ooxml/ReferenceRelationship.java79
-rw-r--r--poi-ooxml/src/main/java/org/apache/poi/openxml4j/opc/PackageRelationship.java6
-rw-r--r--poi-ooxml/src/main/java/org/apache/poi/openxml4j/opc/internal/marshallers/ZipPartMarshaller.java11
-rw-r--r--poi-ooxml/src/main/java/org/apache/poi/xssf/usermodel/XSSFWorkbook.java18
-rw-r--r--poi-ooxml/src/test/java/org/apache/poi/openxml4j/opc/TestPackage.java2
-rw-r--r--poi-ooxml/src/test/java/org/apache/poi/openxml4j/opc/TestRelationships.java9
-rw-r--r--poi-ooxml/src/test/java/org/apache/poi/xslf/TestXSLFBugs.java18
-rw-r--r--poi-ooxml/src/test/java/org/apache/poi/xssf/TestXSSFCloneSheet.java82
-rw-r--r--poi-ooxml/src/test/java/org/apache/poi/xssf/usermodel/TestXSSFBugs.java13
-rw-r--r--test-data/spreadsheet/bug63189.xlsxbin0 -> 11027 bytes
12 files changed, 327 insertions, 45 deletions
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
index 0000000000..e9649b3d52
--- /dev/null
+++ b/poi-ooxml/src/main/java/org/apache/poi/ooxml/HyperlinkRelationship.java
@@ -0,0 +1,44 @@
+/* ====================================================================
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+==================================================================== */
+package org.apache.poi.ooxml;
+
+import org.apache.poi.openxml4j.opc.PackageRelationshipTypes;
+
+import java.net.URI;
+
+/**
+ * Represents a hyperlink relationship.
+ *
+ * @since POI 5.2.6
+ */
+public class HyperlinkRelationship extends ReferenceRelationship {
+ /**
+ * Initializes a new instance of the HyperlinkRelationship.
+ *
+ * @param hyperlinkUri The target uri of the hyperlink relationship.
+ * @param isExternal Is the URI external.
+ * @param id The relationship ID.
+ */
+ protected HyperlinkRelationship(POIXMLDocumentPart container, URI hyperlinkUri, boolean isExternal, String id) {
+ super(container, hyperlinkUri, isExternal, PackageRelationshipTypes.HYPERLINK_PART, id);
+ }
+
+ @Override
+ public String getRelationshipType() {
+ return PackageRelationshipTypes.HYPERLINK_PART;
+ }
+}
diff --git a/poi-ooxml/src/main/java/org/apache/poi/ooxml/POIXMLDocumentPart.java b/poi-ooxml/src/main/java/org/apache/poi/ooxml/POIXMLDocumentPart.java
index c83ccfcaaa..c7c920f4eb 100644
--- a/poi-ooxml/src/main/java/org/apache/poi/ooxml/POIXMLDocumentPart.java
+++ b/poi-ooxml/src/main/java/org/apache/poi/ooxml/POIXMLDocumentPart.java
@@ -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
index 0000000000..12dce49502
--- /dev/null
+++ b/poi-ooxml/src/main/java/org/apache/poi/ooxml/ReferenceRelationship.java
@@ -0,0 +1,79 @@
+/* ====================================================================
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+==================================================================== */
+package org.apache.poi.ooxml;
+
+import org.apache.poi.openxml4j.opc.PackageRelationship;
+import org.apache.poi.openxml4j.opc.TargetMode;
+
+import java.net.URI;
+
+/**
+ * Defines a reference relationship. A reference relationship can be internal or external.
+ *
+ * @since POI 5.2.6
+ */
+public abstract class ReferenceRelationship {
+ private POIXMLDocumentPart container;
+ private final String relationshipType;
+ private final boolean external;
+ private final String id;
+ private final URI uri;
+
+ protected ReferenceRelationship(POIXMLDocumentPart container, PackageRelationship packageRelationship) {
+ if (packageRelationship == null) {
+ throw new IllegalArgumentException("packageRelationship");
+ }
+
+ this.container = container;
+ this.relationshipType = packageRelationship.getRelationshipType();
+ this.uri = packageRelationship.getTargetURI();
+ this.external = packageRelationship.getTargetMode() == TargetMode.EXTERNAL;
+ this.id = packageRelationship.getId();
+ }
+
+ protected ReferenceRelationship(POIXMLDocumentPart container, URI targetUri, boolean isExternal, String relationshipType, String id) {
+ if (targetUri == null) {
+ throw new IllegalArgumentException("targetUri");
+ }
+
+ this.container = container;
+ this.relationshipType = relationshipType;
+ this.uri = targetUri;
+ this.id = id;
+ this.external = isExternal;
+ }
+
+ public POIXMLDocumentPart getContainer() {
+ return container;
+ }
+
+ public String getRelationshipType() {
+ return relationshipType;
+ }
+
+ public boolean isExternal() {
+ return external;
+ }
+
+ public String getId() {
+ return id;
+ }
+
+ public URI getUri() {
+ return uri;
+ }
+}
diff --git a/poi-ooxml/src/main/java/org/apache/poi/openxml4j/opc/PackageRelationship.java b/poi-ooxml/src/main/java/org/apache/poi/openxml4j/opc/PackageRelationship.java
index bb7717492e..417f3899d9 100644
--- a/poi-ooxml/src/main/java/org/apache/poi/openxml4j/opc/PackageRelationship.java
+++ b/poi-ooxml/src/main/java/org/apache/poi/openxml4j/opc/PackageRelationship.java
@@ -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
diff --git a/poi-ooxml/src/main/java/org/apache/poi/openxml4j/opc/internal/marshallers/ZipPartMarshaller.java b/poi-ooxml/src/main/java/org/apache/poi/openxml4j/opc/internal/marshallers/ZipPartMarshaller.java
index 9519af2d35..acfe0ff2f8 100644
--- a/poi-ooxml/src/main/java/org/apache/poi/openxml4j/opc/internal/marshallers/ZipPartMarshaller.java
+++ b/poi-ooxml/src/main/java/org/apache/poi/openxml4j/opc/internal/marshallers/ZipPartMarshaller.java
@@ -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();
diff --git a/poi-ooxml/src/main/java/org/apache/poi/xssf/usermodel/XSSFWorkbook.java b/poi-ooxml/src/main/java/org/apache/poi/xssf/usermodel/XSSFWorkbook.java
index e43bba7321..7c03b27fa4 100644
--- a/poi-ooxml/src/main/java/org/apache/poi/xssf/usermodel/XSSFWorkbook.java
+++ b/poi-ooxml/src/main/java/org/apache/poi/xssf/usermodel/XSSFWorkbook.java
@@ -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);
diff --git a/poi-ooxml/src/test/java/org/apache/poi/openxml4j/opc/TestPackage.java b/poi-ooxml/src/test/java/org/apache/poi/openxml4j/opc/TestPackage.java
index d0d0514a80..6973c0cbc5 100644
--- a/poi-ooxml/src/test/java/org/apache/poi/openxml4j/opc/TestPackage.java
+++ b/poi-ooxml/src/test/java/org/apache/poi/openxml4j/opc/TestPackage.java
@@ -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);
}
diff --git a/poi-ooxml/src/test/java/org/apache/poi/openxml4j/opc/TestRelationships.java b/poi-ooxml/src/test/java/org/apache/poi/openxml4j/opc/TestRelationships.java
index 6af8b2c1f3..fe370a6fe5 100644
--- a/poi-ooxml/src/test/java/org/apache/poi/openxml4j/opc/TestRelationships.java
+++ b/poi-ooxml/src/test/java/org/apache/poi/openxml4j/opc/TestRelationships.java
@@ -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());
diff --git a/poi-ooxml/src/test/java/org/apache/poi/xslf/TestXSLFBugs.java b/poi-ooxml/src/test/java/org/apache/poi/xslf/TestXSLFBugs.java
index 8d7a37dd7a..1b0f468416 100644
--- a/poi-ooxml/src/test/java/org/apache/poi/xslf/TestXSLFBugs.java
+++ b/poi-ooxml/src/test/java/org/apache/poi/xslf/TestXSLFBugs.java
@@ -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) {
diff --git a/poi-ooxml/src/test/java/org/apache/poi/xssf/TestXSSFCloneSheet.java b/poi-ooxml/src/test/java/org/apache/poi/xssf/TestXSSFCloneSheet.java
index 72fc676c6e..e966520bed 100644
--- a/poi-ooxml/src/test/java/org/apache/poi/xssf/TestXSSFCloneSheet.java
+++ b/poi-ooxml/src/test/java/org/apache/poi/xssf/TestXSSFCloneSheet.java
@@ -18,19 +18,31 @@
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());
+ }
+ }
}
diff --git a/poi-ooxml/src/test/java/org/apache/poi/xssf/usermodel/TestXSSFBugs.java b/poi-ooxml/src/test/java/org/apache/poi/xssf/usermodel/TestXSSFBugs.java
index 37013d73be..09d2120c76 100644
--- a/poi-ooxml/src/test/java/org/apache/poi/xssf/usermodel/TestXSSFBugs.java
+++ b/poi-ooxml/src/test/java/org/apache/poi/xssf/usermodel/TestXSSFBugs.java
@@ -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
index 0000000000..cea94b126e
--- /dev/null
+++ b/test-data/spreadsheet/bug63189.xlsx
Binary files differ