WolfDocx - DOCX to PDF
Convert DOCX to PDF in Python without Word or LibreOffice
Render a .docx to PDF with one command and keep a receipt for every file. The wolf command lays out the document with its own engine, so a server needs no Microsoft Word and no LibreOffice install, and a render report names each omission and font substitution it finds.
Render one document
wolf render board_memo.docx --to pdf --outdir out
# out/board_memo.pdf and out/render-report.json are written;
# the receipt (JSON) goes to standard output, or to --receipt <path>Useful options: --pages 1-3 renders a page range, --stem name sets the output file name, --font-dir <dir> (repeatable) adds font folders, --system-fonts turns on installed fonts, and --timeout-ms and --max-pages cap the work for untrusted files.
The exit code tells a script what happened. 0 means every output was written with no error-level loss. 1 means the PDF was written and the render report holds at least one error-level omission or approximation. 3 means the input is missing or is not a valid document, and no PDF is written.
Call it from Python
import json
import subprocess
from pathlib import Path
def render_to_pdf(source: str, outdir: str) -> tuple[Path, str, dict]:
outdir = Path(outdir).resolve()
run = subprocess.run(
["wolf", "render", source, "--to", "pdf", "--outdir", str(outdir)],
capture_output=True,
text=True,
)
receipt = json.loads(run.stdout)
if receipt["status"] == "failed":
raise RuntimeError(receipt["errors"])
report = json.loads(Path(receipt["render_report"]["path"]).read_text())
return Path(receipt["outputs"][0]["path"]), receipt["status"], report["summary"]
pdf, status, summary = render_to_pdf("board_memo.docx", "pdf-out")
print(pdf.name, status, summary["pages"], summary["omissions"]["total"])
# board_memo.pdf succeeded 1 1The receipt carries the input and output digests, the status, and the path of the render report. The render report lists each page's size, and each omission, approximation, and font substitution with a location, a reason, and a severity. Review that list before you send a PDF to a customer.
To edit first and render second, change the document with WolfDocx text replacement or table cell edits, then pass the saved file to render_to_pdf.
Fidelity limits
- The layout is the wolf engine's own. Pagination and line breaks can differ from Microsoft Word's, and the PDF carries no promise of matching Word page for page.
- The command uses a bundled set of metric-compatible fonts plus any
--font-dirfolders, and leaves installed fonts off unless you pass--system-fonts. A font outside those sources is substituted and named in the render report. Supply the document's own font files for the closest match. - Content the engine cannot draw is named as an omission with a location. An error-level omission makes the run
partial, exit code1, so a script can refuse the PDF. - On a frozen corpus of 42 DOCX test files, Wolf's page count matched Microsoft Office's on 37, and 12 of the files ended
partialwith an error-level omission. The corpus is a regression benchmark and no blind test has been run, so test your own documents. The rendering results describe the method.
Where WolfDocx fits
This page covers the wolf command. The WolfDocx Python API on this site covers editing, as in the python-docx comparison. The WolfDocx home page lists every task.
Get the wolf command
The wolf command ships with Wolf Runtime, which is licensed under a written Order. There is no pip package for it. The Wolf Runtime page lists the integration steps: select the build, install the shims or call the command directly, and run your own documents while you check the receipts, omissions, and refusals.