WolfPDF - reference
WolfPDF Python API and request CLI reference
WolfPDF edits existing PDF files against a named source and publishes a new file only after the result has been verified. This page lists the Python API, the request command, the 58 registered actions, and the 111 refusal codes of version 0.1.0, generated from the package source.
What WolfPDF does
Every operation names its source file with a SHA-256 digest. WolfPDF reads the file, checks the digest, applies the action, and re-reads the staged output before publishing it. When any check fails, the call returns a structured refusal with a stable code and no output file is written. Publication never replaces an existing file.
Access and licensing
WolfPDF is a commercial engine delivered through Wolf Runtime. There is no public package and no self-serve evaluation credential for WolfPDF. To evaluate it on your own PDF workflow or to ask about licensing for an integration, request a Wolf Runtime evaluation or contact us about licensing. Include the PDF operations you need and where the engine will run.
The engine runs on a pinned native PDF backend, pdf-oxide 0.3.78+wolfpdf.5, and requires Python 3.10 or later. Ask for installation details when you request an evaluation.
Python quickstart
wolfpdf.open takes PDF bytes or a path. Page numbers in the Python API start at zero. Each chained call runs the matching action on bytes held in memory and verifies the result. Nothing reaches the filesystem until save publishes the verified bytes once.
import wolfpdf
document = wolfpdf.open("board_pack.pdf")
print(len(document), document[0].text()[:40])
span = next(s for s in document[0].spans() if s["text"] == "REGION A")
box = span["bbox"] # x, y, width, height in PDF points
document.compose([2, 0]) # keep pages 2 and 0, in that order: REGION A is now on page 1
document.stamp([{"kind": "text", "text": "STAMPED", "x": 72, "y": 100, "size": 14}])
document.redact([{"page": 1, **box}], required_absent_text=["REGION A"])
receipt = document.save("board_pack_final.pdf")
print(receipt["sha256"], receipt["page_count"])save returns a receipt with the published path, its resolved path, the SHA-256 digest, the byte count, and the page count. Saving to a path that already exists is refused and the existing file stays as it was:
try:
document.save("board_pack_final.pdf") # publication never replaces a file
except wolfpdf.Refusal as refusal:
print(refusal.code) # output_existsA refused call leaves the open document unchanged, so the rest of the chain can continue from the last verified state. To fill a form and stamp it, chain the two calls:
document = wolfpdf.open("application_form.pdf")
document.fill({"customer": "Ada Lovelace", "agree": True}).stamp(
[{"kind": "text", "text": "COPY", "x": 300, "y": 700, "size": 12}]
)
document.save("application_filled.pdf")Encrypted sources open with wolfpdf.open(source, password_env="NAME"), where NAME is the environment variable that holds the password. Credential values are never passed as arguments, and they never appear in results or refusal details.
Request command and result envelope
The wolfpdf command reads one JSON request from standard input or from --request PATH, and writes exactly one JSON result object to standard output. Everything else goes to standard error. wolfpdf --version prints the build and backend identity, wolfpdf --capabilities prints the registered actions, and wolfpdf --help prints usage on standard error.
{
"schema_version": 1,
"action": "optimize",
"sources": [{"id": "doc", "path": "report.pdf", "sha256": "<64-character lowercase hex digest of report.pdf>"}],
"output": "report_small.pdf",
"options": {}
}The request holds schema_version (currently 1), action, sources, options, and output for the actions that publish a file. Each source carries an id, a path, its sha256, and optionally a password_env. Actions that read without writing refuse a request that carries an output path, and actions that publish require one.
wolfpdf --request request.json
echo $?The result carries status, action, sources, output, result, and error. The exit status is 0 for succeeded and 2 for a structured refusal, and a refusal still prints one JSON result object. This is the output of the shipped command, with the digest abbreviated, for a request that names a file that does not exist:
$ echo '{"schema_version":1,"action":"inspect","sources":[{"id":"doc","path":"missing.pdf","sha256":"<64 zeros>"}]}' | wolfpdf
{"schema_version": 1, "status": "refused", "action": "inspect", "sources": [{"id": "doc", "path": "missing.pdf", "realpath": null, "sha256_declared": "<64 zeros>", "sha256_observed": null, "bytes": null}], "output": null, "result": null, "error": {"code": "source_missing", "detail": "source 'doc': 'missing.pdf' does not exist"}}
$ echo $?
2In Python, wolfpdf.execute(payload) runs the same request and returns the result object as a dictionary. It reports refusals in the object instead of raising.
Credentials are named, never carried. Encryption actions take environment variable names for the two passwords, and signing takes the path of a PKCS#12 file plus the environment variable that holds its password.
Python API reference
The wolfpdf package lists 16 names in __all__. The functions are execute and open. The classes are EmbeddedFont, Layout, PdfDocument, PdfPage, Refusal, StandardFont, VectorPath. The remaining names are constants such as REFUSAL_CODES, PAGE_SIZES, STANDARD_FONTS, and REQUEST_SCHEMA_VERSION.
PdfDocument
A document supports len(document), document[index], and iteration over its pages. Each chained action appends its result object to document.results. A call that returns a PdfDocument returns the same document, so calls chain.
add_page(size: str | Sequence[float] = 'A4', *, landscape: bool = False) -> PdfPageAppend a blank page of a named size or (width, height) points.
compose(pages: Sequence[int | Mapping[str, Any]], *, stamps: Sequence[Mapping[str, Any]] = (), sources: Mapping[str, PdfDocument | Source] | None = None) -> PdfDocumentRebuild the document from selected pages (compose).
copy_page(index: int, to: int | None = None) -> PdfPageInsert a copy of page index at to (default: the end).
extract(*, pages: Sequence[int] | None = None, include: Sequence[str] = ('text', 'spans', 'tables')) -> dict[str, Any]The extract action's result for this document's current bytes.
fill(values: Mapping[str, Any], *, flatten: bool = False) -> PdfDocumentFill AcroForm fields by name (forms.fill).
insert_page(index: int, size: str | Sequence[float] = 'A4', *, landscape: bool = False) -> PdfPageInsert a blank page so that it becomes page index.
inspect() -> dict[str, Any]The inspect action's report for this document's current bytes.
move_page(index: int, to: int) -> NoneMove page index so that it becomes page to.
open(source: Source, *, password_env: str | None = None) -> PdfDocumentOpen PDF bytes or a PDF file.
redact(regions: Sequence[Mapping[str, Any]], *, required_absent_text: Sequence[str]) -> PdfDocumentDestructively redact rectangles (redact).
remove_page(index: int) -> NoneRemove page index from the page tree.
reorder(order: Sequence[int]) -> NoneRearrange the pages: order lists every current page index once.
save(path: str | os.PathLike) -> dict[str, Any]Publish the verified bytes once at path, which must not exist yet.
stamp(stamps: Sequence[Mapping[str, Any]] = (), *, header: Mapping[str, Any] | None = None, footer: Mapping[str, Any] | None = None, skip_first_page: bool | None = None, date: str | None = None, filename: str | None = None, assets: Mapping[str, Source] | None = None) -> PdfDocumentAppend stamps, watermarks, headers and footers (stamps.apply).
to_bytes() -> bytesThe document's verified bytes, writing pending edits first.
Properties: pages.
PdfPage
Reading methods mirror the extract action. Drawing methods author new content on a page. Methods that lay out flowing content return a Layout that records the last page and the bottom position of the content.
draw_image(image: Source, x: float, y: float, *, width: float | None = None, height: float | None = None) -> PdfPagePlace a PNG or JPEG image with its lower-left corner at (x, y).
draw_line(x1: float, y1: float, x2: float, y2: float, *, color: Color = (0.0, 0.0, 0.0), width: float = 1.0, dash: Sequence[float] | None = None) -> PdfPageStroke a straight line.
draw_list(items: Sequence[Any], x: float, top: float, *, width: float, font: FontChoice, size: float = 11.0, color: Color = (0.0, 0.0, 0.0), ordered: bool = False, indent: float = 18.0, line_height: float = 1.2, bottom: float = 36.0, top_margin: float = 36.0) -> LayoutDraw a nested list.
draw_path(path: VectorPath, *, stroke: Color | None = (0.0, 0.0, 0.0), fill: Color | None = None, line_width: float = 1.0, dash: Sequence[float] | None = None, even_odd: bool = False) -> PdfPageStroke and/or fill a VectorPath (nonzero or even-odd fill).
draw_rect(x: float, y: float, width: float, height: float, *, stroke: Color | None = (0.0, 0.0, 0.0), fill: Color | None = None, line_width: float = 1.0, dash: Sequence[float] | None = None) -> PdfPageStroke and/or fill a rectangle whose lower-left corner is (x, y).
draw_table(rows: Sequence[Sequence[str]], x: float, top: float, *, column_widths: Sequence[float], font: FontChoice, header_font: FontChoice | None = None, size: float = 10.0, color: Color = (0.0, 0.0, 0.0), header_rows: int = 1, header_fill: Color | None = (0.9, 0.9, 0.9), rule_color: Color = (0.0, 0.0, 0.0), rule_width: float = 0.5, padding: float = 4.0, line_height: float = 1.2, bottom: float = 36.0, top_margin: float = 36.0) -> LayoutDraw a ruled table; the first header_rows rows repeat on every page.
draw_text(text: str, x: float, top: float, *, font: FontChoice, size: float = 12.0, color: Color = (0.0, 0.0, 0.0), width: float | None = None, align: str | None = None, line_height: float = 1.2, bottom: float = 36.0, top_margin: float = 36.0) -> LayoutDraw text whose first line's top edge is at top.
spans() -> list[dict[str, Any]]Positioned text spans as the extract action reports them.
tables() -> list[dict[str, Any]]Detected tables as the extract action reports them.
text() -> strThe page text as the extract action reads it.
Properties: document, height, index, rotation, width, x0, y0.
Actions
58 actions are registered. Run wolfpdf --capabilitiesto print the list with each action's request flags. Actions that write require output. Every action in this list has test coverage in the package test suite.
Read-only actions (12)
These actions read sources and return a result. They write nothing and take no output path.
inspect, extract, forms.inspect, conformance.inspect, accessibility.inspect, annotations.inspect, compare, fonts.inspect, pages.blank_detect, redact.preview, search, security.validate
Pages and composition (7)
Reorder, split, resize, tile and impose pages, and rebuild a document from selected pages of several sources.
compose, pages.blank_remove, pages.geometry, pages.impose, pages.margins, pages.split, pages.tile
AcroForm fields (6)
Fill, create, author, import, export and remove form fields.
forms.fill, forms.author, forms.create, forms.export, forms.import, forms.remove
Page content (9)
Stamps and watermarks, text replacement, redaction, images, font replacement and redlined comparison output.
redact, compare.redline, fonts.replace, images.add, images.remove, images.replace, images.to_pdf, stamps.apply, text.replace
Annotations and document structure (12)
Annotations, attachments, optional content layers, links, outlines and metadata.
annotations.add, annotations.delete, annotations.export, annotations.flatten, annotations.import, attachments.add, attachments.extract, attachments.remove, layers.set, links.edit, metadata.set, outline.set
Encryption and signing (3)
AES-256 encryption and decryption under named credentials, and CMS signing.
security.encrypt, security.sign, security.decrypt
Conformance, conversion and output (9)
PDF/A conversion, PDF/UA tagging, PDF/X-1a, version rewrites, grayscale, linearization, optimization and rasterization.
conformance.convert, accessibility.tag, optimize, pdfa.remove, rasterize, transform.grayscale, transform.linearize, transform.pdfx1a, transform.version
Refusal codes
A refusal always carries one of these 111 codes in error.code and a detail string. Python raises wolfpdf.Refusal with the same code. Branch on the code and keep the detail text for logs.
request_empty, request_not_utf8, request_malformed_json, request_duplicate_member, request_not_object, request_unknown_member, request_missing_member, request_invalid_type, request_invalid_value, request_schema_version_unsupported, request_action_unknown, request_options_invalid, backend_unavailable, backend_version_unsupported, source_id_duplicate, source_id_unknown, source_missing, source_not_regular_file, source_unreadable, source_digest_mismatch, source_digest_changed, source_structure_uncertain, source_page_count_unknown, page_out_of_range, rotation_invalid, profile_invalid, stamp_text_unencodable, stamp_text_invalid, encrypted_source_unsupported, signed_source_unsupported, tagged_source_unsupported, tagged_preservation_unverified, redaction_region_invalid, redaction_region_empty, redaction_marker_absent, redaction_unsupported_content, redaction_residual_detected, form_field_unknown, form_value_type_invalid, form_field_name_invalid, form_field_invalid, form_field_duplicate, form_type_unsupported, form_font_unsupported, form_value_invalid, form_rectangle_invalid, form_appearance_unsupported, form_appearance_overflow, form_xfa_unsupported, flatten_appearance_missing, flatten_value_not_rendered, output_missing, output_not_a_path, output_parent_missing, output_exists, output_path_is_source, staging_unavailable, staging_write_failed, publication_failed, verification_failed, secret_env_missing, secret_value_empty, secret_file_missing, secret_file_unreadable, secret_material_unusable, secret_scope_conflict, native_conversion_failed, native_operation_failed, internal_error, image_format_unsupported, image_corrupt, template_token_unknown, template_value_missing, page_box_invalid, password_required, password_incorrect, encryption_method_unsupported, source_not_encrypted, timestamp_imprint_mismatch, timestamp_token_invalid, trust_anchor_unreadable, source_not_signed, annotation_invalid, annotation_unknown, data_source_invalid, barcode_data_invalid, search_pattern_invalid, form_field_exists, form_field_invalid, glyph_unavailable, incremental_update_unavailable, image_pixel_limit, image_encoding_unsupported, attachment_exists, attachment_unknown, layer_unknown, layer_exists, all_pages_blank, text_match_crosses_lines, font_embedded_replace_unsupported, version_downlevel_unsupported, icc_profile_invalid, color_space_unsupported, source_not_pdfa, profile_feature_conflict, signature_field_unknown, signature_field_not_empty, revocation_data_missing, timestamp_token_missing, image_unknown, docmdp_forbids_edit
Boundaries
- Sources that carry tagged accessibility structure, or whose tagging cannot be read conclusively, are refused with
tagged_source_unsupportedbycompose,forms.fill,redact,security.encryptandconformance.convert. WolfPDF provides no general tagged-structure editing. - The PDF/A and PDF/UA-1 preflights come from the pinned backend. They are native evidence, not independent standards certification, and WolfPDF makes no PDF/UA-2 or WCAG statement and never states that a document is accessible.
- HEIC images are refused by
images.to_pdf. PNG, JPEG, TIFF, GIF, BMP, TGA, WebP and SVG sources are accepted. - Several actions, including
optimize,images.addandtransform.grayscale, refuse signed sources withsigned_source_unsupportedunless the request setsaccept_signature_invalidationto true. The result then names the invalidated signatures. - Publication never replaces an existing file. A second save to the same path is refused with
output_exists.