You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@pdfbox.apache.org by ti...@apache.org on 2020/09/12 12:31:39 UTC
svn commit: r1881664 - in /pdfbox/branches/2.0:
examples/src/test/java/org/apache/pdfbox/examples/pdmodel/
pdfbox/src/main/java/org/apache/pdfbox/pdfwriter/
pdfbox/src/main/java/org/apache/pdfbox/pdmodel/
Author: tilman
Date: Sat Sep 12 12:31:39 2020
New Revision: 1881664
URL: http://svn.apache.org/viewvc?rev=1881664&view=rev
Log:
PDFBOX-45: allow incremental save for specific dictionary objects
Modified:
pdfbox/branches/2.0/examples/src/test/java/org/apache/pdfbox/examples/pdmodel/TestCreateSignature.java
pdfbox/branches/2.0/pdfbox/src/main/java/org/apache/pdfbox/pdfwriter/COSWriter.java
pdfbox/branches/2.0/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/PDDocument.java
Modified: pdfbox/branches/2.0/examples/src/test/java/org/apache/pdfbox/examples/pdmodel/TestCreateSignature.java
URL: http://svn.apache.org/viewvc/pdfbox/branches/2.0/examples/src/test/java/org/apache/pdfbox/examples/pdmodel/TestCreateSignature.java?rev=1881664&r1=1881663&r2=1881664&view=diff
==============================================================================
--- pdfbox/branches/2.0/examples/src/test/java/org/apache/pdfbox/examples/pdmodel/TestCreateSignature.java (original)
+++ pdfbox/branches/2.0/examples/src/test/java/org/apache/pdfbox/examples/pdmodel/TestCreateSignature.java Sat Sep 12 12:31:39 2020
@@ -659,6 +659,45 @@ public class TestCreateSignature
actualData = (DataBufferInt) actualImage1.getRaster().getDataBuffer();
Assert.assertArrayEquals(expectedData.getData(), actualData.getData());
doc.close();
+
+ doc = PDDocument.load(new File(outDir, fileNameSigned));
+
+ fileOutputStream = new FileOutputStream(new File(outDir, fileNameResaved2));
+ field = doc.getDocumentCatalog().getAcroForm().getField("SampleField");
+ field.setValue("New Value 2");
+ expectedImage2 = new PDFRenderer(doc).renderImage(0);
+
+ // compare images, image must has changed
+ Assert.assertEquals(oldImage.getWidth(), expectedImage2.getWidth());
+ Assert.assertEquals(oldImage.getHeight(), expectedImage2.getHeight());
+ Assert.assertEquals(oldImage.getType(), expectedImage2.getType());
+ expectedData = (DataBufferInt) oldImage.getRaster().getDataBuffer();
+ actualData = (DataBufferInt) expectedImage2.getRaster().getDataBuffer();
+ Assert.assertEquals(expectedData.getData().length, actualData.getData().length);
+ Assert.assertFalse(Arrays.equals(expectedData.getData(), actualData.getData()));
+
+ // new style incremental save: add only the objects that have changed
+ Set<COSDictionary> objectsToWrite = new HashSet<COSDictionary>();
+ objectsToWrite.add(field.getCOSObject());
+ objectsToWrite.add(field.getWidgets().get(0).getAppearance().getCOSObject());
+ objectsToWrite.add((COSDictionary) field.getWidgets().get(0).getAppearance().getNormalAppearance().getCOSObject());
+ doc.saveIncremental(fileOutputStream, objectsToWrite);
+ doc.close();
+
+ checkSignature(new File("target/SimpleForm.pdf"), new File(outDir, fileNameResaved2), false);
+ doc = PDDocument.load(new File(outDir, fileNameResaved2));
+
+ field = doc.getDocumentCatalog().getAcroForm().getField("SampleField");
+ Assert.assertEquals("New Value 2", field.getValueAsString());
+ actualImage2 = new PDFRenderer(doc).renderImage(0);
+ // compare images, equality proves that the appearance has been updated too
+ Assert.assertEquals(expectedImage2.getWidth(), actualImage2.getWidth());
+ Assert.assertEquals(expectedImage2.getHeight(), actualImage2.getHeight());
+ Assert.assertEquals(expectedImage2.getType(), actualImage2.getType());
+ expectedData = (DataBufferInt) expectedImage2.getRaster().getDataBuffer();
+ actualData = (DataBufferInt) actualImage2.getRaster().getDataBuffer();
+ Assert.assertArrayEquals(expectedData.getData(), actualData.getData());
+ doc.close();
}
@Test
Modified: pdfbox/branches/2.0/pdfbox/src/main/java/org/apache/pdfbox/pdfwriter/COSWriter.java
URL: http://svn.apache.org/viewvc/pdfbox/branches/2.0/pdfbox/src/main/java/org/apache/pdfbox/pdfwriter/COSWriter.java?rev=1881664&r1=1881663&r2=1881664&view=diff
==============================================================================
--- pdfbox/branches/2.0/pdfbox/src/main/java/org/apache/pdfbox/pdfwriter/COSWriter.java (original)
+++ pdfbox/branches/2.0/pdfbox/src/main/java/org/apache/pdfbox/pdfwriter/COSWriter.java Sat Sep 12 12:31:39 2020
@@ -258,6 +258,39 @@ public class COSWriter implements ICOSVi
incrementalUpdate = true;
}
+ /**
+ * Constructor for incremental updates with a list of objects to write. This allows to
+ * include objects even if there is no path of objects that have
+ * {@link COSUpdateInfo#isNeedToBeUpdated()} set so the incremental update gets smaller. Only
+ * dictionaries are supported; if you need to update other objects classes, then add their
+ * parent dictionary.
+ *
+ * @param outputStream output stream where the new PDF data will be written. It will be closed
+ * when this object is closed.
+ * @param inputData random access read containing source PDF data.
+ * @param objectsToWrite objects that <b>must</b> be part of the incremental saving.
+ * @throws IOException if something went wrong
+ */
+ public COSWriter(OutputStream outputStream, RandomAccessRead inputData,
+ Set<COSDictionary> objectsToWrite) throws IOException
+ {
+ // Implementation notes / summary of April 2019 comments in PDFBOX-45:
+ // we allow only COSDictionary in objectsToWrite because other types,
+ // especially COSArray, are written directly. If we'd allow them with the current
+ // COSWriter implementation, they would be written twice,
+ // once directly and once indirectly as orphan.
+ // One could improve visitFromArray and visitFromDictionary (see commit 1856891)
+ // to handle arrays like dictionaries so that arrays are written indirectly,
+ // but this produces very inefficient files.
+ // If there is every a real need to update arrays, then a future implementation could
+ // recommit change 1856891 (also needs to move the byteRange position detection code)
+ // and also set isDirect in arrays to true by default, to avoid inefficient files.
+ // COSArray.setDirect(true) is called at some places in the current implementation for
+ // documentational purposes only.
+ this(outputStream, inputData);
+ this.objectsToWrite.addAll(objectsToWrite);
+ }
+
private void prepareIncrement(PDDocument doc)
{
try
Modified: pdfbox/branches/2.0/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/PDDocument.java
URL: http://svn.apache.org/viewvc/pdfbox/branches/2.0/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/PDDocument.java?rev=1881664&r1=1881663&r2=1881664&view=diff
==============================================================================
--- pdfbox/branches/2.0/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/PDDocument.java (original)
+++ pdfbox/branches/2.0/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/PDDocument.java Sat Sep 12 12:31:39 2020
@@ -1401,6 +1401,48 @@ public class PDDocument implements Close
}
/**
+ * Save the PDF as an incremental update. This is only possible if the PDF was loaded from a
+ * file or a stream, not if the document was created in PDFBox itself. This allows to include
+ * objects even if there is no path of objects that have
+ * {@link COSUpdateInfo#isNeedToBeUpdated()} set so the incremental update gets smaller. Only
+ * dictionaries are supported; if you need to update other objects classes, then add their
+ * parent dictionary.
+ * <p>
+ * This method is for experienced users only. You will usually never need it. It is useful only
+ * if you are required to keep the current revision and append the changes. A typical use case
+ * is changing a signed file without invalidating the signature. To know which objects are
+ * getting changed, you need to have some understanding of the PDF specification, and look at
+ * the saved file with an editor to verify that you are updating the correct objects. You should
+ * also inspect the page and document structures of the file with PDFDebugger.
+ *
+ * @param output stream to write to. It will be closed when done. It
+ * <i><b>must never</b></i> point to the source file or that one will be harmed!
+ * @param objectsToWrite objects that <b>must</b> be part of the incremental saving.
+ * @throws IOException if the output could not be written
+ * @throws IllegalStateException if the document was not loaded from a file or a stream.
+ */
+ public void saveIncremental(OutputStream output, Set<COSDictionary> objectsToWrite) throws IOException
+ {
+ if (pdfSource == null)
+ {
+ throw new IllegalStateException("document was not loaded from a file or a stream");
+ }
+ COSWriter writer = null;
+ try
+ {
+ writer = new COSWriter(output, pdfSource, objectsToWrite);
+ writer.write(this, signInterface);
+ }
+ finally
+ {
+ if (writer != null)
+ {
+ writer.close();
+ }
+ }
+ }
+
+ /**
* <p>
* <b>(This is a new feature for 2.0.3. The API for external signing might change based on feedback after release!)</b>
* <p>