pdfengine

package
v0.2.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 9, 2026 License: MPL-2.0 Imports: 33 Imported by: 0

Documentation

Overview

Package pdfengine is the pdfcpu adapter: the only package that imports pdfcpu. It inspects source PDFs and assembles the final document from them and from a generated-pages document. It knows nothing of plans, styles or layout; callers hand it facts (SourceInfo), a compact page order (Run) and file paths, and it returns failures as *Error values with stable Codes.

Operations

Inspect reads one captured source once and reports SourceInfo: page count, effective version, whether pages carry page-local objects, whether a form or destinations exist, and the visible size of the first and last pages, plus material scope-specific feature observations captured before validator repairs. With a requested target it also checks every original page and captures compact fit geometry ranges. Assemble takes the inspected sources, the generated resource document and the final order, and writes and verifies one PDF.

Visible page geometry

A page's visible size is the effective CropBox clipped to the effective MediaBox (MediaBox alone when there is no CropBox), swapped in width and height for a rotation of 90 or 270 degrees, and multiplied by the page's UserUnit. MediaBox, CropBox and Rotate are inherited through the page tree, each independently; UserUnit is not inherited. Boxes may be written with any two opposite corners. Only the first and last pages are measured without fitting; fitting checks every page. A malformed box, an empty or non-overlapping CropBox, a rotation that is not a multiple of 90, or a non-positive UserUnit on those pages is an error. Sizes above any generated-page limit are returned unchanged: preserving a legal large page is the caller's rule to apply. The rule was checked against Poppler renderings (pdftoppm -cropbox) in the tests; Poppler ignores UserUnit, which the tests multiply in.

Assembly design

Every document is merged into one pool with pdfcpu's exported merge primitive, then the pool's page tree is replaced in place by a flat tree of the final order. Page objects keep their identity, so links, destinations and /P back-references stay consistent, and fonts, images and content streams stay shared. pdfcpu's page-collection primitive was rejected: with repeated pages it leaves link destinations pointing at orphan page copies, shares annotation arrays between copies, and drops inherited CropBox (the tests keep a negative control showing the independent oracle detects this).

The pool is built in one sequential pass and written once; the growing output is never rewritten. Memory grows with the total size of the imported documents plus a page dictionary per output page: the pool and the written file are both held in memory (the file again, when it is re-read to verify it). There is no streaming mode and no fixed memory bound; MaxOutputPages only bounds a request. The package benchmarks measure wall time, memory and descriptors.

AssemblyPlan.Validate applies source-known instruction policy without opening files; callers use it for preflight as well as assembly. Assemble calls the same validator defensively before importing. Preflight cannot establish later file/destination state or successful output verification.

Source policy

A source whose pages have /Annots, /AA or /B, or that has an AcroForm or destinations, is "per occurrence": it is imported anew for every run that uses it, so each occurrence owns its annotations, widgets and destinations. Such a source must be used whole in each run (a partial range would leave links, fields or destinations pointing at dropped pages: CodePartialRange). A source with the legacy catalog /Dests dictionary is supported for at most one source occurrence across the entire output (CodeLegacyDestsRepeated), including sources with different destination names. The backend conservatively refuses a second dictionary because pdfcpu merges legacy destinations by overwriting equal keys. Every other source is imported once; repeated pages get a cloned page dictionary and share everything else. Inherited MediaBox, CropBox, Rotate and Resources are copied onto each page, so reordering never changes a page's geometry, and the root of the output page tree carries none.

Backend behavior for document-level features

  • Annotations and links: kept with the page, and for per-occurrence sources re-pointed to the occurrence's own pages (direct /Dest, GoTo actions, page /AA actions, /P back-references).
  • Forms: the AcroForm is kept. Fields of equal name in different documents or occurrences stay separate fields; pdfcpu nests the fields of later documents under a generated numeric parent field, so qualified names differ from the source's. Effective DA/Q defaults and actual AcroForm resource bindings are preserved per occurrence; widget AP and page resources retain independent scopes. Ordinary appearance/editability is supported; field-name references in retained scripts are not rewritten. Unsupported form defaults/ancestry reject with CodeFormUnsupported during inspection and import.
  • Named destinations: the /Names /Dests tree is kept and merged. pdfcpu renames a colliding name in a later occurrence (a control-character suffix) and its links follow the rename.
  • Outlines/bookmarks, document information, XMP metadata, page labels, open action, viewer preferences, permissions and other non-rendering catalog entries: dropped. The output has a fresh /Info with pdfcpu's producer and the current date.
  • Catalog tagged-PDF structure (StructTreeRoot, MarkInfo): dropped; output is not tagged.
  • Non-destination catalog name trees, including EmbeddedFiles and JavaScript, and catalog AF associated-file indices: dropped. An index removal does not remove payloads still reachable through kept page FileAttachment annotations. Page/annotation/widget actions remain, including executable actions. The assembler does not execute scripts and output is not sanitized. Material keep/drop facts are returned once per kind/source for application warning projection; ordinary GoTo/URI links, absent/empty features and producer metadata do not warn.
  • Optional content (OCProperties, OCG/OCMD and /OC references) and OutputIntents: rejected with CodeUnsupportedRendering because document-wide layer/color configuration cannot be reconciled.
  • Dynamic XFA forms and NeedsRendering true: rejected before validator repairs, including XFA-only forms with no static fields. Ordinary static AcroForm widgets and NeedsRendering false are supported.
  • Encryption: encrypted sources are rejected (CodeEncrypted), including those that open with an empty password. Output is never encrypted.
  • PDF version: the output is PDF 2.0 when any input is PDF 2.0, else PDF 1.7. A 2.0 source may appear anywhere in the order.
  • Actual signatures, certification/usage-rights signatures and document timestamps: rejected before validator repairs and during import with CodeSignatureUnsupported. Supported empty unsigned signature fields remain allowed. Assemble unsigned sources and sign the final document externally; the adapter does not authenticate signers, strip signatures or claim preserved signed-byte integrity.

Output is written by pdfcpu with a fresh creation date, so it is not byte-reproducible.

Robustness

Sources are hostile input. pdfcpu panics on some malformed files and re-panics from its own recovery; Inspect and Assemble convert panics into errors. The context is checked between imports, while page clones are compiled, and before the write; pdfcpu checks it internally as well.

Index

Constants

View Source
const (
	// CodeUnreadable means a file could not be opened or is not a regular file.
	CodeUnreadable Code = "pdf_unreadable"
	// CodeInvalid means the PDF is malformed or fails validation.
	CodeInvalid Code = "pdf_invalid"
	// CodeEncrypted means the PDF is encrypted; encrypted sources are not supported, even with an empty password.
	CodeEncrypted Code = "pdf_encrypted"
	// CodeUnsupportedRendering means source rendering depends on document state the assembler cannot reconcile.
	CodeUnsupportedRendering Code = "pdf_rendering_unsupported"
	// CodeSignatureUnsupported rejects signature values that assembly would invalidate.
	CodeSignatureUnsupported Code = "pdf_signature_unsupported"
	// CodeFormUnsupported rejects form semantics the backend cannot preserve faithfully.
	CodeFormUnsupported Code = "pdf_form_unsupported"
	// CodeFitUnsupported rejects source geometry or semantics fitting cannot preserve.
	CodeFitUnsupported Code = "pdf_fit_unsupported"
	// CodeNoPages means the PDF has no pages.
	CodeNoPages Code = "pdf_no_pages"
	// CodePageGeometry means the first or last page has missing or malformed page boxes, rotation or UserUnit.
	CodePageGeometry Code = "pdf_page_geometry_invalid"
	// CodePartialRange means a source whose page-local objects, forms or destinations must be re-imported
	// per occurrence was used for only some of its pages.
	CodePartialRange Code = "pdf_partial_range_unsupported"
	// CodeLegacyDestsRepeated means more than one legacy catalog /Dests source occurrence is requested.
	CodeLegacyDestsRepeated Code = "pdf_legacy_dests_repeated"
	// CodeRequestInvalid means the assembly request is inconsistent: a bad index, range, count or total.
	CodeRequestInvalid Code = "assemble_request_invalid"
	// CodeAssemblyFailed means the PDF backend failed while importing, reordering or writing.
	CodeAssemblyFailed Code = "assemble_failed"
	// CodeOutputInvalid means the written PDF failed validation.
	CodeOutputInvalid Code = "output_invalid"
	// CodeOutputPageCount means the written PDF does not contain the expected number of pages.
	CodeOutputPageCount Code = "output_page_count_mismatch"
	// CodeCanceled means the context was canceled or its deadline passed.
	CodeCanceled Code = "canceled"

	// NoSource marks failures not attributable to an inspected source.
	NoSource = -1
	// NoRun marks failures not attributable to a compact-order run.
	NoRun = -1

	// ReasonOrder identifies an empty page order. The other reasons distinguish instruction refusals
	// from the internally derived total invariant.
	ReasonOrder         PolicyReason = "order"
	ReasonCount         PolicyReason = "count"
	ReasonSource        PolicyReason = "source"
	ReasonRange         PolicyReason = "range"
	ReasonResource      PolicyReason = "resource"
	ReasonWholeSource   PolicyReason = "whole_source"
	ReasonLegacyDests   PolicyReason = "legacy_destinations"
	ReasonPageLimit     PolicyReason = "page_limit"
	ReasonTotalMismatch PolicyReason = "total_mismatch"
	ReasonFit           PolicyReason = "fit_to"
)

Failure codes. They are part of the package contract: callers branch on them and reports carry them.

View Source
const (
	FeatureBookmarks          FeatureKind        = "bookmarks"
	FeatureTaggedStructure    FeatureKind        = "tagged_structure"
	FeatureCatalogAttachments FeatureKind        = "catalog_attachments"
	FeatureCatalogActions     FeatureKind        = "catalog_actions"
	FeaturePageActions        FeatureKind        = "page_actions"
	FeaturePageAttachments    FeatureKind        = "page_attachments"
	FeaturePageLabels         FeatureKind        = "page_labels"
	FeatureOtherCatalogNames  FeatureKind        = "other_catalog_names"
	FeatureURIBase            FeatureKind        = "uri_base"
	FeatureRemoved            FeatureDisposition = "removed"
	FeatureRetained           FeatureDisposition = "retained"
)

Feature kinds distinguish catalog removal from page-local retention.

View Source
const MaxOutputPages = 2_000_000

MaxOutputPages is the largest page total Assemble accepts. The pool holds one dictionary per output page in memory, so the bound turns a request that could only exhaust memory into an immediate error. The engine's measured envelope is far smaller (see the package documentation).

Variables

This section is empty.

Functions

This section is empty.

Types

type Affine added in v0.2.0

type Affine [6]float64

Affine maps source default user space to output physical points using PDF's six coefficients.

func (Affine) Bounds added in v0.2.0

func (m Affine) Bounds(box [4]float64) [4]float64

Bounds returns the axis-aligned bounds of the four transformed rectangle corners.

func (Affine) Point added in v0.2.0

func (m Affine) Point(x, y float64) (float64, float64)

Point applies the captured transform; all projected coordinates use this authority.

type AssembleRequest

type AssembleRequest struct {
	Observer     observation.Observer
	FitTarget    *PageSize
	Resource     *ResourceDocument
	Destination  string
	OutputDigest string

	Sources       []SourceFile
	Order         []Run
	ExpectedPages int
	// contains filtered or unexported fields
}

AssembleRequest describes one output document.

type AssemblyPlan added in v0.2.0

type AssemblyPlan struct {
	FitTarget      *PageSize
	Sources        []SourceFile
	Order          []Run
	GeneratedSpecs int
	ExpectedPages  int
}

AssemblyPlan is the source-known instruction policy, independent of destination and resource files.

func (AssemblyPlan) Validate added in v0.2.0

func (r AssemblyPlan) Validate() error

Validate checks inspected source facts and compact order without I/O or page-sized allocations. At most one legacy catalog /Dests source occurrence is supported, even across distinct sources: the backend cannot safely reconcile their catalog dictionaries.

type Code

type Code string

Code is a stable, machine-readable failure category.

func CodeOf

func CodeOf(err error) Code

CodeOf returns the code of the first Error in err's chain, or the empty code.

type Engine

type Engine struct {
	// contains filtered or unexported fields
}

Engine performs PDF inspection and assembly with the embedded pdfcpu library, using stateless, offline configuration. It is safe for concurrent use: every operation works on a clone of its configuration.

func New

func New() (*Engine, error)

New constructs an Engine.

The configuration is stateless (no pdfcpu configuration directory is read or written), offline, and relaxed in validation. Merge bookmark creation is off, and the command mode is MERGECREATE, which makes pdfcpu reject encrypted files at read time, before decrypting anything.

func (*Engine) Assemble

func (e *Engine) Assemble(ctx context.Context, req *AssembleRequest) error

Assemble writes the request's pages, in order, as one PDF at req.Destination and verifies the result.

The sequence, each step once per call:

  1. validate the request and enforce the source policy (no file is read);
  2. import the resource document and the source documents into one pool, merging their object graphs. A source with page-local objects, forms or named destinations is imported anew for every run that uses it; every other source is imported once and its page dictionaries are cloned for repeated pages while all other objects stay shared;
  3. reorder the pool's page tree in place to the compiled order and reduce the catalog to the entries the policy keeps;
  4. optimize (merging duplicate fonts and images) and write the file once;
  5. validate the written file and compare its page count with req.ExpectedPages.

The pool holds every imported object graph in memory until the single write, so memory grows with the total size of the imported documents, not with the number of output pages; there is no bounded or streaming mode. The growing output is never rewritten.

The context is checked between imports, while repeated pages are compiled, and before the write. Failures are *Error values with stable codes naming the source where one is responsible.

func (*Engine) Inspect

func (e *Engine) Inspect(ctx context.Context, path string, target *PageSize) (SourceInfo, error)

Inspect reads and validates the PDF at path and reports its facts in one pass.

The object graph is not retained after Inspect returns: only the returned values survive, so memory use is bounded by one source at a time per concurrent caller.

Encrypted PDFs are rejected with CodeEncrypted even when they open with an empty password. A first or last page whose boxes, rotation or UserUnit are malformed is rejected with CodePageGeometry. Optional content, document output intents and dynamic XFA rendering are rejected with CodeUnsupportedRendering. A nonnil target adds shared raw all-page fitting policy and geometry capture before validation repairs.

type Error

type Error struct {
	// Err is the underlying cause.
	Err error
	// Code is the stable failure category.
	Code Code
	// Path is the file concerned, or empty.
	Path string
	// Reason is nonempty for assembly-plan policy failures.
	Reason PolicyReason
	// Source is the index into AssembleRequest.Sources, or NoSource.
	Source int
	// Run is the offending compact-order index, or NoRun for non-run failures.
	Run int
}

Error is a failure with a stable code and, where known, the identity of the file it concerns.

func (*Error) Error

func (e *Error) Error() string

Error formats the code, the file and the cause.

func (*Error) Unwrap

func (e *Error) Unwrap() error

Unwrap returns the underlying cause.

type FeatureDisposition added in v0.2.0

type FeatureDisposition string

FeatureDisposition is the assembly policy for an observed feature.

type FeatureKind added in v0.2.0

type FeatureKind string

FeatureKind identifies a material source feature whose handling matters to the assembled packet.

type FitRange added in v0.2.0

type FitRange struct {
	First, Last int
	Fit         PageFit
}

FitRange captures consecutive source pages with identical original and final geometry.

type PageFit added in v0.2.0

type PageFit struct {
	Media    [4]float64
	Crop     [4]float64
	Visible  [4]float64
	Original PageSize
	Target   PageSize
	Matrix   Affine
	Scale    float64
	UserUnit float64
	Rotation int
	HasCrop  bool
}

PageFit captures original physical geometry and the final sheet transform once per geometry.

func CanvasFit added in v0.2.0

func CanvasFit(canvas, target PageSize) (PageFit, error)

CanvasFit captures the fit of a generated authored canvas with ordinary origin, units and rotation. Layout and report projections use this same authority as source-page fitting.

func (PageFit) ClippedBounds added in v0.2.0

func (f PageFit) ClippedBounds(box [4]float64) ([4]float64, bool)

ClippedBounds projects the original visible intersection of a captured ink bounding rectangle. It projects existing measurements rather than remeasuring glyphs or opening a PDF.

type PageSize

type PageSize struct {
	Width, Height float64
}

PageSize is a page's visible size in physical points (1/72 inch), as a viewer shows it.

type PolicyReason added in v0.2.0

type PolicyReason string

PolicyReason identifies an instruction-policy refusal without parsing its message.

type ResourceDocument

type ResourceDocument struct {
	// Path is the generated PDF.
	Path string
	// Pages is the number of pages it holds.
	Pages int
}

ResourceDocument is the generated-pages PDF: one page per distinct generated-page specification. Assemble only reads it, and uses its pages by index.

type Run

type Run struct {
	// Generated selects the kind: false for pages of Sources[Index], true for page Index of the resource.
	Generated bool
	// Index is the source index, or the 0-based resource page index.
	Index int
	// Start is the 1-based first source page; it is unused for generated runs.
	Start int
	// Count is the number of consecutive source pages, or the number of repetitions of the generated page.
	Count int
}

Run is one compact element of the final page order.

func GeneratedPages

func GeneratedPages(spec, repeat int) Run

GeneratedPages is a run of repeat copies of the 0-based resource page spec.

func SourcePages

func SourcePages(source, start, count int) Run

SourcePages is a run of count consecutive pages of Sources[source] beginning at the 1-based page start.

type SourceFeature added in v0.2.0

type SourceFeature struct {
	Kind        FeatureKind
	Disposition FeatureDisposition
}

SourceFeature is an observed source fact and its scope-specific assembly policy.

type SourceFile

type SourceFile struct {
	// Path is the private snapshot to read; the bytes inspected are the bytes assembled.
	Path string
	// Info is the result of Inspect(Path).
	Info SourceInfo
}

SourceFile is one captured source document and the facts Inspect reported for it.

type SourceInfo

type SourceInfo struct {
	// Features captures material scope-specific source effects before validator repairs.
	Features []SourceFeature
	// Fits captures compact all-page fitting geometry when a target was requested.
	Fits []FitRange
	// Pages is the number of pages, at least 1.
	Pages int
	// Version is the effective PDF version: the catalog /Version entry when present, else the header.
	Version Version
	// PageLocal reports that some page has an /Annots entry, an /AA additional-actions
	// dictionary or a /B article-beads array: objects that belong to one page and refer to others.
	PageLocal bool
	// AcroForm reports that the catalog has an interactive form.
	AcroForm bool
	// NamedDests reports that the catalog has a /Names /Dests name tree.
	NamedDests bool
	// LegacyDests reports that the catalog has the legacy /Dests dictionary.
	LegacyDests bool
	// First and Last are the visible sizes of the first and last pages; see PageSize.
	First, Last PageSize
}

SourceInfo is what the assembly policy and layout need to know about one PDF.

func (SourceInfo) ImportPerOccurrence

func (s SourceInfo) ImportPerOccurrence() bool

ImportPerOccurrence reports whether Assemble imports the source anew for every run that uses it, because its annotations, forms or destinations are tied to its page objects and a shared copy would leave the second occurrence pointing at the first.

type Version

type Version int

Version is a PDF version encoded as major*10+minor: 14 is PDF 1.4 and 20 is PDF 2.0.

const (
	Version17 Version = 17
	Version20 Version = 20
)

Versions the engine writes. Output is PDF 2.0 when any input is PDF 2.0, and PDF 1.7 otherwise.

func (Version) String

func (v Version) String() string

String formats the version as "major.minor".

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL