From 014daf810bce6f5e3358acc8b66af380ab465dab Mon Sep 17 00:00:00 2001 From: Jason Harrop Date: Mon, 5 Oct 2026 09:14:08 +1100 Subject: [PATCH] FOP-3350: PDFPageLabels: a page label of "0" is written as a prefix label, not read past its end addPageLabel counted a decimal label's leading zeros with a do/while that ran past the end of a label made only of zeros, throwing StringIndexOutOfBoundsException. Such a label is now written as a prefix label (/P), since /St must be at least 1 (ISO 32000-1, 12.4.2). Test: PDFPageLabelsTestCase.testPageNumberedZero, which threw before. Co-Authored-By: Claude Opus 5.5 --- .../java/org/apache/fop/pdf/PDFPageLabels.java | 7 ++++++- .../apache/fop/pdf/PDFPageLabelsTestCase.java | 16 ++++++++++++++++ 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/fop-core/src/main/java/org/apache/fop/pdf/PDFPageLabels.java b/fop-core/src/main/java/org/apache/fop/pdf/PDFPageLabels.java index 59b231d5b84..28d04db3733 100644 --- a/fop-core/src/main/java/org/apache/fop/pdf/PDFPageLabels.java +++ b/fop-core/src/main/java/org/apache/fop/pdf/PDFPageLabels.java @@ -71,7 +71,12 @@ public void addPageLabel(int index, String pageLabel) { currentPageLabelType = DECIMAL; currentPageNumber = Integer.parseInt(pageLabel); int zeroPadding = 0; - if (pageLabel.charAt(zeroPadding) == '0') { + if (currentPageNumber == 0) { + // a page numbered 0: /St must be at least 1, so a label of zeros is written as a + // prefix; counting its leading zeros below would run past its end + currentPageLabelType = PREFIX; + addNewPageLabel = true; + } else if (pageLabel.charAt(zeroPadding) == '0') { do { zeroPadding++; } while (pageLabel.charAt(zeroPadding) == '0'); diff --git a/fop-core/src/test/java/org/apache/fop/pdf/PDFPageLabelsTestCase.java b/fop-core/src/test/java/org/apache/fop/pdf/PDFPageLabelsTestCase.java index 429a38a8a47..918a3fe9f0a 100644 --- a/fop-core/src/test/java/org/apache/fop/pdf/PDFPageLabelsTestCase.java +++ b/fop-core/src/test/java/org/apache/fop/pdf/PDFPageLabelsTestCase.java @@ -89,4 +89,20 @@ public void testAddPageLabel() throws IOException { baos.close(); } + /** + * A page labelled "0" is labelled as a prefix, since /St must be at least 1; counting the leading + * zeros of "0" ran past its end. + */ + @Test + public void testPageNumberedZero() throws IOException { + PDFDocument pdfDoc = mock(PDFDocument.class); + PDFPageLabels pageLabels = new PDFPageLabels(); + pageLabels.setDocument(pdfDoc); + pageLabels.addPageLabel(0, "0"); + pageLabels.addPageLabel(1, "1"); + pageLabels.addPageLabel(2, "2"); + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + pageLabels.getNums().output(baos); + assertEquals("[0 << /P (0) >> 1 << /S /D >>]", baos.toString()); + } }