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-py
from 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-py
With 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 string
from 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 workers
These 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/sec
Aztec 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 4
All 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-decode
The 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