Skip to content

Commit 31f8c23

Browse files
kohlerEgor Martsynkovsky
authored andcommitted
Process Outlines using explicit hierarchy
Some PDF processors generate empty Parent directories, rather than including valid Parent directory links in their Outlines. Previous PdfOutline::constructOutlines() code required correct Parent links. This version is equivalent for correct PDFs, but ignores the Parent links in the PDF; instead, it follows the hierarchy implied by First links.
1 parent 1620498 commit 31f8c23

File tree

1 file changed

+22
-49
lines changed

1 file changed

+22
-49
lines changed

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

Lines changed: 22 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -658,46 +658,6 @@ private void addOutlineToPage(PdfOutline outline, Map<String, PdfObject> names)
658658
}
659659
}
660660

661-
/**
662-
* Get the next outline of the current node in the outline tree by looking for a child or sibling node.
663-
* If there is no child or sibling of the current node {@link PdfCatalog#getParentNextOutline(PdfDictionary)} is called to get a hierarchical parent's next node. {@code null} is returned if one does not exist.
664-
*
665-
* @return the {@link PdfDictionary} object of the next outline if one exists, {@code null} otherwise.
666-
*/
667-
private PdfDictionary getNextOutline(PdfDictionary first, PdfDictionary next, PdfDictionary parent) {
668-
if (first != null) {
669-
return first;
670-
} else if (next != null) {
671-
return next;
672-
} else {
673-
return getParentNextOutline(parent);
674-
}
675-
676-
}
677-
678-
/**
679-
* Gets the parent's next outline of the current node.
680-
* If the parent does not have a next we look at the grand parent, great-grand parent, etc until we find a next node or reach the root at which point {@code null} is returned to signify there is no next node present.
681-
*
682-
* @return the {@link PdfDictionary} object of the next outline if one exists, {@code null} otherwise.
683-
*/
684-
private PdfDictionary getParentNextOutline(PdfDictionary parent) {
685-
if (parent == null) {
686-
return null;
687-
}
688-
PdfDictionary current = null;
689-
while (current == null) {
690-
current = parent.getAsDictionary(PdfName.Next);
691-
if (current == null) {
692-
parent = parent.getAsDictionary(PdfName.Parent);
693-
if (parent == null) {
694-
return null;
695-
}
696-
}
697-
}
698-
return current;
699-
}
700-
701661
private void addOutlineToPage(PdfOutline outline, PdfDictionary item, Map<String, PdfObject> names) {
702662
PdfObject dest = item.get(PdfName.Dest);
703663
if (dest != null) {
@@ -731,17 +691,15 @@ void constructOutlines(PdfDictionary outlineRoot, Map<String, PdfObject> names)
731691
if (outlineRoot == null) {
732692
return;
733693
}
734-
PdfDictionary first = outlineRoot.getAsDictionary(PdfName.First);
735-
PdfDictionary current = first;
736-
HashMap<PdfDictionary, PdfOutline> parentOutlineMap = new HashMap<>();
694+
PdfDictionary current = outlineRoot.getAsDictionary(PdfName.First);
737695

738696
outlines = new PdfOutline(OutlineRoot, outlineRoot, getDocument());
739697
PdfOutline parentOutline = outlines;
740-
parentOutlineMap.put(outlineRoot, parentOutline);
698+
699+
// map `PdfOutline` to the next sibling to process in the hierarchy
700+
HashMap<PdfOutline, PdfDictionary> positionMap = new HashMap<>();
741701

742702
while (current != null) {
743-
first = current.getAsDictionary(PdfName.First);
744-
PdfDictionary next = current.getAsDictionary(PdfName.Next);
745703
PdfDictionary parent = current.getAsDictionary(PdfName.Parent);
746704
if (null == parent) {
747705
throw new PdfException(
@@ -756,15 +714,30 @@ void constructOutlines(PdfDictionary outlineRoot, Map<String, PdfObject> names)
756714
KernelExceptionMessageConstant.CORRUPTED_OUTLINE_NO_TITLE_ENTRY,
757715
current.indirectReference));
758716
}
759-
parentOutline = parentOutlineMap.get(parent);
760717
PdfOutline currentOutline = new PdfOutline(title.toUnicodeString(), current, parentOutline);
761718
addOutlineToPage(currentOutline, current, names);
762719
parentOutline.getAllChildren().add(currentOutline);
763720

721+
PdfDictionary first = current.getAsDictionary(PdfName.First);
722+
PdfDictionary next = current.getAsDictionary(PdfName.Next);
764723
if (first != null) {
765-
parentOutlineMap.put(current, currentOutline);
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+
}
766740
}
767-
current = getNextOutline(first, next, parent);
768741
}
769742
}
770743
}

0 commit comments

Comments
 (0)