PdfRevisionsReaderTest.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.test.ExtendedITextTest;
import java.io.IOException;
import java.util.List;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.Tag;
@Tag("UnitTest")
public class PdfRevisionsReaderTest extends ExtendedITextTest {
private static final String SOURCE_FOLDER = "./src/test/resources/com/itextpdf/kernel/pdf/PdfRevisionsReaderTest/";
@Test
public void singleRevisionDocumentTest() throws IOException {
String filename = SOURCE_FOLDER + "singleRevisionDocument.pdf";
try (PdfReader reader = new PdfReader(filename)) {
PdfRevisionsReader revisionsReader = new PdfRevisionsReader(reader);
List<DocumentRevision> documentRevisions = revisionsReader.getAllRevisions();
Assertions.assertEquals(1, documentRevisions.size());
DocumentRevision firstRevision = documentRevisions.get(0);
assertResultingRevision(firstRevision, 1, 2, 3, 4, 5, 6);
Assertions.assertEquals(929, firstRevision.getEofOffset());
}
}
@Test
public void singleRevisionWithXrefStreamTest() throws IOException {
String filename = SOURCE_FOLDER + "singleRevisionWithXrefStream.pdf";
try (PdfReader reader = new PdfReader(filename)) {
PdfRevisionsReader revisionsReader = new PdfRevisionsReader(reader);
List<DocumentRevision> documentRevisions = revisionsReader.getAllRevisions();
Assertions.assertEquals(1, documentRevisions.size());
DocumentRevision firstRevision = documentRevisions.get(0);
assertResultingRevision(firstRevision, 1, 2, 3, 4, 5, 6, 7, 8);
Assertions.assertEquals(1085, firstRevision.getEofOffset());
}
}
@Test
public void multipleRevisionsDocument() throws IOException {
String filename = SOURCE_FOLDER + "multipleRevisionsDocument.pdf";
try (PdfReader reader = new PdfReader(filename)) {
PdfRevisionsReader revisionsReader = new PdfRevisionsReader(reader);
List<DocumentRevision> documentRevisions = revisionsReader.getAllRevisions();
Assertions.assertEquals(3, documentRevisions.size());
DocumentRevision firstRevision = documentRevisions.get(0);
assertResultingRevision(firstRevision, 1, 2, 3, 4, 5, 6);
Assertions.assertEquals(930, firstRevision.getEofOffset());
DocumentRevision secondRevision = documentRevisions.get(1);
assertResultingRevision(secondRevision, 1, 3, 4, 7, 8, 9, 10, 11, 12, 13, 14, 15);
Assertions.assertEquals(28120, secondRevision.getEofOffset());
DocumentRevision thirdRevision = documentRevisions.get(2);
assertResultingRevision(thirdRevision, 1, 3, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28);
Assertions.assertEquals(36207, thirdRevision.getEofOffset());
}
}
@Test
public void freeReferencesDocument() throws IOException {
String filename = SOURCE_FOLDER + "freeReferencesDocument.pdf";
try (PdfReader reader = new PdfReader(filename)) {
PdfRevisionsReader revisionsReader = new PdfRevisionsReader(reader);
List<DocumentRevision> documentRevisions = revisionsReader.getAllRevisions();
Assertions.assertEquals(5, documentRevisions.size());
DocumentRevision firstRevision = documentRevisions.get(0);
assertResultingRevision(firstRevision, 1, 2, 3, 4, 5, 6);
Assertions.assertEquals(930, firstRevision.getEofOffset());
DocumentRevision secondRevision = documentRevisions.get(1);
assertResultingRevision(secondRevision, 1, 3, 4, 7, 8, 9, 10, 11, 12, 13, 14, 15);
Assertions.assertEquals(28120, secondRevision.getEofOffset());
DocumentRevision thirdRevision = documentRevisions.get(2);
assertResultingRevision(thirdRevision, 1, 3, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28);
Assertions.assertEquals(36208, thirdRevision.getEofOffset());
DocumentRevision fourthRevision = documentRevisions.get(3);
assertResultingRevision(fourthRevision, new int[] {1, 3, 23, 24}, new int[] {0, 0, 1, 1});
Assertions.assertEquals(37007, fourthRevision.getEofOffset());
DocumentRevision fifthRevision = documentRevisions.get(4);
assertResultingRevision(fifthRevision, new int[] {1, 3, 19, 20, 21, 22, 23, 25},
new int[] {0, 0, 1, 1, 1, 1, 1, 1});
Assertions.assertEquals(38094, fifthRevision.getEofOffset());
}
}
@Test
public void multipleRevisionsWithXrefStreamTest() throws IOException {
String filename = SOURCE_FOLDER + "multipleRevisionsWithXrefStream.pdf";
try (PdfReader reader = new PdfReader(filename)) {
PdfRevisionsReader revisionsReader = new PdfRevisionsReader(reader);
List<DocumentRevision> documentRevisions = revisionsReader.getAllRevisions();
Assertions.assertEquals(3, documentRevisions.size());
DocumentRevision firstRevision = documentRevisions.get(0);
assertResultingRevision(firstRevision, 1, 2, 3, 4, 5, 6, 7, 8);
Assertions.assertEquals(1086, firstRevision.getEofOffset());
DocumentRevision secondRevision = documentRevisions.get(1);
assertResultingRevision(secondRevision, 1, 3, 4, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19);
Assertions.assertEquals(28138, secondRevision.getEofOffset());
DocumentRevision thirdRevision = documentRevisions.get(2);
assertResultingRevision(thirdRevision, 1, 3, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34);
Assertions.assertEquals(36059, thirdRevision.getEofOffset());
}
}
@Test
public void freeReferencesWithXrefStream() throws IOException {
String filename = SOURCE_FOLDER + "freeReferencesWithXrefStream.pdf";
try (PdfReader reader = new PdfReader(filename)) {
PdfRevisionsReader revisionsReader = new PdfRevisionsReader(reader);
List<DocumentRevision> documentRevisions = revisionsReader.getAllRevisions();
Assertions.assertEquals(5, documentRevisions.size());
DocumentRevision firstRevision = documentRevisions.get(0);
assertResultingRevision(firstRevision, 1, 2, 3, 4, 5, 6, 7, 8);
Assertions.assertEquals(1086, firstRevision.getEofOffset());
DocumentRevision secondRevision = documentRevisions.get(1);
assertResultingRevision(secondRevision, 1, 3, 4, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19);
Assertions.assertEquals(28138, secondRevision.getEofOffset());
DocumentRevision thirdRevision = documentRevisions.get(2);
assertResultingRevision(thirdRevision, 1, 3, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34);
Assertions.assertEquals(36060, thirdRevision.getEofOffset());
DocumentRevision fourthRevision = documentRevisions.get(3);
assertResultingRevision(fourthRevision, new int[] {1, 3, 27, 28, 35}, new int[] {0, 0, 1, 1, 0});
Assertions.assertEquals(36976, fourthRevision.getEofOffset());
DocumentRevision fifthRevision = documentRevisions.get(4);
assertResultingRevision(fifthRevision, new int[] {1, 3, 23, 24, 25, 26, 27, 29, 36},
new int[] {0, 0, 1, 1, 1, 1, 1, 1, 0});
Assertions.assertEquals(38111, fifthRevision.getEofOffset());
}
}
@Test
public void documentWithStreamAndTableXref() throws IOException {
String filename = SOURCE_FOLDER + "documentWithStreamAndTableXref.pdf";
try (PdfReader reader = new PdfReader(filename)) {
PdfRevisionsReader revisionsReader = new PdfRevisionsReader(reader);
List<DocumentRevision> documentRevisions = revisionsReader.getAllRevisions();
Assertions.assertEquals(3, documentRevisions.size());
DocumentRevision thirdRevision = revisionsReader.getAllRevisions().get(0);
// xref was broken in this revision and fixed in the next one
assertResultingRevision(thirdRevision, new int[] {1, 2, 3, 4, 5, 6, 7, 8, 9},
new int[] {0, 0, 0, 0, 0, 0, 65535, 0, 0});
Assertions.assertEquals(1381, thirdRevision.getEofOffset());
DocumentRevision secondRevision = revisionsReader.getAllRevisions().get(1);
assertResultingRevision(secondRevision, 1, 2, 3, 4, 5, 6, 7, 8);
Assertions.assertEquals(1381, secondRevision.getEofOffset());
DocumentRevision firstRevision = revisionsReader.getAllRevisions().get(2);
assertResultingRevision(firstRevision);
Assertions.assertEquals(1550, firstRevision.getEofOffset());
}
}
private void assertResultingRevision(DocumentRevision documentRevision, int... objNumbers) {
assertResultingRevision(documentRevision, objNumbers, new int[objNumbers.length]);
}
private void assertResultingRevision(DocumentRevision documentRevision, int[] objNumbers, int[] objGens) {
Assertions.assertEquals(objNumbers.length, objGens.length);
Assertions.assertEquals(objNumbers.length + 1, documentRevision.getModifiedObjects().size());
for (int i = 0; i < objNumbers.length; ++i) {
int objNumber = objNumbers[i];
int objGen = objGens[i];
Assertions.assertTrue(documentRevision.getModifiedObjects().stream().anyMatch(
reference -> reference.getObjNumber() == objNumber && reference.getGenNumber() == objGen));
}
Assertions.assertTrue(documentRevision.getModifiedObjects().stream().anyMatch(
reference -> reference.getObjNumber() == 0 && reference.getGenNumber() == 65535 && reference.isFree()));
}
}