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_scalesettings) is supported inbuild_svgthe same way as the JS version — pass a data URI aslogo. 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.jsand 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.
Metadata
Release files for qrcody 1.17.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | |
|---|---|---|---|---|
| qrcody-1.17.0.tar.gz | 14.0 kB | Without provenance | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| qrcody-1.17.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 32.4 kB
Release files / qrcody-1.17.0.tar.gz
| Download URL | qrcody-1.17.0.tar.gz |
|---|---|
| Size | 14.0 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
9cc30068831bdbb15029fcfa07faaf897bb65130e5c6c471b6d3d444541d38ca
|
|
BLAKE2b-256 checksum How to use checksums |
e8a2146a71a597ed1e0a6eddc39663faa83aebf9b97802e732644329a1beeba5
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.12.19 {"installer":{"name":"uv","version":"0.12.19","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.17.0-py3-none-any.whl
| Download URL | qrcody-1.17.0-py3-none-any.whl |
|---|---|
| Size | 18.3 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
2454b1791dbd52a8ec238d69a6d6cc61ed896071a0f57bd25a612e4cb6e1a2f5
|
|
BLAKE2b-256 checksum How to use checksums |
e7fa2c39b6759f3148bf0d6c7374b9bd29f153da5f87f3197001a50ddf9edc84
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.12.19 {"installer":{"name":"uv","version":"0.12.19","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}
|
Security for qrcody 1.17.0
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.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.