Skip to main content

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

Metadata

Release files for aztec-py 1.2.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for aztec-py 1.2.1
File Size Uploaded
aztec_py-1.2.1.tar.gz 51.6 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for aztec-py 1.2.1
File Interpreter ABI Platform
aztec_py-1.2.1-py3-none-any.whl Python 3 none any Details

Total release size: 89.0 kB

Release files / aztec_py-1.2.1.tar.gz

Download URL aztec_py-1.2.1.tar.gz
Size 51.6 kB
Tags Source
SHA-256 checksum
How to use checksums
6884f8e14d5ec8c9cd44121afa012e3e167203b8fd64457ba61479973d884d5a
BLAKE2b-256 checksum
How to use checksums
9773f18d6774fe226566ce88f3c3cdb7a197c34756abb635b5a2f356520903b5
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.12

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 Apr 9, 2026.

Transparency log

Release files / aztec_py-1.2.1-py3-none-any.whl

Download URL aztec_py-1.2.1-py3-none-any.whl
Size 37.4 kB
Tags Python 3
SHA-256 checksum
How to use checksums
41c63ce2b6ace0454f366dfe6031ffbaaaafd4bf64430ea9013aaff6b051b15c
BLAKE2b-256 checksum
How to use checksums
de62d31e07a7f6984b36facaa0550320474628f159194851b1ef0395fa53f1f6
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.12

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 Apr 9, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

1.2.1 This release

2 release files

1.2.0

2 release files

1.1.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page