From: PJ Fanning Date: Fri, 19 Apr 2024 09:23:54 +0000 (+0000) Subject: [bug-63189] support hyperlink relationships. Thanks to Ohyoung Kwon. This closes... X-Git-Tag: REL_5_3_0~54 X-Git-Url: https://source.dussan.org/?a=commitdiff_plain;h=eebb3717e04341aa041be14f8322ac4dd835c286;p=poi.git [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 --- 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 relations = new LinkedHashMap<>(); + private final Map 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 getReferenceRelationships() { + List 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 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 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 rels = slide.getRelationParts(); + Collection 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 rels = drawing.getRelationParts(); - assertEquals(1, rels.size()); - assertEquals("Sheet1!A1", rels.get(0).getRelationship().getTargetURI().getFragment()); + List 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 Binary files /dev/null and b/test-data/spreadsheet/bug63189.xlsx differ