OutlineDistiller { public void appendRelevantOutlineTo(PDOutlineNode to, LookupTable<PDPage> pagesLookup) { requireNonNull(to, "Unable to merge relevant outline items to a null outline."); if (!pagesLookup.isEmpty()) { ofNullable(document.getDocumentCatalog().getDocumentOutline()).ifPresent(outline -> { for (PDOutlineItem child : outline.children()) { cloneNode(child, pagesLookup).ifPresent(c -> to.addLast(c)); } LOG.debug("Appended relevant outline items"); }); } } OutlineDistiller(PDDocument document); void appendRelevantOutlineTo(PDOutlineNode to, LookupTable<PDPage> pagesLookup);  }
@Test public void keepAll() { for (PDPage current : document.getPages()) { mapping.addLookupEntry(current, new PDPage()); } PDDocumentOutline outline = new PDDocumentOutline(); new OutlineDistiller(document).appendRelevantOutlineTo(outline, mapping); assertTrue(outline.hasChildren()); assertEquals(5, outline.getOpenCount()); }
@Test public void keepOneBranch() { mapping.addLookupEntry(document.getPage(2), new PDPage()); PDDocumentOutline outline = new PDDocumentOutline(); new OutlineDistiller(document).appendRelevantOutlineTo(outline, mapping); assertTrue(outline.hasChildren()); assertEquals(2, outline.getOpenCount()); }
@Test public void emptyMapping() { PDDocumentOutline outline = new PDDocumentOutline(); new OutlineDistiller(document).appendRelevantOutlineTo(outline, mapping); assertFalse(outline.hasChildren()); }
@Test public void rootIsKeptChildIsStripped() { PDPage page1 = new PDPage(); PDPage page2 = new PDPage(); PDDocument document = new PDDocument(); document.addPage(page1); document.addPage(page2); PDDocumentOutline outlines = new PDDocumentOutline(); PDOutlineItem root = new PDOutlineItem(); root.setTitle("title"); root.setDestination(page1); PDOutlineItem child = new PDOutlineItem(); child.setTitle("child"); child.setDestination(page2); root.addFirst(child); outlines.addFirst(root); document.getDocumentCatalog().setDocumentOutline(outlines); mapping.addLookupEntry(document.getPage(0), new PDPage()); PDDocumentOutline outline = new PDDocumentOutline(); new OutlineDistiller(document).appendRelevantOutlineTo(outline, mapping); assertTrue(outline.hasChildren()); assertEquals(1, outline.getOpenCount()); }
@Test public void destinationTypeIsPreservedInLeaves() throws IOException { try (PDDocument document = PDFParser.parse(SeekableSources.inMemorySeekableSourceFrom( getClass().getClassLoader().getResourceAsStream("pdf/large_outline.pdf")))) { mapping.addLookupEntry(document.getPage(2), new PDPage()); PDDocumentOutline outline = new PDDocumentOutline(); new OutlineDistiller(document).appendRelevantOutlineTo(outline, mapping); PDDestination clonedDest = outline.getFirstChild().getDestination(); assertThat(clonedDest, is(instanceOf(PDPageFitWidthDestination.class))); assertEquals(806, ((PDPageFitWidthDestination) clonedDest).getTop()); } }
@Test public void destinationTypeIsPreservedInNodes() throws IOException { mapping.addLookupEntry(document.getPage(2), new PDPage()); PDDocumentOutline outline = new PDDocumentOutline(); new OutlineDistiller(document).appendRelevantOutlineTo(outline, mapping); PDDestination clonedDest = outline.getFirstChild().getDestination(); assertThat(clonedDest, is(instanceOf(PDPageXYZDestination.class))); assertEquals(759, ((PDPageXYZDestination) clonedDest).getTop()); assertEquals(56, ((PDPageXYZDestination) clonedDest).getLeft()); }
@Test public void fallbackHandlesBrokenDestinations() throws IOException { try (PDDocument document = PDFParser.parse(SeekableSources.inMemorySeekableSourceFrom( getClass().getClassLoader().getResourceAsStream("pdf/page_dests_with_number_insteadof_refs.pdf")))) { for (PDPage current : document.getPages()) { mapping.addLookupEntry(current, new PDPage()); } PDDocumentOutline outline = new PDDocumentOutline(); new OutlineDistiller(document).appendRelevantOutlineTo(outline, mapping); assertTrue(outline.hasChildren()); } }
@Test public void fallbackHandlesBrokenDestinationsWithNonExistingPageNumber() throws IOException { try (PDDocument document = PDFParser.parse(SeekableSources.inMemorySeekableSourceFrom(getClass() .getClassLoader().getResourceAsStream("pdf/page_dests_with_number_insteadof_refs_wrong_num.pdf")))) { for (PDPage current : document.getPages()) { mapping.addLookupEntry(current, new PDPage()); } PDDocumentOutline outline = new PDDocumentOutline(); new OutlineDistiller(document).appendRelevantOutlineTo(outline, mapping); assertTrue(outline.hasChildren()); } }
@Test public void infiniteLoop() throws IOException { try (PDDocument document = PDFParser.parse(SeekableSources.inMemorySeekableSourceFrom(getClass() .getClassLoader().getResourceAsStream("pdf/infinite_outline.pdf")))) { for (PDPage current : document.getPages()) { mapping.addLookupEntry(current, new PDPage()); } PDDocumentOutline outline = new PDDocumentOutline(); new OutlineDistiller(document).appendRelevantOutlineTo(outline, mapping); assertTrue(outline.hasChildren()); int outlineChildCount = 0; for(PDOutlineItem child: outline.children()) { outlineChildCount++; } assertEquals(3, outlineChildCount); } }