GS1 2027-compliant pure-Python Aztec Code generator. FLG(0) Reader Initialisation, batch encoding, SVG/PDF/PNG, CLI, boarding passes, GS1 labels. ISO 24778.
Project description
aztec-py — Pure-Python Aztec Code Barcode Generator
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.
Why aztec-py?
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 |
Install
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)
Quick Start
Generate a barcode — three lines
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
Use a preset for common real-world formats
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
Batch encode thousands of codes
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
Real-World Use Cases
These are the workloads aztec-py was built for. Copy the pattern, swap the data.
Airline boarding passes — IATA BCBP at scale
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).
Shipping and logistics — GS1 2027-compliant parcel labels
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 8
Event ticketing — concerts, venues, transit
Generate 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
Healthcare — drug serialization and patient wristbands
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")
Retail — shelf labels and price tags at scale
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")
Django / Flask API — on-demand barcode service
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.
CLI
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 |
API Reference
AztecCode
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 |
Batch encoding
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
)
AztecRune
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)
GS1 payload builder
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")])
IATA BCBP boarding pass builder
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.
Round-trip decode (testing / CI validation)
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"
Production Validation
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.
Comparison
| 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 | ✅ | ❌ | ❌ | ✅ | ✅ |
Changelog
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.
Lineage and Attribution
- 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.
Contributing
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
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file aztec_py-1.2.1.tar.gz.
File metadata
- Download URL: aztec_py-1.2.1.tar.gz
- Upload date:
- Size: 51.6 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
6884f8e14d5ec8c9cd44121afa012e3e167203b8fd64457ba61479973d884d5a
|
|
| MD5 |
e40d4d9d87a38c1d748c45f7c5487bc5
|
|
| BLAKE2b-256 |
9773f18d6774fe226566ce88f3c3cdb7a197c34756abb635b5a2f356520903b5
|
Provenance
The following attestation bundles were made for aztec_py-1.2.1.tar.gz:
Publisher:
publish.yml on greyllmmoder/python-aztec
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
aztec_py-1.2.1.tar.gz -
Subject digest:
6884f8e14d5ec8c9cd44121afa012e3e167203b8fd64457ba61479973d884d5a - Sigstore transparency entry: 1261666872
- Sigstore integration time:
-
Permalink:
greyllmmoder/python-aztec@1befe21a46448c116e8c320eef3eb17eb6ce2a70 -
Branch / Tag:
refs/tags/v1.2.1 - Owner: https://github.com/greyllmmoder
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@1befe21a46448c116e8c320eef3eb17eb6ce2a70 -
Trigger Event:
release
-
Statement type:
File details
Details for the file aztec_py-1.2.1-py3-none-any.whl.
File metadata
- Download URL: aztec_py-1.2.1-py3-none-any.whl
- Upload date:
- Size: 37.4 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
41c63ce2b6ace0454f366dfe6031ffbaaaafd4bf64430ea9013aaff6b051b15c
|
|
| MD5 |
d6a6c66d5a6c1331313ca477aad10a6f
|
|
| BLAKE2b-256 |
de62d31e07a7f6984b36facaa0550320474628f159194851b1ef0395fa53f1f6
|
Provenance
The following attestation bundles were made for aztec_py-1.2.1-py3-none-any.whl:
Publisher:
publish.yml on greyllmmoder/python-aztec
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
aztec_py-1.2.1-py3-none-any.whl -
Subject digest:
41c63ce2b6ace0454f366dfe6031ffbaaaafd4bf64430ea9013aaff6b051b15c - Sigstore transparency entry: 1261666959
- Sigstore integration time:
-
Permalink:
greyllmmoder/python-aztec@1befe21a46448c116e8c320eef3eb17eb6ce2a70 -
Branch / Tag:
refs/tags/v1.2.1 - Owner: https://github.com/greyllmmoder
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@1befe21a46448c116e8c320eef3eb17eb6ce2a70 -
Trigger Event:
release
-
Statement type: