PdfDocumentRewriter class

Full rewrite of a PDF, with garbage collection.

Saving a loaded document produces an incremental update: the original bytes are copied whole and the change goes at the end. That preserves signatures, but nothing is ever removed from the file — old revisions, objects nobody references any more and deleted content stay there, readable by any tool that walks the bytes.

The rewrite does the opposite: it builds a new PdfDocument and imports into it the graph reachable from the catalog of the source. Whatever is not reachable simply is not written. There is no "erase" step: there is a step that copies what matters, and the rest is left behind.

final saneado = await PdfDocumentRewriter.rewrite(bytes);

// With a report:
final result = await PdfDocumentRewriter().rewriteBytes(bytes);
print(result.report); // objetos 443 -> 257, bytes 2589459 -> 566480

Rewriting breaks every existing signature

A digital signature covers the exact bytes of the document it was applied to, declared in the /ByteRange. The rewrite renumbers objects, recompresses streams and rebuilds the xref: none of those bytes survive, and no digest checks out again. There is no rewrite that preserves a signature.

This is not a side effect to work around — it is the reason the rewrite serves as the basis for sanitizing. As long as the file keeps the old signature, it keeps along with it the revision that signature covered, and it is exactly in that revision that the content one wanted to remove lives. That is why PdfRewriteOptions.keepInvalidSignatures exists for forensics and history, never for validation, and why safe redaction (phase 9 of the roadmap) requires a rewrite.

The default behavior is that of SEI and of most tools on the market: it rewrites, turns the signature widget into a read-only stamp and warns. PdfRewriteOptions.rejectSignedSources refuses instead of warning.

What this phase still does NOT do

  • It is not redaction. Removing text or an image from inside a content stream is still the business of phases 8 and 9 of the roadmap. The rewrite copies the content streams as they are: whatever was drawn on the page stays drawn, including under a black rectangle. What it guarantees is that no earlier revision with the content is left — not that the content of the current revision is gone.
  • It does not decrypt. A document with /Encrypt is refused with a PdfMergeException; there is no reading security handler.
  • It does not preserve the /ID. The new file gets a new identifier, which is correct for a document whose bytes changed entirely, but breaks any external link that depended on the old identifier.
  • It does not rewrite the structural tagging. With PdfRewriteOptions.keepStructureTree the tree is copied as it is; it is not rebuilt from the content.
  • It does not use object streams in the output. A very compact source file can grow; the gain comes from dropping revisions, not from recompressing.
  • It does not renumber under the caller's control. The output numbering is the import order; there is no option to preserve the source numbers.

Constructors

PdfDocumentRewriter({PdfRewriteOptions? options, bool compress = true, PdfVersion version = PdfVersion.pdf_1_5, DeflateCallback? deflate, bool verbose = false})
Creates a reusable rewriter.

Properties

compress → bool
Compresses the generated streams.
final
deflate → DeflateCallback?
Alternative compressor; zlib.encode on dart:io.
final
hashCode → int
The hash code for this object.
no setterinherited
options → PdfRewriteOptions
What to preserve.
final
runtimeType → Type
A representation of the runtime type of the object.
no setterinherited
verbose → bool
Emits the serialization trace.
final
version → PdfVersion
Version declared in the output file.
final

Methods

noSuchMethod(Invocation invocation) → dynamic
Invoked when a nonexistent method or property is accessed.
inherited
rewriteBytes(Uint8List bytes, {bool useIsolate = false}) → Future<PdfRewriteResult>
Rewrites the bytes of a PDF.
rewriteDocument(PdfDocumentParser source, {int? bytesBefore, bool useIsolate = false}) → Future<PdfRewriteResult>
Rewrites a document already opened by a parser.
toString() → String
A string representation of this object.
inherited

Operators

operator ==(Object other) → bool
The equality operator.
inherited

Static Methods

rewrite(Uint8List bytes, {PdfRewriteOptions? options, bool compress = true, PdfVersion version = PdfVersion.pdf_1_5, DeflateCallback? deflate, bool useIsolate = false}) → Future<Uint8List>
Rewrites bytes and returns only the new file.