PdfPageContentEditor class

Overlay and underlay of content on a page, new or loaded.

The technique

The original content is never touched. The editor adds two streams to the page and assembles the /Contents in this order:

[ underlays ] [ "q" ] [ original content ] [ "Q" + overlays ]

The leading q and the trailing Q isolate the graphics state: any cm, color, clip or line width the page left open at the outer level is discarded before the stamp, and the stamp does not leak state into what comes after. It is the same technique as PDFBox's AppendMode and the one SEI uses on case PDFs (merge roadmap, §10, item 7), and it preserves the byte-for-byte fidelity of the original stream — important for a document that is still going to be checked against its source.

What needed care

PdfPage.prepare() always puts the existing /Contents before the streams created by getGraphics(), so there is no way to get a prefix stream just by stacking new content. The editor solves that by rewriting the page /Contents as an array with the prefix reference in front; since prepare() reinserts that array at the start and then calls uniq(), which keeps the first occurrence, the final order is the wanted one and saving twice neither duplicates nor reorders anything. On a new page, which does not have a /Contents yet, the order is that of the PdfPage.contents list itself and the editor only moves the prefix stream to its start.

The consequence is that drawing done directly with page.getGraphics() before creating the editor ends up inside the isolated block, together with the original content; drawing done after lands past the stamp. Whoever needs a predictable order should do all the drawing through the editor.

Known limits

  • Resources. The /Resources merging is still the one from PdfGraphicStream.prepare(). The editor only makes sure it has a direct dictionary to merge into (see _ensureDirectResources); it still does not resolve name collisions in /Font, /XObject, /ExtGState, /Pattern, /Shading, /ColorSpace and /Properties. In practice the collision is unlikely, because the name of the new resources derives from the object number, which in a loaded document starts after the last one in the file. The complete handling is the PdfResourceManager from F4.
  • Transparency. A stamp with opacity would require registering an /ExtGState on the page, and today prepare() replaces an existing direct /ExtGState with the reference from the document registry, breaking the original content. That is why there is no opacity option: the watermark is made with a light color and PdfStamp.rotationDegrees.
  • replaceContent. Swapping the page content requires discarding the loaded /Contents and depends on the page collection from F3.

Constructors

PdfPageContentEditor(PdfPage page, {PdfBoxType reference = PdfBoxType.crop})
Creates the editor for page.

Properties

document → PdfDocument
The document that owns the page.
no setter
hashCode → int
The hash code for this object.
no setterinherited
page → PdfPage
The edited page.
final
reference → PdfBoxType
The box used as the visible area.
final
runtimeType → Type
A representation of the runtime type of the object.
no setterinherited
transformer → PdfCoordinateTransformer
Coordinate converter of this page.
final

Methods

drawOverlay(void build(PdfGraphics canvas)) → void
Draws over the original content.
drawStamp(PdfStamp stamp) → void
Applies stamp to the page, on the layer it declares.
drawUnderlay(void build(PdfGraphics canvas)) → void
Draws under the original content.
noSuchMethod(Invocation invocation) → dynamic
Invoked when a nonexistent method or property is accessed.
inherited
toString() → String
A string representation of this object.
inherited

Operators

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

Static Methods

distinctPages(PdfDocument document) → List<PdfPage>
The distinct pages of document, in document order.