qrref
Python package and reference implementation of the ISO/IEC 18004:2015 Standard for QR codes, with a simple but convenient CLI for showing, saving and copying generated QR codes.
Purpose
This is first and foremost a reference implementation, meaning I wanted the code to be readable and well-documented, without lots of magic numbers and tables copied straight from the specification. For those wanting to implement their own QR code generator, or extending one to suit their needs (custom colors, styles, inset images, etc.) this should be a good basis.
Wherever possible, I've chosen to implement the table data as functions instead. This doesn't result in the shortest or fastest implementation, but should explain where the numbers come from better.
The priorities of this project have been as follows, in approximate order of importance.
- Referentiability. The code should adhere closely to the specification.
- Most functions and tables are accompanied by comments referencing where they can be found in the specification. Look up "ISO/IEC 18004" and you can probably find it somewhere.
- Clarity. The code should be easy to read and understand.
- Comments and docstrings are mandatory.
- The code is split up into files with clearly different responsibilities, and should be understandable (mostly) on their own.
- Transparency. Table data should be generated by functions wherever possible.
- The implementation could be made shorter by hard-coding certain parts, most notably the coefficients of the generating polynomials for the Solomon-Reed code. This is against the spirit of this project though.
- Table data is still preferred where a function would be less clear.
- Efficiency. Looping over the entire symbol in Python should be avoided.
- NumPy is used extensively to speed up generation.
Usage
Install by running
$ pip install qrref
or to also be able to copy QR codes directly to the clipboard, by running
$ pip install qrref[copy]
This is an optional dependency, since it uses pyperclipimg which states in its README: "This module is separate [from pyperclip] because it has some heavy dependencies (pywin32, Quartz, etc.)" Without it, you can still show and save QR codes as png.
You can then import qrref and use qrref.generate_qr_code in your own code, or use the command-line interface (CLI) directly.
Examples
Show a QR code with the message "This is a QR code". This is done using Matplotlib, which means that "F" toggles fullscreen and "Q" closes the window. Perfect for quickly throwing up a QR code on a big screen.
$ py -m qrref "This is a QR code"
Save the QR code to ./wikipedia_qr.png:
$ py -m qrref "https://wikipedia.org" --png --filename wikipedia_qr
Copy the QR code to the clipboard (requires installing with pip install qrref[copy]):
$ py -m qrref "https://wikipedia.org" --copy
To control the size of the saved/copied image, use -w/--width. The image will be upscaled (by an integer factor) to at least this big. Use -w 0 to get a pixel-perfect QR code.
$ py -m qrref "https://wikipedia.org" --copy -w 0
Run py -m qrref -h to see all arguments.
$ py -m qrref -h
usage: python.exe -m qrref [-h] [-L | -M | -Q | -H] [-v VERSION] [--png | --show | --copy] [-f FILENAME] [-w WIDTH] [-t] [-u] [--debug] [content]
Create a QR code and save, show or copy it.
positional arguments:
content What to encode in the QR code
options:
-h, --help show this help message and exit
-v, --version VERSION
Which version (size) of QR code to use. By default, chooses the smallest one that the content fits in.
-f, --filename FILENAME
Filename to save with
-w, --width WIDTH Minimum width of the image, in pixels
-t, --transparent Use transparent background
-u, --utf-8 Use UTF-8 instead of Latin-1 encoding
--debug Show debug output
Error correction level:
-L Low: 7 percent error correction
-M Medium: 15 percent error correction (default)
-Q Quartile: 25 percent error correction
-H High: 30 percent error correction
How to save the QR code:
--png As PNG
--show Show without saving (default)
--copy Copy image to clipboard
Reference details
The generation of a QR code is handled by qrref.qr.generate_qr_code. This function is fairly readable and illustrates the process from start to finish:
def generate_qr_code(
content: str,
ec_level: Literal["L", "M", "Q", "H"],
version: int | None = None,
):
if version is None:
version, modes = select_version(content, ec_level)
else:
modes = select_modes(content, version)
bitstream = encode_mixed(modes, version)
bytestream = to_data_bytestream(bitstream, version, ec_level)
blockstream = generate_error_correction_blocks(bytestream, version, ec_level)
final_bytestream = interleave_blocks(blockstream, version, ec_level)
symbol = place_bytestream(final_bytestream, version)
insert_timing_patterns(symbol)
insert_finder_patterns(symbol)
insert_alignment_patterns(symbol, version)
symbol, mask_pattern_id = apply_mask(symbol, version)
place_format_bits(symbol, generate_format_bits(ec_level, mask_pattern_id))
if version >= 7:
place_version_bits(symbol, generate_version_bits(version))
symbol = expand_quiet_region(symbol)
return symbol
This uses functions split up over several files. Here's a high-level overview of what each file does, in the order they are used in the above function.
qrref/data_analysis.py: Select the minimum version (qr code size) that the content (input string) fits in. Also select the encoding types that minimizes the length of the data bitstream (numeric data is denser than alphanumeric data, which is denser than byte data).qrref/data_encoding.py: Encode the message into a bitstream and group the bits together into bytes.qrref/error_correction.py: Generate error correction bytes using the Reed-Solomon codes. In practice, does this by finding polynomial remainders over the Galois Field of order 256 (wikipedia), which also usesqrref/galois_field.py. Turn the resulting data and error correction bytes into a bitstream by splitting them into blocks and interleaving those blocks. Also generate the format and version bits after the masking is complete.qrref/placement.py: Place the finalized bitstream into the symbol, as well as finder, alignment and timing patterns. After the format and version bits are generated, place those too.qrref/masking.py: Find and apply the mask that minimizes undesirable patterns, like long runs of the same color, etc. This is done after placing everything but the format and version bits in the symbol.
Auxiliary files:
qrref/custom_types.py: Custom types for type hinting throughout the package.qrref/qr.py: Assemble and visualize the QR code.qrref/settings.py: Global settings. Currently only one, which is the encoding to use for byte strings (Latin-1 or UTF-8).qrref/table_data.py: Data from tables, either in the form of dictionaries or functions.
Metadata
Release files for qrref 0.1.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| qrref-0.1.1.tar.gz | 27.4 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| qrref-0.1.1-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 50.9 kB
Release files / qrref-0.1.1.tar.gz
| Download URL | qrref-0.1.1.tar.gz |
|---|---|
| Size | 27.4 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
83c9298c3ef53915b14223a6811a9ae9d17d9447e3eb43a44df801cabc6289a4
|
|
BLAKE2b-256 checksum How to use checksums |
cab6acf1152ba4845568e01f3f36ed8d700b7590d0dc7219fcfe953e4ddb70ff
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.14.0
|
Release files / qrref-0.1.1-py3-none-any.whl
| Download URL | qrref-0.1.1-py3-none-any.whl |
|---|---|
| Size | 23.5 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
57dc1adfa2745bac4abb6423368e6ed978fa657fe786ce22e1958a20bfe379be
|
|
BLAKE2b-256 checksum How to use checksums |
8f4d267d0af378e2ac44bb7c5fd5b55ac164c1943023d55e04d40d3dbcdba6ab
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.14.0
|