PdfOCPropertiesUnitTest.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.layer;

import com.itextpdf.io.source.ByteArrayOutputStream;
import com.itextpdf.kernel.pdf.*;
import com.itextpdf.test.ExtendedITextTest;

import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Tag;
import org.junit.jupiter.api.Test;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.util.List;

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

    @Test
    public void orderArrayOcgWithTwoParentsTest() throws IOException {
        byte[] docBytes;
        try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) {
            try (PdfDocument document = new PdfDocument(new PdfWriter(outputStream))) {
                PdfDictionary parentOcg1 = new PdfDictionary();
                parentOcg1.put(PdfName.Name, new PdfString("Parent1"));
                parentOcg1.put(PdfName.Type, PdfName.OCG);
                parentOcg1.makeIndirect(document);
                PdfArray orderArray = new PdfArray();
                orderArray.add(parentOcg1);

                PdfDictionary childOcg = new PdfDictionary();
                childOcg.put(PdfName.Name, new PdfString("child"));
                childOcg.put(PdfName.Type, PdfName.OCG);
                childOcg.makeIndirect(document);
                PdfArray childArray = new PdfArray();
                childArray.add(childOcg);
                orderArray.add(childArray);

                PdfDictionary parentOcg2 = new PdfDictionary();
                parentOcg2.put(PdfName.Name, new PdfString("Parent2"));
                parentOcg2.put(PdfName.Type, PdfName.OCG);
                parentOcg2.makeIndirect(document);
                orderArray.add(parentOcg2);
                orderArray.add(new PdfArray(childArray));

                PdfDictionary DDictionary = new PdfDictionary();
                DDictionary.put(PdfName.Order, orderArray);
                PdfArray ocgArray = new PdfArray();
                ocgArray.add(parentOcg1);
                ocgArray.add(parentOcg2);
                ocgArray.add(childOcg);

                PdfDictionary OCPropertiesDic = new PdfDictionary();
                OCPropertiesDic.put(PdfName.D, DDictionary);
                OCPropertiesDic.put(PdfName.OCGs, ocgArray);
                OCPropertiesDic.makeIndirect(document);
                document.getCatalog().getPdfObject().put(PdfName.OCProperties, OCPropertiesDic);
            }
            docBytes = outputStream.toByteArray();
        }

        try (PdfDocument docReopen = new PdfDocument(new PdfReader(new ByteArrayInputStream(docBytes)))) {
            List<PdfLayer> layers = docReopen.getCatalog().getOCProperties(false).getLayers();
            Assertions.assertEquals(3, layers.size());
            Assertions.assertEquals(1, layers.get(0).getChildren().size());
            Assertions.assertEquals(2, layers.get(1).getParents().size());
            Assertions.assertEquals(1, layers.get(2).getChildren().size());
        }
    }

    @Test
    public void orderArrayOcgWithTwoTitleParentsTest() throws IOException {
        byte[] docBytes;
        try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) {
            try (PdfDocument document = new PdfDocument(new PdfWriter(outputStream))) {
                PdfDictionary childOcg = new PdfDictionary();
                childOcg.put(PdfName.Name, new PdfString("child"));
                childOcg.put(PdfName.Type, PdfName.OCG);
                childOcg.makeIndirect(document);
                PdfArray childArray = new PdfArray();
                childArray.add(childOcg);

                PdfArray titleOcg1 = new PdfArray();
                titleOcg1.add(new PdfString("parent title layer 1"));
                titleOcg1.add(childArray);
                PdfArray orderArray = new PdfArray();
                orderArray.add(titleOcg1);

                PdfArray titleOcg2 = new PdfArray();
                titleOcg2.add(new PdfString("parent title 2"));
                titleOcg2.add(childArray);
                orderArray.add(titleOcg2);

                PdfDictionary DDictionary = new PdfDictionary();
                DDictionary.put(PdfName.Order, orderArray);

                PdfArray ocgArray = new PdfArray();
                ocgArray.add(childOcg);

                PdfDictionary ocPropertiesDic = new PdfDictionary();
                ocPropertiesDic.put(PdfName.D, DDictionary);
                ocPropertiesDic.put(PdfName.OCGs, ocgArray);
                ocPropertiesDic.makeIndirect(document);
                document.getCatalog().getPdfObject().put(PdfName.OCProperties, ocPropertiesDic);
            }
            docBytes = outputStream.toByteArray();
        }

        try (PdfDocument docReopen = new PdfDocument(new PdfReader(new ByteArrayInputStream(docBytes)),
                new PdfWriter(new ByteArrayOutputStream()))) {
            List<PdfLayer> layers = docReopen.getCatalog().getOCProperties(false).getLayers();
            Assertions.assertEquals(3, layers.size());
            Assertions.assertEquals(1, layers.get(0).getChildren().size());
            Assertions.assertEquals(2, layers.get(1).getParents().size());
            Assertions.assertEquals(1, layers.get(2).getChildren().size());
        }
    }

    @Test
    public void orderArrayTitleOcgWithTwoParentsTest() throws IOException {
        byte[] docBytes;
        try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) {
            try (PdfDocument document = new PdfDocument(new PdfWriter(outputStream))) {
                PdfDictionary parentOcg1 = new PdfDictionary();
                parentOcg1.put(PdfName.Name, new PdfString("Parent1"));
                parentOcg1.put(PdfName.Type, PdfName.OCG);
                parentOcg1.makeIndirect(document);
                PdfArray orderArray = new PdfArray();
                orderArray.add(parentOcg1);

                PdfArray titleChildOcg = new PdfArray();
                titleChildOcg.add(new PdfString("child title layer"));
                PdfArray childArray = new PdfArray();
                childArray.add(titleChildOcg);
                orderArray.add(childArray);

                PdfDictionary parentOcg2 = new PdfDictionary();
                parentOcg2.put(PdfName.Name, new PdfString("Parent2"));
                parentOcg2.put(PdfName.Type, PdfName.OCG);
                parentOcg2.makeIndirect(document);
                orderArray.add(parentOcg2);
                orderArray.add(new PdfArray(childArray));

                PdfDictionary DDictionary = new PdfDictionary();
                DDictionary.put(PdfName.Order, orderArray);
                PdfArray ocgArray = new PdfArray();
                ocgArray.add(parentOcg1);
                ocgArray.add(parentOcg2);

                PdfDictionary ocPropertiesDic = new PdfDictionary();
                ocPropertiesDic.put(PdfName.D, DDictionary);
                ocPropertiesDic.put(PdfName.OCGs, ocgArray);
                ocPropertiesDic.makeIndirect(document);
                document.getCatalog().getPdfObject().put(PdfName.OCProperties, ocPropertiesDic);
            }
            docBytes = outputStream.toByteArray();
        }

        try (PdfDocument docReopen = new PdfDocument(new PdfReader(new ByteArrayInputStream(docBytes)),
                new PdfWriter(new ByteArrayOutputStream()))) {
            List<PdfLayer> layers = docReopen.getCatalog().getOCProperties(false).getLayers();
            Assertions.assertEquals(3, layers.size());
            Assertions.assertEquals(1, layers.get(0).getChildren().size());
            Assertions.assertEquals(2, layers.get(1).getParents().size());
            Assertions.assertEquals(1, layers.get(2).getChildren().size());
        }
    }

    @Test
    public void orderArrayDuplicatedOcgTest() throws IOException {
        byte[] docBytes;
        try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) {
            try (PdfDocument document = new PdfDocument(new PdfWriter(outputStream))) {
                PdfDictionary ocg = new PdfDictionary();
                ocg.put(PdfName.Name, new PdfString("ocg"));
                ocg.put(PdfName.Type, PdfName.OCG);
                ocg.makeIndirect(document);
                PdfArray orderArray = new PdfArray();
                orderArray.add(ocg);
                orderArray.add(ocg);
                orderArray.add(ocg);

                PdfDictionary parentOcg = new PdfDictionary();
                parentOcg.put(PdfName.Name, new PdfString("Parent"));
                parentOcg.put(PdfName.Type, PdfName.OCG);
                parentOcg.makeIndirect(document);
                orderArray.add(parentOcg);
                orderArray.add(new PdfArray(ocg));

                PdfDictionary DDictionary = new PdfDictionary();
                DDictionary.put(PdfName.Order, orderArray);
                PdfArray ocgArray = new PdfArray();
                ocgArray.add(ocg);
                ocgArray.add(parentOcg);

                PdfDictionary ocPropertiesDic = new PdfDictionary();
                ocPropertiesDic.put(PdfName.D, DDictionary);
                ocPropertiesDic.put(PdfName.OCGs, ocgArray);
                ocPropertiesDic.makeIndirect(document);
                document.getCatalog().getPdfObject().put(PdfName.OCProperties, ocPropertiesDic);
            }
            docBytes = outputStream.toByteArray();
        }

        try (PdfDocument docReopen = new PdfDocument(new PdfReader(new ByteArrayInputStream(docBytes)),
                new PdfWriter(new ByteArrayOutputStream()))) {
            List<PdfLayer> layers = docReopen.getCatalog().getOCProperties(false).getLayers();
            Assertions.assertEquals(2, layers.size());
            Assertions.assertEquals(1, layers.get(0).getParents().size());
            Assertions.assertEquals(1, layers.get(1).getChildren().size());
        }
    }

    @Test
    public void orderArrayDuplicatedTitleOcgTest() throws IOException {
        byte[] docBytes;
        try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) {
            try (PdfDocument document = new PdfDocument(new PdfWriter(outputStream))) {
                PdfArray orderArray = new PdfArray();
                PdfArray titleOcg = new PdfArray();
                titleOcg.add(new PdfString("title layer"));
                orderArray.add(titleOcg);

                PdfDictionary parentOcg = new PdfDictionary();
                parentOcg.put(PdfName.Name, new PdfString("Parent"));
                parentOcg.put(PdfName.Type, PdfName.OCG);
                parentOcg.makeIndirect(document);
                orderArray.add(parentOcg);

                PdfArray nestedOcg = new PdfArray();
                nestedOcg.add(titleOcg);
                orderArray.add(nestedOcg);
                orderArray.add(titleOcg);

                PdfDictionary DDictionary = new PdfDictionary();
                DDictionary.put(PdfName.Order, orderArray);
                PdfArray ocgArray = new PdfArray();
                ocgArray.add(parentOcg);

                PdfDictionary ocPropertiesDic = new PdfDictionary();
                ocPropertiesDic.put(PdfName.D, DDictionary);
                ocPropertiesDic.put(PdfName.OCGs, ocgArray);
                ocPropertiesDic.makeIndirect(document);
                document.getCatalog().getPdfObject().put(PdfName.OCProperties, ocPropertiesDic);
            }
            docBytes = outputStream.toByteArray();
        }
        try (PdfDocument docReopen = new PdfDocument(new PdfReader(new ByteArrayInputStream(docBytes)),
                new PdfWriter(new ByteArrayOutputStream()))) {
            List<PdfLayer> layers = docReopen.getCatalog().getOCProperties(false).getLayers();
            Assertions.assertEquals(2, layers.size());
            Assertions.assertEquals("title layer", layers.get(0).getTitle());
            Assertions.assertEquals(1, layers.get(0).getParents().size());
            Assertions.assertEquals(1, layers.get(1).getChildren().size());
        }
    }
}