@@ -248,7 +248,9 @@ public void setLang(PdfString lang) {
248
248
put (PdfName .Lang , lang );
249
249
}
250
250
251
- public PdfString getLang (){ return getPdfObject ().getAsString (PdfName .Lang );}
251
+ public PdfString getLang () {
252
+ return getPdfObject ().getAsString (PdfName .Lang );
253
+ }
252
254
253
255
public void addDeveloperExtension (PdfDeveloperExtension extension ) {
254
256
PdfDictionary extensions = getPdfObject ().getAsDictionary (PdfName .Extensions );
@@ -381,8 +383,7 @@ PdfOutline getOutlines(boolean updateOutlines) {
381
383
}
382
384
outlines = new PdfOutline (getDocument ());
383
385
} else {
384
- outlines = new PdfOutline (OutlineRoot , outlineRoot , getDocument ());
385
- getNextItem (outlineRoot .getAsDictionary (PdfName .First ), outlines , destsTree .getNames ());
386
+ constructOutlines (outlineRoot , destsTree .getNames ());
386
387
}
387
388
388
389
return outlines ;
@@ -504,26 +505,62 @@ private void addOutlineToPage(PdfOutline outline, Map<String, PdfObject> names)
504
505
}
505
506
}
506
507
507
- private void getNextItem (PdfDictionary item , PdfOutline parent , Map <String , PdfObject > names ) {
508
- if (null == item ) {
509
- return ;
508
+ /**
509
+ * Get the next outline of the current node in the outline tree by looking for a child or sibling node.
510
+ * 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.
511
+ *
512
+ * @return the {@link PdfDictionary} object of the next outline if one exists, {@code null} otherwise.
513
+ */
514
+ private PdfDictionary getNextOutline (PdfDictionary first , PdfDictionary next , PdfDictionary parent ) {
515
+ if (first != null ) {
516
+ return first ;
517
+ } else if (next != null ) {
518
+ return next ;
519
+ } else {
520
+ return getParentNextOutline (parent );
521
+ }
522
+
523
+ }
524
+
525
+ /**
526
+ * Gets the parent's next outline of the current node.
527
+ * 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.
528
+ *
529
+ * @return the {@link PdfDictionary} object of the next outline if one exists, {@code null} otherwise.
530
+ */
531
+ private PdfDictionary getParentNextOutline (PdfDictionary parent ) {
532
+ if (parent == null ) {
533
+ return null ;
510
534
}
511
- PdfOutline outline = new PdfOutline (item .getAsString (PdfName .Title ).toUnicodeString (), item , parent );
535
+ PdfDictionary current = null ;
536
+ while (current == null ) {
537
+ current = parent .getAsDictionary (PdfName .Next );
538
+ if (current == null ) {
539
+ parent = parent .getAsDictionary (PdfName .Parent );
540
+ if (parent == null ) {
541
+ return null ;
542
+ }
543
+ }
544
+ }
545
+ return current ;
546
+ }
547
+
548
+ private void addOutlineToPage (PdfOutline outline , PdfDictionary item , Map <String , PdfObject > names ) {
512
549
PdfObject dest = item .get (PdfName .Dest );
513
550
if (dest != null ) {
514
551
PdfDestination destination = PdfDestination .makeDestination (dest );
515
552
outline .setDestination (destination );
516
553
addOutlineToPage (outline , names );
517
- }else {
554
+ } else {
518
555
//Take into account outlines that specify their destination through an action
519
556
PdfDictionary action = item .getAsDictionary (PdfName .A );
520
- if (action != null ){
557
+ if (action != null ) {
521
558
PdfName actionType = action .getAsName (PdfName .S );
522
- //Check if it a go to action
523
- if (PdfName .GoTo .equals (actionType )) {
559
+ //Check if it is a go to action
560
+ if (PdfName .GoTo .equals (actionType )) {
524
561
//Retrieve destination if it is.
525
562
PdfObject destObject = action .get (PdfName .D );
526
- if (destObject != null ){
563
+ if (destObject != null ) {
527
564
//Page is always the first object
528
565
PdfDestination destination = PdfDestination .makeDestination (destObject );
529
566
outline .setDestination (destination );
@@ -532,15 +569,43 @@ private void getNextItem(PdfDictionary item, PdfOutline parent, Map<String, PdfO
532
569
}
533
570
}
534
571
}
535
- parent . getAllChildren (). add ( outline );
572
+ }
536
573
537
- PdfDictionary processItem = item .getAsDictionary (PdfName .First );
538
- if (processItem != null ) {
539
- getNextItem (processItem , outline , names );
574
+ /**
575
+ * Constructs {@link PdfCatalog#outlines} iteratively
576
+ */
577
+ private void constructOutlines (PdfDictionary outlineRoot , Map <String , PdfObject > names ) {
578
+ if (outlineRoot == null ) {
579
+ return ;
540
580
}
541
- processItem = item .getAsDictionary (PdfName .Next );
542
- if (processItem != null ) {
543
- getNextItem (processItem , parent , names );
581
+ PdfDictionary first = outlineRoot .getAsDictionary (PdfName .First );
582
+ PdfDictionary current = first ;
583
+ PdfDictionary next ;
584
+ PdfDictionary parent ;
585
+ HashMap <PdfDictionary , PdfOutline > parentOutlineMap = new HashMap <>();
586
+
587
+ outlines = new PdfOutline (OutlineRoot , outlineRoot , getDocument ());
588
+ PdfOutline parentOutline = outlines ;
589
+ parentOutlineMap .put (outlineRoot , parentOutline );
590
+
591
+ while (current != null ) {
592
+ first = current .getAsDictionary (PdfName .First );
593
+ next = current .getAsDictionary (PdfName .Next );
594
+ parent = current .getAsDictionary (PdfName .Parent );
595
+
596
+ parentOutline = parentOutlineMap .get (parent );
597
+ PdfOutline currentOutline = new PdfOutline (current .getAsString (PdfName .Title ).toUnicodeString (), current , parentOutline );
598
+ addOutlineToPage (currentOutline , current , names );
599
+ parentOutline .getAllChildren ().add (currentOutline );
600
+
601
+ if (first != null ) {
602
+ parentOutlineMap .put (current , currentOutline );
603
+ } else if (current == parent .getAsDictionary (PdfName .Last )) {
604
+ parentOutlineMap .remove (parent );
605
+ }
606
+ current = getNextOutline (first , next , parent );
607
+
544
608
}
545
609
}
610
+
546
611
}
0 commit comments