datamax-dpl
datamax-dpl turns a label described in JSON into a print job in DPL, the Datamax Programming Language, and sends it to the printer. You describe text, lines, boxes, circles, polygons, barcodes (PDF417, MaxiCode and DataMatrix included) and images in millimetres; datamax-dpl writes the DPL records for the printer model you name.
Datamax models differ a lot. A Prodigy has no circles, an Allegro no metric formats, scalable fonts arrived on the DMX 600 with firmware 2.01. datamax-dpl knows the 18 models of the 1998 DPL manual and checks every label against the one you print on. Where a model lacks a feature it either applies a documented fallback and tells you (a circle becomes a downloaded bitmap) or, if you prefer, refuses the label.
It is a Python library and a command line tool. Every command can answer with a JSON envelope
and meaningful exit codes, so scripts and LLM agents can drive it. With a camera configured,
verify prints a label, photographs it and puts the photo next to the expected preview.
Status: no real printer or camera has been attached yet. Everything is built from the manual and tested against simulated printers. Treat the output as unverified on hardware until this notice goes away.
Install
Python 3.10 or newer. Run it without installing, with uv:
uvx datamax-dpl --help
or install it:
uv tool install datamax-dpl
# or
pip install datamax-dpl
datamax-dpl preview draws text as grey cells by default. With the preview extra (Pillow,
Python 3.11 or newer) it writes the actual text into the picture:
uvx --from "datamax-dpl[preview]" datamax-dpl --help
# or
pip install "datamax-dpl[preview]"
More ways to install are in INSTALL.md.
Quick start
The examples/ folder holds a product label, a shipping label with placeholders, and data
records for it.
Check a label against a model without printing:
datamax-dpl validate examples/product-label.json --model dmx-600
OK: model dmx-600, 1 label(s), 5 field(s), 149 bytes
INFO check_digit_added [element 3]: the printer adds check digit 1
INFO cutter_assumed: cut commands assume the printer's cutter is installed and enabled
Show the DPL it produces (control characters written as <STX>, <CR>):
datamax-dpl render examples/product-label.json --model dmx-600
Write the raw job to a file instead, or draw it as a PNG at the printer's resolution:
datamax-dpl render examples/product-label.json --model dmx-600 -o label.dpl
datamax-dpl preview examples/product-label.json --model dmx-600 -o label.png
Print it. Datamax network cards listen for raw jobs on port 9100:
datamax-dpl print examples/product-label.json --model dmx-600 --printer tcp://192.0.2.60:9100
Fill placeholders from data records, one label per record:
datamax-dpl render examples/shipping-label.json --data examples/shipping-records.json --model pe42
The same from Python:
from datamax_dpl import Barcode, Label, RenderOptions, Symbology, Text, render
label = Label(
width=60,
height=30,
elements=[
Text(x=3, y=20, text="ART. 4711", font=3),
Barcode(x=3, y=3, symbology=Symbology.EAN13, data="400638133393", height=12),
],
)
result = render(label, RenderOptions(model="dmx-600"))
print(result.data) # b'\x02L\rm\r131100002000030ART. 4711\r1F0012000300030400638133393\rQ0001\rE\r'
for finding in result.findings:
print(finding.severity.value, finding.code, finding.message)
render raises LabelRejectedError when a finding is an error; build_job returns the same
result without raising. For data records use render_batch(label, records, options), and to send
bytes open a transport with open_printer(uri, timeout=10) and call send(data) on it. All of
these are imported from datamax_dpl.
The label format
A label is a JSON document with job settings (unit, size, quantity, cutting, speeds, heat, media) and a list of elements:
{
"unit": "mm",
"width": 60,
"height": 30,
"elements": [
{"type": "text", "x": 3, "y": 20, "text": "ART. 4711", "font": 3},
{"type": "barcode", "x": 3, "y": 3, "symbology": "ean13", "data": "400638133393", "height": 12}
]
}
Element types are text, scalable_text, line, box, circle, polygon, barcode and
image. Text and barcode data may contain {name} placeholders. Every field is described in
docs/label-spec.md; datamax-dpl schema prints the JSON Schema.
Coordinates
Coordinates follow DPL, which is not how screens do it:
xis measured from the left edge of the label.yis measured from the BOTTOM edge. A largeryis higher up.- An element is placed by its lower-left corner before rotation; a circle by its centre.
- Lengths are millimetres unless the label sets
"unit": "in"or"unit": "dots".
datamax-dpl preview draws the label with these rules, so you can check the placement before
wasting labels.
Printer models and fallbacks
datamax-dpl models lists the profiles; datamax-dpl capabilities <model> shows one in full.
Besides the 18 models there are generic (the default: only what every model can do) and
permissive (no model checks). Tell datamax-dpl the firmware (--firmware 2.01) and installed
options (--option datamatrix) where a feature depends on them.
The policy decides what happens when a label asks for something the model lacks:
--policy degrade(default) applies the documented fallback and reports a warning.--policy strictturns the same finding into an error, and nothing is printed.
The model matrix, the limits and the full fallback table are in docs/capabilities.md.
Printer URIs
--printer (or [printer] uri in the configuration) says where the job goes:
| URI | Channel |
|---|---|
tcp://HOST:PORT |
Raw socket; the port defaults to 9100. Can read answers. |
serial:///dev/ttyS0?baud=9600&parity=N&bytesize=8&stopbits=1&rtscts=0&xonxoff=0 |
Serial port (serial://COM3?... on Windows); every parameter is optional. Can read answers. |
device:///dev/usb/lp0 |
A device file, write only. |
file:PATH or file:///abs/path |
Appends the job to a file. |
- |
Writes the job to standard output. |
status and detect need a channel that can read the printer's answer (tcp or serial). reset,
cancel, pause, feed and cut send single printer commands.
JSON output for scripts and LLMs
Put --json (indented) or --json-bare (one line) before the command. The answer on stdout is
an envelope:
datamax-dpl --json validate examples/product-label.json --model allegro --policy strict
{
"ok": false,
"command": "validate",
"data": {
"model": "allegro",
"dpl_unit": "inch",
"records": 1,
"fields": 5,
"bytes": 1568,
"downloads": ["DPLR0001"],
"findings": [
{"code": "circle_rasterized", "severity": "error", "message": "circle cannot be drawn natively: circles: Allegro does not have it", "element": 4, "fallback": "drawn as downloaded image DPLR0001 (81x81 dots)", "record": null}
]
},
"skipped": []
}
(Shortened: the real answer also lists the info findings and a few empty fields.) When the
command cannot run at all, the envelope carries error (the exception type) and message
instead of data.
| Exit code | Meaning |
|---|---|
| 0 | Done. |
| 1 | Refused: at least one finding is an error. Nothing was printed or written. |
| 2 | Could not run: unreadable or invalid label, unknown model, bad configuration, printer or camera not reachable. |
Finding codes are stable, so a script can react to circle_rasterized without parsing the
message.
Camera verification
The [camera] configuration section names a camera; see CONFIG.md. Four kinds
exist: none (default), command (run a capture program such as fswebcam), http (fetch a
snapshot URL) and file (take the newest image a camera drops into a folder).
datamax-dpl capture --output-dir photos
datamax-dpl verify examples/product-label.json --model dmx-600 --printer tcp://192.0.2.60:9100 --output-dir photos
verify sends the job, polls the printer status until it is idle (on a one-way channel it waits
five seconds instead), waits settle_seconds, takes one photo, and writes expected-<time>.png
next to it. It does not judge the photo; a person, or an LLM given both pictures, compares them.
This loop has only run against a simulated printer and camera so far.
Claude Code skill
The repository is also a Claude Code plugin with a skill that teaches an agent to use datamax-dpl. Inside Claude Code:
/plugin marketplace add bitranox/datamax-dpl
/plugin install datamax-dpl
Status and limits
- Built from the Datamax DPL Programmer's Manual 88-2051-01 Rev D2 (1998) only. Models and commands that came later, QR codes among them, are not supported.
- Nothing has been checked on a real printer or camera yet.
- The manual contradicts itself in places; docs/dpl-reference.md lists each case and the reading taken.
- The manual is copyrighted and not included or redistributed here. Get it from the vendor.
Configuration
Defaults for the printer (model, firmware, URI, policy, timeout, cutter, options) and the camera
live in a layered configuration: bundled defaults, system, host and user files, .env,
environment variables and --set. CONFIG.md explains every key.
Development
DEVELOPMENT.md covers the make targets and the test setup, CONTRIBUTING.md the workflow, docs/systemdesign/module_reference.md the module layout, and CHANGELOG.md the releases. How AI tools were used is described in ai-transparency.md.
License
MIT, see LICENSE.
Metadata
Release files for datamax-dpl 0.2.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 | |
|---|---|---|---|---|
| datamax_dpl-0.2.0.tar.gz | 301.8 kB | With provenance | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| datamax_dpl-0.2.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 509.0 kB
Release files / datamax_dpl-0.2.0.tar.gz
| Download URL | datamax_dpl-0.2.0.tar.gz |
|---|---|
| Size | 301.8 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
88bfb2d6ea6c9291bcc81946b0fa52deb4161c7ec03773b101593cdc5435911d
|
|
BLAKE2b-256 checksum How to use checksums |
4d343081358ea687573dc56333af1e95b232a715e665a74be99e106c1a7a2b4a
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.16
|
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.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 24, 2026.
Transparency logRelease files / datamax_dpl-0.2.0-py3-none-any.whl
| Download URL | datamax_dpl-0.2.0-py3-none-any.whl |
|---|---|
| Size | 207.2 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
a9a1e5ea0c5e183b5f34eefed6dd60b1f689817074bc083c48d80b2f509aca76
|
|
BLAKE2b-256 checksum How to use checksums |
673981442fb6ad139f067af93bed0f05b0d1571c02bbc33f11a21fed1b6a45fe
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.16
|
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.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 24, 2026.
Transparency logSecurity for datamax-dpl 0.2.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.
Full provenance
All 2 files in this release have been uploaded with provenance metadata.
Attestation for datamax_dpl-0.2.0.tar.gz
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 24, 2026.
Transparency logA 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.
To learn more about protecting yourself from malicious actors, visit our package security documentation.