PdfTargetTest.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.action;

import com.itextpdf.io.logs.IoLogMessageConstant;
import com.itextpdf.kernel.exceptions.KernelExceptionMessageConstant;
import com.itextpdf.kernel.exceptions.PdfException;
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.PdfString;
import com.itextpdf.kernel.pdf.PdfWriter;
import com.itextpdf.kernel.pdf.annot.PdfAnnotation;
import com.itextpdf.kernel.pdf.annot.PdfFileAttachmentAnnotation;
import com.itextpdf.test.ExtendedITextTest;
import com.itextpdf.test.annotations.LogMessage;
import com.itextpdf.test.annotations.LogMessages;

import java.io.ByteArrayOutputStream;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.Tag;

@Tag("UnitTest")
public class PdfTargetTest extends ExtendedITextTest {

    @Test
    public void createInstanceTest() {
        PdfDictionary dictionary = new PdfDictionary();
        PdfTarget target = PdfTarget.create(dictionary);

        Assertions.assertEquals(dictionary, target.getPdfObject());
    }

    @Test
    public void createParentInstanceTest() {
        PdfTarget target = PdfTarget.createParentTarget();
        PdfDictionary dictionary = target.getPdfObject();
        Assertions.assertEquals(PdfName.P, dictionary.get(PdfName.R));
    }

    @Test
    public void createChildInstanceTest() {
        PdfTarget target = PdfTarget.createChildTarget();
        PdfDictionary dictionary = target.getPdfObject();
        Assertions.assertEquals(PdfName.C, dictionary.get(PdfName.R));
    }

    @Test
    public void createChildInstanceWithEmbeddedFileTest() {
        final String embeddedFileName = "EmbeddedFileName.file";

        PdfTarget target = PdfTarget.createChildTarget(embeddedFileName);
        PdfDictionary dictionary = target.getPdfObject();
        Assertions.assertEquals(PdfName.C, dictionary.get(PdfName.R));
        Assertions.assertEquals(new PdfString(embeddedFileName), dictionary.get(PdfName.N));
    }

    @Test
    public void createChildInstanceWithNamedDestinationTest() {
        final String namedDestination = "namedDestination";
        final String annotationIdentifier = "annotationIdentifier";

        PdfTarget target = PdfTarget.createChildTarget(namedDestination, annotationIdentifier);

        PdfDictionary dictionary = target.getPdfObject();
        Assertions.assertEquals(PdfName.C, dictionary.get(PdfName.R));
        Assertions.assertEquals(new PdfString(namedDestination), dictionary.get(PdfName.P));
        Assertions.assertEquals(new PdfString(annotationIdentifier), dictionary.get(PdfName.A));
    }

    @Test
    public void createChildInstanceWithPageNumberTest() {
        final int pageNumber = 23;
        final int annotationIndex = 7;

        PdfTarget target = PdfTarget.createChildTarget(pageNumber, annotationIndex);

        PdfDictionary dictionary = target.getPdfObject();
        Assertions.assertEquals(PdfName.C, dictionary.get(PdfName.R));
        Assertions.assertEquals(new PdfNumber(pageNumber - 1), dictionary.get(PdfName.P));
        Assertions.assertEquals(new PdfNumber(annotationIndex), dictionary.get(PdfName.A));
    }

    @Test
    public void namePropertyTest() {
        final String name = "Name";

        PdfTarget target = PdfTarget.create(new PdfDictionary());
        target.setName(name);

        Assertions.assertEquals(name, target.getName());
        Assertions.assertEquals(new PdfString(name), target.getPdfObject().get(PdfName.N));

    }

    @Test
    public void targetPropertyTest() {

        final PdfDictionary oldDictionary = new PdfDictionary();
        oldDictionary.put(new PdfName("Id"), new PdfString("Old"));

        final PdfDictionary newDictionary = new PdfDictionary();
        newDictionary.put(new PdfName("Id"), new PdfString("New"));

        PdfTarget target = PdfTarget.create(oldDictionary);

        target.setTarget(PdfTarget.create(newDictionary));
        Assertions.assertEquals(newDictionary, target.getTarget().getPdfObject());
        Assertions.assertEquals(newDictionary, target.getPdfObject().get(PdfName.T));
    }

    @Test
    public void setAnnotationTest() {
        try (PdfDocument document = new PdfDocument(new PdfWriter(new ByteArrayOutputStream()))) {
            PdfFileAttachmentAnnotation annotation0 = new PdfFileAttachmentAnnotation(
                    new Rectangle(0,0, 20, 20));
            PdfFileAttachmentAnnotation annotation1 = new PdfFileAttachmentAnnotation(
                    new Rectangle(1,1, 21, 21));
            PdfFileAttachmentAnnotation annotation2 = new PdfFileAttachmentAnnotation(
                    new Rectangle(2,2, 22, 22));

            document.addNewPage();
            document.getPage(1).addAnnotation(annotation0);
            document.getPage(1).addAnnotation(annotation1);
            document.getPage(1).addAnnotation(annotation2);

            PdfTarget target = PdfTarget.create(new PdfDictionary());
            target.setAnnotation(annotation2, document);

            PdfDictionary dictionary = target.getPdfObject();
            Assertions.assertEquals(0, dictionary.getAsNumber(PdfName.P).intValue());
            Assertions.assertEquals(2, dictionary.getAsNumber(PdfName.A).intValue());
        }
    }

    @Test
    public void setAnnotationWhichIsMissedOnThePageTest() {
        try (PdfDocument document = new PdfDocument(new PdfWriter(new ByteArrayOutputStream()))) {
            PdfFileAttachmentAnnotation annotation0 = new PdfFileAttachmentAnnotation(
                    new Rectangle(0,0, 20, 20));
            PdfFileAttachmentAnnotation annotation1 = new PdfFileAttachmentAnnotation(
                    new Rectangle(1,1, 21, 21));
            PdfFileAttachmentAnnotation annotation2 = new PdfFileAttachmentAnnotation(
                    new Rectangle(2,2, 22, 22));

            document.addNewPage();
            document.getPage(1).addAnnotation(annotation0);
            document.getPage(1).addAnnotation(annotation1);
            // The page doesn't know about the annotation
            annotation2.setPage(document.getPage(1));

            PdfTarget target = PdfTarget.create(new PdfDictionary());
            target.setAnnotation(annotation2, document);

            PdfDictionary dictionary = target.getPdfObject();
            Assertions.assertEquals(0, dictionary.getAsNumber(PdfName.P).intValue());
            Assertions.assertEquals(-1, dictionary.getAsNumber(PdfName.A).intValue());
        }
    }

    @Test
    public void setAnnotationWithoutPageTest() {
        try (PdfDocument document = new PdfDocument(new PdfWriter(new ByteArrayOutputStream()))) {
            document.addNewPage();

            PdfFileAttachmentAnnotation annotation = new PdfFileAttachmentAnnotation(
                    new Rectangle(0,0, 20, 20));

            PdfTarget target = PdfTarget.create(new PdfDictionary());

            Exception e = Assertions.assertThrows(PdfException.class,
                    () -> target.setAnnotation(annotation, document)
            );
            Assertions.assertEquals(KernelExceptionMessageConstant.ANNOTATION_SHALL_HAVE_REFERENCE_TO_PAGE, e.getMessage());
        }
    }

    @Test
    public void getAnnotationSetAsAnnotationTest() {
        try (PdfDocument document = new PdfDocument(new PdfWriter(new ByteArrayOutputStream()))) {

            PdfFileAttachmentAnnotation annotation = new PdfFileAttachmentAnnotation(
                    new Rectangle(0,0, 20, 20));
            document.addNewPage();
            document.getPage(1).addAnnotation(annotation);

            PdfDictionary content = new PdfDictionary();
            content.put(new PdfName("Key"), new PdfString("Value"));

            PdfTarget target = PdfTarget.create(new PdfDictionary());
            target.setAnnotation(annotation, document);

            Assertions.assertEquals(annotation.getPdfObject(), target.getAnnotation(document).getPdfObject());
        }
    }

    @Test
    public void getAnnotationSetAsIntsTest() {
        try (PdfDocument document = new PdfDocument(new PdfWriter(new ByteArrayOutputStream()))) {
            final int pageNumber = 1;
            final int annotationIndex = 0;

            PdfTarget target = PdfTarget.createChildTarget(pageNumber, annotationIndex);

            PdfFileAttachmentAnnotation annotation = new PdfFileAttachmentAnnotation(
                    new Rectangle(0,0, 20, 20));
            document.addNewPage();
            document.getPage(1).addAnnotation(annotation);

            Assertions.assertEquals(annotation.getPdfObject(), target.getAnnotation(document).getPdfObject());
        }
    }

    @Test
    public void getAnnotationSetAsStringTest() {
        try (PdfDocument document = new PdfDocument(new PdfWriter(new ByteArrayOutputStream()))) {
            final String namedDestination = "namedDestination";
            final String annotationIdentifier = "annotationIdentifier";

            PdfTarget target = PdfTarget.createChildTarget(namedDestination, annotationIdentifier);

            PdfFileAttachmentAnnotation annotation = new PdfFileAttachmentAnnotation(
                    new Rectangle(0,0, 20, 20));
            annotation.setName(new PdfString(annotationIdentifier));

            document.addNewPage();
            document.getPage(1).addAnnotation(annotation);
            document.getCatalog().getNameTree(PdfName.Dests).addEntry(namedDestination,
                    new PdfArray(new PdfNumber(1)));

            PdfAnnotation retrievedAnnotation = target.getAnnotation(document);

            Assertions.assertEquals(annotation.getPdfObject(), retrievedAnnotation.getPdfObject());
        }
    }

    @Test
    @LogMessages(messages = {
            @LogMessage(messageTemplate = IoLogMessageConstant.SOME_TARGET_FIELDS_ARE_NOT_SET_OR_INCORRECT)
    })
    public void getAnnotationSetAsStringNotAvailableTest() {
        try (PdfDocument document = new PdfDocument(new PdfWriter(new ByteArrayOutputStream()))) {
            final String namedDestination = "namedDestination";
            final String annotationIdentifier = "annotationIdentifier";

            PdfTarget target = PdfTarget.createChildTarget(namedDestination, annotationIdentifier);

            document.addNewPage();
            document.getCatalog().getNameTree(PdfName.Dests).addEntry(namedDestination,
                    new PdfArray(new PdfNumber(1)));
            PdfAnnotation annotation = target.getAnnotation(document);

            Assertions.assertNull(annotation);
        }
    }

    @Test
    public void putTest() {
        final PdfName key1 = new PdfName("Key1");
        final PdfName key2 = new PdfName("Key2");
        final PdfDictionary dictionary = new PdfDictionary();

        PdfTarget target = PdfTarget.create(dictionary);
        target.put(key1, new PdfNumber(23))
                .put(key2, new PdfString("Hello, world!"));
        Assertions.assertEquals(23, dictionary.getAsNumber(key1).intValue());
        Assertions.assertEquals("Hello, world!", dictionary.getAsString(key2).getValue());
    }


    @Test
    public void isWrappedObjectMustBeIndirectTest() {
        PdfDictionary pdfObject = new PdfDictionary();
        PdfTarget target = PdfTarget.create(pdfObject);

        Assertions.assertFalse(target.isWrappedObjectMustBeIndirect());
    }

    @Test
    public void noAnnotationPageReferenceTest() {
        PdfFileAttachmentAnnotation pdfAnnotation = new PdfFileAttachmentAnnotation(new Rectangle(100, 100));
        PdfTarget pdfTarget = PdfTarget.create(new PdfDictionary());
        Exception exception = Assertions.assertThrows(PdfException.class,
                () -> pdfTarget.setAnnotation(pdfAnnotation, null));
        Assertions.assertEquals(KernelExceptionMessageConstant.ANNOTATION_SHALL_HAVE_REFERENCE_TO_PAGE,
                exception.getMessage());
    }
}