PdfDeveloperExtensionTest.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;
import com.itextpdf.io.source.ByteArrayOutputStream;
import com.itextpdf.test.ExtendedITextTest;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.Tag;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import org.junit.jupiter.api.Assertions;
@Tag("IntegrationTest")
public class PdfDeveloperExtensionTest extends ExtendedITextTest {
public static final PdfDeveloperExtension SIMPLE_EXTENSION_L3 =
new PdfDeveloperExtension(new PdfName("Test"), PdfName.Pdf_Version_1_7, 3);
public static final PdfDeveloperExtension SIMPLE_EXTENSION_L5 =
new PdfDeveloperExtension(new PdfName("Test"), PdfName.Pdf_Version_1_7, 5);
public static final PdfDeveloperExtension MULTI_EXTENSION_1 = new PdfDeveloperExtension(
new PdfName("Test"), PdfName.Pdf_Version_2_0, 1, "https://example.com",
":2022", true
);
public static final PdfDeveloperExtension MULTI_EXTENSION_2 = new PdfDeveloperExtension(
new PdfName("Test"), PdfName.Pdf_Version_2_0, 2, "https://example.com",
":2022", true
);
@Test
public void addSingleValuedExtensionTest() throws IOException {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
try (PdfDocument pdfDoc = new PdfDocument(new PdfWriter(baos))) {
pdfDoc.getCatalog().addDeveloperExtension(SIMPLE_EXTENSION_L3);
}
assertSimpleExtension(
baos.toByteArray(),
SIMPLE_EXTENSION_L3.getPrefix(), SIMPLE_EXTENSION_L3.getExtensionLevel()
);
}
@Test
public void addSingleValuedExtensionOverrideTest() throws IOException {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
try (PdfDocument pdfDoc = new PdfDocument(new PdfWriter(baos))) {
pdfDoc.getCatalog().addDeveloperExtension(SIMPLE_EXTENSION_L3);
pdfDoc.getCatalog().addDeveloperExtension(SIMPLE_EXTENSION_L5);
}
assertSimpleExtension(
baos.toByteArray(),
SIMPLE_EXTENSION_L5.getPrefix(), SIMPLE_EXTENSION_L5.getExtensionLevel()
);
}
@Test
public void addSingleValuedExtensionNoOverrideTest() throws IOException {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
try (PdfDocument pdfDoc = new PdfDocument(new PdfWriter(baos))) {
pdfDoc.getCatalog().addDeveloperExtension(SIMPLE_EXTENSION_L5);
pdfDoc.getCatalog().addDeveloperExtension(SIMPLE_EXTENSION_L3);
}
assertSimpleExtension(
baos.toByteArray(),
SIMPLE_EXTENSION_L5.getPrefix(), SIMPLE_EXTENSION_L5.getExtensionLevel()
);
}
@Test
public void addMultivaluedExtensionTest() throws IOException {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
try (PdfDocument pdfDoc = new PdfDocument(new PdfWriter(baos))) {
pdfDoc.getCatalog().addDeveloperExtension(MULTI_EXTENSION_1);
}
assertMultiExtension(
baos.toByteArray(),
MULTI_EXTENSION_1.getPrefix(),
Collections.singletonList(MULTI_EXTENSION_1.getExtensionLevel())
);
}
@Test
public void addMultivaluedExtensionNoDuplicateTest() throws IOException {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
try (PdfDocument pdfDoc = new PdfDocument(new PdfWriter(baos))) {
pdfDoc.getCatalog().addDeveloperExtension(MULTI_EXTENSION_1);
pdfDoc.getCatalog().addDeveloperExtension(MULTI_EXTENSION_1);
}
assertMultiExtension(
baos.toByteArray(),
MULTI_EXTENSION_1.getPrefix(),
Collections.singletonList(MULTI_EXTENSION_1.getExtensionLevel())
);
}
@Test
public void addMultivaluedExtensionNoOverrideTest() throws IOException {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
try (PdfDocument pdfDoc = new PdfDocument(new PdfWriter(baos))) {
pdfDoc.getCatalog().addDeveloperExtension(MULTI_EXTENSION_1);
pdfDoc.getCatalog().addDeveloperExtension(MULTI_EXTENSION_2);
}
assertMultiExtension(
baos.toByteArray(),
MULTI_EXTENSION_1.getPrefix(),
Arrays.asList(MULTI_EXTENSION_1.getExtensionLevel(), MULTI_EXTENSION_2.getExtensionLevel())
);
}
@Test
public void removeSingleValuedExtensionTest() throws IOException {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
try (PdfDocument pdfDoc = new PdfDocument(new PdfWriter(baos))) {
pdfDoc.getCatalog().addDeveloperExtension(SIMPLE_EXTENSION_L5);
pdfDoc.getCatalog().removeDeveloperExtension(SIMPLE_EXTENSION_L5);
}
assertNoExtensionWithPrefix(
baos.toByteArray(),
SIMPLE_EXTENSION_L5.getPrefix()
);
}
@Test
public void removeMultivaluedExtensionTest() throws IOException {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
try (PdfDocument pdfDoc = new PdfDocument(new PdfWriter(baos))) {
pdfDoc.getCatalog().addDeveloperExtension(MULTI_EXTENSION_1);
pdfDoc.getCatalog().addDeveloperExtension(MULTI_EXTENSION_2);
pdfDoc.getCatalog().removeDeveloperExtension(MULTI_EXTENSION_2);
}
assertMultiExtension(
baos.toByteArray(),
MULTI_EXTENSION_1.getPrefix(),
Arrays.asList(MULTI_EXTENSION_1.getExtensionLevel())
);
}
private void assertSimpleExtension(byte[] docData, PdfName prefix, int expectedLevel) throws IOException {
try (PdfDocument pdfDoc = new PdfDocument(new PdfReader(new ByteArrayInputStream(docData)))) {
PdfDictionary extDict = pdfDoc.getCatalog().getPdfObject()
.getAsDictionary(PdfName.Extensions)
.getAsDictionary(prefix);
Assertions.assertEquals(expectedLevel, extDict.getAsNumber(PdfName.ExtensionLevel).intValue());
}
}
private void assertNoExtensionWithPrefix(byte[] docData, PdfName prefix) throws IOException {
try (PdfDocument pdfDoc = new PdfDocument(new PdfReader(new ByteArrayInputStream(docData)))) {
PdfDictionary extDict = pdfDoc.getCatalog().getPdfObject()
.getAsDictionary(PdfName.Extensions)
.getAsDictionary(prefix);
Assertions.assertNull(extDict);
}
}
private void assertMultiExtension(byte[] docData, PdfName prefix, Collection<Integer> expectedLevels) throws IOException {
Set <Integer> seen = new HashSet<>();
try (PdfDocument pdfDoc = new PdfDocument(new PdfReader(new ByteArrayInputStream(docData)))) {
PdfArray exts = pdfDoc.getCatalog().getPdfObject()
.getAsDictionary(PdfName.Extensions)
.getAsArray(prefix);
for (int i = 0; i < exts.size(); i++) {
int level = exts
.getAsDictionary(i)
.getAsInt(PdfName.ExtensionLevel).intValue();
Assertions.assertTrue(expectedLevels.contains(level), "Level " + level + " is not in expected level list");
Assertions.assertFalse(seen.contains(level), "Level " + level + " appears multiple times");
seen.add(level);
}
}
}
}