Skip to content

greyllmmoder/python-aztec

aztec-py — Pure-Python Aztec Code Barcode Generator

CI PyPI version Python versions Coverage mypy: strict License: MIT

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


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

Packages

 
 
 

Contributors