@@ -47,6 +47,8 @@ This file is part of the iText (R) project.
47
47
import com .itextpdf .commons .utils .MessageFormatUtil ;
48
48
import com .itextpdf .kernel .exceptions .PdfException ;
49
49
import com .itextpdf .kernel .exceptions .KernelExceptionMessageConstant ;
50
+ import com .itextpdf .kernel .logs .KernelLogMessageConstant ;
51
+ import com .itextpdf .kernel .pdf .PdfReader .StrictnessLevel ;
50
52
import com .itextpdf .kernel .pdf .action .PdfAction ;
51
53
import com .itextpdf .kernel .pdf .collection .PdfCollection ;
52
54
import com .itextpdf .kernel .pdf .layer .PdfOCProperties ;
@@ -91,7 +93,7 @@ public class PdfCatalog extends PdfObjectWrapper<PdfDictionary> {
91
93
*/
92
94
protected PdfOCProperties ocProperties ;
93
95
94
- private static final String OutlineRoot = "Outlines" ;
96
+ private static final String ROOT_OUTLINE_TITLE = "Outlines" ;
95
97
96
98
private PdfOutline outlines ;
97
99
@@ -561,6 +563,91 @@ void addRootOutline(PdfOutline outline) {
561
563
}
562
564
}
563
565
566
+ /**
567
+ * Construct {@link PdfCatalog dictionary} iteratively. Invalid pdf documents will be processed depending on {@link
568
+ * StrictnessLevel}, if it set to lenient, we will ignore and process invalid outline structure, otherwise {@link
569
+ * PdfException} will be thrown.
570
+ *
571
+ * @param outlineRoot {@link PdfOutline dictionary} root.
572
+ * @param names map containing the PdfObjects stored in the tree.
573
+ */
574
+ void constructOutlines (PdfDictionary outlineRoot , Map <String , PdfObject > names ) {
575
+ if (outlineRoot == null ) {
576
+ return ;
577
+ }
578
+
579
+ PdfReader reader = getDocument ().getReader ();
580
+ final boolean isLenientLevel =
581
+ reader == null || StrictnessLevel .CONSERVATIVE .isStricter (reader .getStrictnessLevel ());
582
+ PdfDictionary current = outlineRoot .getAsDictionary (PdfName .First );
583
+
584
+ outlines = new PdfOutline (ROOT_OUTLINE_TITLE , outlineRoot , getDocument ());
585
+ PdfOutline parentOutline = outlines ;
586
+
587
+ Map <PdfOutline , PdfDictionary > nextUnprocessedChildForParentMap = new HashMap <>();
588
+ Set <PdfDictionary > alreadyVisitedOutlinesSet = new HashSet <>();
589
+
590
+ while (current != null ) {
591
+ PdfDictionary parent = current .getAsDictionary (PdfName .Parent );
592
+ if (null == parent && !isLenientLevel ) {
593
+ throw new PdfException (
594
+ MessageFormatUtil .format (
595
+ KernelExceptionMessageConstant .CORRUPTED_OUTLINE_NO_PARENT_ENTRY ,
596
+ current .indirectReference ));
597
+ }
598
+ PdfString title = current .getAsString (PdfName .Title );
599
+ if (null == title ) {
600
+ throw new PdfException (
601
+ MessageFormatUtil .format (
602
+ KernelExceptionMessageConstant .CORRUPTED_OUTLINE_NO_TITLE_ENTRY ,
603
+ current .indirectReference ));
604
+ }
605
+ PdfOutline currentOutline = new PdfOutline (title .toUnicodeString (), current , parentOutline );
606
+ alreadyVisitedOutlinesSet .add (current );
607
+ addOutlineToPage (currentOutline , current , names );
608
+ parentOutline .getAllChildren ().add (currentOutline );
609
+
610
+ PdfDictionary first = current .getAsDictionary (PdfName .First );
611
+ PdfDictionary next = current .getAsDictionary (PdfName .Next );
612
+ if (first != null ) {
613
+ if (alreadyVisitedOutlinesSet .contains (first )) {
614
+ if (!isLenientLevel ) {
615
+ throw new PdfException (MessageFormatUtil .format (
616
+ KernelExceptionMessageConstant .CORRUPTED_OUTLINE_DICTIONARY_HAS_INFINITE_LOOP , first ));
617
+ }
618
+ LOGGER .warn (MessageFormatUtil .format (
619
+ KernelLogMessageConstant .CORRUPTED_OUTLINE_DICTIONARY_HAS_INFINITE_LOOP , first ));
620
+ return ;
621
+ }
622
+ // Down in hierarchy; when returning up, process `next`.
623
+ nextUnprocessedChildForParentMap .put (parentOutline , next );
624
+ parentOutline = currentOutline ;
625
+ current = first ;
626
+ } else if (next != null ) {
627
+ if (alreadyVisitedOutlinesSet .contains (next )) {
628
+ if (!isLenientLevel ) {
629
+ throw new PdfException (MessageFormatUtil .format (
630
+ KernelExceptionMessageConstant .CORRUPTED_OUTLINE_DICTIONARY_HAS_INFINITE_LOOP , next ));
631
+ }
632
+ LOGGER .warn (MessageFormatUtil .format (
633
+ KernelLogMessageConstant .CORRUPTED_OUTLINE_DICTIONARY_HAS_INFINITE_LOOP , next ));
634
+ return ;
635
+ }
636
+ // Next sibling in hierarchy
637
+ current = next ;
638
+ } else {
639
+ // Up in hierarchy using 'nextUnprocessedChildForParentMap'.
640
+ current = null ;
641
+ while (current == null && parentOutline != null ) {
642
+ parentOutline = parentOutline .getParent ();
643
+ if (parentOutline != null ) {
644
+ current = nextUnprocessedChildForParentMap .get (parentOutline );
645
+ }
646
+ }
647
+ }
648
+ }
649
+ }
650
+
564
651
PdfDestination copyDestination (PdfObject dest , Map <PdfPage , PdfPage > page2page , PdfDocument toDocument ) {
565
652
if (null == dest ) {
566
653
return null ;
@@ -683,61 +770,4 @@ private void addOutlineToPage(PdfOutline outline, PdfDictionary item, Map<String
683
770
}
684
771
}
685
772
}
686
-
687
- /**
688
- * Constructs {@link PdfCatalog#outlines} iteratively
689
- */
690
- void constructOutlines (PdfDictionary outlineRoot , Map <String , PdfObject > names ) {
691
- if (outlineRoot == null ) {
692
- return ;
693
- }
694
- PdfDictionary current = outlineRoot .getAsDictionary (PdfName .First );
695
-
696
- outlines = new PdfOutline (OutlineRoot , outlineRoot , getDocument ());
697
- PdfOutline parentOutline = outlines ;
698
-
699
- // map `PdfOutline` to the next sibling to process in the hierarchy
700
- HashMap <PdfOutline , PdfDictionary > positionMap = new HashMap <>();
701
-
702
- while (current != null ) {
703
- PdfDictionary parent = current .getAsDictionary (PdfName .Parent );
704
- if (null == parent ) {
705
- throw new PdfException (
706
- MessageFormatUtil .format (
707
- KernelExceptionMessageConstant .CORRUPTED_OUTLINE_NO_PARENT_ENTRY ,
708
- current .indirectReference ));
709
- }
710
- PdfString title = current .getAsString (PdfName .Title );
711
- if (null == title ) {
712
- throw new PdfException (
713
- MessageFormatUtil .format (
714
- KernelExceptionMessageConstant .CORRUPTED_OUTLINE_NO_TITLE_ENTRY ,
715
- current .indirectReference ));
716
- }
717
- PdfOutline currentOutline = new PdfOutline (title .toUnicodeString (), current , parentOutline );
718
- addOutlineToPage (currentOutline , current , names );
719
- parentOutline .getAllChildren ().add (currentOutline );
720
-
721
- PdfDictionary first = current .getAsDictionary (PdfName .First );
722
- PdfDictionary next = current .getAsDictionary (PdfName .Next );
723
- if (first != null ) {
724
- // Down in hierarchy; when returning up, process `next`
725
- positionMap .put (parentOutline , next );
726
- parentOutline = currentOutline ;
727
- current = first ;
728
- } else if (next != null ) {
729
- // Next sibling in hierarchy
730
- current = next ;
731
- } else {
732
- // Up in hierarchy using `positionMap`
733
- current = null ;
734
- while (current == null && parentOutline != null ) {
735
- parentOutline = parentOutline .getParent ();
736
- if (parentOutline != null ) {
737
- current = positionMap .get (parentOutline );
738
- }
739
- }
740
- }
741
- }
742
- }
743
773
}
0 commit comments