The only pure-Python Aztec barcode library with GS1 2027-compliant encoding, batch processing, a CLI, SVG/PDF/PNG output, and Rune mode — zero mandatory dependencies.
pip install aztec-pyfrom aztec_py import AztecCode
AztecCode("Hello World").save("hello.svg") # done.Every other pure-Python Aztec generator is either abandoned, broken, or missing features developers actually need.
| Problem | What aztec-py does |
|---|---|
| CRLF inputs crash upstream | Fixed (upstream issue #5, open 14 months) |
| EC capacity off by 3 codewords | Fixed (upstream issue #7, open 3 months) |
| No SVG output | Built-in, zero extra deps |
| No CLI for automation | aztec "payload" --format svg > code.svg |
| No batch encoding | encode_batch([...], workers=4) |
| No GS1 / supply-chain helpers | build_gs1_payload([GS1Element(...)]) |
| No GS1 FLG(0) Reader Initialisation (ISO 24778 §7) | AztecCode(payload, gs1=True) — industrial scanners route to GS1 AI parsing |
| No Aztec Rune (0–255) | AztecRune(42).save("rune.png") |
| No type hints / mypy support | Full mypy --strict coverage |
Core (zero deps):
pip install aztec-pyWith extras:
pip install "aztec-py[image]" # PNG output via Pillow
pip install "aztec-py[svg]" # lxml-backed SVG (optional; built-in SVG works without it)
pip install "aztec-py[pdf]" # PDF output via fpdf2
pip install "aztec-py[decode-fast]" # Round-trip decode via zxingcpp — no Java needed
pip install "aztec-py[decode]" # Round-trip decode via python-zxing (requires Java)from aztec_py import AztecCode
code = AztecCode("Hello World")
code.save("hello.png", module_size=4) # PNG
code.save("hello.svg") # SVG
print(code.svg()) # SVG stringfrom aztec_py import AztecCode
# Boarding pass (IATA BCBP format)
code = AztecCode.from_preset("M1SMITH/JOHN EABCDEF LHRLAXBA 0172 226Y014C0057 100", "boarding_pass")
code.save("boarding.png")
# GS1 shipping label
code = AztecCode.from_preset(payload, "gs1_label")
code.save("label.png")Available presets: boarding_pass, transit_ticket, event_entry, gs1_label
from aztec_py import encode_batch
svgs = encode_batch(
["TICKET-001", "TICKET-002", "TICKET-003"],
output="svg",
workers=4,
preset="event_entry",
)
# Returns list of SVG strings in input order — safe for parallel workersThese are the workloads aztec-py was built for. Copy the pattern, swap the data.
Airlines and ground handlers generate tens of thousands of boarding pass barcodes per day at check-in kiosks, web check-in, and lounge printers. The boarding_pass preset matches IATA BCBP error correction and module density requirements out of the box.
from aztec_py import AztecCode, encode_batch
# Single pass — kiosk / on-demand
bcbp = "M1SMITH/JOHN EABCDEF LHRLAXBA 0172 226Y014C0057 100"
AztecCode.from_preset(bcbp, "boarding_pass").save("pass.svg")
# Batch — pre-generate a full flight manifest (300 passengers)
manifest = load_bcbp_strings_from_db() # your data source
svgs = encode_batch(manifest, output="svg", workers=8, preset="boarding_pass")
# Embed in PDF tickets
from aztec_py import AztecCode
for bcbp, pdf_path in zip(manifest, pdf_paths):
AztecCode.from_preset(bcbp, "boarding_pass").pdf(module_size=3)Throughput target: 5,000+ codes/min on 4 workers (benchmark with --benchmark-workers 4).
GS1 mandates 2D barcode adoption on all retail consumer products globally by 2027
(GS1 General Specifications §5.5.3). For Aztec Code, a compliant symbol must begin
with the FLG(0) Reader Initialisation character (ISO 24778 §7). This signals
industrial scanners (Zebra, Honeywell, DataLogic) to prefix decoded output with ]z3
and route GS1 Application Identifiers to WMS/ERP systems. Without gs1=True, scanners
treat the barcode as plain text and backends cannot identify the GS1 AIs.
aztec-py is the only pure-Python Aztec library that emits FLG(0). See CONFORMANCE.md.
from aztec_py import AztecCode, GS1Element, build_gs1_payload
# GS1 2027-compliant label: GTIN + expiry + lot + ship-to GLN
payload = build_gs1_payload([
GS1Element("01", "03453120000011"), # GTIN-14
GS1Element("17", "260930"), # Expiry YYMMDD
GS1Element("10", "BATCH-2026-04", variable_length=True), # Lot (variable-length)
GS1Element("410", "9501101020917"), # Ship-To GLN
])
# gs1=True emits FLG(0) — required for industrial scanner GS1 routing
AztecCode(payload, gs1=True, ec_percent=23).save("label.png", module_size=4)
# With preset (recommended for production)
AztecCode.from_preset(payload, "gs1_label", gs1=True).save("label.svg")
# High-volume: encode a full dispatch batch from a CSV
import csv
from aztec_py import encode_batch
with open("dispatch.csv") as f:
rows = list(csv.DictReader(f))
payloads = [
build_gs1_payload([
GS1Element("01", row["gtin"]),
GS1Element("21", row["serial"], variable_length=True),
])
for row in rows
]
results = encode_batch(payloads, output="png_bytes", workers=4, preset="gs1_label")Or from the CLI — pipe a JSONL export directly to PNG files:
aztec --input dispatch.jsonl --input-format jsonl \
--preset gs1_label --format png \
--out-dir ./labels --workers 8Generate unique barcodes per ticket at purchase time, or pre-generate the entire run for a sold-out show. The event_entry preset targets scanner read rates in variable-light environments.
from aztec_py import encode_batch
ticket_ids = [f"EVT-2026-{i:06d}" for i in range(10_000)]
# Returns SVG strings in order — ready to inject into HTML/PDF templates
svgs = encode_batch(ticket_ids, output="svg", workers=4, preset="event_entry")
# Embed in HTML email template
for ticket_id, svg in zip(ticket_ids, svgs):
html = TICKET_TEMPLATE.replace("{{BARCODE}}", svg).replace("{{ID}}", ticket_id)
send_email(html)Benchmark your hardware before sizing infrastructure:
aztec --benchmark "EVT-2026-000001" --preset event_entry \
--format svg --benchmark-count 10000 --benchmark-workers 4
# prints: throughput, p50/p95/p99 latency, codes/secAztec Code is mandated for patient wristbands in several national health systems (ISO/IEC 24778). For drug serialization, it encodes batch, serial, expiry, and GTIN in a single scannable symbol.
from aztec_py import AztecCode, GS1Element, build_gs1_payload
# Drug label: GTIN + expiry + batch (GS1 Pharma)
payload = build_gs1_payload([
GS1Element("01", "00889714000057"), # GTIN
GS1Element("17", "270131"), # Expiry
GS1Element("10", "PB2026Q1", variable_length=True), # Batch
GS1Element("21", "SN-00043871", variable_length=True), # Serial
])
AztecCode(payload, ec_percent=40).save("drug_label.svg")
# Patient wristband — binary payload with non-ASCII chars handled correctly
AztecCode(b"\x02MRN:4471823\x03", ec_percent=40).save("wristband.svg")Shelf-edge labels are reprinted nightly across thousands of SKUs. The CLI bulk mode turns a product CSV into a directory of print-ready PNGs in one command — no Python code needed.
# products.csv: sku,price,description,...
aztec --input products.csv --input-format csv \
--format png --module-size 3 \
--out-dir ./shelf_labels --workers 8 \
--name-template "label_{index}.png"Or in Python when you need to merge barcodes into existing label artwork:
from aztec_py import encode_batch
from PIL import Image
import io
skus = load_skus_from_erp() # list of strings
png_bytes_list = encode_batch(skus, output="png_bytes", workers=4)
for sku, png_bytes in zip(skus, png_bytes_list):
barcode_img = Image.open(io.BytesIO(png_bytes))
label = render_label_template(sku, barcode_img)
label.save(f"labels/{sku}.png")Serve barcode images over HTTP with zero subprocess overhead:
# Django view
from django.http import HttpResponse
from aztec_py import AztecCode
def barcode_svg(request, payload: str) -> HttpResponse:
svg = AztecCode(payload, ec_percent=33).svg(module_size=2)
return HttpResponse(svg, content_type="image/svg+xml")
# FastAPI endpoint
from fastapi import FastAPI
from fastapi.responses import Response
from aztec_py import AztecCode
app = FastAPI()
@app.get("/barcode/{payload}")
def barcode(payload: str) -> Response:
svg = AztecCode(payload).svg()
return Response(content=svg, media_type="image/svg+xml")No subprocesses, no Ghostscript, no Java — pure Python, works in any WSGI/ASGI container.
No Python needed after install. Drop it into shell scripts and CI pipelines:
# Single code
aztec "Hello World" --format terminal
aztec "Hello World" --format svg > code.svg
aztec "Hello World" --format png --module-size 4 --output code.png
# With preset
aztec --preset boarding_pass "M1SMITH/JOHN..." --format png --output pass.png
# Bulk encode from file (one payload per line)
aztec --input tickets.txt --format png --out-dir ./out --workers 4 --preset event_entry
# Benchmark your hardware
aztec --benchmark "M1SMITH/JOHN..." --format svg --benchmark-count 5000 --benchmark-workers 4All CLI flags:
| Flag | Default | Description |
|---|---|---|
--format/-f |
terminal |
svg, png, terminal |
--module-size/-m |
1 (or preset) |
Pixels per module |
--ec |
23 (or preset) |
Error correction percent (5–95) |
--charset |
UTF-8 (or preset) |
Character encoding / ECI hint |
--output/-o |
stdout | Output file path (required for PNG) |
--preset |
— | boarding_pass, transit_ticket, event_entry, gs1_label |
--input |
— | Bulk source file |
--input-format |
txt |
txt, csv, jsonl |
--workers |
1 |
Process workers for bulk mode |
--out-dir |
— | Output directory for bulk mode |
--name-template |
— | Filename template with {index} |
--benchmark |
— | Run throughput benchmark |
--benchmark-count |
1000 |
Encode count for benchmark |
--benchmark-workers |
1 |
Workers for benchmark |
AztecCode(
data: str | bytes,
ec_percent: int = 23, # error correction % (5–95)
encoding: str | None = None, # raw mode override
charset: str | None = None, # ECI charset hint
size: int | None = None, # force matrix size
compact: bool | None = None, # force compact/full flag
gs1: bool = False, # emit FLG(0) for GS1 2027 compliance (ISO 24778 §7)
)Methods:
| Method | Returns | Notes |
|---|---|---|
AztecCode.from_preset(data, preset, **overrides) |
AztecCode |
Apply a named preset profile |
.image(module_size=2, border=0) |
PIL.Image.Image |
Requires [image] extra |
.svg(module_size=1, border=1) |
str |
Built-in, no extra deps |
.pdf(module_size=3, border=1) |
bytes |
Requires [pdf] extra |
.save(path, module_size=2, border=0, format=None) |
None |
Auto-detects .png/.svg/.pdf |
.print_out(border=0) |
None |
ASCII terminal output |
.print_fancy(border=0) |
None |
Unicode block terminal output |
from aztec_py import encode_batch, list_presets, get_preset
results = encode_batch(
payloads=["A", "B", "C"],
output="svg", # "matrix" | "svg" | "png_bytes"
workers=4, # multiprocessing workers
preset="boarding_pass",
ec_percent=33, # override preset defaults
)Compact 11×11 barcode encoding a single integer 0–255. Used in mobile boarding passes. Implements ISO 24778 Annex A. No other pure-Python library has this.
from aztec_py import AztecRune
rune = AztecRune(42)
rune.save("rune.svg")
rune.save("rune.png", module_size=8)from aztec_py import AztecCode, GS1Element, build_gs1_payload
payload = build_gs1_payload([
GS1Element("01", "03453120000011"), # GTIN-14
GS1Element("17", "260430"), # Expiry date
GS1Element("10", "ABCD1234", variable_length=True), # Batch/Lot
GS1Element("410", "9501101020917"), # Ship-To
])
AztecCode(payload, ec_percent=33).save("label.png", module_size=4)Common GS1 recipes:
# GTIN + Expiry
build_gs1_payload([GS1Element("01","03453120000011"), GS1Element("17","260430")])
# GTIN + Serial
build_gs1_payload([GS1Element("01","03453120000011"), GS1Element("21","XYZ001",variable_length=True)])
# GTIN + Weight (kg)
build_gs1_payload([GS1Element("01","03453120000011"), GS1Element("3103","001250")])Build a standards-compliant 60-character IATA Resolution 792 Format Code F string and encode it directly into an Aztec symbol:
import datetime
from aztec_py import BCBPSegment, build_bcbp_string, AztecCode
seg = BCBPSegment(
passenger_name="SMITH/JOHN",
pnr_code="ABC123",
from_airport="LHR",
to_airport="JFK",
carrier="BA",
flight_number="0172",
date_of_flight=datetime.date(2026, 6, 15), # auto-converts to Julian day
compartment_code="Y",
seat_number="023A",
sequence_number=42,
passenger_status="0",
electronic_ticket=True,
)
bcbp = build_bcbp_string(seg) # always exactly 60 characters
AztecCode.from_preset(bcbp, "boarding_pass").save("boarding.svg")BCBPSegment validates all fields (IATA airport codes, sequence range, passenger status digit). A name longer than 20 characters is truncated with a UserWarning. Pass the result directly to from_preset(..., "boarding_pass") — no manual padding or field formatting required.
from aztec_py import AztecCode, decode
# Java-free (recommended):
# pip install "aztec-py[decode-fast]"
code = AztecCode("test payload")
assert decode(code.image()) == "test payload"Run the built-in compatibility matrix against real payloads:
# Generate a markdown report
python scripts/decoder_matrix.py --report compat_matrix_report.md
# Strict mode (fail if decode checks can't run)
python scripts/decoder_matrix.py --strict-decodeThe script is skip-safe when ZXing/Java are unavailable — safe for CI environments without Java.
| aztec-py | dlenski fork | delimitry original | treepoem | Aspose | |
|---|---|---|---|---|---|
| Pure Python encode | ✅ | ✅ | ✅ | ❌ (BWIPP) | ❌ (proprietary) |
| SVG output | ✅ | PR pending | ❌ | Via backend | ✅ |
| PDF output | ✅ | ❌ | ❌ | Via backend | ✅ |
| CLI tool | ✅ | ❌ | ❌ | ❌ | N/A |
| Batch encoding API | ✅ | ❌ | ❌ | ❌ | N/A |
| Aztec Rune | ✅ | ❌ | ❌ | Backend-dependent | ✅ |
| GS1 helpers | ✅ | ❌ | ❌ | ❌ | ❌ |
| GS1 FLG(0) / 2027 compliant | ✅ | ❌ | ❌ | ❌ | ✅ |
| Preset profiles | ✅ | ❌ | ❌ | ❌ | ❌ |
| CRLF bug fixed | ✅ | ❌ (open) | ❌ (open) | N/A | N/A |
| EC capacity bug fixed | ✅ | ❌ (open) | ❌ (open) | N/A | N/A |
| Full type hints | ✅ | ❌ | ❌ | Partial | N/A |
| mypy strict clean | ✅ | ❌ | ❌ | N/A | N/A |
| Zero mandatory deps | ✅ | ✅ | ✅ | ❌ | ❌ |
| Active maintenance | ✅ | ❌ | ❌ | ✅ | ✅ |
See CHANGELOG.md for the full history.
Latest: [1.2.0] — GS1 2027 FLG(0) compliance, IATA BCBP boarding pass builder, Java-free decode via zxingcpp, ECI roundtrip tests.
[1.1.0] — Batch encoding API, preset profiles, CLI bulk/benchmark mode, GS1 helpers, AztecRune improvements.
[1.0.0] — Production-grade fork: CRLF fix, EC capacity fix, SVG output, PDF output, AztecRune, CLI, type hints, Hypothesis property tests.
- Forked from
dlenski/aztec_code_generator, itself forked fromdelimitry/aztec_code_generator. - SVG renderer based on upstream PR #6 by Zazzik1.
- License chain: MIT — see LICENSE and LICENSE.upstream.
- Both upstream bugs reported back to maintainers with links to the fixes.
See CONTRIBUTING.md for guidelines, SECURITY.md for vulnerability reporting.
pip install -e ".[dev]"
pytest # run tests + coverage
ruff check aztec_py/ # lint
mypy aztec_py/ # type check
python -m build # build wheel + sdist