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
/Resourcesmerging is still the one fromPdfGraphicStream.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,/ColorSpaceand/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 thePdfResourceManagerfrom F4. - Transparency. A stamp with opacity would require registering an
/ExtGStateon the page, and todayprepare()replaces an existing direct/ExtGStatewith 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/Contentsand 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
stampto 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.