Add append-only incremental update (ModifyIncremental + PdfDocument.SaveIncremental) - #386
Open
AlexanderV wants to merge 1 commit into
Open
Add append-only incremental update (ModifyIncremental + PdfDocument.SaveIncremental)#386AlexanderV wants to merge 1 commit into
AlexanderV wants to merge 1 commit into
Conversation
New open mode ModifyIncremental and new function PdfDocument.SaveIncremental. An incremental update writes the bytes of the original file unchanged, followed by the new and the modified objects and a cross-reference section chained to the cross-reference section of the original file by its /Prev entry. Because no byte of the original file is touched, a digital signature of the original file stays valid and further signatures can be added one by one, which is not possible with Save. Opening a document with Modify runs IrefTable.Compact() and Renumber(). Both are fatal here, because the objects of the original file are written unchanged and are referenced by their original object numbers. ModifyIncremental behaves like Modify but skips both steps, keeps the bytes and the object identifiers of the original file, and is treated as writable by CanModify and IsReadOnly. PDFsharp cannot detect the modification of an object, therefore modified objects must be declared with the new function PdfDocument.MarkAsModified. DigitalSignatureHandler declares the objects it modifies. Encrypted documents, documents with a cross-reference stream and the removal of an object are rejected with a dedicated exception, because appending unencrypted objects, chaining a cross-reference table to a cross-reference stream respectively omitting the free entry of a removed object is not valid.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
This PR adds an append-only incremental update: the bytes of the original file are written unchanged, followed by the new and the modified objects and a cross-reference section chained to the one of the original file by its
/Preventry.Because no byte of the original file is touched, a digital signature the file already contains stays valid and further signatures can be added one by one — which is not possible with
Save, since it rewrites the whole file.This PR adds public API, so please tell me if you would rather discuss it in an issue first — I am happy to rename anything or to change the approach.
New API
PdfDocumentOpenMode.ModifyIncremental— likeModify, but the reader neither compacts nor renumbers the cross-reference table, so the object numbers of the file are preserved. Appended at the end of the enum to keep the values of the existing members stable.PdfDocument.SaveIncremental/SaveIncrementalAsync(stream and path overloads,closeStreamlikeSave).PdfDocument.MarkAsModified(PdfObject)— see below.DigitalSignatureHandlerdeclares the objects it modifies, so signing needs no extra code.Detecting modified objects
An incremental update writes only new and modified objects. New objects are detected automatically (the reader remembers the object identifiers of the file). PDFsharp cannot detect the modification of an object, so a modified object has to be declared with
MarkAsModified. This is the one design decision I am least sure about — an alternative would be to write all objects, which is always correct but doubles the file size.Rejected with a dedicated exception
Known limitation, documented in the XML comments: the bytes of the original file are kept in memory between opening and saving the document, because the stream they were read from is not necessarily open anymore when the document is saved.
Tests
IO/IncrementalUpdateTests.cs, 9 cases: the original bytes are preserved byte for byte, the cross-reference sections are chained, the object numbers survive, two signatures are layered one after the other, and each of the rejected cases. They use a dummyIDigitalSigner, so they need no certificate, no assets and no network. The test for the cross-reference stream builds such a file in the test itself.Verified on top of current
master: builds fornet10.0andnetstandard2.0, fullPdfSharp.Testssuite green (261 passed, 0 failed).This PR is independent of my other PRs; each of them applies to
masteron its own.