Skip to main content

qrcody

Standalone Python port of qrcody's styled QR code SVG rendering engine — square/rounded/dots/blob/bars/squircle pixel styles, styled finder eyes and pupils, gradients, and framed export (perimeter / scan-me / scan bands). No browser, no server — pure functions in, SVG string out.

This is a hand-port of src/lib/buildSVG.js from the qrcody web app. The rendering logic itself has no DOM dependency in the original (it already runs inside a Cloudflare Worker), so the two implementations produce visually equivalent output, though exact numeric string formatting may differ slightly (e.g. 500 vs 500.0) — SVG renderers treat these identically.

Install

uv add qrcody
# or, for PNG export too:
uv add "qrcody[png]"
# or, for WebP export too (implies png — WebP is encoded from the rasterized PNG):
uv add "qrcody[webp]"

PNG export uses cairosvg, which needs the native Cairo library installed on the system (brew install cairo on macOS, apt install libcairo2 on Debian/Ubuntu). WebP export uses the same cairosvg rasterization, then re-encodes via Pillow.

Library usage

from qrcody import DEFAULT, build_svg, build_framed_svg

settings = {
    **DEFAULT,
    "text": "https://example.com",
    "pixels_style": "dots",
    "eyes_style": "circle",
    "frame": "scan-me",
    "frame_text": "Scan me",
}

qr_svg = build_svg(settings)
framed = build_framed_svg(qr_svg, settings)
svg_string = framed["svg"]  # framed["w"] / framed["h"] give the output dimensions

Layout check

validate_qr_layout(settings) inspects the same settings for problems that hurt scannability — an inadequate quiet zone, text that overflows the error-correction level, a center logo over a finder pattern or beyond error-correction capacity, frame text crowding the QR body — before you render:

from qrcody import validate_qr_layout

result = validate_qr_layout(settings)
if result["severity"] == "error":
    raise ValueError("; ".join(i["message"] for i in result["issues"]))

result["severity"] is "ok", "warn", or "error"; each issue is a dict with code, severity, and a human-readable message.

For PNG (requires the png extra):

from qrcody.png import svg_to_png

png_bytes = svg_to_png(svg_string, width=1000)

For WebP (requires the webp extra):

from qrcody.webp import svg_to_webp

webp_bytes = svg_to_webp(svg_string, width=1000, quality=90)

CLI

qrcody --url "https://example.com" --pixels squircle --frame perimeter -o qr.svg
qrcody --url "https://example.com" --pixels dots --eyes circle -o qr.png
qrcody --url "https://example.com" --pixels dots --eyes circle -o qr.webp --quality 90

Run qrcody --help for the full flag list — it mirrors the qrcody /api/qr HTTP API parameters (see the qrcody repo's functions/api/qr.js and the parameter table in its CLAUDE.md).

Settings dict

All keys from qrcody.DEFAULT (see defaults.py) are recognized; unspecified keys fall back to their default. Notable ones:

Key Values
pixels_style square / rounded / dots / blob / bars-h / bars-v / squircle
eyes_style square / rounded / circle / leaf / squircle
pupils_style square / rounded / circle / squircle
corners_style none / round / extra-round / squircle
frame none / perimeter / scan-me / scan
correction_level L / M / Q / H

Development

uv sync --extra webp  # implies png too
uv run pytest
uv run ruff check .

Known gaps vs. the JS implementation

  • Logo embedding (logo / logo_scale settings) is supported in build_svg the same way as the JS version — pass a data URI as logo. There is no server-side "fetch a logo URL" helper here (that lives in the qrcody Cloudflare Worker's SSRF-hardened fetch, which is deployment-specific and out of scope for this package).
  • No automated JS/Python golden-file parity test yet — the port was done by hand against src/lib/buildSVG.js and verified with a matching unit test suite, but the two aren't diffed against each other in CI. If the JS renderer changes, this package needs a manual re-sync.

Note on the package name

This package is published as qrcody on PyPI, distinct from the qrcody npm/web app it's ported from — there's no npm/PyPI namespace collision since they're different registries.

Release files for qrcody 1.16.0

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

Source distribution (sdist)

Source distribution for qrcody 1.16.0
File Size Uploaded
qrcody-1.16.0.tar.gz 14.0 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for qrcody 1.16.0
File Interpreter ABI Platform
qrcody-1.16.0-py3-none-any.whl Python 3 none any Details

Total release size: 32.4 kB

Release files / qrcody-1.16.0.tar.gz

Download URL qrcody-1.16.0.tar.gz
Size 14.0 kB
Tags Source
SHA-256 checksum
How to use checksums
52b930e277f70fc12731c2ac8af2a85b2403b8d6358e2ade0fbc756eb0d6e8fc
BLAKE2b-256 checksum
How to use checksums
42d9b47020e868b7f8dc55d5f46721e96ad882e6ef844f2cc98243440c5f583f
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.12.13 {"installer":{"name":"uv","version":"0.12.13","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Debian GNU/Linux","version":"12","id":"bookworm","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

Release files / qrcody-1.16.0-py3-none-any.whl

Download URL qrcody-1.16.0-py3-none-any.whl
Size 18.3 kB
Tags Python 3
SHA-256 checksum
How to use checksums
ba471f96ea5c2979556bc7c65f4a20e314b7e17603b37be1a045e33e6df85335
BLAKE2b-256 checksum
How to use checksums
adb89adde961a530615771c9485098d8422238c0f8c2ef83a1ff963d8ff4b74a
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.12.13 {"installer":{"name":"uv","version":"0.12.13","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Debian GNU/Linux","version":"12","id":"bookworm","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

Release history Release notifications | RSS feed

1.17.0

2 release files

This release

1.16.0 This release

2 release files

1.15.0

2 release files

1.12.1

2 release files

1.12.0

2 release files

1.11.3

2 release files

1.11.2

2 release files

1.11.1

2 release files

1.8.1

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