FlushPdfDocumentEventTest.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.actions.events;

import com.itextpdf.commons.actions.AbstractProductProcessITextEvent;
import com.itextpdf.commons.actions.EventManager;
import com.itextpdf.commons.actions.confirmations.ConfirmEvent;
import com.itextpdf.commons.actions.confirmations.ConfirmedEventWrapper;
import com.itextpdf.commons.actions.confirmations.EventConfirmationType;
import com.itextpdf.commons.actions.data.ProductData;
import com.itextpdf.commons.actions.processors.ITextProductEventProcessor;
import com.itextpdf.commons.actions.sequence.SequenceId;
import com.itextpdf.io.source.ByteArrayOutputStream;
import com.itextpdf.kernel.actions.ProductEventHandlerAccess;
import com.itextpdf.kernel.actions.data.ITextCoreProductData;
import com.itextpdf.kernel.actions.ecosystem.ITextTestEvent;
import com.itextpdf.kernel.logs.KernelLogMessageConstant;
import com.itextpdf.kernel.pdf.PdfDocument;
import com.itextpdf.kernel.pdf.PdfReader;
import com.itextpdf.kernel.pdf.PdfWriter;
import com.itextpdf.test.AssertUtil;
import com.itextpdf.test.ExtendedITextTest;
import com.itextpdf.test.annotations.LogMessage;
import com.itextpdf.test.annotations.LogMessages;

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

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

    private static final String SOURCE_FOLDER = "./src/test/resources/com/itextpdf/kernel/actions/";

    @Test
    public void onCloseReportingTest() throws IOException {
        try (ProductEventHandlerAccess access = new ProductEventHandlerAccess();
                PdfDocument document = new PdfDocument(new PdfReader(SOURCE_FOLDER + "hello.pdf"))) {

            ITextTestEvent event = new ITextTestEvent(document.getDocumentIdWrapper(), ITextCoreProductData.getInstance(), null, "test-event", EventConfirmationType.ON_CLOSE);
            int initialLength = access.publicGetEvents(document.getDocumentIdWrapper()).size();

            EventManager.getInstance().onEvent(event);
            new FlushPdfDocumentEvent(document).doAction();

            AbstractProductProcessITextEvent reportedEvent = access.publicGetEvents(document.getDocumentIdWrapper()).get(initialLength);
            Assertions.assertTrue(reportedEvent instanceof ConfirmedEventWrapper);
            ConfirmedEventWrapper wrappedEvent = (ConfirmedEventWrapper) reportedEvent;
            Assertions.assertEquals(event, wrappedEvent.getEvent());
        }
    }

    @Test
    @LogMessages(
            messages = {
                    @LogMessage(messageTemplate = KernelLogMessageConstant.UNCONFIRMED_EVENT)
            }
    )
    public void onDemandReportingIgnoredTest() throws IOException {
        try (ProductEventHandlerAccess access = new ProductEventHandlerAccess();
                PdfDocument document = new PdfDocument(new PdfReader(SOURCE_FOLDER + "hello.pdf"))) {

            ITextTestEvent event = new ITextTestEvent(document.getDocumentIdWrapper(),
                    ITextCoreProductData.getInstance(), null, "test-event", EventConfirmationType.ON_DEMAND);
            int initialLength = access.publicGetEvents(document.getDocumentIdWrapper()).size();

            EventManager.getInstance().onEvent(event);
            new FlushPdfDocumentEvent(document).doAction();

            AbstractProductProcessITextEvent reportedEvent = access.publicGetEvents(document.getDocumentIdWrapper()).get(initialLength);
            Assertions.assertFalse(reportedEvent instanceof ConfirmedEventWrapper);
        }
    }

    @Test
    public void onDemandReportingConfirmedTest() throws IOException {
        try (ProductEventHandlerAccess access = new ProductEventHandlerAccess();
                PdfDocument document = new PdfDocument(new PdfReader(SOURCE_FOLDER + "hello.pdf"))) {

            ITextTestEvent event = new ITextTestEvent(document.getDocumentIdWrapper(), ITextCoreProductData.getInstance(), null, "test-event", EventConfirmationType.ON_DEMAND);
            int initialLength = access.publicGetEvents(document.getDocumentIdWrapper()).size();

            EventManager.getInstance().onEvent(event);

            AbstractProductProcessITextEvent reportedEvent = access.publicGetEvents(document.getDocumentIdWrapper()).get(initialLength);
            Assertions.assertFalse(reportedEvent instanceof ConfirmedEventWrapper);
            Assertions.assertEquals(event, reportedEvent);

            EventManager.getInstance().onEvent(new ConfirmEvent(document.getDocumentIdWrapper(), event));
            new FlushPdfDocumentEvent(document).doAction();

            AbstractProductProcessITextEvent confirmedEvent = access.publicGetEvents(document.getDocumentIdWrapper()).get(initialLength);
            Assertions.assertTrue(confirmedEvent instanceof ConfirmedEventWrapper);
            ConfirmedEventWrapper wrappedEvent = (ConfirmedEventWrapper) confirmedEvent;
            Assertions.assertEquals(event, wrappedEvent.getEvent());
        }
    }

    @Test
    @LogMessages(
            messages = {
                    @LogMessage(messageTemplate = KernelLogMessageConstant.UNKNOWN_PRODUCT_INVOLVED)
            }
    )
    public void unknownProductTest() throws IOException {
            try (ProductEventHandlerAccess access = new ProductEventHandlerAccess();
                    PdfDocument document = new PdfDocument(new PdfReader(SOURCE_FOLDER + "hello.pdf"))) {

            access.publicAddEvent(document.getDocumentIdWrapper(), getEvent("unknown product", document.getDocumentIdWrapper()));

            AssertUtil.doesNotThrow(() -> new FlushPdfDocumentEvent(document).doAction());
        }
    }

    @Test
    public void doActionNullDocumentTest() {
        FlushPdfDocumentEvent closeEvent = new FlushPdfDocumentEvent(null);
        AssertUtil.doesNotThrow(() -> closeEvent.doAction());
    }

    @Test
    public void doActionNullEventMapTest() throws IOException {
        final ProductData productData = ITextCoreProductData.getInstance();
        final String expectedProducer = "iText\u00ae " + productData.getPublicProductName() + " " +
                productData.getVersion() + " \u00a9" + productData.getSinceCopyrightYear() + "-"
                + productData.getToCopyrightYear() + " Apryse Group NV";
        try (PdfDocument document = new DummyPdfDocument(new PdfReader(SOURCE_FOLDER + "hello.pdf"))) {
            AssertUtil.doesNotThrow(() -> new FlushPdfDocumentEvent(document).doAction());
            Assertions.assertTrue(document.getDocumentInfo().getProducer()
                    .contains(expectedProducer));
        }
    }

    @Test
    public void flushEventAfterEachEventTest() throws IOException {
        String resourceInit = SOURCE_FOLDER + "hello.pdf";
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        try (PdfDocument pdf = new PdfDocument(new PdfReader(resourceInit), new PdfWriter(baos))) {
            pdf.addNewPage();
            EventManager.getInstance().onEvent(new FlushPdfDocumentEvent(pdf));
        }

        try (PdfDocument pdf = new PdfDocument(new PdfReader(new ByteArrayInputStream(baos.toByteArray())))) {
            String producerLine = pdf.getDocumentInfo().getProducer();
            String modifiedByItext = "modified using iText\u00ae Core";
            Assertions.assertEquals(producerLine.indexOf(modifiedByItext), producerLine.lastIndexOf(modifiedByItext));
        }
    }

    private static class DummyPdfDocument extends PdfDocument {

        public DummyPdfDocument(PdfReader reader) {
            super(reader);
        }

        public SequenceId getDocumentIdWrapper() {
            return null;
        }
    }

    private static class TestProductEventProcessor implements ITextProductEventProcessor {
        private final String processorId;

        public TestProductEventProcessor(String processorId) {
            this.processorId = processorId;
        }

        @Override
        public void onEvent(AbstractProductProcessITextEvent event) {
            // do nothing here
        }

        @Override
        public String getProductName() {
            return processorId;
        }

        @Override
        public String getUsageType() {
            return "AGPL Version";
        }

        @Override
        public String getProducer() {
            return "iText";
        }
    }

    private static ConfirmedEventWrapper getEvent(String productName, SequenceId sequenceId) {
        ProductData productData = new ProductData(productName, productName, "2.0", 1999, 2020);
        return new ConfirmedEventWrapper(new ITextTestEvent(sequenceId, productData, null, "testing"), "AGPL Version", "iText");
    }
}