Skip to content

Commit 0e5b707

Browse files
committed
Fix several issues with PdfOutline processing
- support processing of incorrect hierarchy of outlines when merging - process PdfNumber as page argument for outline destinations - support copying of named destinations DEVSIX-2517
1 parent 98dae1f commit 0e5b707

File tree

10 files changed

+70
-8
lines changed

10 files changed

+70
-8
lines changed

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

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ This file is part of the iText (R) project.
4949
import com.itextpdf.kernel.pdf.layer.PdfOCProperties;
5050
import com.itextpdf.kernel.pdf.navigation.PdfDestination;
5151
import com.itextpdf.kernel.pdf.navigation.PdfExplicitDestination;
52+
import com.itextpdf.kernel.pdf.navigation.PdfNamedDestination;
5253
import com.itextpdf.kernel.pdf.navigation.PdfStringDestination;
5354
import org.slf4j.Logger;
5455
import org.slf4j.LoggerFactory;
@@ -454,27 +455,32 @@ PdfDestination copyDestination(PdfObject dest, Map<PdfPage, PdfPage> page2page,
454455
break;
455456
}
456457
}
457-
} else if (dest.isString()) {
458+
} else if (dest.isString() || dest.isName()) {
458459
PdfNameTree destsTree = getNameTree(PdfName.Dests);
459460
Map<String, PdfObject> dests = destsTree.getNames();
460-
String srcDestName = ((PdfString) dest).toUnicodeString();
461+
String srcDestName = dest.isString() ? ((PdfString) dest).toUnicodeString() : ((PdfName) dest).getValue();
461462
PdfArray srcDestArray = (PdfArray) dests.get(srcDestName);
462463
if (srcDestArray != null) {
463464
PdfObject pageObject = srcDestArray.get(0);
465+
if (pageObject instanceof PdfNumber)
466+
pageObject = getDocument().getPage(((PdfNumber) pageObject).intValue() + 1).getPdfObject();
464467
for (PdfPage oldPage : page2page.keySet()) {
465468
if (oldPage.getPdfObject() == pageObject) {
466469
d = new PdfStringDestination(srcDestName);
467470
if (!isEqualSameNameDestExist(page2page, toDocument, srcDestName, srcDestArray, oldPage)) {
468471
// in the copiedArray old page ref will be correctly replaced by the new page ref as this page is already copied
469472
PdfArray copiedArray = (PdfArray) srcDestArray.copyTo(toDocument, false);
473+
// here we can safely replace first item of the array because array of NamedDestination or StringDestination
474+
// never refers to page in another document via PdfNumber, but should always refer to page within current document
475+
// via page object reference.
476+
copiedArray.set(0, page2page.get(oldPage).getPdfObject());
470477
toDocument.addNamedDestination(srcDestName, copiedArray);
471478
}
472479
break;
473480
}
474481
}
475482
}
476483
}
477-
478484
return d;
479485
}
480486

@@ -495,6 +501,8 @@ private boolean isEqualSameNameDestExist(Map<PdfPage, PdfPage> page2page, PdfDoc
495501

496502
private void addOutlineToPage(PdfOutline outline, Map<String, PdfObject> names) {
497503
PdfObject pageObj = outline.getDestination().getDestinationPage(names);
504+
if (pageObj instanceof PdfNumber)
505+
pageObj = getDocument().getPage(((PdfNumber) pageObj).intValue() + 1).getPdfObject();
498506
if (pageObj != null) {
499507
List<PdfOutline> outs = pagesWithOutlines.get(pageObj);
500508
if (outs == null) {
@@ -600,8 +608,6 @@ private void constructOutlines(PdfDictionary outlineRoot, Map<String, PdfObject>
600608

601609
if (first != null) {
602610
parentOutlineMap.put(current, currentOutline);
603-
} else if (current == parent.getAsDictionary(PdfName.Last)) {
604-
parentOutlineMap.remove(parent);
605611
}
606612
current = getNextOutline(first, next, parent);
607613

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

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1174,9 +1174,8 @@ public List<PdfPage> copyPagesTo(List<Integer> pagesToCopy, PdfDocument toDocume
11741174
PdfPage page = getPage((int) pageNum);
11751175
PdfPage newPage = page.copyTo(toDocument, copier);
11761176
copiedPages.add(newPage);
1177-
if (!page2page.containsKey(page)) {
1178-
page2page.put(page, newPage);
1179-
}
1177+
page2page.put(page, newPage);
1178+
11801179

11811180
if (lastCopiedPageNum >= pageNum) {
11821181
rangesOfPagesWithIncreasingNumbers.add(new HashMap<PdfPage, PdfPage>());

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

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -182,6 +182,38 @@ public void destCopyingTest05() throws IOException, InterruptedException {
182182
assertNull(new CompareTool().compareByContent(outFile, cmpFile, destinationFolder, "diff_"));
183183
}
184184

185+
@Test
186+
public void destCopyingTest06() throws IOException, InterruptedException {
187+
String srcFile = sourceFolder + "sourceWithNamedDestination.pdf";
188+
String outFile = destinationFolder + "destCopyingTest06.pdf";
189+
String cmpFile = sourceFolder + "cmp_destCopyingTest06.pdf";
190+
PdfDocument srcDoc = new PdfDocument(new PdfReader(srcFile));
191+
192+
PdfDocument destDoc = new PdfDocument(new PdfWriter(outFile));
193+
srcDoc.copyPagesTo(Arrays.asList(1, 2, 1), destDoc);
194+
destDoc.close();
195+
196+
srcDoc.close();
197+
198+
assertNull(new CompareTool().compareByContent(outFile, cmpFile, destinationFolder, "diff_"));
199+
}
200+
201+
@Test
202+
public void destCopyingTest07() throws IOException, InterruptedException {
203+
String srcFile = sourceFolder + "sourceStringDestWithPageNumber.pdf";
204+
String outFile = destinationFolder + "destCopyingTest07.pdf";
205+
String cmpFile = sourceFolder + "cmp_destCopyingTest07.pdf";
206+
PdfDocument srcDoc = new PdfDocument(new PdfReader(srcFile));
207+
208+
PdfDocument destDoc = new PdfDocument(new PdfWriter(outFile));
209+
srcDoc.copyPagesTo(Arrays.asList(1, 2, 1), destDoc);
210+
destDoc.close();
211+
212+
srcDoc.close();
213+
214+
assertNull(new CompareTool().compareByContent(outFile, cmpFile, destinationFolder, "diff_"));
215+
}
216+
185217
@Test
186218
public void structureDestination01Test() throws IOException, InterruptedException {
187219
String srcFile = sourceFolder + "customRolesMappingPdf2.pdf";

kernel/src/test/java/com/itextpdf/kernel/utils/PdfMergerTest.java

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -268,4 +268,29 @@ public void mergeTableWithEmptyTdTest() throws IOException, InterruptedException
268268
Assert.fail(errorMessage);
269269
}
270270
}
271+
272+
@Test
273+
@LogMessages(messages = {@LogMessage(messageTemplate = LogMessageConstant.NAME_ALREADY_EXISTS_IN_THE_NAME_TREE, count = 2)})
274+
public void mergeOutlinesNamedDestinations() throws IOException, InterruptedException {
275+
String filename = sourceFolder + "outlinesNamedDestinations.pdf";
276+
String resultFile = destinationFolder + "mergeOutlinesNamedDestinations.pdf";
277+
278+
PdfReader reader = new PdfReader(filename);
279+
280+
PdfDocument sourceDoc = new PdfDocument(reader);
281+
PdfDocument output = new PdfDocument(new PdfWriter(resultFile));
282+
PdfMerger merger = new PdfMerger(output).setCloseSourceDocuments(false);
283+
merger.merge(sourceDoc, 2, 3);
284+
merger.merge(sourceDoc, 2, 3);
285+
sourceDoc.close();
286+
reader.close();
287+
merger.close();
288+
output.close();
289+
290+
CompareTool compareTool = new CompareTool();
291+
String errorMessage = compareTool.compareByContent(resultFile, sourceFolder + "cmp_mergeOutlinesNamedDestinations.pdf", destinationFolder, "diff_");
292+
if (errorMessage != null) {
293+
Assert.fail(errorMessage);
294+
}
295+
}
271296
}

0 commit comments

Comments
 (0)