AddLinkAnnotationTest.java
/*
This file is part of the iText (R) project.
Copyright (c) 1998-2025 Apryse Group NV
Authors: Apryse Software.
This program is offered under a commercial and under the AGPL license.
For commercial licensing, contact us at https://itextpdf.com/sales. For AGPL licensing, see below.
AGPL licensing:
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.itextpdf.kernel.pdf.annot;
import com.itextpdf.io.font.constants.StandardFonts;
import com.itextpdf.io.logs.IoLogMessageConstant;
import com.itextpdf.kernel.font.PdfFontFactory;
import com.itextpdf.kernel.geom.Rectangle;
import com.itextpdf.kernel.pdf.PdfArray;
import com.itextpdf.kernel.pdf.PdfDictionary;
import com.itextpdf.kernel.pdf.PdfDocument;
import com.itextpdf.kernel.pdf.PdfName;
import com.itextpdf.kernel.pdf.PdfNumber;
import com.itextpdf.kernel.pdf.PdfPage;
import com.itextpdf.kernel.pdf.PdfReader;
import com.itextpdf.kernel.pdf.PdfVersion;
import com.itextpdf.kernel.pdf.WriterProperties;
import com.itextpdf.kernel.pdf.action.PdfAction;
import com.itextpdf.kernel.pdf.canvas.PdfCanvas;
import com.itextpdf.kernel.pdf.filespec.PdfStringFS;
import com.itextpdf.kernel.pdf.navigation.PdfDestination;
import com.itextpdf.kernel.pdf.navigation.PdfExplicitDestination;
import com.itextpdf.kernel.pdf.navigation.PdfExplicitRemoteGoToDestination;
import com.itextpdf.kernel.pdf.navigation.PdfNamedDestination;
import com.itextpdf.kernel.pdf.navigation.PdfStringDestination;
import com.itextpdf.kernel.pdf.tagging.PdfNamespace;
import com.itextpdf.kernel.utils.CompareTool;
import com.itextpdf.test.ExtendedITextTest;
import com.itextpdf.test.TestUtil;
import com.itextpdf.test.annotations.LogMessage;
import com.itextpdf.test.annotations.LogMessages;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Tag;
import org.junit.jupiter.api.Test;
import java.io.IOException;
import java.util.List;
@Tag("IntegrationTest")
public class AddLinkAnnotationTest extends ExtendedITextTest {
public static final String sourceFolder = "./src/test/resources/com/itextpdf/kernel/pdf/annot/AddLinkAnnotationTest/";
public static final String destinationFolder = TestUtil.getOutputPath() + "/kernel/pdf/annot/AddLinkAnnotationTest/";
@BeforeAll
public static void beforeClass() {
createDestinationFolder(destinationFolder);
}
@AfterAll
public static void afterClass() {
CompareTool.cleanup(destinationFolder);
}
@Test
public void addLinkAnnotation01() throws Exception {
PdfDocument document = new PdfDocument(CompareTool.createTestPdfWriter(destinationFolder + "linkAnnotation01.pdf"));
document.setTagged();
PdfPage page1 = document.addNewPage();
PdfPage page2 = document.addNewPage();
PdfCanvas canvas = new PdfCanvas(page1);
canvas.beginText();
canvas.setFontAndSize(PdfFontFactory.createFont(StandardFonts.COURIER_BOLD), 14);
canvas.moveText(100, 600);
canvas.showText("Page 1");
canvas.moveText(0, -30);
canvas.showText("Link to page 2. Click here!");
canvas.endText();
canvas.release();
page1.addAnnotation(new PdfLinkAnnotation(new Rectangle(100, 560, 260, 25)).setDestination(
PdfExplicitDestination.createFit(page2)).setBorder(new PdfArray(new float[] {0, 0, 1})));
page1.flush();
canvas = new PdfCanvas(page2);
canvas.beginText();
canvas.setFontAndSize(PdfFontFactory.createFont(StandardFonts.COURIER_BOLD), 14);
canvas.moveText(100, 600);
canvas.showText("Page 2");
canvas.endText();
canvas.release();
page2.flush();
document.close();
Assertions.assertNull(new CompareTool()
.compareByContent(destinationFolder + "linkAnnotation01.pdf", sourceFolder + "cmp_linkAnnotation01.pdf",
destinationFolder, "diff_"));
}
@Test
public void addLinkAnnotation02() throws Exception {
PdfDocument document = new PdfDocument(CompareTool.createTestPdfWriter(destinationFolder + "linkAnnotation02.pdf"));
PdfPage page = document.addNewPage();
PdfCanvas canvas = new PdfCanvas(page);
canvas.beginText();
canvas.setFontAndSize(PdfFontFactory.createFont(StandardFonts.COURIER_BOLD), 14);
canvas.moveText(100, 600);
canvas.showText("Click here to go to itextpdf site.");
canvas.endText();
canvas.release();
page.addAnnotation(new PdfLinkAnnotation(new Rectangle(100, 590, 300, 25)).
setDestination(PdfExplicitDestination.createFit(page)).
setBorder(new PdfArray(new float[] {0, 0, 1})).
setColor(new PdfArray(new float[] {1, 0, 0})));
page.flush();
document.close();
Assertions.assertNull(new CompareTool()
.compareByContent(destinationFolder + "linkAnnotation02.pdf", sourceFolder + "cmp_linkAnnotation02.pdf",
destinationFolder, "diff_"));
}
@Test
public void linkAnnotationReferenceTest() throws Exception {
PdfDocument document = new PdfDocument(
CompareTool.createTestPdfWriter(destinationFolder + "linkAnnotationReference.pdf",
new WriterProperties().setPdfVersion(PdfVersion.PDF_2_0)));
document.setTagged();
document.getTagStructureContext().getAutoTaggingPointer().addTag("P");
PdfPage page = document.addNewPage();
PdfCanvas canvas = new PdfCanvas(page);
canvas.beginText();
canvas.setFontAndSize(PdfFontFactory.createFont(StandardFonts.COURIER_BOLD), 14);
canvas.moveText(100, 600);
canvas.showText("Click here to go to itextpdf site.");
canvas.endText();
canvas.release();
page.addAnnotation(new PdfLinkAnnotation(new Rectangle(100, 590, 300, 25)).
setDestination(PdfExplicitDestination.createFit(page)).
setBorder(new PdfArray(new float[] {0, 0, 1})).
setColor(new PdfArray(new float[] {1, 0, 0})));
page.flush();
document.close();
Assertions.assertNull(new CompareTool().compareByContent(destinationFolder + "linkAnnotationReference.pdf",
sourceFolder + "cmp_linkAnnotationReference.pdf", destinationFolder, "diff_"));
}
@Test
public void linkAnnotationReference2Test() throws Exception {
PdfDocument document = new PdfDocument(
CompareTool.createTestPdfWriter(destinationFolder + "linkAnnotationReference2.pdf",
new WriterProperties().setPdfVersion(PdfVersion.PDF_2_0)));
document.setTagged();
document.getTagStructureContext().getAutoTaggingPointer().addTag("P")
.setNamespaceForNewTags(PdfNamespace.getDefault(document)).addTag("Reference");
PdfPage page = document.addNewPage();
PdfCanvas canvas = new PdfCanvas(page);
canvas.beginText();
canvas.setFontAndSize(PdfFontFactory.createFont(StandardFonts.COURIER_BOLD), 14);
canvas.moveText(100, 600);
canvas.showText("Click here to go to itextpdf site.");
canvas.endText();
canvas.release();
page.addAnnotation(new PdfLinkAnnotation(new Rectangle(100, 590, 300, 25)).
setAction(PdfAction.createGoTo(PdfExplicitDestination.createFit(page))).
setBorder(new PdfArray(new float[] {0, 0, 1})).
setColor(new PdfArray(new float[] {1, 0, 0})));
page.flush();
document.close();
Assertions.assertNull(new CompareTool().compareByContent(destinationFolder + "linkAnnotationReference2.pdf",
sourceFolder + "cmp_linkAnnotationReference2.pdf", destinationFolder, "diff_"));
}
@Test
public void severalLinkAnnotationsTest() throws Exception {
try (PdfDocument document = new PdfDocument(
CompareTool.createTestPdfWriter(destinationFolder + "severalLinkAnnotations.pdf"))) {
document.setTagged();
document.getTagStructureContext().getAutoTaggingPointer();
PdfPage page = document.addNewPage();
page.addAnnotation(new PdfLinkAnnotation(new Rectangle(100, 590, 300, 25))
.setAction(PdfAction.createGoTo(PdfExplicitDestination.createFit(page))));
page.addAnnotation(new PdfLinkAnnotation(new Rectangle(100, 590, 300, 25))
.setAction(PdfAction.createGoToR(new PdfStringFS("Some fake destination"),
new PdfExplicitDestination(new PdfArray(new PdfNumber(2))))));
PdfDictionary destinationDictionary = new PdfDictionary();
destinationDictionary.put(PdfName.D, PdfExplicitDestination.createFit(page).getPdfObject());
PdfDictionary destinationDictionary2 = new PdfDictionary();
destinationDictionary2.put(PdfName.SD, PdfExplicitDestination.createFit(page).getPdfObject());
PdfDictionary dests = new PdfDictionary();
dests.put(new PdfName("destination_name"), destinationDictionary);
dests.put(new PdfName("destination_name_2"), destinationDictionary2);
document.getCatalog().put(PdfName.Dests, dests);
page.addAnnotation(new PdfLinkAnnotation(new Rectangle(100, 590, 300, 25)).
setDestination(new PdfNamedDestination("destination_name")));
document.getCatalog().getNameTree(PdfName.Dests).addEntry("destination_name2",
new PdfExplicitRemoteGoToDestination(new PdfArray(new PdfNumber(1))).getPdfObject());
page.addAnnotation(new PdfLinkAnnotation(new Rectangle(100, 590, 300, 25)).
setDestination(new PdfStringDestination("destination_name2")));
}
Assertions.assertNull(new CompareTool().compareByContent(destinationFolder + "severalLinkAnnotations.pdf",
sourceFolder + "cmp_severalLinkAnnotations.pdf", destinationFolder, "diff_"));
}
@Test
public void linkAnnotationWithDictionaryStringDestinationTest() throws Exception {
try (PdfDocument document = new PdfDocument(
CompareTool.createTestPdfWriter(destinationFolder + "linkAnnotationWithDictionaryStringDestination.pdf"))) {
document.setTagged();
document.getTagStructureContext().getAutoTaggingPointer();
PdfPage page = document.addNewPage();
PdfDictionary destinationDictionary = new PdfDictionary();
destinationDictionary.put(PdfName.D, PdfExplicitDestination.createFit(page).getPdfObject());
document.getCatalog().getNameTree(PdfName.Dests)
.addEntry("destination_name", destinationDictionary);
page.addAnnotation(new PdfLinkAnnotation(new Rectangle(100, 590, 300, 25)).
setDestination(new PdfStringDestination("destination_name")));
}
Assertions.assertNull(new CompareTool().compareByContent(destinationFolder + "linkAnnotationWithDictionaryStringDestination.pdf",
sourceFolder + "cmp_linkAnnotationWithDictionaryStringDestination.pdf", destinationFolder, "diff_"));
}
@Test
public void linkAnnotationWithCyclicReferencesTest() throws Exception {
try (PdfDocument document = new PdfDocument(
CompareTool.createTestPdfWriter(destinationFolder + "linkAnnotationWithCyclicReferences.pdf"))) {
document.setTagged();
document.getTagStructureContext().getAutoTaggingPointer();
PdfPage page = document.addNewPage();
PdfDictionary dests = new PdfDictionary();
dests.put(new PdfName("destination_name"), new PdfName("destination_name"));
document.getCatalog().put(PdfName.Dests, dests);
page.addAnnotation(new PdfLinkAnnotation(new Rectangle(100, 590, 300, 25))
.setAction(PdfAction.createGoTo(new PdfNamedDestination("destination_name"))));
}
Assertions.assertNull(new CompareTool().compareByContent(destinationFolder + "linkAnnotationWithCyclicReferences.pdf",
sourceFolder + "cmp_linkAnnotationWithCyclicReferences.pdf", destinationFolder, "diff_"));
}
@Test
public void addAndGetLinkAnnotations() throws Exception {
PdfDocument document = new PdfDocument(CompareTool.createTestPdfWriter(destinationFolder + "linkAnnotation03.pdf"));
PdfPage page = document.addNewPage();
PdfCanvas canvas = new PdfCanvas(page);
canvas.beginText();
canvas.setFontAndSize(PdfFontFactory.createFont(StandardFonts.COURIER_BOLD), 14);
canvas.moveText(100, 600);
canvas.showText("Click here to go to itextpdf site.");
canvas.moveText(0, -50);
canvas.showText("Click here to go to itextpdf blog.");
canvas.moveText(0, -50);
canvas.showText("Click here to go to itextpdf FAQ.");
canvas.endText();
canvas.release();
int[] borders = {0, 0, 1};
page.addAnnotation(new PdfLinkAnnotation(new Rectangle(100, 590, 300, 25)).
setAction(PdfAction.createURI("http://itextpdf.com")).
setBorder(new PdfArray(borders)).
setColor(new PdfArray(new float[] {1, 0, 0})));
page.addAnnotation(new PdfLinkAnnotation(new Rectangle(100, 540, 300, 25)).
setAction(PdfAction.createURI("http://itextpdf.com/node")).
setBorder(new PdfArray(borders)).
setColor(new PdfArray(new float[] {0, 1, 0})));
page.addAnnotation(new PdfLinkAnnotation(new Rectangle(100, 490, 300, 25)).
setAction(PdfAction.createURI("http://itextpdf.com/salesfaq")).
setBorder(new PdfArray(borders)).
setColor(new PdfArray(new float[] {0, 0, 1})));
page.flush();
document.close();
Assertions.assertNull(new CompareTool()
.compareByContent(destinationFolder + "linkAnnotation03.pdf", sourceFolder + "cmp_linkAnnotation03.pdf",
destinationFolder, "diff_"));
document = new PdfDocument(CompareTool.createOutputReader(destinationFolder + "linkAnnotation03.pdf"));
page = document.getPage(1);
Assertions.assertEquals(3, page.getAnnotsSize());
List<PdfAnnotation> annotations = page.getAnnotations();
Assertions.assertEquals(3, annotations.size());
PdfLinkAnnotation link = (PdfLinkAnnotation) annotations.get(0);
Assertions.assertEquals(page, link.getPage());
document.close();
}
@Test
@LogMessages(messages = {
@LogMessage(messageTemplate = IoLogMessageConstant.DESTINATION_NOT_PERMITTED_WHEN_ACTION_IS_SET)})
public void linkAnnotationActionDestinationTest() throws IOException, InterruptedException {
String fileName = "linkAnnotationActionDestinationTest.pdf";
PdfDocument pdfDocument = new PdfDocument(CompareTool.createTestPdfWriter(destinationFolder + fileName));
PdfArray array = new PdfArray();
array.add(pdfDocument.addNewPage().getPdfObject());
array.add(PdfName.XYZ);
array.add(new PdfNumber(36));
array.add(new PdfNumber(100));
array.add(new PdfNumber(1));
PdfDestination dest = PdfDestination.makeDestination(array);
PdfLinkAnnotation link = new PdfLinkAnnotation(new Rectangle(0, 0, 0, 0));
link.setAction(PdfAction.createGoTo("abc"));
link.setDestination(dest);
pdfDocument.getPage(1).addAnnotation(link);
pdfDocument.close();
Assertions.assertNull(new CompareTool()
.compareByContent(destinationFolder + fileName, sourceFolder + "cmp_" + fileName, destinationFolder));
}
@Test
public void removeLinkAnnotationTaggedAsLinkTest() throws IOException, InterruptedException {
String input = sourceFolder + "taggedLinkAnnotationAsLink.pdf";
String output = destinationFolder + "removeLinkAnnotationTaggedAsLinkTest.pdf";
String cmp = sourceFolder + "cmp_" + "removeLinkAnnotationTaggedAsLinkTest.pdf";
try (PdfDocument pdfDoc = new PdfDocument(new PdfReader(input), CompareTool.createTestPdfWriter(output))) {
PdfPage page = pdfDoc.getPage(1);
page.removeAnnotation(page.getAnnotations().get(0));
}
Assertions.assertNull(new CompareTool().compareByContent(output, cmp, destinationFolder));
}
@Test
public void removeLinkAnnotationTaggedAsAnnotTest() throws IOException, InterruptedException {
String input = sourceFolder + "taggedLinkAnnotationAsAnnot.pdf";
String output = destinationFolder + "removeLinkAnnotationTaggedAsAnnotTest.pdf";
String cmp = sourceFolder + "cmp_" + "removeLinkAnnotationTaggedAsAnnotTest.pdf";
try (PdfDocument pdfDoc = new PdfDocument(new PdfReader(input), CompareTool.createTestPdfWriter(output))) {
PdfPage page = pdfDoc.getPage(1);
page.removeAnnotation(page.getAnnotations().get(0));
}
Assertions.assertNull(new CompareTool().compareByContent(output, cmp, destinationFolder));
}
@Test
public void removeLinkAnnotationTagWithContentTest() throws IOException, InterruptedException {
String input = sourceFolder + "taggedLinkAnnotationTagWithContent.pdf";
String output = destinationFolder + "removeLinkAnnotationTagWithContentTest.pdf";
String cmp = sourceFolder + "cmp_" + "removeLinkAnnotationTagWithContentTest.pdf";
try (PdfDocument pdfDoc = new PdfDocument(new PdfReader(input), CompareTool.createTestPdfWriter(output))) {
PdfPage page = pdfDoc.getPage(1);
page.removeAnnotation(page.getAnnotations().get(0));
}
Assertions.assertNull(new CompareTool().compareByContent(output, cmp, destinationFolder));
}
@Test
public void removeLinkAnnotationWithNoTagTest() throws IOException, InterruptedException {
String input = sourceFolder + "taggedInvalidNoLinkAnnotationTag.pdf";
String output = destinationFolder + "removeLinkAnnotationWithNoTagTest.pdf";
String cmp = sourceFolder + "cmp_" + "removeLinkAnnotationWithNoTagTest.pdf";
try (PdfDocument pdfDoc = new PdfDocument(new PdfReader(input), CompareTool.createTestPdfWriter(output))) {
PdfPage page = pdfDoc.getPage(1);
page.removeAnnotation(page.getAnnotations().get(0));
}
Assertions.assertNull(new CompareTool().compareByContent(output, cmp, destinationFolder));
}
@Test
public void addLinkAnnotInTagged13PdfTest() throws Exception {
String outPdf = destinationFolder + "addLinkAnnotInTagged13PdfTest.pdf";
String cmpPdf = sourceFolder + "cmp_addLinkAnnotInTagged13PdfTest.pdf";
try (PdfDocument pdfDoc = new PdfDocument(CompareTool.createTestPdfWriter(
outPdf, new WriterProperties().setPdfVersion(PdfVersion.PDF_1_3)))) {
pdfDoc.setTagged();
PdfPage page = pdfDoc.addNewPage();
PdfLinkAnnotation annot = (PdfLinkAnnotation) new PdfLinkAnnotation(new Rectangle(100, 600, 50, 40))
.setAction(PdfAction.createURI("http://itextpdf.com"))
.setBorder(new PdfArray(new float[]{0, 0, 1}))
.setColor(new PdfArray(new float[]{1, 0, 0}));
page.addAnnotation(annot);
}
Assertions.assertNull(new CompareTool().compareByContent(outPdf, cmpPdf, destinationFolder, "diff_"));
}
}