Skip to content

Commit 1176bc4

Browse files
committed
Check whether an outline contain required fields and throw an exception if not
For more information check out PDF Reference 8.2.2 Tables 8.3 and 8.4. DEVSIX-3633
1 parent 6f3974d commit 1176bc4

File tree

3 files changed

+78
-9
lines changed

3 files changed

+78
-9
lines changed

kernel/src/main/java/com/itextpdf/kernel/PdfException.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -330,6 +330,11 @@ public class PdfException extends RuntimeException {
330330
public static final String YouNeedASingleBooleanForThisCollectionSortDictionary = "You need a single boolean for this collection sort dictionary.";
331331
public static final String QuadPointArrayLengthIsNotAMultipleOfEight = "The QuadPoint Array length is not a multiple of 8.";
332332

333+
public static final String CORRUPTED_OUTLINE_NO_PARENT_ENTRY =
334+
"Document outline is corrupted: some outline (PDF object: \"{0}\") lacks the required parent entry.";
335+
public static final String CORRUPTED_OUTLINE_NO_TITLE_ENTRY =
336+
"Document outline is corrupted: some outline (PDF object: \"{0}\") lacks the required title entry.";
337+
333338
/**
334339
* Object for more details
335340
*/

kernel/src/main/java/com/itextpdf/kernel/pdf/PdfCatalog.java

Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -724,14 +724,12 @@ private void addOutlineToPage(PdfOutline outline, PdfDictionary item, Map<String
724724
/**
725725
* Constructs {@link PdfCatalog#outlines} iteratively
726726
*/
727-
private void constructOutlines(PdfDictionary outlineRoot, Map<String, PdfObject> names) {
727+
void constructOutlines(PdfDictionary outlineRoot, Map<String, PdfObject> names) {
728728
if (outlineRoot == null) {
729729
return;
730730
}
731731
PdfDictionary first = outlineRoot.getAsDictionary(PdfName.First);
732732
PdfDictionary current = first;
733-
PdfDictionary next;
734-
PdfDictionary parent;
735733
HashMap<PdfDictionary, PdfOutline> parentOutlineMap = new HashMap<>();
736734

737735
outlines = new PdfOutline(OutlineRoot, outlineRoot, getDocument());
@@ -740,20 +738,30 @@ private void constructOutlines(PdfDictionary outlineRoot, Map<String, PdfObject>
740738

741739
while (current != null) {
742740
first = current.getAsDictionary(PdfName.First);
743-
next = current.getAsDictionary(PdfName.Next);
744-
parent = current.getAsDictionary(PdfName.Parent);
745-
741+
PdfDictionary next = current.getAsDictionary(PdfName.Next);
742+
PdfDictionary parent = current.getAsDictionary(PdfName.Parent);
743+
if (null == parent) {
744+
throw new PdfException(
745+
MessageFormatUtil.format(
746+
PdfException.CORRUPTED_OUTLINE_NO_PARENT_ENTRY,
747+
current.indirectReference));
748+
}
749+
PdfString title = current.getAsString(PdfName.Title);
750+
if (null == title) {
751+
throw new PdfException(
752+
MessageFormatUtil.format(
753+
PdfException.CORRUPTED_OUTLINE_NO_TITLE_ENTRY,
754+
current.indirectReference));
755+
}
746756
parentOutline = parentOutlineMap.get(parent);
747-
PdfOutline currentOutline = new PdfOutline(current.getAsString(PdfName.Title).toUnicodeString(), current, parentOutline);
757+
PdfOutline currentOutline = new PdfOutline(title.toUnicodeString(), current, parentOutline);
748758
addOutlineToPage(currentOutline, current, names);
749759
parentOutline.getAllChildren().add(currentOutline);
750760

751761
if (first != null) {
752762
parentOutlineMap.put(current, currentOutline);
753763
}
754764
current = getNextOutline(first, next, parent);
755-
756765
}
757766
}
758-
759767
}

kernel/src/test/java/com/itextpdf/kernel/pdf/PdfOutlineTest.java

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,8 @@ This file is part of the iText (R) project.
4343
package com.itextpdf.kernel.pdf;
4444

4545
import com.itextpdf.io.LogMessageConstant;
46+
import com.itextpdf.io.util.MessageFormatUtil;
47+
import com.itextpdf.kernel.PdfException;
4648
import com.itextpdf.kernel.pdf.navigation.PdfDestination;
4749
import com.itextpdf.kernel.pdf.navigation.PdfExplicitDestination;
4850
import com.itextpdf.kernel.pdf.navigation.PdfStringDestination;
@@ -52,6 +54,8 @@ This file is part of the iText (R) project.
5254
import com.itextpdf.test.annotations.LogMessages;
5355
import com.itextpdf.test.annotations.type.IntegrationTest;
5456

57+
import java.io.ByteArrayOutputStream;
58+
import java.util.HashMap;
5559
import org.xml.sax.SAXException;
5660
import javax.xml.parsers.ParserConfigurationException;
5761
import java.io.FileOutputStream;
@@ -508,4 +512,56 @@ public void removePageInDocWithComplexOutlineTreeStructTest() throws IOException
508512

509513
Assert.assertNull(new CompareTool().compareByContent(output, cmp, DESTINATION_FOLDER, "diff_"));
510514
}
515+
516+
@Test
517+
public void constructOutlinesNoParentTest() throws IOException {
518+
try (
519+
ByteArrayOutputStream baos = new ByteArrayOutputStream();
520+
PdfDocument pdfDocument = new PdfDocument(new PdfWriter(baos))) {
521+
pdfDocument.addNewPage();
522+
523+
PdfDictionary first = new PdfDictionary();
524+
first.makeIndirect(pdfDocument);
525+
526+
PdfDictionary outlineDictionary = new PdfDictionary();
527+
outlineDictionary.put(PdfName.First, first);
528+
529+
Exception exception = Assert.assertThrows(
530+
PdfException.class,
531+
() -> pdfDocument.getCatalog().constructOutlines(outlineDictionary, new HashMap<String, PdfObject>())
532+
);
533+
Assert.assertEquals(
534+
MessageFormatUtil.format(PdfException.CORRUPTED_OUTLINE_NO_PARENT_ENTRY,
535+
first.indirectReference),
536+
exception.getMessage());
537+
}
538+
}
539+
540+
@Test
541+
public void constructOutlinesNoTitleTest() throws IOException {
542+
try (
543+
ByteArrayOutputStream baos = new ByteArrayOutputStream();
544+
PdfDocument pdfDocument = new PdfDocument(new PdfWriter(baos))) {
545+
pdfDocument.addNewPage();
546+
547+
PdfDictionary first = new PdfDictionary();
548+
first.makeIndirect(pdfDocument);
549+
550+
PdfDictionary outlineDictionary = new PdfDictionary();
551+
outlineDictionary.makeIndirect(pdfDocument);
552+
553+
outlineDictionary.put(PdfName.First, first);
554+
first.put(PdfName.Parent, outlineDictionary);
555+
556+
Exception exception = Assert.assertThrows(
557+
PdfException.class,
558+
() -> pdfDocument.getCatalog()
559+
.constructOutlines(outlineDictionary, new HashMap<String, PdfObject>())
560+
);
561+
Assert.assertEquals(
562+
MessageFormatUtil.format(PdfException.CORRUPTED_OUTLINE_NO_TITLE_ENTRY,
563+
first.indirectReference),
564+
exception.getMessage());
565+
}
566+
}
511567
}

0 commit comments

Comments
 (0)