Skip to main content

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

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

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

aztec_py-1.2.1.tar.gz (51.6 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

aztec_py-1.2.1-py3-none-any.whl (37.4 kB view details)

Uploaded Python 3

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

Hashes for aztec_py-1.2.1.tar.gz
Algorithm Hash digest
SHA256 6884f8e14d5ec8c9cd44121afa012e3e167203b8fd64457ba61479973d884d5a
MD5 e40d4d9d87a38c1d748c45f7c5487bc5
BLAKE2b-256 9773f18d6774fe226566ce88f3c3cdb7a197c34756abb635b5a2f356520903b5

See more details on using hashes here.

Provenance

The following attestation bundles were made for aztec_py-1.2.1.tar.gz:

Publisher: publish.yml on greyllmmoder/python-aztec

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

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

Hashes for aztec_py-1.2.1-py3-none-any.whl
Algorithm Hash digest
SHA256 41c63ce2b6ace0454f366dfe6031ffbaaaafd4bf64430ea9013aaff6b051b15c
MD5 d6a6c66d5a6c1331313ca477aad10a6f
BLAKE2b-256 de62d31e07a7f6984b36facaa0550320474628f159194851b1ef0395fa53f1f6

See more details on using hashes here.

Provenance

The following attestation bundles were made for aztec_py-1.2.1-py3-none-any.whl:

Publisher: publish.yml on greyllmmoder/python-aztec

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page