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_HOMEor the system's default Java. To use a specific JVM library (jvm.dll,libjvm.so), setPD4ML_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-pythonon Windows,~/.cache/pd4ml-pythonelsewhere, orPD4ML_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:
pd4ml.start(classpath=[...]), called before first use;PD4ML_CLASSPATH: jars and class directories separated by;on Windows and:elsewhere, withdir/*for all jars of a directory;PD4ML_CLASSPATH_FILE: a file with one entry per line;- the build
pd4ml installdownloaded.
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;bytesforbyte[];Nonefornull.
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): aPdfDocumentfrom a path, URL, bytes or filepd4ml.write_pdf(document, target=None, password=None, permissions=None)pd4ml.merge_pdfs(first, second, target=None)pd4ml.pdf_spec(keyword): thePdfSpecfor a keyword; unknown keywords raiseValueErrorpd4ml.progress_listener(callback): a JavaProgressListenerfor a callablepd4ml.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
ResourceProviderandCustomTagare Java classes to extend. Write those in Java and register them by class name (addCustomResourceProvider) or instance. Python can implement only Java interfaces, such asProgressListener.- As of PD4ML 4.2.0, the v42 layout engine reports only
DOC_WRITE_BEGINto aProgressListener; v40 reports every stage.
Documentation
- User guide:
docs/guide.mdin the source distribution (pip download --no-binary :all: pd4ml). It covers prerequisites, installation, use cases with code, tips and troubleshooting. - The PD4ML manual: https://pd4ml.com/docs/.
- Usage examples: https://pd4ml.com/docs/usage-examples.html.
- Licensing: without a license, PD4ML runs in evaluation mode and watermarks its output. See https://pd4ml.com/docs/#3.-licensing.
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(orPD4ML_CLASSPATH/PD4ML_CLASSPATH_FILE), and are skipped without one.PD4ML_NETWORK_TESTS=1adds tests that download the latest release, both explicitly and through the automatic first-use download. - Type stubs:
src/pd4ml/__init__.pyideclares the whole API for IDEs and type checkers. After an API change, regenerate it withpython 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)
| File | Size | Uploaded | Provenance | |
|---|---|---|---|---|
| pd4ml-1.0.0.tar.gz | 47.7 kB | Without provenance | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| 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.