Skip to main content

pd4ml — PD4ML for Python

HTML and CSS to PDF, PDF/A, PDF/UA, ZUGFeRD/Factur-X, DOCX, RTF and page images, with the full public PD4ML API (Java package com.pd4ml) available to Python applications.

PD4ML runs in a Java virtual machine inside the Python process, through JPype. Every public Java class, method, constant and enum can be used directly, and the module adds Python I/O (paths, bytes, file objects) on top. New converters use PD4ML's v42 layout engine by default.

pip install pd4ml
import pd4ml

pd = pd4ml.PD4ML()                                   # v42 layout engine
pd.setPageSize(pd4ml.PageSize.A4)
pd.setPageMargins(pd4ml.PageMargins(15, 15, 15, 15, pd4ml.Units.MM))
pd.readHTML("invoice.html")                          # a path, URL, bytes or file object
pd.writePDF("invoice.pdf", pd4ml.PdfSpec.PDFA_3B)    # a path or file object; no target: bytes

The first use downloads the most recent PD4ML build (see Installation below).

Requirements

  • Python 3.9 or later.
  • Java 8 or later: a JDK or JRE of the same bitness as Python; 17 or 21 is recommended. JPype finds it through JAVA_HOME or the system's default Java. To use a specific JVM library (jvm.dll, libjvm.so), set PD4ML_JVM.

Installation

pip install pd4ml

The package contains the Python module only. The PD4ML library itself comes from the PD4ML Maven repository (https://pd4ml.com/maven2):

  • Automatically. If no PD4ML build is installed or configured, the first use of PD4ML downloads the most recent 4.x release. Bouncy Castle (for encryption and signing) comes along from Maven Central. Every file is verified against its published SHA-1 checksum and stored in a per-user cache directory: %LOCALAPPDATA%\pd4ml-python on Windows, ~/.cache/pd4ml-python elsewhere, or PD4ML_HOME. A notice on standard error reports the download.

  • Ahead of time, which is better for servers and container images:

    pd4ml install                       # the most recent 4.x release (also: python -m pd4ml install)
    

    Run it again to update to a newer release. From Python, call pd4ml.install().

PD4ML_AUTO_INSTALL=0 turns the automatic download off; the module then raises pd4ml.ClasspathError until a build is installed or configured.

Command
pd4ml install download the most recent 4.x release, or update to it
pd4ml install --version 4.0.23fx2 an exact version
pd4ml install --snapshots the most recent build, including development snapshots
pd4ml install --jakarta the -jakarta variant
pd4ml install --list the available versions
pd4ml install --home DIR install into DIR (then set PD4ML_HOME=DIR for the application)
pd4ml version the PD4ML version the module runs

Other PD4ML builds. The module takes PD4ML from the first of these that is set:

  1. pd4ml.start(classpath=[...]), called before first use;
  2. PD4ML_CLASSPATH: jars and class directories separated by ; on Windows and : elsewhere, with dir/* for all jars of a directory;
  3. PD4ML_CLASSPATH_FILE: a file with one entry per line;
  4. the build pd4ml install downloaded.

The v42 layout engine is new in PD4ML 4.2. With an earlier build, such as the published 4.1.x, a converter uses that build's own engine and warns once. pd4ml.has_layout_engines() tells which kind of build is loaded.

API

The Java API

All public classes of com.pd4ml are module attributes under their own names:

Python Java
pd4ml.PD4ML com.pd4ml.PD4ML (wrapped, see below; the bare class is pd4ml.JPD4ML)
pd4ml.Constants, pd4ml.LayoutEngine, pd4ml.ErrorPolicy Constants, Constants.LayoutEngine, Constants.ErrorPolicy
pd4ml.PageSize, pd4ml.PageMargins, pd4ml.Dimensions, pd4ml.Units PageSize, PageMargins, Dimensions, Dimensions.Units
pd4ml.PdfSpec, pd4ml.PdfDocument PdfSpec, PdfDocument
pd4ml.ProgressListener, pd4ml.ResourceProvider, pd4ml.CustomTag, pd4ml.StatusMessage the same
pd4ml.InvokeException, pd4ml.InvalidPasswordException, pd4ml.ForcedTerminationException, pd4ml.ConflictingParametersException, pd4ml.PdfSpecViolationException, pd4ml.PdfSpecUnsupportedException the exceptions

Methods keep their Java names and take Python values:

  • str, int, float, bool;
  • bytes for byte[];
  • None for null.

Java null results become None, and Java strings become str. Any other Java class is available through pd4ml.java_class("java.util.HashMap").

Java exceptions are Python exceptions; catch them by their class:

try:
    pd.readHTML("missing.html")
    pd.writePDF("out.pdf")
except pd4ml.java_class("java.io.IOException") as e:
    print("conversion failed:", e)

pd4ml.PD4ML

PD4ML(*args, engine="v42", html_width=None) creates a com.pd4ml.PD4ML, passing *args to the Java constructor: nothing, a license key, or an HTML width. It then selects the layout engine: engine="v40" for the previous engine, engine=None for the Java default.

Every Java method is available. These also accept Python I/O:

Method Python additions
readHTML(source, base_url=None, encoding=None) source: a path, pathlib.Path, URL string, bytes, binary file object, or Java InputStream/URL
read_html_string(html, base_url=None) the document as a string
writePDF(target=None, spec=None) target: a path, file object or Java OutputStream; with no target, returns bytes. spec: a PdfSpec, a keyword ("pdf/a-2b", see PdfSpec.listKeywords()) or "PDF"/"PDFA"/"PDFUA"
writeDOCX(target=None), writeRTF(target=None, rtf_wmf=False) as writePDF
renderAsImages(format) the pages as a list of bytes; render_png() for PNG
monitorProgressWith(listener) a Python callable (message_id, progress, note, msec)
useHtmlRenderer(engine, html_width=None, params=None) the engine as "v42"/"v40"; params as a dict

Static members of the Java class are on the wrapper class: pd4ml.PD4ML.getVersion(), pd4ml.PD4ML.disableBatik = True. The underlying Java object is pd.java.

Helpers

  • pd4ml.html_to_pdf(source, target=None, *, base_url, page_size, page_margins, spec, engine, html_width)
  • pd4ml.open_pdf(source, password=None): a PdfDocument from a path, URL, bytes or file
  • pd4ml.write_pdf(document, target=None, password=None, permissions=None)
  • pd4ml.merge_pdfs(first, second, target=None)
  • pd4ml.pdf_spec(keyword): the PdfSpec for a keyword; unknown keywords raise ValueError
  • pd4ml.progress_listener(callback): a Java ProgressListener for a callable
  • pd4ml.version(), pd4ml.start(...), pd4ml.is_started(), pd4ml.java_class(name)
  • pd4ml.install(...), pd4ml.installed_jar(), pd4ml.installed_version(), pd4ml.has_layout_engines()

The JVM starts on the first use of PD4ML. To pass JVM options, call pd4ml.start(jvm_options=["-Xmx2g"]) first, or set PD4ML_JVM_OPTIONS. The JVM runs headless, and it can be started only once per process.

Limitations

  • ResourceProvider and CustomTag are Java classes to extend. Write those in Java and register them by class name (addCustomResourceProvider) or instance. Python can implement only Java interfaces, such as ProgressListener.
  • As of PD4ML 4.2.0, the v42 layout engine reports only DOC_WRITE_BEGIN to a ProgressListener; v40 reports every stage.

Documentation

Development

These steps need the source distribution or repository.

pip install -e ".[test]"
python tools/pd4ml_classpath.py <pd4ml maven project>    # writes ./pd4ml.classpath (optional)
python -m pytest
  • Tests use ./pd4ml.classpath (or PD4ML_CLASSPATH/PD4ML_CLASSPATH_FILE), and are skipped without one. PD4ML_NETWORK_TESTS=1 adds tests that download the latest release, both explicitly and through the automatic first-use download.
  • Type stubs: src/pd4ml/__init__.pyi declares the whole API for IDEs and type checkers. After an API change, regenerate it with python tools/gen_stubs.py.
  • Releases to PyPI are described in docs/releasing.md.
  • Example: python examples/convert.py page.html page.pdf --pdfa pdf/a-2b.

Metadata

Release files for pd4ml 1.0.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for pd4ml 1.0.0
File Size Uploaded Provenance
pd4ml-1.0.0.tar.gz 47.7 kB Without provenance Details

Built distribution (wheel)

Table of built distributions (wheels) for pd4ml 1.0.0
File Interpreter ABI Platform
pd4ml-1.0.0-py3-none-any.whl Python 3 none any Details

Total release size: 72.4 kB

Release files / pd4ml-1.0.0.tar.gz

Download URL pd4ml-1.0.0.tar.gz
Size 47.7 kB
Tags Source
SHA-256 checksum
How to use checksums
5a5319615b5001bfcf4e47c84dbf8095d5ef1bcb3167d9ec0187e7a3b2d70e20
BLAKE2b-256 checksum
How to use checksums
f685d81de74b1afc139e3a23f1a89f08d2521fb2b2671e7d3eda69c10eb504ee
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.14.0

Release files / pd4ml-1.0.0-py3-none-any.whl

Download URL pd4ml-1.0.0-py3-none-any.whl
Size 24.7 kB
Tags Python 3
SHA-256 checksum
How to use checksums
db92c1b288a23f9b1f4c962392ebff13cd4ed8faa5304a68f1ee734d5ee39078
BLAKE2b-256 checksum
How to use checksums
6a1392db35ce21e1710ed3c5b6819e5e7b3e44179fbb86d16884e603f51773f5
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.14.0

Security for pd4ml 1.0.0

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

Release history Release notifications | RSS feed

This release

1.0.0 This release

2 release files No provenance

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page