WolfSDKWolfXLWolfPPTWolfDocx

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_exists

A 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 $?
2

In 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) -> PdfPage

Append 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) -> PdfDocument

Rebuild the document from selected pages (compose).

copy_page(index: int, to: int | None = None) -> PdfPage

Insert 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) -> PdfDocument

Fill AcroForm fields by name (forms.fill).

insert_page(index: int, size: str | Sequence[float] = 'A4', *, landscape: bool = False) -> PdfPage

Insert 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) -> None

Move page index so that it becomes page to.

open(source: Source, *, password_env: str | None = None) -> PdfDocument

Open PDF bytes or a PDF file.

redact(regions: Sequence[Mapping[str, Any]], *, required_absent_text: Sequence[str]) -> PdfDocument

Destructively redact rectangles (redact).

remove_page(index: int) -> None

Remove page index from the page tree.

reorder(order: Sequence[int]) -> None

Rearrange 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) -> PdfDocument

Append stamps, watermarks, headers and footers (stamps.apply).

to_bytes() -> bytes

The 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) -> PdfPage

Place 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) -> PdfPage

Stroke 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) -> Layout

Draw 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) -> PdfPage

Stroke 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) -> PdfPage

Stroke 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) -> Layout

Draw 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) -> Layout

Draw 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() -> str

The 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_unsupported by compose, forms.fill, redact, security.encrypt and conformance.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.add and transform.grayscale, refuse signed sources with signed_source_unsupported unless the request sets accept_signature_invalidation to 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.