PageCopier { public PDPage copyOf(PDPage page) { PDPage copy = new PDPage(page.getCOSObject().duplicate()); copy.setCropBox(page.getCropBox()); copy.setMediaBox(page.getMediaBox()); copy.setResources(page.getResources()); copy.setRotation(page.getRotation()); copy.getCOSObject().removeItem(COSName.B); COSArray annots = page.getCOSObject().getDictionaryObject(COSName.ANNOTS, COSArray.class); if (nonNull(annots)) { COSArray cleanedAnnotationsCopy = new COSArray(); annots.stream().map(COSBase::getCOSObject).filter(d -> d instanceof COSDictionary) .map(d -> (COSDictionary) d).map(COSDictionary::duplicate).forEach(a -> { a.removeItem(COSName.P); a.removeItem(COSName.DEST); a.removeItem(COSName.getPDFName("Popup")); a.removeItem(COSName.PARENT); if (ofNullable(a.getDictionaryObject(COSName.A, COSDictionary.class)) .map(d -> d.containsKey(COSName.D)).orElse(false)) { a.removeItem(COSName.A); } cleanedAnnotationsCopy.add(a); }); copy.getCOSObject().setItem(COSName.ANNOTS, cleanedAnnotationsCopy); } if (optimize) { COSDictionary resources = ofNullable(copy.getResources().getCOSObject()).map(COSDictionary::duplicate) .orElseGet(COSDictionary::new); copy.setResources(new PDResources(resources)); ofNullable(resources.getDictionaryObject(COSName.XOBJECT, COSDictionary.class)).filter(Objects::nonNull) .map(COSDictionary::duplicate).ifPresent(d -> resources.setItem(COSName.XOBJECT, d)); ofNullable(resources.getDictionaryObject(COSName.FONT, COSDictionary.class)).filter(Objects::nonNull) .map(COSDictionary::duplicate).ifPresent(d -> resources.setItem(COSName.FONT, d)); hitter.accept(copy); cleaner.clean(copy); } duplicatePageStreams(page, copy); copy.sanitizeDictionary(); return copy; } PageCopier(boolean optimize); PDPage copyOf(PDPage page);  }
@Test public void existingPage() throws IOException { try (PDDocument document = PDFParser.parse(SeekableSources.inMemorySeekableSourceFrom( getClass().getClassLoader().getResourceAsStream("pdf/shared_resource_dic_w_images.pdf")))) { PDPage page = document.getPage(0); PDPage copy = new PageCopier(false).copyOf(page); PDPage optimizedCopy = new PageCopier(true).copyOf(page); assertEquals(page.getMediaBox(), copy.getMediaBox()); assertEquals(page.getCropBox(), copy.getCropBox()); assertEquals(page.getRotation(), copy.getRotation()); assertEquals(page.getResources(), copy.getResources()); assertNotEquals(page.getResources(), optimizedCopy.getResources()); } }
@Test public void discardBeads() throws IOException { try (PDDocument document = PDFParser.parse(SeekableSources.inMemorySeekableSourceFrom( getClass().getClassLoader().getResourceAsStream("pdf/shared_resource_dic_w_images.pdf")))) { PDPage page = document.getPage(0); page.setThreadBeads(Arrays.asList(new PDThreadBead())); assertFalse(page.getThreadBeads().isEmpty()); PDPage copy = new PageCopier(false).copyOf(page); assertTrue(copy.getThreadBeads().isEmpty()); } }
@Test public void removesParentAndPopup() throws IOException { try (PDDocument document = PDFParser.parse(SeekableSources.inMemorySeekableSourceFrom( getClass().getClassLoader().getResourceAsStream("pdf/popup_annotation.pdf")))) { PDPage page = document.getPage(0); PDPage copy = new PageCopier(false).copyOf(page); copy.getAnnotations().stream().map(PDAnnotation::getCOSObject).forEach(d -> { assertFalse(d.containsKey(COSName.PARENT)); assertFalse(d.containsKey(COSName.getPDFName("Popup"))); }); } }
@Test public void pageWithAnnots() throws IOException { try (PDDocument document = PDFParser.parse(SeekableSources.inMemorySeekableSourceFrom( getClass().getClassLoader().getResourceAsStream("pdf/forms/simple_form_with_full_dic.pdf")))) { PDPage page = document.getPage(0); PDPage copy = new PageCopier(false).copyOf(page); assertEquals(page.getCOSObject().getDictionaryObject(COSName.ANNOTS), page.getCOSObject().getDictionaryObject(COSName.ANNOTS)); assertNotEquals(page.getCOSObject().getDictionaryObject(COSName.ANNOTS), copy.getCOSObject().getDictionaryObject(COSName.ANNOTS)); copy.getAnnotations().stream().map(PDAnnotation::getCOSObject).forEach(d -> { assertFalse(d.containsKey(COSName.P)); assertFalse(d.containsKey(COSName.DEST)); }); } }
@Test public void removeActionWithDestination() throws IOException { try (PDDocument document = PDFParser.parse(SeekableSources.inMemorySeekableSourceFrom( getClass().getClassLoader().getResourceAsStream("pdf/forms/simple_form_with_full_dic.pdf")))) { PDPage page = document.getPage(0); PDAnnotationLink link = new PDAnnotationLink(); PDActionGoTo action = new PDActionGoTo(); action.setDestination(PDDestination.create(COSName.C)); link.setAction(action); page.setAnnotations(Arrays.asList(link)); PDPage copy = new PageCopier(false).copyOf(page); assertEquals(page.getCOSObject().getDictionaryObject(COSName.ANNOTS), page.getCOSObject().getDictionaryObject(COSName.ANNOTS)); assertNotEquals(page.getCOSObject().getDictionaryObject(COSName.ANNOTS), copy.getCOSObject().getDictionaryObject(COSName.ANNOTS)); copy.getAnnotations().stream().map(PDAnnotation::getCOSObject).forEach(d -> { assertFalse(d.containsKey(COSName.P)); assertFalse(d.containsKey(COSName.DEST)); assertFalse(d.containsKey(COSName.A)); }); } }
@Test public void doesntRemoveActionWithoutDestination() throws IOException { try (PDDocument document = PDFParser.parse(SeekableSources.inMemorySeekableSourceFrom( getClass().getClassLoader().getResourceAsStream("pdf/forms/simple_form_with_full_dic.pdf")))) { PDPage page = document.getPage(0); PDAnnotationLink link = new PDAnnotationLink(); PDActionGoTo action = new PDActionGoTo(); link.setAction(action); page.setAnnotations(Arrays.asList(link)); PDPage copy = new PageCopier(false).copyOf(page); assertEquals(page.getCOSObject().getDictionaryObject(COSName.ANNOTS), page.getCOSObject().getDictionaryObject(COSName.ANNOTS)); assertNotEquals(page.getCOSObject().getDictionaryObject(COSName.ANNOTS), copy.getCOSObject().getDictionaryObject(COSName.ANNOTS)); copy.getAnnotations().stream().map(PDAnnotation::getCOSObject).forEach(d -> { assertFalse(d.containsKey(COSName.P)); assertFalse(d.containsKey(COSName.DEST)); assertTrue(d.containsKey(COSName.A)); }); } }
@Test public void copyStreamIsSanitized() throws IOException { try (PDDocument document = PDFParser.parse(SeekableSources.inMemorySeekableSourceFrom( getClass().getClassLoader().getResourceAsStream("pdf/annots_in_stream.pdf")))) { PDPage page = document.getPage(0); assertNotNull( page.getCOSObject().getDictionaryObject(COSName.CONTENTS, COSStream.class).getItem(COSName.ANNOTS)); PDPage copy = new PageCopier(false).copyOf(page); assertNull( copy.getCOSObject().getDictionaryObject(COSName.CONTENTS, COSStream.class).getItem(COSName.ANNOTS)); } }
@Test public void copyStreamInArrayIsSanitized() throws IOException { try (PDDocument document = PDFParser.parse(SeekableSources.inMemorySeekableSourceFrom( getClass().getClassLoader().getResourceAsStream("pdf/annots_in_contents_array_stream.pdf")))) { PDPage page = document.getPage(0); assertNotNull(page.getCOSObject().getDictionaryObject(COSName.CONTENTS, COSArray.class) .getObject(0, COSStream.class).getItem(COSName.ANNOTS)); PDPage copy = new PageCopier(false).copyOf(page); assertNull(copy.getCOSObject().getDictionaryObject(COSName.CONTENTS, COSArray.class) .getObject(0, COSStream.class).getItem(COSName.ANNOTS)); } }