From 6610bf8ff77284899d79ff78c9cd571ea087a842 Mon Sep 17 00:00:00 2001 From: AryanRG Date: Sun, 18 Oct 2026 00:09:00 +0530 Subject: [PATCH 1/2] PDFBOX-6286: Scope page-tree recursion state to each lookup AI-assisted-by: OpenAI Codex --- .../org/apache/pdfbox/pdmodel/PDPageTree.java | 15 ++++++--------- .../apache/pdfbox/pdmodel/TestPDPageTree.java | 16 ++++++++++++++++ 2 files changed, 22 insertions(+), 9 deletions(-) diff --git a/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/PDPageTree.java b/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/PDPageTree.java index 81404e904d2..d20effd1fa0 100644 --- a/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/PDPageTree.java +++ b/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/PDPageTree.java @@ -47,8 +47,6 @@ public class PDPageTree implements COSObjectable, Iterable private final COSDictionary root; private final PDDocument document; // optional - private final Set pageSet = new HashSet<>(); - /** * Constructor for embedding. */ @@ -273,7 +271,7 @@ public void remove() */ public PDPage get(int index) { - COSDictionary dict = get(index + 1, root, 0); + COSDictionary dict = get(index + 1, root, 0, new HashSet<>()); sanitizeType(dict); @@ -305,7 +303,8 @@ private static void sanitizeType(COSDictionary dictionary) * @throws IllegalStateException if the requested page number isn't found * @throws IndexOutOfBoundsException if the requested page number is higher than the page count */ - private COSDictionary get(int pageNum, COSDictionary node, int encountered) + private COSDictionary get(int pageNum, COSDictionary node, int encountered, + Set pageSet) { if (pageNum < 1) { @@ -313,7 +312,6 @@ private COSDictionary get(int pageNum, COSDictionary node, int encountered) } if (pageSet.contains(node)) { - pageSet.clear(); throw new IllegalStateException( "Possible recursion found when searching for page " + pageNum); } @@ -338,7 +336,7 @@ private COSDictionary get(int pageNum, COSDictionary node, int encountered) if (pageNum <= encountered + kidCount) { // it's this kid - return get(pageNum, kid, encountered); + return get(pageNum, kid, encountered, pageSet); } else { @@ -352,7 +350,7 @@ private COSDictionary get(int pageNum, COSDictionary node, int encountered) if (pageNum == encountered) { // it's this page - return get(pageNum, kid, encountered); + return get(pageNum, kid, encountered, pageSet); } } } @@ -368,7 +366,6 @@ private COSDictionary get(int pageNum, COSDictionary node, int encountered) { if (encountered == pageNum) { - pageSet.clear(); return node; } else @@ -466,7 +463,7 @@ public COSDictionary getCOSObject() */ public void remove(int index) { - COSDictionary node = get(index + 1, root, 0); + COSDictionary node = get(index + 1, root, 0, new HashSet<>()); remove(node); } diff --git a/pdfbox/src/test/java/org/apache/pdfbox/pdmodel/TestPDPageTree.java b/pdfbox/src/test/java/org/apache/pdfbox/pdmodel/TestPDPageTree.java index 0dbe00a6022..5378e44763a 100644 --- a/pdfbox/src/test/java/org/apache/pdfbox/pdmodel/TestPDPageTree.java +++ b/pdfbox/src/test/java/org/apache/pdfbox/pdmodel/TestPDPageTree.java @@ -18,6 +18,8 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.junit.jupiter.api.Assertions.assertThrows; import java.io.IOException; @@ -133,6 +135,20 @@ void testInsertAfterBlankPage() throws IOException } } + @Test + void getValidPageAfterOutOfBoundsLookup() throws IOException + { + try (PDDocument document = new PDDocument()) + { + PDPage page = new PDPage(); + document.addPage(page); + PDPageTree pages = document.getPages(); + + assertThrows(IndexOutOfBoundsException.class, () -> pages.get(1)); + assertSame(page.getCOSObject(), pages.get(0).getCOSObject()); + } + } + /** * PDFBOX-6040: avoid loop in getInheritableAttribute(). * From 6301182c2c2647949b10a8b4a037c579eb2d9ee4 Mon Sep 17 00:00:00 2001 From: AryanRG Date: Sun, 18 Oct 2026 15:39:36 +0530 Subject: [PATCH 2/2] PDFBOX-6286: Clear visited pages before lookup errors --- .../org/apache/pdfbox/pdmodel/PDPageTree.java | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/PDPageTree.java b/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/PDPageTree.java index d20effd1fa0..79e274a924d 100644 --- a/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/PDPageTree.java +++ b/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/PDPageTree.java @@ -47,6 +47,8 @@ public class PDPageTree implements COSObjectable, Iterable private final COSDictionary root; private final PDDocument document; // optional + private final Set pageSet = new HashSet<>(); + /** * Constructor for embedding. */ @@ -271,7 +273,7 @@ public void remove() */ public PDPage get(int index) { - COSDictionary dict = get(index + 1, root, 0, new HashSet<>()); + COSDictionary dict = get(index + 1, root, 0); sanitizeType(dict); @@ -303,15 +305,16 @@ private static void sanitizeType(COSDictionary dictionary) * @throws IllegalStateException if the requested page number isn't found * @throws IndexOutOfBoundsException if the requested page number is higher than the page count */ - private COSDictionary get(int pageNum, COSDictionary node, int encountered, - Set pageSet) + private COSDictionary get(int pageNum, COSDictionary node, int encountered) { if (pageNum < 1) { + pageSet.clear(); throw new IndexOutOfBoundsException("Index out of bounds: " + pageNum); } if (pageSet.contains(node)) { + pageSet.clear(); throw new IllegalStateException( "Possible recursion found when searching for page " + pageNum); } @@ -336,7 +339,7 @@ private COSDictionary get(int pageNum, COSDictionary node, int encountered, if (pageNum <= encountered + kidCount) { // it's this kid - return get(pageNum, kid, encountered, pageSet); + return get(pageNum, kid, encountered); } else { @@ -350,15 +353,17 @@ private COSDictionary get(int pageNum, COSDictionary node, int encountered, if (pageNum == encountered) { // it's this page - return get(pageNum, kid, encountered, pageSet); + return get(pageNum, kid, encountered); } } } + pageSet.clear(); throw new IllegalStateException("1-based index not found: " + pageNum); } else { + pageSet.clear(); throw new IndexOutOfBoundsException("1-based index out of bounds: " + pageNum); } } @@ -366,10 +371,12 @@ private COSDictionary get(int pageNum, COSDictionary node, int encountered, { if (encountered == pageNum) { + pageSet.clear(); return node; } else { + pageSet.clear(); throw new IllegalStateException("1-based index not found: " + pageNum); } } @@ -463,7 +470,7 @@ public COSDictionary getCOSObject() */ public void remove(int index) { - COSDictionary node = get(index + 1, root, 0, new HashSet<>()); + COSDictionary node = get(index + 1, root, 0); remove(node); }