lincolnloop-python-qrcode
lincolnloop-python-qrcode
└── lincolnloop-python-qrcode/
├── CHANGES.rst
├── LICENSE
├── PACKAGING.rst
├── README.rst
├── TESTING.rst
├── pyproject.toml
├── signing-key.asc
├── tox.ini
├── .editorconfig
├── .git-blame-ignore-revs
├── doc/
│ └── qr.1
├── qrcode/
│ ├── LUT.py
│ ├── __init__.py
│ ├── base.py
│ ├── console_scripts.py
│ ├── constants.py
│ ├── exceptions.py
│ ├── main.py
│ ├── release.py
│ ├── util.py
│ ├── compat/
│ │ ├── __init__.py
│ │ ├── etree.py
│ │ └── png.py
│ ├── image/
│ │ ├── __init__.py
│ │ ├── base.py
│ │ ├── pil.py
│ │ ├── pure.py
│ │ ├── styledpil.py
│ │ ├── svg.py
│ │ └── styles/
│ │ ├── __init__.py
│ │ ├── colormasks.py
│ │ └── moduledrawers/
│ │ ├── __init__.py
│ │ ├── base.py
│ │ ├── pil.py
│ │ └── svg.py
│ └── tests/
│ ├── __init__.py
│ ├── consts.py
│ ├── test_example.py
│ ├── test_qrcode.py
│ ├── test_qrcode_pil.py
│ ├── test_qrcode_pypng.py
│ ├── test_qrcode_svg.py
│ ├── test_release.py
│ ├── test_script.py
│ └── test_util.py
└── .github/
└── workflows/
├── linting.yml
├── push.yml
└── python-app.yml
================================================
File: CHANGES.rst
================================================
==========
Change log
==========
- Testsuite and code quality checks are done through Github Actions.
.. _Poetry: https://python-poetry.org
.. _ruff: https://astral.sh/ruff
- Optimize the output for the ``SVGPathImage`` factory (more than 30% reduction
in file sizes).
- Add Styled PIL image factory, allowing different color masks and shapes in QR
codes
- Add --ascii parameter to command line interface allowing to output ascii when
stdout is piped
- Fix short chunks of data not being optimized to the correct mode.
- Better calculation of the best mask pattern and related optimizations. Big
thanks to cryptogun!
* Fix incomplete block table for QR version 15. Thanks Rodrigo Queiro for the
report and Jacob Welsh for the investigation and fix.
5.2.1
-----
5.2.2
-----
* Revert some terminal changes from 5.2 that broke Python 3's real life tty
code generation and introduce a better way from Jacob Welsh.
* Much better test coverage (now only officially supporting Python 2.6+)
5.0 (17 Jun 2014)
=================
* Speed optimizations.
* Change the output when using the ``qr`` script to use ASCII rather than
just colors, better using the terminal real estate.
* Introduce a ``print_ascii`` method and use it as the default for the ``qr``
script rather than ``print_tty``.
5.0.1
-----
* Support pure-python PNG generation (via pymaging) for Python 2.6+ -- thanks
Adam Wisniewski!
* SVG image generation now supports alternate sizing (the default box size of
10 == 1mm per rectangle).
* SVG path image generation allows cleaner SVG output by combining all QR rects
into a single path. Thank you, Viktor Stískala.
* Added some extra simple SVG factories that fill the background white.
4.0.1
-----
* Fix the pymaging backend not able to save the image to a buffer. Thanks ilj!
4.0.2
-----
4.0.3
-----
* Fix bad QR code generation due to the regex comma fix in version 4.0.2.
4.0.4
-----
* Optimized chunking -- if the parts of the data stream can be encoded more
efficiently, the data will be split into chunks of the most efficient modes.
3.1.1
-----
* Python 3 support.
* Add in a workaround so that Python 2.6 users can use SVG generation (they
must install ``lxml``).
* Some initial tests! And tox support (``pip install tox``) for testing across
Python platforms.
* Fix the first four columns incorrectly shifted by one. Thanks to Josep
Gómez-Suay for the report and fix.
* Fixed the first column of the QR Code coming up empty! Thanks to BecoKo.
2.5.1
-----
* Use a pluggable backend system for generating images, thanks to Branko Čibej!
Comes with PIL and SVG backends built in.
2.4.1
-----
2.4.2
-----
* When adding data, auto-select the more efficient encoding methods for numbers
and alphanumeric data (KANJI still not supported).
2.3.1
-----
* Added a ``qr`` script which can be used to output a qr code to the tty using
background colors, or to a file via a pipe.
================================================
File: LICENSE
================================================
Copyright (c) 2011, Lincoln Loop
All rights reserved.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-------------------------------------------------------------------------------
Original text and license from the pyqrnative package where this was forked
from (http://code.google.com/p/pyqrnative):
================================================
File: PACKAGING.rst
================================================
Packaging quick reminder
========================
================================================
File: README.rst
================================================
=============================
Pure python QR Code generator
=============================
Generate QR codes.
A standard install uses pypng_ to generate PNG files and can also render QR
codes directly to the console. A standard install is just::
For more image functionality, install qrcode with the ``pil`` dependency so
that pillow_ is installed and can be used for generating images::
.. _pypng: https://pypi.python.org/pypi/pypng
.. _pillow: https://pypi.python.org/pypi/Pillow
What is a QR Code?
==================
A Quick Response code is a two-dimensional pictographic code used for its fast
readability and comparatively large storage capacity. The code consists of
black modules arranged in a square pattern on a white background. The
information encoded can be made up of any kind of data (e.g., binary,
alphanumeric, or Kanji symbols)
Usage
=====
.. code:: python
import qrcode
img = qrcode.make('Some data here')
type(img) # qrcode.image.pil.PilImage
img.save("some_file.png")
Advanced Usage
--------------
import qrcode
qr = qrcode.QRCode(
version=1,
error_correction=qrcode.constants.ERROR_CORRECT_L,
box_size=10,
border=4,
)
qr.add_data('Some data')
qr.make(fit=True)
``fill_color`` and ``back_color`` can change the background and the painting
color of the QR, when using the default image factory. Both parameters accept
RGB color tuples.
.. code:: python
The ``error_correction`` parameter controls the error correction used for the
QR Code. The following four constants are made available on the ``qrcode``
package:
``ERROR_CORRECT_L``
About 7% or less errors can be corrected.
``ERROR_CORRECT_M`` (default)
About 15% or less errors can be corrected.
``ERROR_CORRECT_Q``
About 25% or less errors can be corrected.
``ERROR_CORRECT_H``.
About 30% or less errors can be corrected.
The ``box_size`` parameter controls how many pixels each "box" of the QR code
is.
The ``border`` parameter controls how many boxes thick the border should be
(the default is 4, which is the minimum according to the specs).
You can encode as SVG, or use a new pure Python image processor to encode to
PNG images.
The Python examples below use the ``make`` shortcut. The same ``image_factory``
keyword argument is a valid option for the ``QRCode`` class for more advanced
usage.
SVG
---
You can create the entire SVG or an SVG fragment. When building an entire SVG
image, you can use the factory that combines as a path (recommended, and
default for the script) or a factory that creates a simple set of rectangles.
Or in Python:
.. code:: python
import qrcode
import qrcode.image.svg
if method == 'basic':
# Simple factory, just a set of rects.
factory = qrcode.image.svg.SvgImage
elif method == 'fragment':
# Fragment factory (also just a set of rects)
factory = qrcode.image.svg.SvgFragmentImage
else:
# Combined path factory, fixes white space that may occur when zooming
factory = qrcode.image.svg.SvgPathImage
Two other related factories are available that work the same, but also fill the
background of the SVG with white::
qrcode.image.svg.SvgFillImage
qrcode.image.svg.SvgPathFillImage
.. code:: python
import qrcode
qr = qrcode.QRCode(image_factory=qrcode.image.svg.SvgPathImage)
qr.add_data('Some data')
qr.make(fit=True)
You can convert the SVG image into strings using the ``to_string()`` method.
Additional keyword arguments are forwarded to ElementTrees ``tostring()``:
.. code:: python
img.to_string(encoding='unicode')
If Pillow is not installed, the default image factory will be a pure Python PNG
encoder that uses `pypng`.
You can use the factory explicitly from your command line::
Or in Python:
.. code:: python
import qrcode
from qrcode.image.pure import PyPNGImage
img = qrcode.make('Some data here', image_factory=PyPNGImage)
Styled Image
------------
Works only with versions_ >=7.2 (SVG styled images require 7.4).
.. _versions:
https://github.com/lincolnloop/python-qrcode/blob/master/CHANGES.rst#72-19-july-
2021
These QR Codes are not guaranteed to work with all readers, so do some
experimentation and set the error correction to high (especially if embedding
an image).
.. image:: doc/module_drawers.png
These all accept a ``size_ratio`` argument which allows for "gapped" squares or
circles by reducing this less than the default of ``Decimal(1)``.
.. image:: doc/color_masks.png
Here is a code example to draw a QR code with rounded corners, radial gradient
and an embedded image:
.. code:: python
import qrcode
from qrcode.image.styledpil import StyledPilImage
from qrcode.image.styles.moduledrawers.pil import RoundedModuleDrawer
from qrcode.image.styles.colormasks import RadialGradiantColorMask
qr = qrcode.QRCode(error_correction=qrcode.constants.ERROR_CORRECT_H)
qr.add_data('Some data')
img_1 = qr.make_image(image_factory=StyledPilImage,
module_drawer=RoundedModuleDrawer())
img_2 = qr.make_image(image_factory=StyledPilImage,
color_mask=RadialGradiantColorMask())
img_3 = qr.make_image(image_factory=StyledPilImage,
embeded_image_path="/path/to/image.png")
Examples
========
.. code:: python
import io
import qrcode
qr = qrcode.QRCode()
qr.add_data("Some text")
f = io.StringIO()
qr.print_ascii(out=f)
f.seek(0)
print(f.read())
The `add_data` method will append data to the current QR object. To add new data by
replacing previous content in the same object, first use clear method:
.. code:: python
import qrcode
qr = qrcode.QRCode()
qr.add_data('Some data')
img = qr.make_image()
qr.clear()
qr.add_data('New data')
other_img = qr.make_image()
================================================
File: TESTING.rst
================================================
Testing
=======
Depending on if you can install the wheels directly for your OS, you may need
the libraries to build PIL, too. Here's the Ubuntu commands::
If you want, you can test against a specific version like this: ``tox -e py312-
pil``
Linting
-------
================================================
File: pyproject.toml
================================================
[build-system]
requires = ["poetry-core"]
build-backend = "poetry.core.masonry.api"
[tool.poetry]
name = "qrcode"
version = "8.0"
packages = [{include = "qrcode"}]
description = "QR Code image generator"
authors = ["Lincoln Loop <[email protected]>"]
license = "BSD"
readme = ["README.rst", "CHANGES.rst"]
homepage = "https://github.com/lincolnloop/python-qrcode"
keywords = ["qr", "denso-wave", "IEC18004"]
classifiers = [
"Development Status :: 5 - Production/Stable",
"License :: OSI Approved :: BSD License",
"Operating System :: OS Independent",
"Intended Audience :: Developers",
"Programming Language :: Python",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
"Programming Language :: Python :: 3 :: Only",
"Topic :: Multimedia :: Graphics",
"Topic :: Software Development :: Libraries :: Python Modules",
]
[tool.poetry.scripts]
qr = 'qrcode.console_scripts:main'
[tool.poetry.dependencies]
python = "^3.9"
colorama = {version = "*", platform = "win32"}
pypng = {version = "*", optional = true}
pillow = {version = ">=9.1.0", optional = true}
[tool.poetry.extras]
pil = ["pillow"]
png = ["pypng"]
all = ["pypng","pillow"]
[tool.poetry.group.dev.dependencies]
pytest = {version = "*"}
pytest-cov = {version = "*"}
tox = {version = "*"}
ruff = {version = "*"}
pypng = {version = "*"}
pillow = {version = ">=9.1.0"}
docutils = "^0.21.2"
zest-releaser = {extras = ["recommended"], version = "^9.2.0"}
[tool.zest-releaser]
less-zeros = "yes"
version-levels = 2
tag-format = "v{version}"
tag-message = "Version {version}"
tag-signing = "yes"
date-format =" %%-d %%B %%Y"
prereleaser.middle = [
"qrcode.release.update_manpage"
]
================================================
File: signing-key.asc
================================================
-----BEGIN PGP PUBLIC KEY BLOCK-----
Version: GnuPG v1
mQENBFDTudIBCACagG1d2hP80O3aNi+9S4rgpd1Lfa7To11XI1HsGI74jkshSxCU
dRPc/OmPAgLjjr+iEpEZeI/h8XyCem0QOgmr2mr1nu58dJ6UvCG0yX4DvXX9SDfh
ZeAHRzFi9tUEN4BbBf79EYfma9iqNpQPXKurvxLeHGDW48rMbT5dJd8WSN96rNEo
OrS7EMHy3tvrU5lX4JURwHQUYmO+wXYbX5aZdvZRVG5onD1ettP3Ie87Mc/bRJ91
6NIGXX3vlk9Gou0EpJbs6zhjvTxmjU4trjmnhUNf7+dDZpHpId5eU2sMl959qjsO
zdQWtSgyjZ4rN8kw8MuaFqaMaBbbWnrECBAJABEBAAG0JENocmlzIEJlYXZlbiA8
c21pbGV5Y2hyaXNAZ21haWwuY29tPokBPgQTAQIAKAIbAwYLCQgHAwIGFQgCCQoL
BBYCAwECHgECF4AFAlc+K5MFCQwdXW4ACgkQ1vRASXANOvCRMAgAjHOqeoBYZi6h
OKPFcyshl+qiDkpHNEAZnxMvCX5gqfc+aLQA4FmmjwkbIBKYYgT/SmnAB/txpTgN
QByOw+LzWFjhsqKGEUlpva/o6N80Y7EH7HE9PldEandEZEAb4XXqQHItrDkY80/P
WznO66TmmsNYa4cbb21+Q9bRZDZyaGPc/ZukL8rzFpXsQWwVf41fVAp0gWrnXwwC
GqGIAC8WcmKLK0K62LmSJ5HnO+PSC0K6UFAcBg8XwCWaI+BqGw81Bi/Dho2GxkDv
RyF8JK+HrNm1AJgn3kILhQKDxRb9HamCrAbZ5leB7NNX855MyXCoIsZMJkDksHxW
PcF+l39thbQvQ2hyaXMgQmVhdmVuICgyMDEyIGtleSkgPHNtaWxleWNocmlzQGdt
YWlsLmNvbT6JATgEEwECACIFAlDTudICGwMGCwkIBwMCBhUIAgkKCwQWAgMBAh4B
AheAAAoJENb0QElwDTrwhPAH/RAD40WAqHErF47yoJhrurTUDESRqcz1CSEFYEyJ
vx4a3ZOAoCe7okw/DWz934vRggToTNfhEpGFqDjwM1rrnYvlT577ay095ZZveDw+
zU4SzseamBHO07uDugBF2XDXiW7iAWLgvRRp+9fFuHdJxAy0OcYceIsKVm9O7QOG
EyZSm/rugXQzx62yZkMMWVRDBkClRP2AmubqXhZWMYll2jnqo7dSFUKSHikt/Dpw
8BG4vFZiOurFoThgkQt0yxbXNjKa1Q3RfVQO3f0WtKwIiE8zg7abFdK1LlMybfde
Jf2l5H1d17wfdXnnzGnwzJ06Ss/1NOdI8Pd+/Gnh+mlm/Ou5AQ0EUNO50gEIAPmL
jErCZsDkBsijy6LAvC1C9uiye5nRAPxB85H58u/IyRaPwoReBcMQMTeT92f3A+uV
F7yvNm598L9dY/+QK5L6JOwvpOMEBBInSbJNXxnSlSSeyaEN2jbRbkkTrxWz93xQ
lLAjU8ROjTLojzW5LqC4A8XebuH9XKp7rX9XMb4yKkxmDyqPaJLWUtMig6XZStwG
odILFURHPLfsreWx7spYhA3NiC6OIrPmN4kORt6GT7g6umBjellsG/h+XZZW3jQV
v768BhOsTeTdZZxbPApwrWqO/Y2fswVC+rsrd0sRXUnNFv8o4EVWdIsgQ/cdG4cM
S79fczQVxLL+UGYLswEAEQEAAYkBJQQYAQIADwIbDAUCVz4rgwUJCFr2bgAKCRDW
9EBJcA068AB2B/9LGmNceTLY3ckKqtxTaTc9OqSbu/B9kbuOSaOO1KB4A7a1F6Ay
I5z3r50x88GqAIr3FigDlGNLfgWQAdeAzeaOV8CX/J7MHwAvZDg4qLPIfiVeq9r1
ztXi9wITBLrn2TUugj9lK3x1uUzwmFhmyrnLl8wvqw74m6b3iY5zvSY24mpTBupD
ZjuYt/fCrdAtjyGIKXkQBQzmLqrOFX2jq3tOBE+qGPECiUVdDYH2X/NrJFqMgu0/
IjsGMlA46GlzypOXDNrgPX3EJ7w59H3Fn9cQgH8C2OIE40Nr6AF7oAR+/vxwG/Er
yCUbp4StCe9l6Jv7I6XTqofSZaQVkWNLwbOi
=OWh8
-----END PGP PUBLIC KEY BLOCK-----
================================================
File: tox.ini
================================================
[tox]
distribute = False
envlist = py{39,310,311,312}-{pil,png,none}
skip_missing_interpreters = True
[gh-actions]
python =
3.9: py39
3.10: py310
3.11: py311
3.12: py312
[testenv]
commands =
pytest --cov
deps =
pil: pillow>=9.1.0
png: pypng
pytest
pytest-cov
================================================
File: .editorconfig
================================================
# editorconfig.org
root = true
[*]
indent_style = space
indent_size = 2
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
[*.py]
indent_size = 4
max_line_length = 88
[*.{rst,md}]
indent_size = 4
================================================
File: .git-blame-ignore-revs
================================================
# Ruff format the entire project
5d60da17455607e9c8b7e7fef9940d20027894f2
================================================
File: doc/qr.1
================================================
.\" Manpage for qr
.TH QR 1 "6 Feb 2023" "7.4.2" "Python QR tool"
.SH NAME
qr \- script to create QR codes at the command line
.SH SYNOPSIS
qr [\-\-help] [\-\-factory=FACTORY] [\-\-optimize=OPTIMIZE] [\-\-error\-
correction=LEVEL] [data]
.SH DESCRIPTION
This script uses the python qrcode module. It can take data from stdin or from the
commandline and generate a QR code.
Normally it will output the QR code as ascii art to the terminal. If the output is
piped to a file, it will output the image (default type of PNG).
.SH OPTIONS
.PP
\fB\ \-h, \-\-help\fR
.RS 4
Show a help message.
.RE
.PP
\fB\ \-\-factory=FACTORY\fR
.RS 4
Full python path to the image factory class to create the
image with. You can use the following shortcuts to the
built-in image factory classes: pil (default), png (
default if pillow is not installed), svg, svg-fragment,
svg-path.
.RE
.PP
\fB\ \-\-optimize=OPTIMIZE\fR
.RS 4
Optimize the data by looking for chunks of at least this
many characters that could use a more efficient encoding
method. Use 0 to turn off chunk optimization.
.RE
.PP
\fB\ \-\-error\-correction=LEVEL\fR
.RS 4
The error correction level to use. Choices are L (7%),
M (15%, default), Q (25%), and H (30%).
.RE
.PP
\fB\ data\fR
.RS 4
The data from which the QR code will be generated.
.RE
================================================
File: qrcode/LUT.py
================================================
# Store all kinds of lookup table.
# def create_bytes(rs_blocks):
# for r in range(len(rs_blocks)):
# dcCount = rs_blocks[r].data_count
# ecCount = rs_blocks[r].total_count - dcCount
# rsPoly = base.Polynomial([1], 0)
# for i in range(ecCount):
# rsPoly = rsPoly * base.Polynomial([1, base.gexp(i)], 0)
# return ecCount, rsPoly
# rsPoly_LUT = {}
# for version in range(1,41):
# for error_correction in range(4):
# rs_blocks_list = base.rs_blocks(version, error_correction)
# ecCount, rsPoly = create_bytes(rs_blocks_list)
# rsPoly_LUT[ecCount]=rsPoly.num
# print(rsPoly_LUT)
================================================
File: qrcode/__init__.py
================================================
from qrcode.main import QRCode
from qrcode.main import make # noqa
from qrcode.constants import ( # noqa
ERROR_CORRECT_L,
ERROR_CORRECT_M,
ERROR_CORRECT_Q,
ERROR_CORRECT_H,
)
There's an even easier way than the code here though: just use the ``make``
shortcut.
"""
qr = QRCode(*args, **kwargs)
qr.add_data(data)
im = qr.make_image()
im.show()
if __name__ == "__main__": # pragma: no cover
import sys
run_example(*sys.argv[1:])
================================================
File: qrcode/base.py
================================================
from typing import NamedTuple
from qrcode import constants
EXP_TABLE = list(range(256))
LOG_TABLE = list(range(256))
for i in range(8):
EXP_TABLE[i] = 1 << i
for i in range(255):
LOG_TABLE[EXP_TABLE[i]] = i
RS_BLOCK_OFFSET = {
constants.ERROR_CORRECT_L: 0,
constants.ERROR_CORRECT_M: 1,
constants.ERROR_CORRECT_Q: 2,
constants.ERROR_CORRECT_H: 3,
}
RS_BLOCK_TABLE = (
# L
# M
# Q
# H
# 1
(1, 26, 19),
(1, 26, 16),
(1, 26, 13),
(1, 26, 9),
# 2
(1, 44, 34),
(1, 44, 28),
(1, 44, 22),
(1, 44, 16),
# 3
(1, 70, 55),
(1, 70, 44),
(2, 35, 17),
(2, 35, 13),
# 4
(1, 100, 80),
(2, 50, 32),
(2, 50, 24),
(4, 25, 9),
# 5
(1, 134, 108),
(2, 67, 43),
(2, 33, 15, 2, 34, 16),
(2, 33, 11, 2, 34, 12),
# 6
(2, 86, 68),
(4, 43, 27),
(4, 43, 19),
(4, 43, 15),
# 7
(2, 98, 78),
(4, 49, 31),
(2, 32, 14, 4, 33, 15),
(4, 39, 13, 1, 40, 14),
# 8
(2, 121, 97),
(2, 60, 38, 2, 61, 39),
(4, 40, 18, 2, 41, 19),
(4, 40, 14, 2, 41, 15),
# 9
(2, 146, 116),
(3, 58, 36, 2, 59, 37),
(4, 36, 16, 4, 37, 17),
(4, 36, 12, 4, 37, 13),
# 10
(2, 86, 68, 2, 87, 69),
(4, 69, 43, 1, 70, 44),
(6, 43, 19, 2, 44, 20),
(6, 43, 15, 2, 44, 16),
# 11
(4, 101, 81),
(1, 80, 50, 4, 81, 51),
(4, 50, 22, 4, 51, 23),
(3, 36, 12, 8, 37, 13),
# 12
(2, 116, 92, 2, 117, 93),
(6, 58, 36, 2, 59, 37),
(4, 46, 20, 6, 47, 21),
(7, 42, 14, 4, 43, 15),
# 13
(4, 133, 107),
(8, 59, 37, 1, 60, 38),
(8, 44, 20, 4, 45, 21),
(12, 33, 11, 4, 34, 12),
# 14
(3, 145, 115, 1, 146, 116),
(4, 64, 40, 5, 65, 41),
(11, 36, 16, 5, 37, 17),
(11, 36, 12, 5, 37, 13),
# 15
(5, 109, 87, 1, 110, 88),
(5, 65, 41, 5, 66, 42),
(5, 54, 24, 7, 55, 25),
(11, 36, 12, 7, 37, 13),
# 16
(5, 122, 98, 1, 123, 99),
(7, 73, 45, 3, 74, 46),
(15, 43, 19, 2, 44, 20),
(3, 45, 15, 13, 46, 16),
# 17
(1, 135, 107, 5, 136, 108),
(10, 74, 46, 1, 75, 47),
(1, 50, 22, 15, 51, 23),
(2, 42, 14, 17, 43, 15),
# 18
(5, 150, 120, 1, 151, 121),
(9, 69, 43, 4, 70, 44),
(17, 50, 22, 1, 51, 23),
(2, 42, 14, 19, 43, 15),
# 19
(3, 141, 113, 4, 142, 114),
(3, 70, 44, 11, 71, 45),
(17, 47, 21, 4, 48, 22),
(9, 39, 13, 16, 40, 14),
# 20
(3, 135, 107, 5, 136, 108),
(3, 67, 41, 13, 68, 42),
(15, 54, 24, 5, 55, 25),
(15, 43, 15, 10, 44, 16),
# 21
(4, 144, 116, 4, 145, 117),
(17, 68, 42),
(17, 50, 22, 6, 51, 23),
(19, 46, 16, 6, 47, 17),
# 22
(2, 139, 111, 7, 140, 112),
(17, 74, 46),
(7, 54, 24, 16, 55, 25),
(34, 37, 13),
# 23
(4, 151, 121, 5, 152, 122),
(4, 75, 47, 14, 76, 48),
(11, 54, 24, 14, 55, 25),
(16, 45, 15, 14, 46, 16),
# 24
(6, 147, 117, 4, 148, 118),
(6, 73, 45, 14, 74, 46),
(11, 54, 24, 16, 55, 25),
(30, 46, 16, 2, 47, 17),
# 25
(8, 132, 106, 4, 133, 107),
(8, 75, 47, 13, 76, 48),
(7, 54, 24, 22, 55, 25),
(22, 45, 15, 13, 46, 16),
# 26
(10, 142, 114, 2, 143, 115),
(19, 74, 46, 4, 75, 47),
(28, 50, 22, 6, 51, 23),
(33, 46, 16, 4, 47, 17),
# 27
(8, 152, 122, 4, 153, 123),
(22, 73, 45, 3, 74, 46),
(8, 53, 23, 26, 54, 24),
(12, 45, 15, 28, 46, 16),
# 28
(3, 147, 117, 10, 148, 118),
(3, 73, 45, 23, 74, 46),
(4, 54, 24, 31, 55, 25),
(11, 45, 15, 31, 46, 16),
# 29
(7, 146, 116, 7, 147, 117),
(21, 73, 45, 7, 74, 46),
(1, 53, 23, 37, 54, 24),
(19, 45, 15, 26, 46, 16),
# 30
(5, 145, 115, 10, 146, 116),
(19, 75, 47, 10, 76, 48),
(15, 54, 24, 25, 55, 25),
(23, 45, 15, 25, 46, 16),
# 31
(13, 145, 115, 3, 146, 116),
(2, 74, 46, 29, 75, 47),
(42, 54, 24, 1, 55, 25),
(23, 45, 15, 28, 46, 16),
# 32
(17, 145, 115),
(10, 74, 46, 23, 75, 47),
(10, 54, 24, 35, 55, 25),
(19, 45, 15, 35, 46, 16),
# 33
(17, 145, 115, 1, 146, 116),
(14, 74, 46, 21, 75, 47),
(29, 54, 24, 19, 55, 25),
(11, 45, 15, 46, 46, 16),
# 34
(13, 145, 115, 6, 146, 116),
(14, 74, 46, 23, 75, 47),
(44, 54, 24, 7, 55, 25),
(59, 46, 16, 1, 47, 17),
# 35
(12, 151, 121, 7, 152, 122),
(12, 75, 47, 26, 76, 48),
(39, 54, 24, 14, 55, 25),
(22, 45, 15, 41, 46, 16),
# 36
(6, 151, 121, 14, 152, 122),
(6, 75, 47, 34, 76, 48),
(46, 54, 24, 10, 55, 25),
(2, 45, 15, 64, 46, 16),
# 37
(17, 152, 122, 4, 153, 123),
(29, 74, 46, 14, 75, 47),
(49, 54, 24, 10, 55, 25),
(24, 45, 15, 46, 46, 16),
# 38
(4, 152, 122, 18, 153, 123),
(13, 74, 46, 32, 75, 47),
(48, 54, 24, 14, 55, 25),
(42, 45, 15, 32, 46, 16),
# 39
(20, 147, 117, 4, 148, 118),
(40, 75, 47, 7, 76, 48),
(43, 54, 24, 22, 55, 25),
(10, 45, 15, 67, 46, 16),
# 40
(19, 148, 118, 6, 149, 119),
(18, 75, 47, 31, 76, 48),
(34, 54, 24, 34, 55, 25),
(20, 45, 15, 61, 46, 16),
)
def glog(n):
if n < 1: # pragma: no cover
raise ValueError(f"glog({n})")
return LOG_TABLE[n]
def gexp(n):
return EXP_TABLE[n % 255]
class Polynomial:
def __init__(self, num, shift):
if not num: # pragma: no cover
raise Exception(f"{len(num)}/{shift}")
offset = 0
for offset in range(len(num)):
if num[offset] != 0:
break
def __iter__(self):
return iter(self.num)
def __len__(self):
return len(self.num)
return Polynomial(num, 0)
num = [
item ^ gexp(glog(other_item) + ratio)
for item, other_item in zip(self, other)
]
if difference:
num.extend(self[-difference:])
# recursive call
return Polynomial(num, 0) % other
class RSBlock(NamedTuple):
total_count: int
data_count: int
blocks = []
return blocks
================================================
File: qrcode/console_scripts.py
================================================
#!/usr/bin/env python
"""
qr - Convert stdin (or the first argument) to a QR Code.
When stdout is a tty the QR Code is printed to the terminal and when stdout is
a pipe to a file an image is written. The default image format is PNG.
"""
import optparse
import os
import sys
from typing import Dict, Iterable, NoReturn, Optional, Set, Type
from importlib import metadata
import qrcode
from qrcode.image.base import BaseImage, DrawerAliases
# The next block is added to get the terminal to display properly on MS platforms
if sys.platform.startswith(("win", "cygwin")): # pragma: no cover
import colorama # type: ignore
colorama.init()
default_factories = {
"pil": "qrcode.image.pil.PilImage",
"png": "qrcode.image.pure.PyPNGImage",
"svg": "qrcode.image.svg.SvgImage",
"svg-fragment": "qrcode.image.svg.SvgFragmentImage",
"svg-path": "qrcode.image.svg.SvgPathImage",
# Keeping for backwards compatibility:
"pymaging": "qrcode.image.pure.PymagingImage",
}
error_correction = {
"L": qrcode.ERROR_CORRECT_L,
"M": qrcode.ERROR_CORRECT_M,
"Q": qrcode.ERROR_CORRECT_Q,
"H": qrcode.ERROR_CORRECT_H,
}
def main(args=None):
if args is None:
args = sys.argv[1:]
version = metadata.version("qrcode")
parser = optparse.OptionParser(usage=(__doc__ or "").strip(), version=version)
parser.add_option(
"--factory",
help="Full python path to the image factory class to "
"create the image with. You can use the following shortcuts to the "
f"built-in image factory classes: {commas(default_factories)}.",
)
parser.add_option(
"--factory-drawer",
help=f"Use an alternate drawer. {get_drawer_help()}.",
)
parser.add_option(
"--optimize",
type=int,
help="Optimize the data by looking for chunks "
"of at least this many characters that could use a more efficient "
"encoding method. Use 0 to turn off chunk optimization.",
)
parser.add_option(
"--error-correction",
type="choice",
choices=sorted(error_correction.keys()),
default="M",
help="The error correction level to use. Choices are L (7%), "
"M (15%, default), Q (25%), and H (30%).",
)
parser.add_option(
"--ascii", help="Print as ascii even if stdout is piped.",
action="store_true"
)
parser.add_option(
"--output",
help="The output file. If not specified, the image is sent to "
"the standard output.",
)
if opts.factory:
module = default_factories.get(opts.factory, opts.factory)
try:
image_factory = get_factory(module)
except ValueError as e:
raise_error(str(e))
else:
image_factory = None
qr = qrcode.QRCode(
error_correction=error_correction[opts.error_correction],
image_factory=image_factory,
)
if args:
data = args[0]
data = data.encode(errors="surrogateescape")
else:
data = sys.stdin.buffer.read()
if opts.optimize is None:
qr.add_data(data)
else:
qr.add_data(data, optimize=opts.optimize)
if opts.output:
img = qr.make_image()
with open(opts.output, "wb") as out:
img.save(out)
else:
if image_factory is None and (os.isatty(sys.stdout.fileno()) or
opts.ascii):
qr.print_ascii(tty=not opts.ascii)
return
kwargs = {}
aliases: Optional[DrawerAliases] = getattr(
qr.image_factory, "drawer_aliases", None
)
if opts.factory_drawer:
if not aliases:
raise_error("The selected factory has no drawer aliases.")
if opts.factory_drawer not in aliases:
raise_error(
f"{opts.factory_drawer} factory drawer not found."
f" Expected {commas(aliases)}"
)
drawer_cls, drawer_kwargs = aliases[opts.factory_drawer]
kwargs["module_drawer"] = drawer_cls(**drawer_kwargs)
img = qr.make_image(**kwargs)
sys.stdout.flush()
img.save(sys.stdout.buffer)
================================================
File: qrcode/constants.py
================================================
# QR error correct levels
ERROR_CORRECT_L = 1
ERROR_CORRECT_M = 0
ERROR_CORRECT_Q = 3
ERROR_CORRECT_H = 2
================================================
File: qrcode/exceptions.py
================================================
class DataOverflowError(Exception):
pass
================================================
File: qrcode/main.py
================================================
import sys
from bisect import bisect_left
from typing import (
Dict,
Generic,
List,
NamedTuple,
Optional,
Type,
TypeVar,
cast,
overload,
Literal,
)
ModulesType = List[List[Optional[bool]]]
# Cache modules generated just based on the QR Code version
precomputed_qr_blanks: Dict[int, ModulesType] = {}
def _check_box_size(size):
if int(size) <= 0:
raise ValueError(f"Invalid box size (was {size}, expected larger than 0)")
def _check_border(size):
if int(size) < 0:
raise ValueError(
"Invalid border value (was %s, expected 0 or larger than that)" % size
)
def _check_mask_pattern(mask_pattern):
if mask_pattern is None:
return
if not isinstance(mask_pattern, int):
raise TypeError(
f"Invalid mask pattern (was {type(mask_pattern)}, expected int)"
)
if mask_pattern < 0 or mask_pattern > 7:
raise ValueError(f"Mask pattern should be in range(8) (got
{mask_pattern})")
def copy_2d_array(x):
return [row[:] for row in x]
class ActiveWithNeighbors(NamedTuple):
NW: bool
N: bool
NE: bool
W: bool
me: bool
E: bool
SW: bool
S: bool
SE: bool
class QRCode(Generic[GenericImage]):
modules: ModulesType
_version: Optional[int] = None
def __init__(
self,
version=None,
error_correction=constants.ERROR_CORRECT_M,
box_size=10,
border=4,
image_factory: Optional[Type[GenericImage]] = None,
mask_pattern=None,
):
_check_box_size(box_size)
_check_border(border)
self.version = version
self.error_correction = int(error_correction)
self.box_size = int(box_size)
# Spec says border should be at least four boxes wide, but allow for
# any (e.g. for producing printable QR codes).
self.border = int(border)
self.mask_pattern = mask_pattern
self.image_factory = image_factory
if image_factory is not None:
assert issubclass(image_factory, BaseImage)
self.clear()
@property
def version(self) -> int:
if self._version is None:
self.best_fit()
return cast(int, self._version)
@version.setter
def version(self, value) -> None:
if value is not None:
value = int(value)
util.check_version(value)
self._version = value
@property
def mask_pattern(self):
return self._mask_pattern
@mask_pattern.setter
def mask_pattern(self, pattern):
_check_mask_pattern(pattern)
self._mask_pattern = pattern
def clear(self):
"""
Reset the internal data.
"""
self.modules = [[]]
self.modules_count = 0
self.data_cache = None
self.data_list = []
:param fit: If ``True`` (or if a size has not been provided), find the
best fit for the data to avoid data overflow errors.
"""
if fit or (self.version is None):
self.best_fit(start=self.version)
if self.mask_pattern is None:
self.makeImpl(False, self.best_mask_pattern())
else:
self.makeImpl(False, self.mask_pattern)
if self.version in precomputed_qr_blanks:
self.modules = copy_2d_array(precomputed_qr_blanks[self.version])
else:
self.modules = [
[None] * self.modules_count for i in range(self.modules_count)
]
self.setup_position_probe_pattern(0, 0)
self.setup_position_probe_pattern(self.modules_count - 7, 0)
self.setup_position_probe_pattern(0, self.modules_count - 7)
self.setup_position_adjust_pattern()
self.setup_timing_pattern()
precomputed_qr_blanks[self.version] = copy_2d_array(self.modules)
self.setup_type_info(test, mask_pattern)
if self.version >= 7:
self.setup_type_number(test)
if self.data_cache is None:
self.data_cache = util.create_data(
self.version, self.error_correction, self.data_list
)
self.map_data(self.data_cache, mask_pattern)
if (
(0 <= r <= 6 and c in {0, 6})
or (0 <= c <= 6 and r in {0, 6})
or (2 <= r <= 4 and 2 <= c <= 4)
):
self.modules[row + r][col + c] = True
else:
self.modules[row + r][col + c] = False
needed_bits = len(buffer)
self.version = bisect_left(
util.BIT_LIMIT_TABLE[self.error_correction], needed_bits, start
)
if self.version == 41:
raise exceptions.DataOverflowError()
# Now check whether we need more bits for the mode sizes, recursing if
# our guess was too low
if mode_sizes is not util.mode_sizes_for_version(self.version):
self.best_fit(start=self.version)
return self.version
def best_mask_pattern(self):
"""
Find the most efficient mask pattern.
"""
min_lost_point = 0
pattern = 0
for i in range(8):
self.makeImpl(True, i)
lost_point = util.lost_point(self.modules)
return pattern
out = sys.stdout
if not out.isatty():
raise OSError("Not a tty")
if self.data_cache is None:
self.make()
modcount = self.modules_count
out.write("\x1b[1;47m" + (" " * (modcount * 2 + 4)) + "\x1b[0m\n")
for r in range(modcount):
out.write("\x1b[1;47m \x1b[40m")
for c in range(modcount):
if self.modules[r][c]:
out.write(" ")
else:
out.write("\x1b[1;47m \x1b[40m")
out.write("\x1b[1;47m \x1b[0m\n")
out.write("\x1b[1;47m" + (" " * (modcount * 2 + 4)) + "\x1b[0m\n")
out.flush()
if self.data_cache is None:
self.make()
modcount = self.modules_count
codes = [bytes((code,)).decode("cp437") for code in (255, 223, 220, 219)]
if tty:
invert = True
if invert:
codes.reverse()
@overload
def make_image(
self, image_factory: Literal[None] = None, **kwargs
) -> GenericImage: ...
@overload
def make_image(
self, image_factory: Type[GenericImageLocal] = None, **kwargs
) -> GenericImageLocal: ...
im = image_factory(
self.border,
self.modules_count,
self.box_size,
qrcode_modules=self.modules,
**kwargs,
)
if im.needs_drawrect:
for r in range(self.modules_count):
for c in range(self.modules_count):
if im.needs_context:
im.drawrect_context(r, c, qr=self)
elif self.modules[r][c]:
im.drawrect(r, c)
if im.needs_processing:
im.process()
return im
def setup_timing_pattern(self):
for r in range(8, self.modules_count - 8):
if self.modules[r][6] is not None:
continue
self.modules[r][6] = r % 2 == 0
def setup_position_adjust_pattern(self):
pos = util.pattern_position(self.version)
for i in range(len(pos)):
row = pos[i]
for j in range(len(pos)):
col = pos[j]
for i in range(18):
mod = not test and ((bits >> i) & 1) == 1
self.modules[i // 3][i % 3 + self.modules_count - 8 - 3] = mod
for i in range(18):
mod = not test and ((bits >> i) & 1) == 1
self.modules[i % 3 + self.modules_count - 8 - 3][i // 3] = mod
# vertical
for i in range(15):
mod = not test and ((bits >> i) & 1) == 1
if i < 6:
self.modules[i][8] = mod
elif i < 8:
self.modules[i + 1][8] = mod
else:
self.modules[self.modules_count - 15 + i][8] = mod
# horizontal
for i in range(15):
mod = not test and ((bits >> i) & 1) == 1
if i < 8:
self.modules[8][self.modules_count - i - 1] = mod
elif i < 9:
self.modules[8][15 - i - 1 + 1] = mod
else:
self.modules[8][15 - i - 1] = mod
# fixed module
self.modules[self.modules_count - 8][8] = not test
mask_func = util.mask_func(mask_pattern)
data_len = len(data)
while True:
for c in col_range:
if self.modules[row][c] is None:
dark = False
if mask_func(row, c):
dark = not dark
self.modules[row][c] = dark
bitIndex -= 1
if bitIndex == -1:
byteIndex += 1
bitIndex = 7
row += inc
def get_matrix(self):
"""
Return the QR Code as a multidimensional array, including the border.
if not self.border:
return self.modules
return code
================================================
File: qrcode/release.py
================================================
"""
This file provides zest.releaser entrypoints using when releasing new
qrcode versions.
"""
import os
import re
import datetime
def update_manpage(data):
"""
Update the version in the manpage document.
"""
if data["name"] != "qrcode":
return
base_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
filename = os.path.join(base_dir, "doc", "qr.1")
with open(filename) as f:
lines = f.readlines()
changed = False
for i, line in enumerate(lines):
if not line.startswith(".TH "):
continue
parts = re.split(r'"([^"]*)"', line)
if len(parts) < 5:
continue
changed = parts[3] != data["new_version"]
if changed:
# Update version
parts[3] = data["new_version"]
# Update date
parts[1] = datetime.datetime.now().strftime("%-d %b %Y")
lines[i] = '"'.join(parts)
break
if changed:
with open(filename, "w") as f:
for line in lines:
f.write(line)
================================================
File: qrcode/util.py
================================================
import math
import re
from typing import List
# QR encoding modes.
MODE_NUMBER = 1 << 0
MODE_ALPHA_NUM = 1 << 1
MODE_8BIT_BYTE = 1 << 2
MODE_KANJI = 1 << 3
PATTERN_POSITION_TABLE = [
[],
[6, 18],
[6, 22],
[6, 26],
[6, 30],
[6, 34],
[6, 22, 38],
[6, 24, 42],
[6, 26, 46],
[6, 28, 50],
[6, 30, 54],
[6, 32, 58],
[6, 34, 62],
[6, 26, 46, 66],
[6, 26, 48, 70],
[6, 26, 50, 74],
[6, 30, 54, 78],
[6, 30, 56, 82],
[6, 30, 58, 86],
[6, 34, 62, 90],
[6, 28, 50, 72, 94],
[6, 26, 50, 74, 98],
[6, 30, 54, 78, 102],
[6, 28, 54, 80, 106],
[6, 32, 58, 84, 110],
[6, 30, 58, 86, 114],
[6, 34, 62, 90, 118],
[6, 26, 50, 74, 98, 122],
[6, 30, 54, 78, 102, 126],
[6, 26, 52, 78, 104, 130],
[6, 30, 56, 82, 108, 134],
[6, 34, 60, 86, 112, 138],
[6, 30, 58, 86, 114, 142],
[6, 34, 62, 90, 118, 146],
[6, 30, 54, 78, 102, 126, 150],
[6, 24, 50, 76, 102, 128, 154],
[6, 28, 54, 80, 106, 132, 158],
[6, 32, 58, 84, 110, 136, 162],
[6, 26, 54, 82, 110, 138, 166],
[6, 30, 58, 86, 114, 142, 170],
]
PAD0 = 0xEC
PAD1 = 0x11
# Precompute bit count limits, indexed by error correction level and code size
def _data_count(block):
return block.data_count
BIT_LIMIT_TABLE = [
[0]
+ [
8 * sum(map(_data_count, base.rs_blocks(version, error_correction)))
for version in range(1, 41)
]
for error_correction in range(4)
]
def BCH_type_info(data):
d = data << 10
while BCH_digit(d) - BCH_digit(G15) >= 0:
d ^= G15 << (BCH_digit(d) - BCH_digit(G15))
def BCH_type_number(data):
d = data << 12
while BCH_digit(d) - BCH_digit(G18) >= 0:
d ^= G18 << (BCH_digit(d) - BCH_digit(G18))
return (data << 12) | d
def BCH_digit(data):
digit = 0
while data != 0:
digit += 1
data >>= 1
return digit
def pattern_position(version):
return PATTERN_POSITION_TABLE[version - 1]
def mask_func(pattern):
"""
Return the mask function for the given mask pattern.
"""
if pattern == 0: # 000
return lambda i, j: (i + j) % 2 == 0
if pattern == 1: # 001
return lambda i, j: i % 2 == 0
if pattern == 2: # 010
return lambda i, j: j % 3 == 0
if pattern == 3: # 011
return lambda i, j: (i + j) % 3 == 0
if pattern == 4: # 100
return lambda i, j: (math.floor(i / 2) + math.floor(j / 3)) % 2 == 0
if pattern == 5: # 101
return lambda i, j: (i * j) % 2 + (i * j) % 3 == 0
if pattern == 6: # 110
return lambda i, j: ((i * j) % 2 + (i * j) % 3) % 2 == 0
if pattern == 7: # 111
return lambda i, j: ((i * j) % 3 + (i + j) % 2) % 2 == 0
raise TypeError("Bad mask pattern: " + pattern) # pragma: no cover
def mode_sizes_for_version(version):
if version < 10:
return MODE_SIZE_SMALL
elif version < 27:
return MODE_SIZE_MEDIUM
else:
return MODE_SIZE_LARGE
def length_in_bits(mode, version):
if mode not in (MODE_NUMBER, MODE_ALPHA_NUM, MODE_8BIT_BYTE, MODE_KANJI):
raise TypeError(f"Invalid mode ({mode})") # pragma: no cover
check_version(version)
return mode_sizes_for_version(version)[mode]
def check_version(version):
if version < 1 or version > 40:
raise ValueError(f"Invalid version (was {version}, expected 1 to 40)")
def lost_point(modules):
modules_count = len(modules)
lost_point = 0
return lost_point
modules_range = range(modules_count)
container = [0] * (modules_count + 1)
lost_point += sum(
container[each_length] * (each_length - 2)
for each_length in range(5, modules_count + 1)
)
return lost_point
modules_range = range(modules_count - 1)
for row in modules_range:
this_row = modules[row]
next_row = modules[row + 1]
# use iter() and next() to skip next four-block. e.g.
# d a f if top-right a != b bottom-right,
# c b e then both abcd and abef won't lost any point.
modules_range_iter = iter(modules_range)
for col in modules_range_iter:
top_right = this_row[col + 1]
if top_right != next_row[col + 1]:
# reduce 33.3% of runtime via next().
# None: raise nothing if there is no next item.
next(modules_range_iter, None)
elif top_right != this_row[col]:
continue
elif top_right != next_row[col]:
continue
else:
lost_point += 3
return lost_point
return lost_point
def to_bytestring(data):
"""
Convert data to a (utf-8 encoded) byte-string if it isn't a byte-string
already.
"""
if not isinstance(data, bytes):
data = str(data).encode("utf-8")
return data
def optimal_mode(data):
"""
Calculate the optimal mode for this chunk of data.
"""
if data.isdigit():
return MODE_NUMBER
if RE_ALPHA_NUM.match(data):
return MODE_ALPHA_NUM
return MODE_8BIT_BYTE
class QRData:
"""
Data held in a QR compatible format.
if mode is None:
self.mode = optimal_mode(data)
else:
self.mode = mode
if mode not in (MODE_NUMBER, MODE_ALPHA_NUM, MODE_8BIT_BYTE):
raise TypeError(f"Invalid mode ({mode})") # pragma: no cover
if check_data and mode < optimal_mode(data): # pragma: no cover
raise ValueError(f"Provided data can not be represented in mode
{mode}")
self.data = data
def __len__(self):
return len(self.data)
class BitBuffer:
def __init__(self):
self.buffer: List[int] = []
self.length = 0
def __repr__(self):
return ".".join([str(n) for n in self.buffer])
def __len__(self):
return self.length
maxDcCount = 0
maxEcCount = 0
dcdata: List[List[int]] = []
ecdata: List[List[int]] = []
dcdata.append(current_dc)
ecdata.append(current_ec)
data = []
for i in range(maxDcCount):
for dc in dcdata:
if i < len(dc):
data.append(dc[i])
for i in range(maxEcCount):
for ec in ecdata:
if i < len(ec):
data.append(ec[i])
return data
================================================
File: qrcode/compat/etree.py
================================================
try:
import lxml.etree as ET # type: ignore # noqa: F401
except ImportError:
import xml.etree.ElementTree as ET # type: ignore # noqa: F401
================================================
File: qrcode/compat/png.py
================================================
# Try to import png library.
PngWriter = None
try:
from png import Writer as PngWriter # type: ignore # noqa: F401
except ImportError: # pragma: no cover
pass
================================================
File: qrcode/image/base.py
================================================
import abc
from typing import TYPE_CHECKING, Any, Dict, Optional, Tuple, Type, Union
if TYPE_CHECKING:
from qrcode.main import ActiveWithNeighbors, QRCode
class BaseImage:
"""
Base QRCode image output class.
"""
def process(self):
"""
Processes QR code after completion
"""
raise NotImplementedError("BaseImage.drawimage") # pragma: no cover
@abc.abstractmethod
def save(self, stream, kind=None):
"""
Save the image file.
"""
@abc.abstractmethod
def new_image(self, **kwargs) -> Any:
"""
Build the image class. Subclasses should return the class created.
"""
def init_new_image(self):
pass
class BaseImageWithDrawer(BaseImage):
default_drawer_class: Type[QRModuleDrawer]
drawer_aliases: DrawerAliases = {}
needs_context = True
module_drawer: "QRModuleDrawer"
eye_drawer: "QRModuleDrawer"
def __init__(
self,
*args,
module_drawer: Union[QRModuleDrawer, str, None] = None,
eye_drawer: Union[QRModuleDrawer, str, None] = None,
**kwargs,
):
self.module_drawer = (
self.get_drawer(module_drawer) or self.get_default_module_drawer()
)
# The eye drawer can be overridden by another module drawer as well,
# but you have to be more careful with these in order to make the QR
# code still parseable
self.eye_drawer = self.get_drawer(eye_drawer) or
self.get_default_eye_drawer()
super().__init__(*args, **kwargs)
def get_drawer(
self, drawer: Union[QRModuleDrawer, str, None]
) -> Optional[QRModuleDrawer]:
if not isinstance(drawer, str):
return drawer
drawer_cls, kwargs = self.drawer_aliases[drawer]
return drawer_cls(**kwargs)
def init_new_image(self):
self.module_drawer.initialize(img=self)
self.eye_drawer.initialize(img=self)
return super().init_new_image()
drawer.drawrect(box, is_active)
================================================
File: qrcode/image/pil.py
================================================
import qrcode.image.base
from PIL import Image, ImageDraw
class PilImage(qrcode.image.base.BaseImage):
"""
PIL image builder, default format is PNG.
"""
kind = "PNG"
try:
fill_color = fill_color.lower()
except AttributeError:
pass
try:
back_color = back_color.lower()
except AttributeError:
pass
================================================
File: qrcode/image/pure.py
================================================
from itertools import chain
class PyPNGImage(BaseImage):
"""
pyPNG image builder.
"""
kind = "PNG"
allowed_kinds = ("PNG",)
needs_drawrect = False
def rows_iter(self):
yield from self.border_rows_iter()
border_col = [1] * (self.box_size * self.border)
for module_row in self.modules:
row = (
border_col
+ list(
chain.from_iterable(
([not point] * self.box_size) for point in module_row
)
)
+ border_col
)
for _ in range(self.box_size):
yield row
yield from self.border_rows_iter()
def border_rows_iter(self):
border_row = [1] * (self.box_size * (self.width + self.border * 2))
for _ in range(self.border * self.box_size):
yield border_row
================================================
File: qrcode/image/styledpil.py
================================================
import qrcode.image.base
from PIL import Image
from qrcode.image.styles.colormasks import QRColorMask, SolidFillColorMask
from qrcode.image.styles.moduledrawers import SquareModuleDrawer
class StyledPilImage(qrcode.image.base.BaseImageWithDrawer):
"""
Styled PIL image builder, default format is PNG.
The module_drawer should extend the QRModuleDrawer class and implement the
drawrect_context(self, box, active, context), and probably also the
initialize function. This will draw an individual "module" or square on
the QR code.
The color_mask will extend the QRColorMask class and will at very least
implement the get_fg_pixel(image, x, y) function, calculating a color to
put on the image at the pixel location (x,y) (more advanced functionality
can be gotten by instead overriding other functions defined in the
QRColorMask class)
The Image can be specified either by path or with a Pillow Image, and if it
is there will be placed in the middle of the QR code. No effort is done to
ensure that the QR code is still legible after the image has been placed
there; Q or H level error correction levels are recommended to maintain
data integrity A resampling filter can be specified (defaulting to
PIL.Image.Resampling.LANCZOS) for resizing; see PIL.Image.resize() for possible
options for this parameter.
The image size can be controlled by `embeded_image_ratio` which is a ratio
between 0 and 1 that's set in relation to the overall width of the QR code.
"""
kind = "PNG"
needs_processing = True
color_mask: QRColorMask
default_drawer_class = SquareModuleDrawer
# the paint_color is the color the module drawer will use to draw upon
# a canvas During the color mask process, pixels that are paint_color
# are replaced by a newly-calculated color
self.paint_color = tuple(0 for i in self.color_mask.back_color)
if self.color_mask.has_transparency:
self.paint_color = tuple([*self.color_mask.back_color[:3], 255])
super().__init__(*args, **kwargs)
def init_new_image(self):
self.color_mask.initialize(self, self._img)
super().init_new_image()
def process(self):
self.color_mask.apply_mask(self._img)
if self.embeded_image:
self.draw_embeded_image()
def draw_embeded_image(self):
if not self.embeded_image:
return
total_width, _ = self._img.size
total_width = int(total_width)
logo_width_ish = int(total_width * self.embeded_image_ratio)
logo_offset = (
int((int(total_width / 2) - int(logo_width_ish / 2)) / self.box_size)
* self.box_size
) # round the offset to the nearest module
logo_position = (logo_offset, logo_offset)
logo_width = total_width - logo_offset * 2
region = self.embeded_image
region = region.resize((logo_width, logo_width),
self.embeded_image_resample)
if "A" in region.getbands():
self._img.alpha_composite(region, logo_position)
else:
self._img.paste(region, logo_position)
================================================
File: qrcode/image/svg.py
================================================
import decimal
from decimal import Decimal
from typing import List, Optional, Type, Union, overload, Literal
import qrcode.image.base
from qrcode.compat.etree import ET
from qrcode.image.styles.moduledrawers import svg as svg_drawers
from qrcode.image.styles.moduledrawers.base import QRModuleDrawer
class SvgFragmentImage(qrcode.image.base.BaseImageWithDrawer):
"""
SVG image builder
_SVG_namespace = "http://www.w3.org/2000/svg"
kind = "SVG"
allowed_kinds = ("SVG",)
default_drawer_class: Type[QRModuleDrawer] = svg_drawers.SvgSquareDrawer
@overload
def units(self, pixels: Union[int, Decimal], text: Literal[False]) ->
Decimal: ...
@overload
def units(self, pixels: Union[int, Decimal], text: Literal[True] = True) ->
str: ...
class SvgImage(SvgFragmentImage):
"""
Standalone SVG image builder
class SvgPathImage(SvgImage):
"""
SVG image builder with one single <path> element (removes white spaces
between individual QR points).
"""
QR_PATH_STYLE = {
"fill": "#000000",
"fill-opacity": "1",
"fill-rule": "nonzero",
"stroke": "none",
}
needs_processing = True
path: Optional[ET.Element] = None
default_drawer_class: Type[QRModuleDrawer] = svg_drawers.SvgPathSquareDrawer
drawer_aliases = {
"circle": (svg_drawers.SvgPathCircleDrawer, {}),
"gapped-circle": (
svg_drawers.SvgPathCircleDrawer,
{"size_ratio": Decimal(0.8)},
),
"gapped-square": (
svg_drawers.SvgPathSquareDrawer,
{"size_ratio": Decimal(0.8)},
),
}
def process(self):
# Store the path just in case someone wants to use it again or in some
# unique way.
self.path = ET.Element(
ET.QName("path"), # type: ignore
d="".join(self._subpaths),
id="qr-path",
**self.QR_PATH_STYLE,
)
self._subpaths = []
self._img.append(self.path)
class SvgFillImage(SvgImage):
"""
An SvgImage that fills the background to white.
"""
background = "white"
class SvgPathFillImage(SvgPathImage):
"""
An SvgPathImage that fills the background to white.
"""
background = "white"
================================================
File: qrcode/image/styles/colormasks.py
================================================
import math
class QRColorMask:
"""
QRColorMask is used to color in the QRCode.
All QRColorMask objects should be careful about RGB vs RGBA color spaces.
class RadialGradiantColorMask(QRColorMask):
"""
Fills in the foreground with a radial gradient from the center to the edge
"""
def __init__(
self, back_color=(255, 255, 255), center_color=(0, 0, 0), edge_color=(0, 0,
255)
):
self.back_color = back_color
self.center_color = center_color
self.edge_color = edge_color
self.has_transparency = len(self.back_color) == 4
class SquareGradiantColorMask(QRColorMask):
"""
Fills in the foreground with a square gradient from the center to the edge
"""
def __init__(
self, back_color=(255, 255, 255), center_color=(0, 0, 0), edge_color=(0, 0,
255)
):
self.back_color = back_color
self.center_color = center_color
self.edge_color = edge_color
self.has_transparency = len(self.back_color) == 4
class HorizontalGradiantColorMask(QRColorMask):
"""
Fills in the foreground with a gradient sweeping from the left to the right
"""
def __init__(
self, back_color=(255, 255, 255), left_color=(0, 0, 0), right_color=(0, 0,
255)
):
self.back_color = back_color
self.left_color = left_color
self.right_color = right_color
self.has_transparency = len(self.back_color) == 4
class VerticalGradiantColorMask(QRColorMask):
"""
Fills in the forefround with a gradient sweeping from the top to the bottom
"""
def __init__(
self, back_color=(255, 255, 255), top_color=(0, 0, 0), bottom_color=(0, 0,
255)
):
self.back_color = back_color
self.top_color = top_color
self.bottom_color = bottom_color
self.has_transparency = len(self.back_color) == 4
class ImageColorMask(QRColorMask):
"""
Fills in the foreground with pixels from another image, either passed by
path or passed by image object.
"""
def __init__(
self, back_color=(255, 255, 255), color_mask_path=None,
color_mask_image=None
):
self.back_color = back_color
if color_mask_image:
self.color_img = color_mask_image
else:
self.color_img = Image.open(color_mask_path)
self.has_transparency = len(self.back_color) == 4
================================================
File: qrcode/image/styles/moduledrawers/__init__.py
================================================
# For backwards compatibility, importing the PIL drawers here.
try:
from .pil import CircleModuleDrawer # noqa: F401
from .pil import GappedSquareModuleDrawer # noqa: F401
from .pil import HorizontalBarsDrawer # noqa: F401
from .pil import RoundedModuleDrawer # noqa: F401
from .pil import SquareModuleDrawer # noqa: F401
from .pil import VerticalBarsDrawer # noqa: F401
except ImportError:
pass
================================================
File: qrcode/image/styles/moduledrawers/base.py
================================================
import abc
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from qrcode.image.base import BaseImage
class QRModuleDrawer(abc.ABC):
"""
QRModuleDrawer exists to draw the modules of the QR Code onto images.
needs_neighbors = False
@abc.abstractmethod
def drawrect(self, box, is_active) -> None: ...
================================================
File: qrcode/image/styles/moduledrawers/pil.py
================================================
from typing import TYPE_CHECKING, List
if TYPE_CHECKING:
from qrcode.image.styledpil import StyledPilImage
from qrcode.main import ActiveWithNeighbors
# When drawing antialiased things, make them bigger and then shrink them down
# to size after the geometry has been drawn.
ANTIALIASING_FACTOR = 4
class StyledPilQRModuleDrawer(QRModuleDrawer):
"""
A base class for StyledPilImage module drawers.
img: "StyledPilImage"
class SquareModuleDrawer(StyledPilQRModuleDrawer):
"""
Draws the modules as simple squares
"""
The size_ratio determines how wide the squares are relative to the width of
the space they are printed in
"""
class CircleModuleDrawer(StyledPilQRModuleDrawer):
"""
Draws the modules as circles
"""
circle = None
class RoundedModuleDrawer(StyledPilQRModuleDrawer):
"""
Draws the modules with all 90 degree corners replaced with rounded edges.
radius_ratio determines the radius of the rounded edges - a value of 1
means that an isolated module will be drawn as a circle, while a value of 0
means that the radius of the rounded edge will be 0 (and thus back to 90
degrees again).
"""
needs_neighbors = True
def setup_corners(self):
mode = self.img.mode
back_color = self.img.color_mask.back_color
front_color = self.img.paint_color
self.SQUARE = Image.new(
mode, (self.corner_width, self.corner_width), front_color
)
class VerticalBarsDrawer(StyledPilQRModuleDrawer):
"""
Draws vertically contiguous groups of modules as long rounded rectangles,
with gaps between neighboring bands (the size of these gaps is inversely
proportional to the horizontal_shrink).
"""
needs_neighbors = True
def setup_edges(self):
mode = self.img.mode
back_color = self.img.color_mask.back_color
front_color = self.img.paint_color
height = self.half_height
width = height * 2
shrunken_width = int(width * self.horizontal_shrink)
self.SQUARE = Image.new(mode, (shrunken_width, height), front_color)
needs_neighbors = True
def setup_edges(self):
mode = self.img.mode
back_color = self.img.color_mask.back_color
front_color = self.img.paint_color
width = self.half_width
height = width * 2
shrunken_height = int(height * self.vertical_shrink)
self.SQUARE = Image.new(mode, (width, shrunken_height), front_color)
self.ROUND_LEFT = base.resize(
(width, shrunken_height), Image.Resampling.LANCZOS
)
self.ROUND_RIGHT =
self.ROUND_LEFT.transpose(Image.Transpose.FLIP_LEFT_RIGHT)
================================================
File: qrcode/image/styles/moduledrawers/svg.py
================================================
import abc
from decimal import Decimal
from typing import TYPE_CHECKING, NamedTuple
if TYPE_CHECKING:
from qrcode.image.svg import SvgFragmentImage, SvgPathImage
ANTIALIASING_FACTOR = 4
class Coords(NamedTuple):
x0: Decimal
y0: Decimal
x1: Decimal
y1: Decimal
xh: Decimal
yh: Decimal
class BaseSvgQRModuleDrawer(QRModuleDrawer):
img: "SvgFragmentImage"
return Coords(
x,
y,
x + self.box_size,
y + self.box_size,
x + self.box_half,
y + self.box_half,
)
class SvgQRModuleDrawer(BaseSvgQRModuleDrawer):
tag = "rect"
@abc.abstractmethod
def el(self, box): ...
class SvgSquareDrawer(SvgQRModuleDrawer):
def initialize(self, *args, **kwargs) -> None:
super().initialize(*args, **kwargs)
self.unit_size = self.img.units(self.box_size)
class SvgCircleDrawer(SvgQRModuleDrawer):
tag = "circle"
class SvgPathQRModuleDrawer(BaseSvgQRModuleDrawer):
img: "SvgPathImage"
@abc.abstractmethod
def subpath(self, box) -> str: ...
class SvgPathSquareDrawer(SvgPathQRModuleDrawer):
def subpath(self, box) -> str:
coords = self.coords(box)
x0 = self.img.units(coords.x0, text=False)
y0 = self.img.units(coords.y0, text=False)
x1 = self.img.units(coords.x1, text=False)
y1 = self.img.units(coords.y1, text=False)
return f"M{x0},{y0}H{x1}V{y1}H{x0}z"
class SvgPathCircleDrawer(SvgPathQRModuleDrawer):
def initialize(self, *args, **kwargs) -> None:
super().initialize(*args, **kwargs)
================================================
File: qrcode/tests/consts.py
================================================
UNICODE_TEXT = "\u03b1\u03b2\u03b3"
WHITE = (255, 255, 255)
BLACK = (0, 0, 0)
RED = (255, 0, 0)
================================================
File: qrcode/tests/test_example.py
================================================
from unittest import mock
import pytest
@mock.patch("PIL.Image.Image.show")
def test_example(mock_show):
run_example()
mock_show.assert_called_with()
================================================
File: qrcode/tests/test_qrcode.py
================================================
import io
from unittest import mock
import pytest
import qrcode
import qrcode.util
from qrcode.exceptions import DataOverflowError
from qrcode.image.base import BaseImage
from qrcode.tests.consts import UNICODE_TEXT
from qrcode.util import MODE_8BIT_BYTE, MODE_ALPHA_NUM, MODE_NUMBER, QRData
def test_basic():
qr = qrcode.QRCode(version=1)
qr.add_data("a")
qr.make(fit=False)
def test_large():
qr = qrcode.QRCode(version=27)
qr.add_data("a")
qr.make(fit=False)
def test_invalid_version():
with pytest.raises(ValueError):
qrcode.QRCode(version=42)
def test_invalid_border():
with pytest.raises(ValueError):
qrcode.QRCode(border=-1)
def test_overflow():
qr = qrcode.QRCode(version=1)
qr.add_data("abcdefghijklmno")
with pytest.raises(DataOverflowError):
qr.make(fit=False)
def test_add_qrdata():
qr = qrcode.QRCode(version=1)
data = QRData("a")
qr.add_data(data)
qr.make(fit=False)
def test_fit():
qr = qrcode.QRCode()
qr.add_data("a")
qr.make()
assert qr.version == 1
qr.add_data("bcdefghijklmno")
qr.make()
assert qr.version == 2
def test_mode_number():
qr = qrcode.QRCode()
qr.add_data("1234567890123456789012345678901234", optimize=0)
qr.make()
assert qr.version == 1
assert qr.data_list[0].mode == MODE_NUMBER
def test_mode_alpha():
qr = qrcode.QRCode()
qr.add_data("ABCDEFGHIJ1234567890", optimize=0)
qr.make()
assert qr.version == 1
assert qr.data_list[0].mode == MODE_ALPHA_NUM
def test_regression_mode_comma():
qr = qrcode.QRCode()
qr.add_data(",", optimize=0)
qr.make()
assert qr.data_list[0].mode == MODE_8BIT_BYTE
def test_mode_8bit():
qr = qrcode.QRCode()
qr.add_data("abcABC" + UNICODE_TEXT, optimize=0)
qr.make()
assert qr.version == 1
assert qr.data_list[0].mode == MODE_8BIT_BYTE
def test_mode_8bit_newline():
qr = qrcode.QRCode()
qr.add_data("ABCDEFGHIJ1234567890\n", optimize=0)
qr.make()
assert qr.data_list[0].mode == MODE_8BIT_BYTE
def test_make_image_with_wrong_pattern():
with pytest.raises(TypeError):
qrcode.QRCode(mask_pattern="string pattern")
with pytest.raises(ValueError):
qrcode.QRCode(mask_pattern=-1)
with pytest.raises(ValueError):
qrcode.QRCode(mask_pattern=42)
def test_mask_pattern_setter():
qr = qrcode.QRCode()
with pytest.raises(TypeError):
qr.mask_pattern = "string pattern"
with pytest.raises(ValueError):
qr.mask_pattern = -1
with pytest.raises(ValueError):
qr.mask_pattern = 8
def test_qrcode_bad_factory():
with pytest.raises(TypeError):
qrcode.QRCode(image_factory="not_BaseImage") # type: ignore
with pytest.raises(AssertionError):
qrcode.QRCode(image_factory=dict) # type: ignore
def test_qrcode_factory():
class MockFactory(BaseImage):
drawrect = mock.Mock()
new_image = mock.Mock()
qr = qrcode.QRCode(image_factory=MockFactory)
qr.add_data(UNICODE_TEXT)
qr.make_image()
assert MockFactory.new_image.called
assert MockFactory.drawrect.called
def test_optimize():
qr = qrcode.QRCode()
text = "A1abc12345def1HELLOa"
qr.add_data(text, optimize=4)
qr.make()
assert [d.mode for d in qr.data_list] == [
MODE_8BIT_BYTE,
MODE_NUMBER,
MODE_8BIT_BYTE,
MODE_ALPHA_NUM,
MODE_8BIT_BYTE,
]
assert qr.version == 2
def test_optimize_short():
qr = qrcode.QRCode()
text = "A1abc1234567def1HELLOa"
qr.add_data(text, optimize=7)
qr.make()
assert len(qr.data_list) == 3
assert [d.mode for d in qr.data_list] == [
MODE_8BIT_BYTE,
MODE_NUMBER,
MODE_8BIT_BYTE,
]
assert qr.version == 2
def test_optimize_longer_than_data():
qr = qrcode.QRCode()
text = "ABCDEFGHIJK"
qr.add_data(text, optimize=12)
assert len(qr.data_list) == 1
assert qr.data_list[0].mode == MODE_ALPHA_NUM
def test_optimize_size():
text = "A1abc12345123451234512345def1HELLOHELLOHELLOHELLOa" * 5
qr = qrcode.QRCode()
qr.add_data(text)
qr.make()
assert qr.version == 10
qr = qrcode.QRCode()
qr.add_data(text, optimize=0)
qr.make()
assert qr.version == 11
def test_qrdata_repr():
data = b"hello"
data_obj = qrcode.util.QRData(data)
assert repr(data_obj) == repr(data)
def test_print_ascii_stdout():
qr = qrcode.QRCode()
with mock.patch("sys.stdout") as fake_stdout:
fake_stdout.isatty.return_value = None
with pytest.raises(OSError):
qr.print_ascii(tty=True)
assert fake_stdout.isatty.called
def test_print_ascii():
qr = qrcode.QRCode(border=0)
f = io.StringIO()
qr.print_ascii(out=f)
printed = f.getvalue()
f.close()
expected = "\u2588\u2580\u2580\u2580\u2580\u2580\u2588"
assert printed[: len(expected)] == expected
f = io.StringIO()
f.isatty = lambda: True
qr.print_ascii(out=f, tty=True)
printed = f.getvalue()
f.close()
expected = "\x1b[48;5;232m\x1b[38;5;255m" + "\xa0\u2584\u2584\u2584\u2584\
u2584\xa0"
assert printed[: len(expected)] == expected
def test_print_tty_stdout():
qr = qrcode.QRCode()
with mock.patch("sys.stdout") as fake_stdout:
fake_stdout.isatty.return_value = None
pytest.raises(OSError, qr.print_tty)
assert fake_stdout.isatty.called
def test_print_tty():
qr = qrcode.QRCode()
f = io.StringIO()
f.isatty = lambda: True
qr.print_tty(out=f)
printed = f.getvalue()
f.close()
BOLD_WHITE_BG = "\x1b[1;47m"
BLACK_BG = "\x1b[40m"
WHITE_BLOCK = BOLD_WHITE_BG + " " + BLACK_BG
EOL = "\x1b[0m\n"
expected = BOLD_WHITE_BG + " " * 23 + EOL + WHITE_BLOCK + " " * 7 +
WHITE_BLOCK
assert printed[: len(expected)] == expected
def test_get_matrix():
qr = qrcode.QRCode(border=0)
qr.add_data("1")
assert qr.get_matrix() == qr.modules
def test_get_matrix_border():
qr = qrcode.QRCode(border=1)
qr.add_data("1")
matrix = [row[1:-1] for row in qr.get_matrix()[1:-1]]
assert matrix == qr.modules
def test_negative_size_at_construction():
with pytest.raises(ValueError):
qrcode.QRCode(box_size=-1)
def test_negative_size_at_usage():
qr = qrcode.QRCode()
qr.box_size = -1
with pytest.raises(ValueError):
qr.make_image()
================================================
File: qrcode/tests/test_qrcode_pil.py
================================================
import io
import pytest
import qrcode
import qrcode.util
from qrcode.tests.consts import BLACK, RED, UNICODE_TEXT, WHITE
if Image:
from qrcode.image.styledpil import StyledPilImage
from qrcode.image.styles import colormasks, moduledrawers
def test_render_pil():
qr = qrcode.QRCode()
qr.add_data(UNICODE_TEXT)
img = qr.make_image()
img.save(io.BytesIO())
assert isinstance(img.get_image(), Image.Image)
@pytest.mark.parametrize("back_color", ["TransParent", "red", (255, 195, 235)])
def test_render_pil_background(back_color):
qr = qrcode.QRCode()
qr.add_data(UNICODE_TEXT)
img = qr.make_image(back_color="TransParent")
img.save(io.BytesIO())
def test_render_pil_with_rgb_color_tuples():
qr = qrcode.QRCode()
qr.add_data(UNICODE_TEXT)
img = qr.make_image(back_color=(255, 195, 235), fill_color=(55, 95, 35))
img.save(io.BytesIO())
def test_render_with_pattern():
qr = qrcode.QRCode(mask_pattern=3)
qr.add_data(UNICODE_TEXT)
img = qr.make_image()
img.save(io.BytesIO())
def test_render_styled_Image():
qr = qrcode.QRCode(error_correction=qrcode.ERROR_CORRECT_L)
qr.add_data(UNICODE_TEXT)
img = qr.make_image(image_factory=StyledPilImage)
img.save(io.BytesIO())
def test_render_styled_with_embeded_image():
embeded_img = Image.new("RGB", (10, 10), color="red")
qr = qrcode.QRCode(error_correction=qrcode.ERROR_CORRECT_H)
qr.add_data(UNICODE_TEXT)
img = qr.make_image(image_factory=StyledPilImage, embeded_image=embeded_img)
img.save(io.BytesIO())
def test_render_styled_with_embeded_image_path(tmp_path):
tmpfile = str(tmp_path / "test.png")
embeded_img = Image.new("RGB", (10, 10), color="red")
embeded_img.save(tmpfile)
qr = qrcode.QRCode(error_correction=qrcode.ERROR_CORRECT_H)
qr.add_data(UNICODE_TEXT)
img = qr.make_image(image_factory=StyledPilImage, embeded_image_path=tmpfile)
img.save(io.BytesIO())
@pytest.mark.parametrize(
"drawer",
[
moduledrawers.CircleModuleDrawer,
moduledrawers.GappedSquareModuleDrawer,
moduledrawers.HorizontalBarsDrawer,
moduledrawers.RoundedModuleDrawer,
moduledrawers.SquareModuleDrawer,
moduledrawers.VerticalBarsDrawer,
],
)
def test_render_styled_with_drawer(drawer):
qr = qrcode.QRCode(error_correction=qrcode.ERROR_CORRECT_L)
qr.add_data(UNICODE_TEXT)
img = qr.make_image(
image_factory=StyledPilImage,
module_drawer=drawer(),
)
img.save(io.BytesIO())
@pytest.mark.parametrize(
"mask",
[
colormasks.SolidFillColorMask(),
colormasks.SolidFillColorMask(back_color=WHITE, front_color=RED),
colormasks.SolidFillColorMask(back_color=(255, 0, 255, 255),
front_color=RED),
colormasks.RadialGradiantColorMask(
back_color=WHITE, center_color=BLACK, edge_color=RED
),
colormasks.SquareGradiantColorMask(
back_color=WHITE, center_color=BLACK, edge_color=RED
),
colormasks.HorizontalGradiantColorMask(
back_color=WHITE, left_color=RED, right_color=BLACK
),
colormasks.VerticalGradiantColorMask(
back_color=WHITE, top_color=RED, bottom_color=BLACK
),
colormasks.ImageColorMask(
back_color=WHITE, color_mask_image=Image.new("RGB", (10, 10),
color="red")
),
],
)
def test_render_styled_with_mask(mask):
qr = qrcode.QRCode(error_correction=qrcode.ERROR_CORRECT_L)
qr.add_data(UNICODE_TEXT)
img = qr.make_image(image_factory=StyledPilImage, color_mask=mask)
img.save(io.BytesIO())
def test_embedded_image_and_error_correction(tmp_path):
"If an embedded image is specified, error correction must be the highest so the
QR code is readable"
tmpfile = str(tmp_path / "test.png")
embedded_img = Image.new("RGB", (10, 10), color="red")
embedded_img.save(tmpfile)
qr = qrcode.QRCode(error_correction=qrcode.ERROR_CORRECT_L)
qr.add_data(UNICODE_TEXT)
with pytest.raises(ValueError):
qr.make_image(embeded_image_path=tmpfile)
with pytest.raises(ValueError):
qr.make_image(embeded_image=embedded_img)
qr = qrcode.QRCode(error_correction=qrcode.ERROR_CORRECT_M)
qr.add_data(UNICODE_TEXT)
with pytest.raises(ValueError):
qr.make_image(embeded_image_path=tmpfile)
with pytest.raises(ValueError):
qr.make_image(embeded_image=embedded_img)
qr = qrcode.QRCode(error_correction=qrcode.ERROR_CORRECT_Q)
qr.add_data(UNICODE_TEXT)
with pytest.raises(ValueError):
qr.make_image(embeded_image_path=tmpfile)
with pytest.raises(ValueError):
qr.make_image(embeded_image=embedded_img)
def test_shortcut():
qrcode.make("image")
================================================
File: qrcode/tests/test_qrcode_pypng.py
================================================
import io
from unittest import mock
import pytest
import qrcode
import qrcode.util
from qrcode.image.pure import PyPNGImage
from qrcode.tests.consts import UNICODE_TEXT
def test_render_pypng():
qr = qrcode.QRCode()
qr.add_data(UNICODE_TEXT)
img = qr.make_image(image_factory=PyPNGImage)
assert isinstance(img.get_image(), png.Writer)
def test_render_pypng_to_str():
qr = qrcode.QRCode()
qr.add_data(UNICODE_TEXT)
img = qr.make_image(image_factory=PyPNGImage)
assert isinstance(img.get_image(), png.Writer)
mock_open = mock.mock_open()
with mock.patch("qrcode.image.pure.open", mock_open, create=True):
img.save("test_file.png")
mock_open.assert_called_once_with("test_file.png", "wb")
mock_open("test_file.png", "wb").write.assert_called()
================================================
File: qrcode/tests/test_qrcode_svg.py
================================================
import io
import qrcode
from qrcode.image import svg
from qrcode.tests.consts import UNICODE_TEXT
class SvgImageWhite(svg.SvgImage):
background = "white"
def test_render_svg():
qr = qrcode.QRCode()
qr.add_data(UNICODE_TEXT)
img = qr.make_image(image_factory=svg.SvgImage)
img.save(io.BytesIO())
def test_render_svg_path():
qr = qrcode.QRCode()
qr.add_data(UNICODE_TEXT)
img = qr.make_image(image_factory=svg.SvgPathImage)
img.save(io.BytesIO())
def test_render_svg_fragment():
qr = qrcode.QRCode()
qr.add_data(UNICODE_TEXT)
img = qr.make_image(image_factory=svg.SvgFragmentImage)
img.save(io.BytesIO())
def test_svg_string():
qr = qrcode.QRCode()
qr.add_data(UNICODE_TEXT)
img = qr.make_image(image_factory=svg.SvgFragmentImage)
file_like = io.BytesIO()
img.save(file_like)
file_like.seek(0)
assert file_like.read() in img.to_string()
def test_render_svg_with_background():
qr = qrcode.QRCode()
qr.add_data(UNICODE_TEXT)
img = qr.make_image(image_factory=SvgImageWhite)
img.save(io.BytesIO())
def test_svg_circle_drawer():
qr = qrcode.QRCode()
qr.add_data(UNICODE_TEXT)
img = qr.make_image(image_factory=svg.SvgPathImage, module_drawer="circle")
img.save(io.BytesIO())
================================================
File: qrcode/tests/test_release.py
================================================
import builtins
import datetime
import re
from unittest import mock
OPEN = f"{builtins.__name__}.open"
DATA = 'test\n.TH "date" "version" "description"\nthis'
================================================
File: qrcode/tests/test_script.py
================================================
import sys
from unittest import mock
import pytest
def test_optimize():
pytest.importorskip("PIL", reason="Requires PIL")
main("testtext --optimize 0".split())
def test_factory():
main(["testtext", "--factory", "svg"])
def test_bad_factory():
with pytest.raises(SystemExit):
main(["testtext", "--factory", "nope"])
def test_output(tmp_path):
pytest.importorskip("PIL", reason="Requires PIL")
main(["testtext", "--output", str(tmp_path / "test.png")])
def test_factory_drawer_none(capsys):
pytest.importorskip("PIL", reason="Requires PIL")
with pytest.raises(SystemExit):
main("testtext --factory pil --factory-drawer nope".split())
assert "The selected factory has no drawer aliases" in capsys.readouterr()[1]
def test_factory_drawer_bad(capsys):
with pytest.raises(SystemExit):
main("testtext --factory svg --factory-drawer sobad".split())
assert "sobad factory drawer not found" in capsys.readouterr()[1]
def test_factory_drawer(capsys):
main("testtext --factory svg --factory-drawer circle".split())
def test_commas():
assert commas([]) == ""
assert commas(["A"]) == "A"
assert commas("AB") == "A or B"
assert commas("ABC") == "A, B or C"
assert commas("ABC", joiner="and") == "A, B and C"
================================================
File: qrcode/tests/test_util.py
================================================
import pytest
def test_check_wrong_version():
with pytest.raises(ValueError):
util.check_version(0)
with pytest.raises(ValueError):
util.check_version(41)
================================================
File: .github/workflows/linting.yml
================================================
name: Code Quality Checks
on:
push:
jobs:
linting:
name: Code Quality Checks
runs-on: ubuntu-latest
steps:
- name: Checkout Code
uses: actions/checkout@v4
================================================
File: .github/workflows/push.yml
================================================
name: Testsuite Run
on: [push]
jobs:
build:
runs-on: ubuntu-latest
strategy:
max-parallel: 4
matrix:
python-version: ["3.9", "3.10", "3.11", "3.12"]
steps:
- uses: actions/checkout@v4
================================================
File: .github/workflows/python-app.yml
================================================
# This workflow will install Python dependencies, run tests and lint with a single
version of Python
# For more information see: https://help.github.com/actions/language-and-framework-
guides/using-python-with-github-actions
jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Set up Python
uses: actions/setup-python@v3
with:
python-version: "3.10"
- name: Install dependencies
run: |
python -m pip install --upgrade pip
python -m pip install flake8
- name: Lint with flake8
run: |
# stop the build if there are Python syntax errors or undefined names
flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics
# exit-zero treats all errors as warnings. The GitHub editor is 127 chars
wide
flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --
statistics
tests:
needs: lint
runs-on: ubuntu-latest
strategy:
matrix:
python-version: [3.7, 3.8, 3.9, "3.10"]
steps:
- uses: actions/checkout@v2
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v3
with:
python-version: ${{ matrix.python-version }}
- name: Install dependencies
run: |
python -m pip install --upgrade pip
python -m pip install tox tox-gh-actions
- name: Test with tox
run: tox
env:
PLATFORM: ${{ matrix.platform }}
- uses: actions/upload-artifact@v2
with:
name: coverage-results
path: .coverage.*
coverage:
needs: tests
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v3
with:
python-version: 3.9
- name: Install dependencies
run: |
python -m pip install coverage
- uses: actions/download-artifact@v2
with:
name: coverage-results
- name: Coverage check
run: |
coverage combine
coverage report --omit="qrcode/tests/*" --fail-under=98 -m
coverage report --include="qrcode/tests/*" --fail-under=100 -m