updates
This commit is contained in:
223
Backend/venv/lib/python3.12/site-packages/qrcode/LUT.py
Normal file
223
Backend/venv/lib/python3.12/site-packages/qrcode/LUT.py
Normal file
@@ -0,0 +1,223 @@
|
||||
# Store all kinds of lookup table.
|
||||
|
||||
|
||||
# # generate rsPoly lookup table.
|
||||
|
||||
# from qrcode import base
|
||||
|
||||
# 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)
|
||||
|
||||
# Result. Usage: input: ecCount, output: Polynomial.num
|
||||
# e.g. rsPoly = base.Polynomial(LUT.rsPoly_LUT[ecCount], 0)
|
||||
rsPoly_LUT = {
|
||||
7: [1, 127, 122, 154, 164, 11, 68, 117],
|
||||
10: [1, 216, 194, 159, 111, 199, 94, 95, 113, 157, 193],
|
||||
13: [1, 137, 73, 227, 17, 177, 17, 52, 13, 46, 43, 83, 132, 120],
|
||||
15: [1, 29, 196, 111, 163, 112, 74, 10, 105, 105, 139, 132, 151, 32, 134, 26],
|
||||
16: [1, 59, 13, 104, 189, 68, 209, 30, 8, 163, 65, 41, 229, 98, 50, 36, 59],
|
||||
17: [1, 119, 66, 83, 120, 119, 22, 197, 83, 249, 41, 143, 134, 85, 53, 125, 99, 79],
|
||||
18: [
|
||||
1,
|
||||
239,
|
||||
251,
|
||||
183,
|
||||
113,
|
||||
149,
|
||||
175,
|
||||
199,
|
||||
215,
|
||||
240,
|
||||
220,
|
||||
73,
|
||||
82,
|
||||
173,
|
||||
75,
|
||||
32,
|
||||
67,
|
||||
217,
|
||||
146,
|
||||
],
|
||||
20: [
|
||||
1,
|
||||
152,
|
||||
185,
|
||||
240,
|
||||
5,
|
||||
111,
|
||||
99,
|
||||
6,
|
||||
220,
|
||||
112,
|
||||
150,
|
||||
69,
|
||||
36,
|
||||
187,
|
||||
22,
|
||||
228,
|
||||
198,
|
||||
121,
|
||||
121,
|
||||
165,
|
||||
174,
|
||||
],
|
||||
22: [
|
||||
1,
|
||||
89,
|
||||
179,
|
||||
131,
|
||||
176,
|
||||
182,
|
||||
244,
|
||||
19,
|
||||
189,
|
||||
69,
|
||||
40,
|
||||
28,
|
||||
137,
|
||||
29,
|
||||
123,
|
||||
67,
|
||||
253,
|
||||
86,
|
||||
218,
|
||||
230,
|
||||
26,
|
||||
145,
|
||||
245,
|
||||
],
|
||||
24: [
|
||||
1,
|
||||
122,
|
||||
118,
|
||||
169,
|
||||
70,
|
||||
178,
|
||||
237,
|
||||
216,
|
||||
102,
|
||||
115,
|
||||
150,
|
||||
229,
|
||||
73,
|
||||
130,
|
||||
72,
|
||||
61,
|
||||
43,
|
||||
206,
|
||||
1,
|
||||
237,
|
||||
247,
|
||||
127,
|
||||
217,
|
||||
144,
|
||||
117,
|
||||
],
|
||||
26: [
|
||||
1,
|
||||
246,
|
||||
51,
|
||||
183,
|
||||
4,
|
||||
136,
|
||||
98,
|
||||
199,
|
||||
152,
|
||||
77,
|
||||
56,
|
||||
206,
|
||||
24,
|
||||
145,
|
||||
40,
|
||||
209,
|
||||
117,
|
||||
233,
|
||||
42,
|
||||
135,
|
||||
68,
|
||||
70,
|
||||
144,
|
||||
146,
|
||||
77,
|
||||
43,
|
||||
94,
|
||||
],
|
||||
28: [
|
||||
1,
|
||||
252,
|
||||
9,
|
||||
28,
|
||||
13,
|
||||
18,
|
||||
251,
|
||||
208,
|
||||
150,
|
||||
103,
|
||||
174,
|
||||
100,
|
||||
41,
|
||||
167,
|
||||
12,
|
||||
247,
|
||||
56,
|
||||
117,
|
||||
119,
|
||||
233,
|
||||
127,
|
||||
181,
|
||||
100,
|
||||
121,
|
||||
147,
|
||||
176,
|
||||
74,
|
||||
58,
|
||||
197,
|
||||
],
|
||||
30: [
|
||||
1,
|
||||
212,
|
||||
246,
|
||||
77,
|
||||
73,
|
||||
195,
|
||||
192,
|
||||
75,
|
||||
98,
|
||||
5,
|
||||
70,
|
||||
103,
|
||||
177,
|
||||
22,
|
||||
217,
|
||||
138,
|
||||
51,
|
||||
181,
|
||||
246,
|
||||
72,
|
||||
25,
|
||||
18,
|
||||
46,
|
||||
228,
|
||||
74,
|
||||
216,
|
||||
195,
|
||||
11,
|
||||
106,
|
||||
130,
|
||||
150,
|
||||
],
|
||||
}
|
||||
30
Backend/venv/lib/python3.12/site-packages/qrcode/__init__.py
Normal file
30
Backend/venv/lib/python3.12/site-packages/qrcode/__init__.py
Normal file
@@ -0,0 +1,30 @@
|
||||
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,
|
||||
)
|
||||
|
||||
from qrcode import image # noqa
|
||||
|
||||
|
||||
def run_example(data="http://www.lincolnloop.com", *args, **kwargs):
|
||||
"""
|
||||
Build an example QR Code and display it.
|
||||
|
||||
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:])
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
313
Backend/venv/lib/python3.12/site-packages/qrcode/base.py
Normal file
313
Backend/venv/lib/python3.12/site-packages/qrcode/base.py
Normal file
@@ -0,0 +1,313 @@
|
||||
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(8, 256):
|
||||
EXP_TABLE[i] = (
|
||||
EXP_TABLE[i - 4] ^ EXP_TABLE[i - 5] ^ EXP_TABLE[i - 6] ^ EXP_TABLE[i - 8]
|
||||
)
|
||||
|
||||
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
|
||||
|
||||
self.num = num[offset:] + [0] * shift
|
||||
|
||||
def __getitem__(self, index):
|
||||
return self.num[index]
|
||||
|
||||
def __iter__(self):
|
||||
return iter(self.num)
|
||||
|
||||
def __len__(self):
|
||||
return len(self.num)
|
||||
|
||||
def __mul__(self, other):
|
||||
num = [0] * (len(self) + len(other) - 1)
|
||||
|
||||
for i, item in enumerate(self):
|
||||
for j, other_item in enumerate(other):
|
||||
num[i + j] ^= gexp(glog(item) + glog(other_item))
|
||||
|
||||
return Polynomial(num, 0)
|
||||
|
||||
def __mod__(self, other):
|
||||
difference = len(self) - len(other)
|
||||
if difference < 0:
|
||||
return self
|
||||
|
||||
ratio = glog(self[0]) - glog(other[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
|
||||
|
||||
|
||||
def rs_blocks(version, error_correction):
|
||||
if error_correction not in RS_BLOCK_OFFSET: # pragma: no cover
|
||||
raise Exception(
|
||||
"bad rs block @ version: %s / error_correction: %s"
|
||||
% (version, error_correction)
|
||||
)
|
||||
offset = RS_BLOCK_OFFSET[error_correction]
|
||||
rs_block = RS_BLOCK_TABLE[(version - 1) * 4 + offset]
|
||||
|
||||
blocks = []
|
||||
|
||||
for i in range(0, len(rs_block), 3):
|
||||
count, total_count, data_count = rs_block[i : i + 3]
|
||||
for _ in range(count):
|
||||
blocks.append(RSBlock(total_count, data_count))
|
||||
|
||||
return blocks
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,4 @@
|
||||
try:
|
||||
import lxml.etree as ET # type: ignore # noqa: F401
|
||||
except ImportError:
|
||||
import xml.etree.ElementTree as ET # type: ignore # noqa: F401
|
||||
@@ -0,0 +1,12 @@
|
||||
# Try to import PIL in either of the two ways it can be installed.
|
||||
Image = None
|
||||
ImageDraw = None
|
||||
|
||||
try:
|
||||
from PIL import Image, ImageDraw # type: ignore # noqa: F401
|
||||
except ImportError: # pragma: no cover
|
||||
try:
|
||||
import Image # type: ignore # noqa: F401
|
||||
import ImageDraw # type: ignore # noqa: F401
|
||||
except ImportError:
|
||||
pass
|
||||
@@ -0,0 +1,179 @@
|
||||
#!/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
|
||||
|
||||
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:]
|
||||
from pkg_resources import get_distribution
|
||||
|
||||
version = get_distribution("qrcode").version
|
||||
parser = optparse.OptionParser(usage=(__doc__ or "").strip(), version=version)
|
||||
|
||||
# Wrap parser.error in a typed NoReturn method for better typing.
|
||||
def raise_error(msg: str) -> NoReturn:
|
||||
parser.error(msg)
|
||||
raise # pragma: no cover
|
||||
|
||||
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.",
|
||||
)
|
||||
|
||||
opts, args = parser.parse_args(args)
|
||||
|
||||
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)
|
||||
|
||||
|
||||
def get_factory(module: str) -> Type[BaseImage]:
|
||||
if "." not in module:
|
||||
raise ValueError("The image factory is not a full python path")
|
||||
module, name = module.rsplit(".", 1)
|
||||
imp = __import__(module, {}, {}, [name])
|
||||
return getattr(imp, name)
|
||||
|
||||
|
||||
def get_drawer_help() -> str:
|
||||
help: Dict[str, Set] = {}
|
||||
for alias, module in default_factories.items():
|
||||
try:
|
||||
image = get_factory(module)
|
||||
except ImportError: # pragma: no cover
|
||||
continue
|
||||
aliases: Optional[DrawerAliases] = getattr(image, "drawer_aliases", None)
|
||||
if not aliases:
|
||||
continue
|
||||
factories = help.setdefault(commas(aliases), set())
|
||||
factories.add(alias)
|
||||
|
||||
return ". ".join(
|
||||
f"For {commas(factories, 'and')}, use: {aliases}"
|
||||
for aliases, factories in help.items()
|
||||
)
|
||||
|
||||
|
||||
def commas(items: Iterable[str], joiner="or") -> str:
|
||||
items = tuple(items)
|
||||
if not items:
|
||||
return ""
|
||||
if len(items) == 1:
|
||||
return items[0]
|
||||
return f"{', '.join(items[:-1])} {joiner} {items[-1]}"
|
||||
|
||||
|
||||
if __name__ == "__main__": # pragma: no cover
|
||||
main()
|
||||
@@ -0,0 +1,5 @@
|
||||
# QR error correct levels
|
||||
ERROR_CORRECT_L = 1
|
||||
ERROR_CORRECT_M = 0
|
||||
ERROR_CORRECT_Q = 3
|
||||
ERROR_CORRECT_H = 2
|
||||
@@ -0,0 +1,2 @@
|
||||
class DataOverflowError(Exception):
|
||||
pass
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
164
Backend/venv/lib/python3.12/site-packages/qrcode/image/base.py
Normal file
164
Backend/venv/lib/python3.12/site-packages/qrcode/image/base.py
Normal file
@@ -0,0 +1,164 @@
|
||||
import abc
|
||||
from typing import TYPE_CHECKING, Any, Dict, Optional, Tuple, Type, Union
|
||||
|
||||
from qrcode.image.styles.moduledrawers.base import QRModuleDrawer
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from qrcode.main import ActiveWithNeighbors, QRCode
|
||||
|
||||
|
||||
DrawerAliases = Dict[str, Tuple[Type[QRModuleDrawer], Dict[str, Any]]]
|
||||
|
||||
|
||||
class BaseImage:
|
||||
"""
|
||||
Base QRCode image output class.
|
||||
"""
|
||||
|
||||
kind: Optional[str] = None
|
||||
allowed_kinds: Optional[Tuple[str]] = None
|
||||
needs_context = False
|
||||
needs_processing = False
|
||||
needs_drawrect = True
|
||||
|
||||
def __init__(self, border, width, box_size, *args, **kwargs):
|
||||
self.border = border
|
||||
self.width = width
|
||||
self.box_size = box_size
|
||||
self.pixel_size = (self.width + self.border * 2) * self.box_size
|
||||
self.modules = kwargs.pop("qrcode_modules")
|
||||
self._img = self.new_image(**kwargs)
|
||||
self.init_new_image()
|
||||
|
||||
@abc.abstractmethod
|
||||
def drawrect(self, row, col):
|
||||
"""
|
||||
Draw a single rectangle of the QR code.
|
||||
"""
|
||||
|
||||
def drawrect_context(self, row: int, col: int, qr: "QRCode"):
|
||||
"""
|
||||
Draw a single rectangle of the QR code given the surrounding context
|
||||
"""
|
||||
raise NotImplementedError("BaseImage.drawrect_context") # pragma: no cover
|
||||
|
||||
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.
|
||||
"""
|
||||
|
||||
def pixel_box(self, row, col):
|
||||
"""
|
||||
A helper method for pixel-based image generators that specifies the
|
||||
four pixel coordinates for a single rect.
|
||||
"""
|
||||
x = (col + self.border) * self.box_size
|
||||
y = (row + self.border) * self.box_size
|
||||
return (
|
||||
(x, y),
|
||||
(x + self.box_size - 1, y + self.box_size - 1),
|
||||
)
|
||||
|
||||
@abc.abstractmethod
|
||||
def new_image(self, **kwargs) -> Any:
|
||||
"""
|
||||
Build the image class. Subclasses should return the class created.
|
||||
"""
|
||||
|
||||
def init_new_image(self):
|
||||
pass
|
||||
|
||||
def get_image(self, **kwargs):
|
||||
"""
|
||||
Return the image class for further processing.
|
||||
"""
|
||||
return self._img
|
||||
|
||||
def check_kind(self, kind, transform=None):
|
||||
"""
|
||||
Get the image type.
|
||||
"""
|
||||
if kind is None:
|
||||
kind = self.kind
|
||||
allowed = not self.allowed_kinds or kind in self.allowed_kinds
|
||||
if transform:
|
||||
kind = transform(kind)
|
||||
if not allowed:
|
||||
allowed = kind in self.allowed_kinds
|
||||
if not allowed:
|
||||
raise ValueError(f"Cannot set {type(self).__name__} type to {kind}")
|
||||
return kind
|
||||
|
||||
def is_eye(self, row: int, col: int):
|
||||
"""
|
||||
Find whether the referenced module is in an eye.
|
||||
"""
|
||||
return (
|
||||
(row < 7 and col < 7)
|
||||
or (row < 7 and self.width - col < 8)
|
||||
or (self.width - row < 8 and col < 7)
|
||||
)
|
||||
|
||||
|
||||
class BaseImageWithDrawer(BaseImage):
|
||||
default_drawer_class: Type[QRModuleDrawer]
|
||||
drawer_aliases: DrawerAliases = {}
|
||||
|
||||
def get_default_module_drawer(self) -> QRModuleDrawer:
|
||||
return self.default_drawer_class()
|
||||
|
||||
def get_default_eye_drawer(self) -> QRModuleDrawer:
|
||||
return self.default_drawer_class()
|
||||
|
||||
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()
|
||||
|
||||
def drawrect_context(self, row: int, col: int, qr: "QRCode"):
|
||||
box = self.pixel_box(row, col)
|
||||
drawer = self.eye_drawer if self.is_eye(row, col) else self.module_drawer
|
||||
is_active: Union[bool, ActiveWithNeighbors] = (
|
||||
qr.active_with_neighbors(row, col)
|
||||
if drawer.needs_neighbors
|
||||
else bool(qr.modules[row][col])
|
||||
)
|
||||
|
||||
drawer.drawrect(box, is_active)
|
||||
@@ -0,0 +1,54 @@
|
||||
import qrcode.image.base
|
||||
from qrcode.compat.pil import Image, ImageDraw
|
||||
|
||||
|
||||
class PilImage(qrcode.image.base.BaseImage):
|
||||
"""
|
||||
PIL image builder, default format is PNG.
|
||||
"""
|
||||
|
||||
kind = "PNG"
|
||||
|
||||
def new_image(self, **kwargs):
|
||||
back_color = kwargs.get("back_color", "white")
|
||||
fill_color = kwargs.get("fill_color", "black")
|
||||
|
||||
try:
|
||||
fill_color = fill_color.lower()
|
||||
except AttributeError:
|
||||
pass
|
||||
|
||||
try:
|
||||
back_color = back_color.lower()
|
||||
except AttributeError:
|
||||
pass
|
||||
|
||||
# L mode (1 mode) color = (r*299 + g*587 + b*114)//1000
|
||||
if fill_color == "black" and back_color == "white":
|
||||
mode = "1"
|
||||
fill_color = 0
|
||||
if back_color == "white":
|
||||
back_color = 255
|
||||
elif back_color == "transparent":
|
||||
mode = "RGBA"
|
||||
back_color = None
|
||||
else:
|
||||
mode = "RGB"
|
||||
|
||||
img = Image.new(mode, (self.pixel_size, self.pixel_size), back_color)
|
||||
self.fill_color = fill_color
|
||||
self._idr = ImageDraw.Draw(img)
|
||||
return img
|
||||
|
||||
def drawrect(self, row, col):
|
||||
box = self.pixel_box(row, col)
|
||||
self._idr.rectangle(box, fill=self.fill_color)
|
||||
|
||||
def save(self, stream, format=None, **kwargs):
|
||||
kind = kwargs.pop("kind", self.kind)
|
||||
if format is None:
|
||||
format = kind
|
||||
self._img.save(stream, format=format, **kwargs)
|
||||
|
||||
def __getattr__(self, name):
|
||||
return getattr(self._img, name)
|
||||
@@ -0,0 +1,54 @@
|
||||
from itertools import chain
|
||||
|
||||
import png
|
||||
|
||||
import qrcode.image.base
|
||||
|
||||
|
||||
class PyPNGImage(qrcode.image.base.BaseImage):
|
||||
"""
|
||||
pyPNG image builder.
|
||||
"""
|
||||
|
||||
kind = "PNG"
|
||||
allowed_kinds = ("PNG",)
|
||||
needs_drawrect = False
|
||||
|
||||
def new_image(self, **kwargs):
|
||||
return png.Writer(self.pixel_size, self.pixel_size, greyscale=True, bitdepth=1)
|
||||
|
||||
def drawrect(self, row, col):
|
||||
"""
|
||||
Not used.
|
||||
"""
|
||||
|
||||
def save(self, stream, kind=None):
|
||||
if isinstance(stream, str):
|
||||
stream = open(stream, "wb")
|
||||
self._img.write(stream, self.rows_iter())
|
||||
|
||||
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
|
||||
|
||||
|
||||
# Keeping this for backwards compatibility.
|
||||
PymagingImage = PyPNGImage
|
||||
@@ -0,0 +1,112 @@
|
||||
# Needed on case-insensitive filesystems
|
||||
from __future__ import absolute_import
|
||||
|
||||
import qrcode.image.base
|
||||
from qrcode.compat.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.
|
||||
|
||||
This differs from the PilImage in that there is a module_drawer, a
|
||||
color_mask, and an optional image
|
||||
|
||||
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.
|
||||
"""
|
||||
|
||||
kind = "PNG"
|
||||
|
||||
needs_processing = True
|
||||
color_mask: QRColorMask
|
||||
default_drawer_class = SquareModuleDrawer
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
self.color_mask = kwargs.get("color_mask", SolidFillColorMask())
|
||||
embeded_image_path = kwargs.get("embeded_image_path", None)
|
||||
self.embeded_image = kwargs.get("embeded_image", None)
|
||||
self.embeded_image_resample = kwargs.get(
|
||||
"embeded_image_resample", Image.Resampling.LANCZOS
|
||||
)
|
||||
if not self.embeded_image and embeded_image_path:
|
||||
self.embeded_image = Image.open(embeded_image_path)
|
||||
|
||||
# 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 new_image(self, **kwargs):
|
||||
mode = (
|
||||
"RGBA"
|
||||
if (
|
||||
self.color_mask.has_transparency
|
||||
or (self.embeded_image and "A" in self.embeded_image.getbands())
|
||||
)
|
||||
else "RGB"
|
||||
)
|
||||
# This is the background color. Should be white or whiteish
|
||||
back_color = self.color_mask.back_color
|
||||
|
||||
return Image.new(mode, (self.pixel_size, self.pixel_size), back_color)
|
||||
|
||||
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 / 4)
|
||||
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)
|
||||
|
||||
def save(self, stream, format=None, **kwargs):
|
||||
if format is None:
|
||||
format = kwargs.get("kind", self.kind)
|
||||
if "kind" in kwargs:
|
||||
del kwargs["kind"]
|
||||
self._img.save(stream, format=format, **kwargs)
|
||||
|
||||
def __getattr__(self, name):
|
||||
return getattr(self._img, name)
|
||||
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,220 @@
|
||||
# Needed on case-insensitive filesystems
|
||||
from __future__ import absolute_import
|
||||
|
||||
import math
|
||||
|
||||
from qrcode.compat.pil import Image
|
||||
|
||||
|
||||
class QRColorMask:
|
||||
"""
|
||||
QRColorMask is used to color in the QRCode.
|
||||
|
||||
By the time apply_mask is called, the QRModuleDrawer of the StyledPilImage
|
||||
will have drawn all of the modules on the canvas (the color of these
|
||||
modules will be mostly black, although antialiasing may result in
|
||||
gradients) In the base class, apply_mask is implemented such that the
|
||||
background color will remain, but the foreground pixels will be replaced by
|
||||
a color determined by a call to get_fg_pixel. There is additional
|
||||
calculation done to preserve the gradient artifacts of antialiasing.
|
||||
|
||||
All QRColorMask objects should be careful about RGB vs RGBA color spaces.
|
||||
|
||||
For examples of what these look like, see doc/color_masks.png
|
||||
"""
|
||||
|
||||
back_color = (255, 255, 255)
|
||||
has_transparency = False
|
||||
paint_color = back_color
|
||||
|
||||
def initialize(self, styledPilImage, image):
|
||||
self.paint_color = styledPilImage.paint_color
|
||||
|
||||
def apply_mask(self, image):
|
||||
width, height = image.size
|
||||
for x in range(width):
|
||||
for y in range(height):
|
||||
norm = self.extrap_color(
|
||||
self.back_color, self.paint_color, image.getpixel((x, y))
|
||||
)
|
||||
if norm is not None:
|
||||
image.putpixel(
|
||||
(x, y),
|
||||
self.interp_color(
|
||||
self.get_bg_pixel(image, x, y),
|
||||
self.get_fg_pixel(image, x, y),
|
||||
norm,
|
||||
),
|
||||
)
|
||||
else:
|
||||
image.putpixel((x, y), self.get_bg_pixel(image, x, y))
|
||||
|
||||
def get_fg_pixel(self, image, x, y):
|
||||
raise NotImplementedError("QRModuleDrawer.paint_fg_pixel")
|
||||
|
||||
def get_bg_pixel(self, image, x, y):
|
||||
return self.back_color
|
||||
|
||||
# The following functions are helpful for color calculation:
|
||||
|
||||
# interpolate a number between two numbers
|
||||
def interp_num(self, n1, n2, norm):
|
||||
return int(n2 * norm + n1 * (1 - norm))
|
||||
|
||||
# interpolate a color between two colorrs
|
||||
def interp_color(self, col1, col2, norm):
|
||||
return tuple(self.interp_num(col1[i], col2[i], norm) for i in range(len(col1)))
|
||||
|
||||
# find the interpolation coefficient between two numbers
|
||||
def extrap_num(self, n1, n2, interped_num):
|
||||
if n2 == n1:
|
||||
return None
|
||||
else:
|
||||
return (interped_num - n1) / (n2 - n1)
|
||||
|
||||
# find the interpolation coefficient between two numbers
|
||||
def extrap_color(self, col1, col2, interped_color):
|
||||
normed = []
|
||||
for c1, c2, ci in zip(col1, col2, interped_color):
|
||||
extrap = self.extrap_num(c1, c2, ci)
|
||||
if extrap is not None:
|
||||
normed.append(extrap)
|
||||
if not normed:
|
||||
return None
|
||||
return sum(normed) / len(normed)
|
||||
|
||||
|
||||
class SolidFillColorMask(QRColorMask):
|
||||
"""
|
||||
Just fills in the background with one color and the foreground with another
|
||||
"""
|
||||
|
||||
def __init__(self, back_color=(255, 255, 255), front_color=(0, 0, 0)):
|
||||
self.back_color = back_color
|
||||
self.front_color = front_color
|
||||
self.has_transparency = len(self.back_color) == 4
|
||||
|
||||
def apply_mask(self, image):
|
||||
if self.back_color == (255, 255, 255) and self.front_color == (0, 0, 0):
|
||||
# Optimization: the image is already drawn by QRModuleDrawer in
|
||||
# black and white, so if these are also our mask colors we don't
|
||||
# need to do anything. This is much faster than actually applying a
|
||||
# mask.
|
||||
pass
|
||||
else:
|
||||
# TODO there's probably a way to use PIL.ImageMath instead of doing
|
||||
# the individual pixel comparisons that the base class uses, which
|
||||
# would be a lot faster. (In fact doing this would probably remove
|
||||
# the need for the B&W optimization above.)
|
||||
QRColorMask.apply_mask(self, image)
|
||||
|
||||
def get_fg_pixel(self, image, x, y):
|
||||
return self.front_color
|
||||
|
||||
|
||||
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
|
||||
|
||||
def get_fg_pixel(self, image, x, y):
|
||||
width, _ = image.size
|
||||
normedDistanceToCenter = math.sqrt(
|
||||
(x - width / 2) ** 2 + (y - width / 2) ** 2
|
||||
) / (math.sqrt(2) * width / 2)
|
||||
return self.interp_color(
|
||||
self.center_color, self.edge_color, normedDistanceToCenter
|
||||
)
|
||||
|
||||
|
||||
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
|
||||
|
||||
def get_fg_pixel(self, image, x, y):
|
||||
width, _ = image.size
|
||||
normedDistanceToCenter = max(abs(x - width / 2), abs(y - width / 2)) / (
|
||||
width / 2
|
||||
)
|
||||
return self.interp_color(
|
||||
self.center_color, self.edge_color, normedDistanceToCenter
|
||||
)
|
||||
|
||||
|
||||
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
|
||||
|
||||
def get_fg_pixel(self, image, x, y):
|
||||
width, _ = image.size
|
||||
return self.interp_color(self.left_color, self.right_color, x / width)
|
||||
|
||||
|
||||
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
|
||||
|
||||
def get_fg_pixel(self, image, x, y):
|
||||
width, _ = image.size
|
||||
return self.interp_color(self.top_color, self.bottom_color, y / width)
|
||||
|
||||
|
||||
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
|
||||
|
||||
def initialize(self, styledPilImage, image):
|
||||
self.paint_color = styledPilImage.paint_color
|
||||
self.color_img = self.color_img.resize(image.size)
|
||||
|
||||
def get_fg_pixel(self, image, x, y):
|
||||
width, _ = image.size
|
||||
return self.color_img.getpixel((x, y))
|
||||
@@ -0,0 +1,10 @@
|
||||
# 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
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,36 @@
|
||||
from __future__ import absolute_import
|
||||
|
||||
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.
|
||||
|
||||
For this, technically all that is necessary is a ``drawrect(self, box,
|
||||
is_active)`` function which takes in the box in which it is to draw,
|
||||
whether or not the box is "active" (a module exists there). If
|
||||
``needs_neighbors`` is set to True, then the method should also accept a
|
||||
``neighbors`` kwarg (the neighboring pixels).
|
||||
|
||||
It is frequently necessary to also implement an "initialize" function to
|
||||
set up values that only the containing Image class knows about.
|
||||
|
||||
For examples of what these look like, see doc/module_drawers.png
|
||||
"""
|
||||
|
||||
needs_neighbors = False
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
pass
|
||||
|
||||
def initialize(self, img: "BaseImage") -> None:
|
||||
self.img = img
|
||||
|
||||
@abc.abstractmethod
|
||||
def drawrect(self, box, is_active) -> None:
|
||||
...
|
||||
@@ -0,0 +1,266 @@
|
||||
# Needed on case-insensitive filesystems
|
||||
from __future__ import absolute_import
|
||||
|
||||
from typing import TYPE_CHECKING, List
|
||||
|
||||
from qrcode.compat.pil import Image, ImageDraw
|
||||
from qrcode.image.styles.moduledrawers.base import QRModuleDrawer
|
||||
|
||||
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.
|
||||
|
||||
NOTE: the color that this draws in should be whatever is equivalent to
|
||||
black in the color space, and the specified QRColorMask will handle adding
|
||||
colors as necessary to the image
|
||||
"""
|
||||
|
||||
img: "StyledPilImage"
|
||||
|
||||
|
||||
class SquareModuleDrawer(StyledPilQRModuleDrawer):
|
||||
"""
|
||||
Draws the modules as simple squares
|
||||
"""
|
||||
|
||||
def initialize(self, *args, **kwargs):
|
||||
super().initialize(*args, **kwargs)
|
||||
self.imgDraw = ImageDraw.Draw(self.img._img)
|
||||
|
||||
def drawrect(self, box, is_active: bool):
|
||||
if is_active:
|
||||
self.imgDraw.rectangle(box, fill=self.img.paint_color)
|
||||
|
||||
|
||||
class GappedSquareModuleDrawer(StyledPilQRModuleDrawer):
|
||||
"""
|
||||
Draws the modules as simple squares that are not contiguous.
|
||||
|
||||
The size_ratio determines how wide the squares are relative to the width of
|
||||
the space they are printed in
|
||||
"""
|
||||
|
||||
def __init__(self, size_ratio=0.8):
|
||||
self.size_ratio = size_ratio
|
||||
|
||||
def initialize(self, *args, **kwargs):
|
||||
super().initialize(*args, **kwargs)
|
||||
self.imgDraw = ImageDraw.Draw(self.img._img)
|
||||
self.delta = (1 - self.size_ratio) * self.img.box_size / 2
|
||||
|
||||
def drawrect(self, box, is_active: bool):
|
||||
if is_active:
|
||||
smaller_box = (
|
||||
box[0][0] + self.delta,
|
||||
box[0][1] + self.delta,
|
||||
box[1][0] - self.delta,
|
||||
box[1][1] - self.delta,
|
||||
)
|
||||
self.imgDraw.rectangle(smaller_box, fill=self.img.paint_color)
|
||||
|
||||
|
||||
class CircleModuleDrawer(StyledPilQRModuleDrawer):
|
||||
"""
|
||||
Draws the modules as circles
|
||||
"""
|
||||
|
||||
circle = None
|
||||
|
||||
def initialize(self, *args, **kwargs):
|
||||
super().initialize(*args, **kwargs)
|
||||
box_size = self.img.box_size
|
||||
fake_size = box_size * ANTIALIASING_FACTOR
|
||||
self.circle = Image.new(
|
||||
self.img.mode,
|
||||
(fake_size, fake_size),
|
||||
self.img.color_mask.back_color,
|
||||
)
|
||||
ImageDraw.Draw(self.circle).ellipse(
|
||||
(0, 0, fake_size, fake_size), fill=self.img.paint_color
|
||||
)
|
||||
self.circle = self.circle.resize((box_size, box_size), Image.Resampling.LANCZOS)
|
||||
|
||||
def drawrect(self, box, is_active: bool):
|
||||
if is_active:
|
||||
self.img._img.paste(self.circle, (box[0][0], box[0][1]))
|
||||
|
||||
|
||||
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 __init__(self, radius_ratio=1):
|
||||
self.radius_ratio = radius_ratio
|
||||
|
||||
def initialize(self, *args, **kwargs):
|
||||
super().initialize(*args, **kwargs)
|
||||
self.corner_width = int(self.img.box_size / 2)
|
||||
self.setup_corners()
|
||||
|
||||
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
|
||||
)
|
||||
|
||||
fake_width = self.corner_width * ANTIALIASING_FACTOR
|
||||
radius = self.radius_ratio * fake_width
|
||||
diameter = radius * 2
|
||||
base = Image.new(
|
||||
mode, (fake_width, fake_width), back_color
|
||||
) # make something 4x bigger for antialiasing
|
||||
base_draw = ImageDraw.Draw(base)
|
||||
base_draw.ellipse((0, 0, diameter, diameter), fill=front_color)
|
||||
base_draw.rectangle((radius, 0, fake_width, fake_width), fill=front_color)
|
||||
base_draw.rectangle((0, radius, fake_width, fake_width), fill=front_color)
|
||||
self.NW_ROUND = base.resize(
|
||||
(self.corner_width, self.corner_width), Image.Resampling.LANCZOS
|
||||
)
|
||||
self.SW_ROUND = self.NW_ROUND.transpose(Image.Transpose.FLIP_TOP_BOTTOM)
|
||||
self.SE_ROUND = self.NW_ROUND.transpose(Image.Transpose.ROTATE_180)
|
||||
self.NE_ROUND = self.NW_ROUND.transpose(Image.Transpose.FLIP_LEFT_RIGHT)
|
||||
|
||||
def drawrect(self, box: List[List[int]], is_active: "ActiveWithNeighbors"):
|
||||
if not is_active:
|
||||
return
|
||||
# find rounded edges
|
||||
nw_rounded = not is_active.W and not is_active.N
|
||||
ne_rounded = not is_active.N and not is_active.E
|
||||
se_rounded = not is_active.E and not is_active.S
|
||||
sw_rounded = not is_active.S and not is_active.W
|
||||
|
||||
nw = self.NW_ROUND if nw_rounded else self.SQUARE
|
||||
ne = self.NE_ROUND if ne_rounded else self.SQUARE
|
||||
se = self.SE_ROUND if se_rounded else self.SQUARE
|
||||
sw = self.SW_ROUND if sw_rounded else self.SQUARE
|
||||
self.img._img.paste(nw, (box[0][0], box[0][1]))
|
||||
self.img._img.paste(ne, (box[0][0] + self.corner_width, box[0][1]))
|
||||
self.img._img.paste(
|
||||
se, (box[0][0] + self.corner_width, box[0][1] + self.corner_width)
|
||||
)
|
||||
self.img._img.paste(sw, (box[0][0], box[0][1] + self.corner_width))
|
||||
|
||||
|
||||
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 __init__(self, horizontal_shrink=0.8):
|
||||
self.horizontal_shrink = horizontal_shrink
|
||||
|
||||
def initialize(self, *args, **kwargs):
|
||||
super().initialize(*args, **kwargs)
|
||||
self.half_height = int(self.img.box_size / 2)
|
||||
self.delta = int((1 - self.horizontal_shrink) * self.half_height)
|
||||
self.setup_edges()
|
||||
|
||||
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)
|
||||
|
||||
fake_width = width * ANTIALIASING_FACTOR
|
||||
fake_height = height * ANTIALIASING_FACTOR
|
||||
base = Image.new(
|
||||
mode, (fake_width, fake_height), back_color
|
||||
) # make something 4x bigger for antialiasing
|
||||
base_draw = ImageDraw.Draw(base)
|
||||
base_draw.ellipse((0, 0, fake_width, fake_height * 2), fill=front_color)
|
||||
|
||||
self.ROUND_TOP = base.resize((shrunken_width, height), Image.Resampling.LANCZOS)
|
||||
self.ROUND_BOTTOM = self.ROUND_TOP.transpose(Image.Transpose.FLIP_TOP_BOTTOM)
|
||||
|
||||
def drawrect(self, box, is_active: "ActiveWithNeighbors"):
|
||||
if is_active:
|
||||
# find rounded edges
|
||||
top_rounded = not is_active.N
|
||||
bottom_rounded = not is_active.S
|
||||
|
||||
top = self.ROUND_TOP if top_rounded else self.SQUARE
|
||||
bottom = self.ROUND_BOTTOM if bottom_rounded else self.SQUARE
|
||||
self.img._img.paste(top, (box[0][0] + self.delta, box[0][1]))
|
||||
self.img._img.paste(
|
||||
bottom, (box[0][0] + self.delta, box[0][1] + self.half_height)
|
||||
)
|
||||
|
||||
|
||||
class HorizontalBarsDrawer(StyledPilQRModuleDrawer):
|
||||
"""
|
||||
Draws horizontally contiguous groups of modules as long rounded rectangles,
|
||||
with gaps between neighboring bands (the size of these gaps is inversely
|
||||
proportional to the vertical_shrink).
|
||||
"""
|
||||
|
||||
needs_neighbors = True
|
||||
|
||||
def __init__(self, vertical_shrink=0.8):
|
||||
self.vertical_shrink = vertical_shrink
|
||||
|
||||
def initialize(self, *args, **kwargs):
|
||||
super().initialize(*args, **kwargs)
|
||||
self.half_width = int(self.img.box_size / 2)
|
||||
self.delta = int((1 - self.vertical_shrink) * self.half_width)
|
||||
self.setup_edges()
|
||||
|
||||
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)
|
||||
|
||||
fake_width = width * ANTIALIASING_FACTOR
|
||||
fake_height = height * ANTIALIASING_FACTOR
|
||||
base = Image.new(
|
||||
mode, (fake_width, fake_height), back_color
|
||||
) # make something 4x bigger for antialiasing
|
||||
base_draw = ImageDraw.Draw(base)
|
||||
base_draw.ellipse((0, 0, fake_width * 2, fake_height), fill=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)
|
||||
|
||||
def drawrect(self, box, is_active: "ActiveWithNeighbors"):
|
||||
if is_active:
|
||||
# find rounded edges
|
||||
left_rounded = not is_active.W
|
||||
right_rounded = not is_active.E
|
||||
|
||||
left = self.ROUND_LEFT if left_rounded else self.SQUARE
|
||||
right = self.ROUND_RIGHT if right_rounded else self.SQUARE
|
||||
self.img._img.paste(left, (box[0][0], box[0][1] + self.delta))
|
||||
self.img._img.paste(
|
||||
right, (box[0][0] + self.half_width, box[0][1] + self.delta)
|
||||
)
|
||||
@@ -0,0 +1,141 @@
|
||||
import abc
|
||||
from decimal import Decimal
|
||||
from typing import TYPE_CHECKING, NamedTuple
|
||||
|
||||
from qrcode.image.styles.moduledrawers.base import QRModuleDrawer
|
||||
from qrcode.compat.etree import ET
|
||||
|
||||
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"
|
||||
|
||||
def __init__(self, *, size_ratio: Decimal = Decimal(1), **kwargs):
|
||||
self.size_ratio = size_ratio
|
||||
|
||||
def initialize(self, *args, **kwargs) -> None:
|
||||
super().initialize(*args, **kwargs)
|
||||
self.box_delta = (1 - self.size_ratio) * self.img.box_size / 2
|
||||
self.box_size = Decimal(self.img.box_size) * self.size_ratio
|
||||
self.box_half = self.box_size / 2
|
||||
|
||||
def coords(self, box) -> Coords:
|
||||
row, col = box[0]
|
||||
x = row + self.box_delta
|
||||
y = col + self.box_delta
|
||||
|
||||
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"
|
||||
|
||||
def initialize(self, *args, **kwargs) -> None:
|
||||
super().initialize(*args, **kwargs)
|
||||
self.tag_qname = ET.QName(self.img._SVG_namespace, self.tag)
|
||||
|
||||
def drawrect(self, box, is_active: bool):
|
||||
if not is_active:
|
||||
return
|
||||
self.img._img.append(self.el(box))
|
||||
|
||||
@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)
|
||||
|
||||
def el(self, box):
|
||||
coords = self.coords(box)
|
||||
return ET.Element(
|
||||
self.tag_qname, # type: ignore
|
||||
x=self.img.units(coords.x0),
|
||||
y=self.img.units(coords.y0),
|
||||
width=self.unit_size,
|
||||
height=self.unit_size,
|
||||
)
|
||||
|
||||
|
||||
class SvgCircleDrawer(SvgQRModuleDrawer):
|
||||
tag = "circle"
|
||||
|
||||
def initialize(self, *args, **kwargs) -> None:
|
||||
super().initialize(*args, **kwargs)
|
||||
self.radius = self.img.units(self.box_half)
|
||||
|
||||
def el(self, box):
|
||||
coords = self.coords(box)
|
||||
return ET.Element(
|
||||
self.tag_qname, # type: ignore
|
||||
cx=self.img.units(coords.xh),
|
||||
cy=self.img.units(coords.yh),
|
||||
r=self.radius,
|
||||
)
|
||||
|
||||
|
||||
class SvgPathQRModuleDrawer(BaseSvgQRModuleDrawer):
|
||||
img: "SvgPathImage"
|
||||
|
||||
def drawrect(self, box, is_active: bool):
|
||||
if not is_active:
|
||||
return
|
||||
self.img._subpaths.append(self.subpath(box))
|
||||
|
||||
@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)
|
||||
|
||||
def subpath(self, box) -> str:
|
||||
coords = self.coords(box)
|
||||
x0 = self.img.units(coords.x0, text=False)
|
||||
yh = self.img.units(coords.yh, text=False)
|
||||
h = self.img.units(self.box_half - self.box_delta, text=False)
|
||||
x1 = self.img.units(coords.x1, text=False)
|
||||
|
||||
# rx,ry is the centerpoint of the arc
|
||||
# 1? is the x-axis-rotation
|
||||
# 2? is the large-arc-flag
|
||||
# 3? is the sweep flag
|
||||
# x,y is the point the arc is drawn to
|
||||
|
||||
return f"M{x0},{yh}A{h},{h} 0 0 0 {x1},{yh}A{h},{h} 0 0 0 {x0},{yh}z"
|
||||
179
Backend/venv/lib/python3.12/site-packages/qrcode/image/svg.py
Normal file
179
Backend/venv/lib/python3.12/site-packages/qrcode/image/svg.py
Normal file
@@ -0,0 +1,179 @@
|
||||
import decimal
|
||||
from decimal import Decimal
|
||||
from typing import List, Optional, Type, Union, overload
|
||||
|
||||
from typing_extensions import 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
|
||||
|
||||
Creates a QR-code image as a SVG document fragment.
|
||||
"""
|
||||
|
||||
_SVG_namespace = "http://www.w3.org/2000/svg"
|
||||
kind = "SVG"
|
||||
allowed_kinds = ("SVG",)
|
||||
default_drawer_class: Type[QRModuleDrawer] = svg_drawers.SvgSquareDrawer
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
ET.register_namespace("svg", self._SVG_namespace)
|
||||
super().__init__(*args, **kwargs)
|
||||
# Save the unit size, for example the default box_size of 10 is '1mm'.
|
||||
self.unit_size = self.units(self.box_size)
|
||||
|
||||
@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:
|
||||
...
|
||||
|
||||
def units(self, pixels, text=True):
|
||||
"""
|
||||
A box_size of 10 (default) equals 1mm.
|
||||
"""
|
||||
units = Decimal(pixels) / 10
|
||||
if not text:
|
||||
return units
|
||||
units = units.quantize(Decimal("0.001"))
|
||||
context = decimal.Context(traps=[decimal.Inexact])
|
||||
try:
|
||||
for d in (Decimal("0.01"), Decimal("0.1"), Decimal("0")):
|
||||
units = units.quantize(d, context=context)
|
||||
except decimal.Inexact:
|
||||
pass
|
||||
return f"{units}mm"
|
||||
|
||||
def save(self, stream, kind=None):
|
||||
self.check_kind(kind=kind)
|
||||
self._write(stream)
|
||||
|
||||
def to_string(self, **kwargs):
|
||||
return ET.tostring(self._img, **kwargs)
|
||||
|
||||
def new_image(self, **kwargs):
|
||||
return self._svg(**kwargs)
|
||||
|
||||
def _svg(self, tag=None, version="1.1", **kwargs):
|
||||
if tag is None:
|
||||
tag = ET.QName(self._SVG_namespace, "svg")
|
||||
dimension = self.units(self.pixel_size)
|
||||
return ET.Element(
|
||||
tag, # type: ignore
|
||||
width=dimension,
|
||||
height=dimension,
|
||||
version=version,
|
||||
**kwargs,
|
||||
)
|
||||
|
||||
def _write(self, stream):
|
||||
ET.ElementTree(self._img).write(stream, xml_declaration=False)
|
||||
|
||||
|
||||
class SvgImage(SvgFragmentImage):
|
||||
"""
|
||||
Standalone SVG image builder
|
||||
|
||||
Creates a QR-code image as a standalone SVG document.
|
||||
"""
|
||||
|
||||
background: Optional[str] = None
|
||||
drawer_aliases: qrcode.image.base.DrawerAliases = {
|
||||
"circle": (svg_drawers.SvgCircleDrawer, {}),
|
||||
"gapped-circle": (svg_drawers.SvgCircleDrawer, {"size_ratio": Decimal(0.8)}),
|
||||
"gapped-square": (svg_drawers.SvgSquareDrawer, {"size_ratio": Decimal(0.8)}),
|
||||
}
|
||||
|
||||
def _svg(self, tag="svg", **kwargs):
|
||||
svg = super()._svg(tag=tag, **kwargs)
|
||||
svg.set("xmlns", self._SVG_namespace)
|
||||
if self.background:
|
||||
svg.append(
|
||||
ET.Element(
|
||||
"rect",
|
||||
fill=self.background,
|
||||
x="0",
|
||||
y="0",
|
||||
width="100%",
|
||||
height="100%",
|
||||
)
|
||||
)
|
||||
return svg
|
||||
|
||||
def _write(self, stream):
|
||||
ET.ElementTree(self._img).write(stream, encoding="UTF-8", xml_declaration=True)
|
||||
|
||||
|
||||
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 __init__(self, *args, **kwargs):
|
||||
self._subpaths: List[str] = []
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
def _svg(self, viewBox=None, **kwargs):
|
||||
if viewBox is None:
|
||||
dimension = self.units(self.pixel_size, text=False)
|
||||
viewBox = "0 0 {d} {d}".format(d=dimension)
|
||||
return super()._svg(viewBox=viewBox, **kwargs)
|
||||
|
||||
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"
|
||||
547
Backend/venv/lib/python3.12/site-packages/qrcode/main.py
Normal file
547
Backend/venv/lib/python3.12/site-packages/qrcode/main.py
Normal file
@@ -0,0 +1,547 @@
|
||||
import sys
|
||||
from bisect import bisect_left
|
||||
from typing import (
|
||||
Dict,
|
||||
Generic,
|
||||
List,
|
||||
NamedTuple,
|
||||
Optional,
|
||||
Type,
|
||||
TypeVar,
|
||||
cast,
|
||||
overload,
|
||||
)
|
||||
|
||||
from typing_extensions import Literal
|
||||
|
||||
from qrcode import constants, exceptions, util
|
||||
from qrcode.image.base import BaseImage
|
||||
from qrcode.image.pure import PyPNGImage
|
||||
|
||||
ModulesType = List[List[Optional[bool]]]
|
||||
# Cache modules generated just based on the QR Code version
|
||||
precomputed_qr_blanks: Dict[int, ModulesType] = {}
|
||||
|
||||
|
||||
def make(data=None, **kwargs):
|
||||
qr = QRCode(**kwargs)
|
||||
qr.add_data(data)
|
||||
return qr.make_image()
|
||||
|
||||
|
||||
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
|
||||
|
||||
def __bool__(self) -> bool:
|
||||
return self.me
|
||||
|
||||
|
||||
GenericImage = TypeVar("GenericImage", bound=BaseImage)
|
||||
GenericImageLocal = TypeVar("GenericImageLocal", bound=BaseImage)
|
||||
|
||||
|
||||
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 = []
|
||||
|
||||
def add_data(self, data, optimize=20):
|
||||
"""
|
||||
Add data to this QR Code.
|
||||
|
||||
:param optimize: Data will be split into multiple chunks to optimize
|
||||
the QR size by finding to more compressed modes of at least this
|
||||
length. Set to ``0`` to avoid optimizing at all.
|
||||
"""
|
||||
if isinstance(data, util.QRData):
|
||||
self.data_list.append(data)
|
||||
elif optimize:
|
||||
self.data_list.extend(util.optimal_data_chunks(data, minimum=optimize))
|
||||
else:
|
||||
self.data_list.append(util.QRData(data))
|
||||
self.data_cache = None
|
||||
|
||||
def make(self, fit=True):
|
||||
"""
|
||||
Compile the data into a QR Code array.
|
||||
|
||||
: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)
|
||||
|
||||
def makeImpl(self, test, mask_pattern):
|
||||
self.modules_count = self.version * 4 + 17
|
||||
|
||||
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)
|
||||
|
||||
def setup_position_probe_pattern(self, row, col):
|
||||
for r in range(-1, 8):
|
||||
|
||||
if row + r <= -1 or self.modules_count <= row + r:
|
||||
continue
|
||||
|
||||
for c in range(-1, 8):
|
||||
|
||||
if col + c <= -1 or self.modules_count <= col + c:
|
||||
continue
|
||||
|
||||
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
|
||||
|
||||
def best_fit(self, start=None):
|
||||
"""
|
||||
Find the minimum size required to fit in the data.
|
||||
"""
|
||||
if start is None:
|
||||
start = 1
|
||||
util.check_version(start)
|
||||
|
||||
# Corresponds to the code in util.create_data, except we don't yet know
|
||||
# version, so optimistically assume start and check later
|
||||
mode_sizes = util.mode_sizes_for_version(start)
|
||||
buffer = util.BitBuffer()
|
||||
for data in self.data_list:
|
||||
buffer.put(data.mode, 4)
|
||||
buffer.put(len(data), mode_sizes[data.mode])
|
||||
data.write(buffer)
|
||||
|
||||
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)
|
||||
|
||||
if i == 0 or min_lost_point > lost_point:
|
||||
min_lost_point = lost_point
|
||||
pattern = i
|
||||
|
||||
return pattern
|
||||
|
||||
def print_tty(self, out=None):
|
||||
"""
|
||||
Output the QR Code only using TTY colors.
|
||||
|
||||
If the data has not been compiled yet, make it first.
|
||||
"""
|
||||
if out is None:
|
||||
import sys
|
||||
|
||||
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()
|
||||
|
||||
def print_ascii(self, out=None, tty=False, invert=False):
|
||||
"""
|
||||
Output the QR Code using ASCII characters.
|
||||
|
||||
:param tty: use fixed TTY color codes (forces invert=True)
|
||||
:param invert: invert the ASCII characters (solid <-> transparent)
|
||||
"""
|
||||
if out is None:
|
||||
out = sys.stdout
|
||||
|
||||
if tty and not out.isatty():
|
||||
raise OSError("Not a tty")
|
||||
|
||||
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()
|
||||
|
||||
def get_module(x, y) -> int:
|
||||
if invert and self.border and max(x, y) >= modcount + self.border:
|
||||
return 1
|
||||
if min(x, y) < 0 or max(x, y) >= modcount:
|
||||
return 0
|
||||
return cast(int, self.modules[x][y])
|
||||
|
||||
for r in range(-self.border, modcount + self.border, 2):
|
||||
if tty:
|
||||
if not invert or r < modcount + self.border - 1:
|
||||
out.write("\x1b[48;5;232m") # Background black
|
||||
out.write("\x1b[38;5;255m") # Foreground white
|
||||
for c in range(-self.border, modcount + self.border):
|
||||
pos = get_module(r, c) + (get_module(r + 1, c) << 1)
|
||||
out.write(codes[pos])
|
||||
if tty:
|
||||
out.write("\x1b[0m")
|
||||
out.write("\n")
|
||||
out.flush()
|
||||
|
||||
@overload
|
||||
def make_image(self, image_factory: Literal[None] = None, **kwargs) -> GenericImage:
|
||||
...
|
||||
|
||||
@overload
|
||||
def make_image(
|
||||
self, image_factory: Type[GenericImageLocal] = None, **kwargs
|
||||
) -> GenericImageLocal:
|
||||
...
|
||||
|
||||
def make_image(self, image_factory=None, **kwargs):
|
||||
"""
|
||||
Make an image from the QR Code data.
|
||||
|
||||
If the data has not been compiled yet, make it first.
|
||||
"""
|
||||
_check_box_size(self.box_size)
|
||||
if self.data_cache is None:
|
||||
self.make()
|
||||
|
||||
if image_factory is not None:
|
||||
assert issubclass(image_factory, BaseImage)
|
||||
else:
|
||||
image_factory = self.image_factory
|
||||
if image_factory is None:
|
||||
from qrcode.image.pil import Image, PilImage
|
||||
|
||||
# Use PIL by default if available, otherwise use PyPNG.
|
||||
image_factory = PilImage if Image else PyPNGImage
|
||||
|
||||
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
|
||||
|
||||
# return true if and only if (row, col) is in the module
|
||||
def is_constrained(self, row: int, col: int) -> bool:
|
||||
return (
|
||||
row >= 0
|
||||
and row < len(self.modules)
|
||||
and col >= 0
|
||||
and col < len(self.modules[row])
|
||||
)
|
||||
|
||||
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
|
||||
|
||||
for c in range(8, self.modules_count - 8):
|
||||
if self.modules[6][c] is not None:
|
||||
continue
|
||||
self.modules[6][c] = c % 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]
|
||||
|
||||
if self.modules[row][col] is not None:
|
||||
continue
|
||||
|
||||
for r in range(-2, 3):
|
||||
|
||||
for c in range(-2, 3):
|
||||
|
||||
if (
|
||||
r == -2
|
||||
or r == 2
|
||||
or c == -2
|
||||
or c == 2
|
||||
or (r == 0 and c == 0)
|
||||
):
|
||||
self.modules[row + r][col + c] = True
|
||||
else:
|
||||
self.modules[row + r][col + c] = False
|
||||
|
||||
def setup_type_number(self, test):
|
||||
bits = util.BCH_type_number(self.version)
|
||||
|
||||
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
|
||||
|
||||
def setup_type_info(self, test, mask_pattern):
|
||||
data = (self.error_correction << 3) | mask_pattern
|
||||
bits = util.BCH_type_info(data)
|
||||
|
||||
# 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
|
||||
|
||||
def map_data(self, data, mask_pattern):
|
||||
inc = -1
|
||||
row = self.modules_count - 1
|
||||
bitIndex = 7
|
||||
byteIndex = 0
|
||||
|
||||
mask_func = util.mask_func(mask_pattern)
|
||||
|
||||
data_len = len(data)
|
||||
|
||||
for col in range(self.modules_count - 1, 0, -2):
|
||||
|
||||
if col <= 6:
|
||||
col -= 1
|
||||
|
||||
col_range = (col, col - 1)
|
||||
|
||||
while True:
|
||||
|
||||
for c in col_range:
|
||||
|
||||
if self.modules[row][c] is None:
|
||||
|
||||
dark = False
|
||||
|
||||
if byteIndex < data_len:
|
||||
dark = ((data[byteIndex] >> bitIndex) & 1) == 1
|
||||
|
||||
if mask_func(row, c):
|
||||
dark = not dark
|
||||
|
||||
self.modules[row][c] = dark
|
||||
bitIndex -= 1
|
||||
|
||||
if bitIndex == -1:
|
||||
byteIndex += 1
|
||||
bitIndex = 7
|
||||
|
||||
row += inc
|
||||
|
||||
if row < 0 or self.modules_count <= row:
|
||||
row -= inc
|
||||
inc = -inc
|
||||
break
|
||||
|
||||
def get_matrix(self):
|
||||
"""
|
||||
Return the QR Code as a multidimensional array, including the border.
|
||||
|
||||
To return the array without a border, set ``self.border`` to 0 first.
|
||||
"""
|
||||
if self.data_cache is None:
|
||||
self.make()
|
||||
|
||||
if not self.border:
|
||||
return self.modules
|
||||
|
||||
width = len(self.modules) + self.border * 2
|
||||
code = [[False] * width] * self.border
|
||||
x_border = [False] * self.border
|
||||
for module in self.modules:
|
||||
code.append(x_border + cast(List[bool], module) + x_border)
|
||||
code += [[False] * width] * self.border
|
||||
|
||||
return code
|
||||
|
||||
def active_with_neighbors(self, row: int, col: int) -> ActiveWithNeighbors:
|
||||
context: List[bool] = []
|
||||
for r in range(row - 1, row + 2):
|
||||
for c in range(col - 1, col + 2):
|
||||
context.append(self.is_constrained(r, c) and bool(self.modules[r][c]))
|
||||
return ActiveWithNeighbors(*context)
|
||||
41
Backend/venv/lib/python3.12/site-packages/qrcode/release.py
Normal file
41
Backend/venv/lib/python3.12/site-packages/qrcode/release.py
Normal file
@@ -0,0 +1,41 @@
|
||||
"""
|
||||
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)
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,13 @@
|
||||
import unittest
|
||||
from unittest import mock
|
||||
|
||||
from qrcode import run_example
|
||||
from qrcode.compat.pil import Image
|
||||
|
||||
|
||||
class ExampleTest(unittest.TestCase):
|
||||
@unittest.skipIf(not Image, "Requires PIL")
|
||||
@mock.patch("PIL.Image.Image.show")
|
||||
def runTest(self, mock_show):
|
||||
run_example()
|
||||
mock_show.assert_called_with()
|
||||
@@ -0,0 +1,487 @@
|
||||
import io
|
||||
import os
|
||||
import unittest
|
||||
import warnings
|
||||
from tempfile import mkdtemp
|
||||
from unittest import mock
|
||||
|
||||
import png
|
||||
|
||||
import qrcode
|
||||
import qrcode.util
|
||||
from qrcode.compat.pil import Image as pil_Image
|
||||
from qrcode.exceptions import DataOverflowError
|
||||
from qrcode.image.base import BaseImage
|
||||
from qrcode.image.pure import PyPNGImage
|
||||
from qrcode.image.styledpil import StyledPilImage
|
||||
from qrcode.image.styles import colormasks, moduledrawers
|
||||
from qrcode.util import MODE_8BIT_BYTE, MODE_ALPHA_NUM, MODE_NUMBER, QRData
|
||||
|
||||
UNICODE_TEXT = "\u03b1\u03b2\u03b3"
|
||||
WHITE = (255, 255, 255)
|
||||
BLACK = (0, 0, 0)
|
||||
RED = (255, 0, 0)
|
||||
|
||||
|
||||
class QRCodeTests(unittest.TestCase):
|
||||
def setUp(self):
|
||||
self.tmpdir = mkdtemp()
|
||||
|
||||
def tearDown(self):
|
||||
os.rmdir(self.tmpdir)
|
||||
|
||||
def test_basic(self):
|
||||
qr = qrcode.QRCode(version=1)
|
||||
qr.add_data("a")
|
||||
qr.make(fit=False)
|
||||
|
||||
def test_large(self):
|
||||
qr = qrcode.QRCode(version=27)
|
||||
qr.add_data("a")
|
||||
qr.make(fit=False)
|
||||
|
||||
def test_invalid_version(self):
|
||||
self.assertRaises(ValueError, qrcode.QRCode, version=41)
|
||||
|
||||
def test_invalid_border(self):
|
||||
self.assertRaises(ValueError, qrcode.QRCode, border=-1)
|
||||
|
||||
def test_overflow(self):
|
||||
qr = qrcode.QRCode(version=1)
|
||||
qr.add_data("abcdefghijklmno")
|
||||
self.assertRaises(DataOverflowError, qr.make, fit=False)
|
||||
|
||||
def test_add_qrdata(self):
|
||||
qr = qrcode.QRCode(version=1)
|
||||
data = QRData("a")
|
||||
qr.add_data(data)
|
||||
qr.make(fit=False)
|
||||
|
||||
def test_fit(self):
|
||||
qr = qrcode.QRCode()
|
||||
qr.add_data("a")
|
||||
qr.make()
|
||||
self.assertEqual(qr.version, 1)
|
||||
qr.add_data("bcdefghijklmno")
|
||||
qr.make()
|
||||
self.assertEqual(qr.version, 2)
|
||||
|
||||
def test_mode_number(self):
|
||||
qr = qrcode.QRCode()
|
||||
qr.add_data("1234567890123456789012345678901234", optimize=0)
|
||||
qr.make()
|
||||
self.assertEqual(qr.version, 1)
|
||||
self.assertEqual(qr.data_list[0].mode, MODE_NUMBER)
|
||||
|
||||
def test_mode_alpha(self):
|
||||
qr = qrcode.QRCode()
|
||||
qr.add_data("ABCDEFGHIJ1234567890", optimize=0)
|
||||
qr.make()
|
||||
self.assertEqual(qr.version, 1)
|
||||
self.assertEqual(qr.data_list[0].mode, MODE_ALPHA_NUM)
|
||||
|
||||
def test_regression_mode_comma(self):
|
||||
qr = qrcode.QRCode()
|
||||
qr.add_data(",", optimize=0)
|
||||
qr.make()
|
||||
self.assertEqual(qr.data_list[0].mode, MODE_8BIT_BYTE)
|
||||
|
||||
def test_mode_8bit(self):
|
||||
qr = qrcode.QRCode()
|
||||
qr.add_data("abcABC" + UNICODE_TEXT, optimize=0)
|
||||
qr.make()
|
||||
self.assertEqual(qr.version, 1)
|
||||
self.assertEqual(qr.data_list[0].mode, MODE_8BIT_BYTE)
|
||||
|
||||
def test_mode_8bit_newline(self):
|
||||
qr = qrcode.QRCode()
|
||||
qr.add_data("ABCDEFGHIJ1234567890\n", optimize=0)
|
||||
qr.make()
|
||||
self.assertEqual(qr.data_list[0].mode, MODE_8BIT_BYTE)
|
||||
|
||||
@unittest.skipIf(not pil_Image, "Requires PIL")
|
||||
def test_render_pil(self):
|
||||
qr = qrcode.QRCode()
|
||||
qr.add_data(UNICODE_TEXT)
|
||||
img = qr.make_image()
|
||||
img.save(io.BytesIO())
|
||||
self.assertIsInstance(img.get_image(), pil_Image.Image)
|
||||
|
||||
@unittest.skipIf(not pil_Image, "Requires PIL")
|
||||
def test_render_pil_with_transparent_background(self):
|
||||
qr = qrcode.QRCode()
|
||||
qr.add_data(UNICODE_TEXT)
|
||||
img = qr.make_image(back_color="TransParent")
|
||||
img.save(io.BytesIO())
|
||||
|
||||
@unittest.skipIf(not pil_Image, "Requires PIL")
|
||||
def test_render_pil_with_red_background(self):
|
||||
qr = qrcode.QRCode()
|
||||
qr.add_data(UNICODE_TEXT)
|
||||
img = qr.make_image(back_color="red")
|
||||
img.save(io.BytesIO())
|
||||
|
||||
@unittest.skipIf(not pil_Image, "Requires PIL")
|
||||
def test_render_pil_with_rgb_color_tuples(self):
|
||||
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())
|
||||
|
||||
@unittest.skipIf(not pil_Image, "Requires PIL")
|
||||
def test_render_with_pattern(self):
|
||||
qr = qrcode.QRCode(mask_pattern=3)
|
||||
qr.add_data(UNICODE_TEXT)
|
||||
img = qr.make_image()
|
||||
img.save(io.BytesIO())
|
||||
|
||||
def test_make_image_with_wrong_pattern(self):
|
||||
with self.assertRaises(TypeError):
|
||||
qrcode.QRCode(mask_pattern="string pattern")
|
||||
|
||||
with self.assertRaises(ValueError):
|
||||
qrcode.QRCode(mask_pattern=-1)
|
||||
|
||||
with self.assertRaises(ValueError):
|
||||
qrcode.QRCode(mask_pattern=42)
|
||||
|
||||
def test_mask_pattern_setter(self):
|
||||
qr = qrcode.QRCode()
|
||||
|
||||
with self.assertRaises(TypeError):
|
||||
qr.mask_pattern = "string pattern"
|
||||
|
||||
with self.assertRaises(ValueError):
|
||||
qr.mask_pattern = -1
|
||||
|
||||
with self.assertRaises(ValueError):
|
||||
qr.mask_pattern = 8
|
||||
|
||||
def test_qrcode_bad_factory(self):
|
||||
with self.assertRaises(TypeError):
|
||||
qrcode.QRCode(image_factory="not_BaseImage") # type: ignore
|
||||
|
||||
with self.assertRaises(AssertionError):
|
||||
qrcode.QRCode(image_factory=dict) # type: ignore
|
||||
|
||||
def test_qrcode_factory(self):
|
||||
class MockFactory(BaseImage):
|
||||
drawrect = mock.Mock()
|
||||
new_image = mock.Mock()
|
||||
|
||||
qr = qrcode.QRCode(image_factory=MockFactory)
|
||||
qr.add_data(UNICODE_TEXT)
|
||||
qr.make_image()
|
||||
self.assertTrue(MockFactory.new_image.called)
|
||||
self.assertTrue(MockFactory.drawrect.called)
|
||||
|
||||
def test_render_pypng(self):
|
||||
qr = qrcode.QRCode()
|
||||
qr.add_data(UNICODE_TEXT)
|
||||
img = qr.make_image(image_factory=PyPNGImage)
|
||||
self.assertIsInstance(img.get_image(), png.Writer)
|
||||
|
||||
print(img.width, img.box_size, img.border)
|
||||
img.save(io.BytesIO())
|
||||
|
||||
def test_render_pypng_to_str(self):
|
||||
qr = qrcode.QRCode()
|
||||
qr.add_data(UNICODE_TEXT)
|
||||
img = qr.make_image(image_factory=PyPNGImage)
|
||||
self.assertIsInstance(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()
|
||||
|
||||
@unittest.skipIf(not pil_Image, "Requires PIL")
|
||||
def test_render_styled_Image(self):
|
||||
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())
|
||||
|
||||
@unittest.skipIf(not pil_Image, "Requires PIL")
|
||||
def test_render_styled_with_embeded_image(self):
|
||||
embeded_img = pil_Image.new("RGB", (10, 10), color="red")
|
||||
qr = qrcode.QRCode(error_correction=qrcode.ERROR_CORRECT_L)
|
||||
qr.add_data(UNICODE_TEXT)
|
||||
img = qr.make_image(image_factory=StyledPilImage, embeded_image=embeded_img)
|
||||
img.save(io.BytesIO())
|
||||
|
||||
@unittest.skipIf(not pil_Image, "Requires PIL")
|
||||
def test_render_styled_with_embeded_image_path(self):
|
||||
tmpfile = os.path.join(self.tmpdir, "test.png")
|
||||
embeded_img = pil_Image.new("RGB", (10, 10), color="red")
|
||||
embeded_img.save(tmpfile)
|
||||
qr = qrcode.QRCode(error_correction=qrcode.ERROR_CORRECT_L)
|
||||
qr.add_data(UNICODE_TEXT)
|
||||
img = qr.make_image(image_factory=StyledPilImage, embeded_image_path=tmpfile)
|
||||
img.save(io.BytesIO())
|
||||
os.remove(tmpfile)
|
||||
|
||||
@unittest.skipIf(not pil_Image, "Requires PIL")
|
||||
def test_render_styled_with_square_module_drawer(self):
|
||||
qr = qrcode.QRCode(error_correction=qrcode.ERROR_CORRECT_L)
|
||||
qr.add_data(UNICODE_TEXT)
|
||||
img = qr.make_image(
|
||||
image_factory=StyledPilImage,
|
||||
module_drawer=moduledrawers.SquareModuleDrawer(),
|
||||
)
|
||||
img.save(io.BytesIO())
|
||||
|
||||
@unittest.skipIf(not pil_Image, "Requires PIL")
|
||||
def test_render_styled_with_gapped_module_drawer(self):
|
||||
qr = qrcode.QRCode(error_correction=qrcode.ERROR_CORRECT_L)
|
||||
qr.add_data(UNICODE_TEXT)
|
||||
img = qr.make_image(
|
||||
image_factory=StyledPilImage,
|
||||
module_drawer=moduledrawers.GappedSquareModuleDrawer(),
|
||||
)
|
||||
img.save(io.BytesIO())
|
||||
|
||||
@unittest.skipIf(not pil_Image, "Requires PIL")
|
||||
def test_render_styled_with_circle_module_drawer(self):
|
||||
qr = qrcode.QRCode(error_correction=qrcode.ERROR_CORRECT_L)
|
||||
qr.add_data(UNICODE_TEXT)
|
||||
img = qr.make_image(
|
||||
image_factory=StyledPilImage,
|
||||
module_drawer=moduledrawers.CircleModuleDrawer(),
|
||||
)
|
||||
img.save(io.BytesIO())
|
||||
|
||||
@unittest.skipIf(not pil_Image, "Requires PIL")
|
||||
def test_render_styled_with_rounded_module_drawer(self):
|
||||
qr = qrcode.QRCode(error_correction=qrcode.ERROR_CORRECT_L)
|
||||
qr.add_data(UNICODE_TEXT)
|
||||
img = qr.make_image(
|
||||
image_factory=StyledPilImage,
|
||||
module_drawer=moduledrawers.RoundedModuleDrawer(),
|
||||
)
|
||||
img.save(io.BytesIO())
|
||||
|
||||
@unittest.skipIf(not pil_Image, "Requires PIL")
|
||||
def test_render_styled_with_vertical_bars_module_drawer(self):
|
||||
qr = qrcode.QRCode(error_correction=qrcode.ERROR_CORRECT_L)
|
||||
qr.add_data(UNICODE_TEXT)
|
||||
img = qr.make_image(
|
||||
image_factory=StyledPilImage,
|
||||
module_drawer=moduledrawers.VerticalBarsDrawer(),
|
||||
)
|
||||
img.save(io.BytesIO())
|
||||
|
||||
@unittest.skipIf(not pil_Image, "Requires PIL")
|
||||
def test_render_styled_with_horizontal_bars_module_drawer(self):
|
||||
qr = qrcode.QRCode(error_correction=qrcode.ERROR_CORRECT_L)
|
||||
qr.add_data(UNICODE_TEXT)
|
||||
img = qr.make_image(
|
||||
image_factory=StyledPilImage,
|
||||
module_drawer=moduledrawers.HorizontalBarsDrawer(),
|
||||
)
|
||||
img.save(io.BytesIO())
|
||||
|
||||
@unittest.skipIf(not pil_Image, "Requires PIL")
|
||||
def test_render_styled_with_default_solid_color_mask(self):
|
||||
qr = qrcode.QRCode(error_correction=qrcode.ERROR_CORRECT_L)
|
||||
qr.add_data(UNICODE_TEXT)
|
||||
mask = colormasks.SolidFillColorMask()
|
||||
img = qr.make_image(image_factory=StyledPilImage, color_mask=mask)
|
||||
img.save(io.BytesIO())
|
||||
|
||||
@unittest.skipIf(not pil_Image, "Requires PIL")
|
||||
def test_render_styled_with_solid_color_mask(self):
|
||||
qr = qrcode.QRCode(error_correction=qrcode.ERROR_CORRECT_L)
|
||||
qr.add_data(UNICODE_TEXT)
|
||||
mask = colormasks.SolidFillColorMask(back_color=WHITE, front_color=RED)
|
||||
img = qr.make_image(image_factory=StyledPilImage, color_mask=mask)
|
||||
img.save(io.BytesIO())
|
||||
|
||||
@unittest.skipIf(not pil_Image, "Requires PIL")
|
||||
def test_render_styled_with_color_mask_with_transparency(self):
|
||||
qr = qrcode.QRCode(error_correction=qrcode.ERROR_CORRECT_L)
|
||||
qr.add_data(UNICODE_TEXT)
|
||||
mask = colormasks.SolidFillColorMask(
|
||||
back_color=(255, 0, 255, 255), front_color=RED
|
||||
)
|
||||
img = qr.make_image(image_factory=StyledPilImage, color_mask=mask)
|
||||
img.save(io.BytesIO())
|
||||
assert img.mode == "RGBA"
|
||||
|
||||
@unittest.skipIf(not pil_Image, "Requires PIL")
|
||||
def test_render_styled_with_radial_gradient_color_mask(self):
|
||||
qr = qrcode.QRCode(error_correction=qrcode.ERROR_CORRECT_L)
|
||||
qr.add_data(UNICODE_TEXT)
|
||||
mask = colormasks.RadialGradiantColorMask(
|
||||
back_color=WHITE, center_color=BLACK, edge_color=RED
|
||||
)
|
||||
img = qr.make_image(image_factory=StyledPilImage, color_mask=mask)
|
||||
img.save(io.BytesIO())
|
||||
|
||||
@unittest.skipIf(not pil_Image, "Requires PIL")
|
||||
def test_render_styled_with_square_gradient_color_mask(self):
|
||||
qr = qrcode.QRCode(error_correction=qrcode.ERROR_CORRECT_L)
|
||||
qr.add_data(UNICODE_TEXT)
|
||||
mask = colormasks.SquareGradiantColorMask(
|
||||
back_color=WHITE, center_color=BLACK, edge_color=RED
|
||||
)
|
||||
img = qr.make_image(image_factory=StyledPilImage, color_mask=mask)
|
||||
img.save(io.BytesIO())
|
||||
|
||||
@unittest.skipIf(not pil_Image, "Requires PIL")
|
||||
def test_render_styled_with_horizontal_gradient_color_mask(self):
|
||||
qr = qrcode.QRCode(error_correction=qrcode.ERROR_CORRECT_L)
|
||||
qr.add_data(UNICODE_TEXT)
|
||||
mask = colormasks.HorizontalGradiantColorMask(
|
||||
back_color=WHITE, left_color=RED, right_color=BLACK
|
||||
)
|
||||
img = qr.make_image(image_factory=StyledPilImage, color_mask=mask)
|
||||
img.save(io.BytesIO())
|
||||
|
||||
@unittest.skipIf(not pil_Image, "Requires PIL")
|
||||
def test_render_styled_with_vertical_gradient_color_mask(self):
|
||||
qr = qrcode.QRCode(error_correction=qrcode.ERROR_CORRECT_L)
|
||||
qr.add_data(UNICODE_TEXT)
|
||||
mask = colormasks.VerticalGradiantColorMask(
|
||||
back_color=WHITE, top_color=RED, bottom_color=BLACK
|
||||
)
|
||||
img = qr.make_image(image_factory=StyledPilImage, color_mask=mask)
|
||||
img.save(io.BytesIO())
|
||||
|
||||
@unittest.skipIf(not pil_Image, "Requires PIL")
|
||||
def test_render_styled_with_image_color_mask(self):
|
||||
img_mask = pil_Image.new("RGB", (10, 10), color="red")
|
||||
qr = qrcode.QRCode(error_correction=qrcode.ERROR_CORRECT_L)
|
||||
qr.add_data(UNICODE_TEXT)
|
||||
mask = colormasks.ImageColorMask(back_color=WHITE, color_mask_image=img_mask)
|
||||
img = qr.make_image(image_factory=StyledPilImage, color_mask=mask)
|
||||
img.save(io.BytesIO())
|
||||
|
||||
def test_optimize(self):
|
||||
qr = qrcode.QRCode()
|
||||
text = "A1abc12345def1HELLOa"
|
||||
qr.add_data(text, optimize=4)
|
||||
qr.make()
|
||||
self.assertEqual(
|
||||
[d.mode for d in qr.data_list],
|
||||
[
|
||||
MODE_8BIT_BYTE,
|
||||
MODE_NUMBER,
|
||||
MODE_8BIT_BYTE,
|
||||
MODE_ALPHA_NUM,
|
||||
MODE_8BIT_BYTE,
|
||||
],
|
||||
)
|
||||
self.assertEqual(qr.version, 2)
|
||||
|
||||
def test_optimize_short(self):
|
||||
qr = qrcode.QRCode()
|
||||
text = "A1abc1234567def1HELLOa"
|
||||
qr.add_data(text, optimize=7)
|
||||
qr.make()
|
||||
self.assertEqual(len(qr.data_list), 3)
|
||||
self.assertEqual(
|
||||
[d.mode for d in qr.data_list],
|
||||
[MODE_8BIT_BYTE, MODE_NUMBER, MODE_8BIT_BYTE],
|
||||
)
|
||||
self.assertEqual(qr.version, 2)
|
||||
|
||||
def test_optimize_longer_than_data(self):
|
||||
qr = qrcode.QRCode()
|
||||
text = "ABCDEFGHIJK"
|
||||
qr.add_data(text, optimize=12)
|
||||
self.assertEqual(len(qr.data_list), 1)
|
||||
self.assertEqual(qr.data_list[0].mode, MODE_ALPHA_NUM)
|
||||
|
||||
def test_optimize_size(self):
|
||||
text = "A1abc12345123451234512345def1HELLOHELLOHELLOHELLOa" * 5
|
||||
|
||||
qr = qrcode.QRCode()
|
||||
qr.add_data(text)
|
||||
qr.make()
|
||||
self.assertEqual(qr.version, 10)
|
||||
|
||||
qr = qrcode.QRCode()
|
||||
qr.add_data(text, optimize=0)
|
||||
qr.make()
|
||||
self.assertEqual(qr.version, 11)
|
||||
|
||||
def test_qrdata_repr(self):
|
||||
data = b"hello"
|
||||
data_obj = qrcode.util.QRData(data)
|
||||
self.assertEqual(repr(data_obj), repr(data))
|
||||
|
||||
def test_print_ascii_stdout(self):
|
||||
qr = qrcode.QRCode()
|
||||
with mock.patch("sys.stdout") as fake_stdout:
|
||||
fake_stdout.isatty.return_value = None
|
||||
self.assertRaises(OSError, qr.print_ascii, tty=True)
|
||||
self.assertTrue(fake_stdout.isatty.called)
|
||||
|
||||
def test_print_ascii(self):
|
||||
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"
|
||||
self.assertEqual(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"
|
||||
)
|
||||
self.assertEqual(printed[: len(expected)], expected)
|
||||
|
||||
def test_print_tty_stdout(self):
|
||||
qr = qrcode.QRCode()
|
||||
with mock.patch("sys.stdout") as fake_stdout:
|
||||
fake_stdout.isatty.return_value = None
|
||||
self.assertRaises(OSError, qr.print_tty)
|
||||
self.assertTrue(fake_stdout.isatty.called)
|
||||
|
||||
def test_print_tty(self):
|
||||
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
|
||||
)
|
||||
self.assertEqual(printed[: len(expected)], expected)
|
||||
|
||||
def test_get_matrix(self):
|
||||
qr = qrcode.QRCode(border=0)
|
||||
qr.add_data("1")
|
||||
self.assertEqual(qr.get_matrix(), qr.modules)
|
||||
|
||||
def test_get_matrix_border(self):
|
||||
qr = qrcode.QRCode(border=1)
|
||||
qr.add_data("1")
|
||||
matrix = [row[1:-1] for row in qr.get_matrix()[1:-1]]
|
||||
self.assertEqual(matrix, qr.modules)
|
||||
|
||||
def test_negative_size_at_construction(self):
|
||||
self.assertRaises(ValueError, qrcode.QRCode, box_size=-1)
|
||||
|
||||
def test_negative_size_at_usage(self):
|
||||
qr = qrcode.QRCode()
|
||||
qr.box_size = -1
|
||||
self.assertRaises(ValueError, qr.make_image)
|
||||
|
||||
|
||||
class ShortcutTest(unittest.TestCase):
|
||||
@unittest.skipIf(not pil_Image, "Requires PIL")
|
||||
def runTest(self):
|
||||
qrcode.make("image")
|
||||
@@ -0,0 +1,60 @@
|
||||
import io
|
||||
import os
|
||||
import unittest
|
||||
from tempfile import mkdtemp
|
||||
|
||||
import qrcode
|
||||
from qrcode.image import svg
|
||||
|
||||
UNICODE_TEXT = "\u03b1\u03b2\u03b3"
|
||||
|
||||
|
||||
class SvgImageWhite(svg.SvgImage):
|
||||
background = "white"
|
||||
|
||||
|
||||
class QRCodeSvgTests(unittest.TestCase):
|
||||
def setUp(self):
|
||||
self.tmpdir = mkdtemp()
|
||||
|
||||
def tearDown(self):
|
||||
os.rmdir(self.tmpdir)
|
||||
|
||||
def test_render_svg(self):
|
||||
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(self):
|
||||
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(self):
|
||||
qr = qrcode.QRCode()
|
||||
qr.add_data(UNICODE_TEXT)
|
||||
img = qr.make_image(image_factory=svg.SvgFragmentImage)
|
||||
img.save(io.BytesIO())
|
||||
|
||||
def test_svg_string(self):
|
||||
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(self):
|
||||
qr = qrcode.QRCode()
|
||||
qr.add_data(UNICODE_TEXT)
|
||||
img = qr.make_image(image_factory=SvgImageWhite)
|
||||
img.save(io.BytesIO())
|
||||
|
||||
def test_svg_circle_drawer(self):
|
||||
qr = qrcode.QRCode()
|
||||
qr.add_data(UNICODE_TEXT)
|
||||
img = qr.make_image(image_factory=svg.SvgPathImage, module_drawer="circle")
|
||||
img.save(io.BytesIO())
|
||||
@@ -0,0 +1,40 @@
|
||||
import re
|
||||
import builtins
|
||||
import datetime
|
||||
import unittest
|
||||
from unittest import mock
|
||||
|
||||
from qrcode.release import update_manpage
|
||||
|
||||
OPEN = f"{builtins.__name__}.open"
|
||||
DATA = 'test\n.TH "date" "version" "description"\nthis'
|
||||
|
||||
|
||||
class UpdateManpageTests(unittest.TestCase):
|
||||
@mock.patch(OPEN, new_callable=mock.mock_open, read_data=".TH invalid")
|
||||
def test_invalid_data(self, mock_file):
|
||||
update_manpage({"name": "qrcode", "new_version": "1.23"})
|
||||
mock_file.assert_called()
|
||||
mock_file().write.assert_not_called()
|
||||
|
||||
@mock.patch(OPEN, new_callable=mock.mock_open, read_data=DATA)
|
||||
def test_not_qrcode(self, mock_file):
|
||||
update_manpage({"name": "not-qrcode"})
|
||||
mock_file.assert_not_called()
|
||||
|
||||
@mock.patch(OPEN, new_callable=mock.mock_open, read_data=DATA)
|
||||
def test_no_change(self, mock_file):
|
||||
update_manpage({"name": "qrcode", "new_version": "version"})
|
||||
mock_file.assert_called()
|
||||
mock_file().write.assert_not_called()
|
||||
|
||||
@mock.patch(OPEN, new_callable=mock.mock_open, read_data=DATA)
|
||||
def test_change(self, mock_file):
|
||||
update_manpage({"name": "qrcode", "new_version": "3.11"})
|
||||
expected = re.split(r"([^\n]*(?:\n|$))", DATA)[1::2]
|
||||
expected[1] = (
|
||||
expected[1]
|
||||
.replace("version", "3.11")
|
||||
.replace("date", datetime.datetime.now().strftime("%-d %b %Y"))
|
||||
)
|
||||
mock_file().write.has_calls([mock.call(line) for line in expected])
|
||||
@@ -0,0 +1,105 @@
|
||||
import io
|
||||
import os
|
||||
import sys
|
||||
import unittest
|
||||
from tempfile import mkdtemp
|
||||
from unittest import mock
|
||||
|
||||
from qrcode.compat.pil import Image
|
||||
from qrcode.console_scripts import commas, main
|
||||
|
||||
|
||||
def bad_read():
|
||||
raise UnicodeDecodeError("utf-8", b"0x80", 0, 1, "invalid start byte")
|
||||
|
||||
|
||||
class ScriptTest(unittest.TestCase):
|
||||
def setUp(self):
|
||||
self.tmpdir = mkdtemp()
|
||||
|
||||
def tearDown(self):
|
||||
os.rmdir(self.tmpdir)
|
||||
|
||||
@mock.patch("os.isatty", lambda *args: True)
|
||||
@mock.patch("qrcode.main.QRCode.print_ascii")
|
||||
def test_isatty(self, mock_print_ascii):
|
||||
main(["testtext"])
|
||||
mock_print_ascii.assert_called_with(tty=True)
|
||||
|
||||
@mock.patch("os.isatty", lambda *args: False)
|
||||
@mock.patch("sys.stdout")
|
||||
@unittest.skipIf(not Image, "Requires PIL")
|
||||
def test_piped(self, mock_stdout):
|
||||
main(["testtext"])
|
||||
|
||||
@mock.patch("os.isatty", lambda *args: True)
|
||||
@mock.patch("qrcode.main.QRCode.print_ascii")
|
||||
@mock.patch("sys.stdin")
|
||||
def test_stdin(self, mock_stdin, mock_print_ascii):
|
||||
mock_stdin.buffer.read.return_value = "testtext"
|
||||
main([])
|
||||
self.assertTrue(mock_stdin.buffer.read.called)
|
||||
mock_print_ascii.assert_called_with(tty=True)
|
||||
|
||||
@mock.patch("os.isatty", lambda *args: True)
|
||||
@mock.patch("qrcode.main.QRCode.print_ascii")
|
||||
def test_stdin_py3_unicodedecodeerror(self, mock_print_ascii):
|
||||
mock_stdin = mock.Mock(sys.stdin)
|
||||
mock_stdin.buffer.read.return_value = "testtext"
|
||||
mock_stdin.read.side_effect = bad_read
|
||||
with mock.patch("sys.stdin", mock_stdin):
|
||||
# sys.stdin.read() will raise an error...
|
||||
self.assertRaises(UnicodeDecodeError, sys.stdin.read)
|
||||
# ... but it won't be used now.
|
||||
main([])
|
||||
mock_print_ascii.assert_called_with(tty=True)
|
||||
|
||||
@mock.patch("os.isatty", lambda *args: True)
|
||||
@mock.patch("qrcode.main.QRCode.print_ascii")
|
||||
def test_optimize(self, mock_print_ascii):
|
||||
main("testtext --optimize 0".split())
|
||||
|
||||
@mock.patch("sys.stdout")
|
||||
def test_factory(self, mock_stdout):
|
||||
main("testtext --factory svg".split())
|
||||
|
||||
@mock.patch("sys.stderr")
|
||||
def test_bad_factory(self, mock_stderr):
|
||||
self.assertRaises(SystemExit, main, "testtext --factory fish".split())
|
||||
|
||||
@mock.patch.object(sys, "argv", "qr testtext output".split())
|
||||
@unittest.skipIf(not Image, "Requires PIL")
|
||||
def test_sys_argv(self):
|
||||
main()
|
||||
|
||||
@unittest.skipIf(not Image, "Requires PIL")
|
||||
def test_output(self):
|
||||
tmpfile = os.path.join(self.tmpdir, "test.png")
|
||||
main(["testtext", "--output", tmpfile])
|
||||
os.remove(tmpfile)
|
||||
|
||||
@mock.patch("sys.stderr", new_callable=io.StringIO)
|
||||
@unittest.skipIf(not Image, "Requires PIL")
|
||||
def test_factory_drawer_none(self, mock_stderr):
|
||||
with self.assertRaises(SystemExit):
|
||||
main("testtext --factory pil --factory-drawer nope".split())
|
||||
self.assertIn(
|
||||
"The selected factory has no drawer aliases", mock_stderr.getvalue()
|
||||
)
|
||||
|
||||
@mock.patch("sys.stderr", new_callable=io.StringIO)
|
||||
def test_factory_drawer_bad(self, mock_stderr):
|
||||
with self.assertRaises(SystemExit):
|
||||
main("testtext --factory svg --factory-drawer sobad".split())
|
||||
self.assertIn("sobad factory drawer not found", mock_stderr.getvalue())
|
||||
|
||||
@mock.patch("sys.stderr", new_callable=io.StringIO)
|
||||
def test_factory_drawer(self, mock_stderr):
|
||||
main("testtext --factory svg --factory-drawer circle".split())
|
||||
|
||||
def test_commas(self):
|
||||
self.assertEqual(commas([]), "")
|
||||
self.assertEqual(commas(["A"]), "A")
|
||||
self.assertEqual(commas("AB"), "A or B")
|
||||
self.assertEqual(commas("ABC"), "A, B or C")
|
||||
self.assertEqual(commas("ABC", joiner="and"), "A, B and C")
|
||||
@@ -0,0 +1,12 @@
|
||||
import unittest
|
||||
|
||||
from qrcode import util
|
||||
|
||||
|
||||
class UtilTests(unittest.TestCase):
|
||||
def test_check_wrong_version(self):
|
||||
with self.assertRaises(ValueError):
|
||||
util.check_version(0)
|
||||
|
||||
with self.assertRaises(ValueError):
|
||||
util.check_version(41)
|
||||
586
Backend/venv/lib/python3.12/site-packages/qrcode/util.py
Normal file
586
Backend/venv/lib/python3.12/site-packages/qrcode/util.py
Normal file
@@ -0,0 +1,586 @@
|
||||
import math
|
||||
import re
|
||||
from typing import List
|
||||
|
||||
from qrcode import LUT, base, exceptions
|
||||
from qrcode.base import RSBlock
|
||||
|
||||
# QR encoding modes.
|
||||
MODE_NUMBER = 1 << 0
|
||||
MODE_ALPHA_NUM = 1 << 1
|
||||
MODE_8BIT_BYTE = 1 << 2
|
||||
MODE_KANJI = 1 << 3
|
||||
|
||||
# Encoding mode sizes.
|
||||
MODE_SIZE_SMALL = {
|
||||
MODE_NUMBER: 10,
|
||||
MODE_ALPHA_NUM: 9,
|
||||
MODE_8BIT_BYTE: 8,
|
||||
MODE_KANJI: 8,
|
||||
}
|
||||
MODE_SIZE_MEDIUM = {
|
||||
MODE_NUMBER: 12,
|
||||
MODE_ALPHA_NUM: 11,
|
||||
MODE_8BIT_BYTE: 16,
|
||||
MODE_KANJI: 10,
|
||||
}
|
||||
MODE_SIZE_LARGE = {
|
||||
MODE_NUMBER: 14,
|
||||
MODE_ALPHA_NUM: 13,
|
||||
MODE_8BIT_BYTE: 16,
|
||||
MODE_KANJI: 12,
|
||||
}
|
||||
|
||||
ALPHA_NUM = b"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ $%*+-./:"
|
||||
RE_ALPHA_NUM = re.compile(b"^[" + re.escape(ALPHA_NUM) + rb"]*\Z")
|
||||
|
||||
# The number of bits for numeric delimited data lengths.
|
||||
NUMBER_LENGTH = {3: 10, 2: 7, 1: 4}
|
||||
|
||||
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],
|
||||
]
|
||||
|
||||
G15 = (1 << 10) | (1 << 8) | (1 << 5) | (1 << 4) | (1 << 2) | (1 << 1) | (1 << 0)
|
||||
G18 = (
|
||||
(1 << 12)
|
||||
| (1 << 11)
|
||||
| (1 << 10)
|
||||
| (1 << 9)
|
||||
| (1 << 8)
|
||||
| (1 << 5)
|
||||
| (1 << 2)
|
||||
| (1 << 0)
|
||||
)
|
||||
G15_MASK = (1 << 14) | (1 << 12) | (1 << 10) | (1 << 4) | (1 << 1)
|
||||
|
||||
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))
|
||||
|
||||
return ((data << 10) | d) ^ G15_MASK
|
||||
|
||||
|
||||
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
|
||||
|
||||
lost_point = _lost_point_level1(modules, modules_count)
|
||||
lost_point += _lost_point_level2(modules, modules_count)
|
||||
lost_point += _lost_point_level3(modules, modules_count)
|
||||
lost_point += _lost_point_level4(modules, modules_count)
|
||||
|
||||
return lost_point
|
||||
|
||||
|
||||
def _lost_point_level1(modules, modules_count):
|
||||
lost_point = 0
|
||||
|
||||
modules_range = range(modules_count)
|
||||
container = [0] * (modules_count + 1)
|
||||
|
||||
for row in modules_range:
|
||||
this_row = modules[row]
|
||||
previous_color = this_row[0]
|
||||
length = 0
|
||||
for col in modules_range:
|
||||
if this_row[col] == previous_color:
|
||||
length += 1
|
||||
else:
|
||||
if length >= 5:
|
||||
container[length] += 1
|
||||
length = 1
|
||||
previous_color = this_row[col]
|
||||
if length >= 5:
|
||||
container[length] += 1
|
||||
|
||||
for col in modules_range:
|
||||
previous_color = modules[0][col]
|
||||
length = 0
|
||||
for row in modules_range:
|
||||
if modules[row][col] == previous_color:
|
||||
length += 1
|
||||
else:
|
||||
if length >= 5:
|
||||
container[length] += 1
|
||||
length = 1
|
||||
previous_color = modules[row][col]
|
||||
if length >= 5:
|
||||
container[length] += 1
|
||||
|
||||
lost_point += sum(
|
||||
container[each_length] * (each_length - 2)
|
||||
for each_length in range(5, modules_count + 1)
|
||||
)
|
||||
|
||||
return lost_point
|
||||
|
||||
|
||||
def _lost_point_level2(modules, modules_count):
|
||||
lost_point = 0
|
||||
|
||||
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
|
||||
|
||||
|
||||
def _lost_point_level3(modules, modules_count):
|
||||
# 1 : 1 : 3 : 1 : 1 ratio (dark:light:dark:light:dark) pattern in
|
||||
# row/column, preceded or followed by light area 4 modules wide. From ISOIEC.
|
||||
# pattern1: 10111010000
|
||||
# pattern2: 00001011101
|
||||
modules_range = range(modules_count)
|
||||
modules_range_short = range(modules_count - 10)
|
||||
lost_point = 0
|
||||
|
||||
for row in modules_range:
|
||||
this_row = modules[row]
|
||||
modules_range_short_iter = iter(modules_range_short)
|
||||
col = 0
|
||||
for col in modules_range_short_iter:
|
||||
if (
|
||||
not this_row[col + 1]
|
||||
and this_row[col + 4]
|
||||
and not this_row[col + 5]
|
||||
and this_row[col + 6]
|
||||
and not this_row[col + 9]
|
||||
and (
|
||||
this_row[col + 0]
|
||||
and this_row[col + 2]
|
||||
and this_row[col + 3]
|
||||
and not this_row[col + 7]
|
||||
and not this_row[col + 8]
|
||||
and not this_row[col + 10]
|
||||
or not this_row[col + 0]
|
||||
and not this_row[col + 2]
|
||||
and not this_row[col + 3]
|
||||
and this_row[col + 7]
|
||||
and this_row[col + 8]
|
||||
and this_row[col + 10]
|
||||
)
|
||||
):
|
||||
lost_point += 40
|
||||
# horspool algorithm.
|
||||
# if this_row[col + 10]:
|
||||
# pattern1 shift 4, pattern2 shift 2. So min=2.
|
||||
# else:
|
||||
# pattern1 shift 1, pattern2 shift 1. So min=1.
|
||||
if this_row[col + 10]:
|
||||
next(modules_range_short_iter, None)
|
||||
|
||||
for col in modules_range:
|
||||
modules_range_short_iter = iter(modules_range_short)
|
||||
row = 0
|
||||
for row in modules_range_short_iter:
|
||||
if (
|
||||
not modules[row + 1][col]
|
||||
and modules[row + 4][col]
|
||||
and not modules[row + 5][col]
|
||||
and modules[row + 6][col]
|
||||
and not modules[row + 9][col]
|
||||
and (
|
||||
modules[row + 0][col]
|
||||
and modules[row + 2][col]
|
||||
and modules[row + 3][col]
|
||||
and not modules[row + 7][col]
|
||||
and not modules[row + 8][col]
|
||||
and not modules[row + 10][col]
|
||||
or not modules[row + 0][col]
|
||||
and not modules[row + 2][col]
|
||||
and not modules[row + 3][col]
|
||||
and modules[row + 7][col]
|
||||
and modules[row + 8][col]
|
||||
and modules[row + 10][col]
|
||||
)
|
||||
):
|
||||
lost_point += 40
|
||||
if modules[row + 10][col]:
|
||||
next(modules_range_short_iter, None)
|
||||
|
||||
return lost_point
|
||||
|
||||
|
||||
def _lost_point_level4(modules, modules_count):
|
||||
dark_count = sum(map(sum, modules))
|
||||
percent = float(dark_count) / (modules_count**2)
|
||||
# Every 5% departure from 50%, rating++
|
||||
rating = int(abs(percent * 100 - 50) / 5)
|
||||
return rating * 10
|
||||
|
||||
|
||||
def optimal_data_chunks(data, minimum=4):
|
||||
"""
|
||||
An iterator returning QRData chunks optimized to the data content.
|
||||
|
||||
:param minimum: The minimum number of bytes in a row to split as a chunk.
|
||||
"""
|
||||
data = to_bytestring(data)
|
||||
num_pattern = rb"\d"
|
||||
alpha_pattern = b"[" + re.escape(ALPHA_NUM) + b"]"
|
||||
if len(data) <= minimum:
|
||||
num_pattern = re.compile(b"^" + num_pattern + b"+$")
|
||||
alpha_pattern = re.compile(b"^" + alpha_pattern + b"+$")
|
||||
else:
|
||||
re_repeat = b"{" + str(minimum).encode("ascii") + b",}"
|
||||
num_pattern = re.compile(num_pattern + re_repeat)
|
||||
alpha_pattern = re.compile(alpha_pattern + re_repeat)
|
||||
num_bits = _optimal_split(data, num_pattern)
|
||||
for is_num, chunk in num_bits:
|
||||
if is_num:
|
||||
yield QRData(chunk, mode=MODE_NUMBER, check_data=False)
|
||||
else:
|
||||
for is_alpha, sub_chunk in _optimal_split(chunk, alpha_pattern):
|
||||
mode = MODE_ALPHA_NUM if is_alpha else MODE_8BIT_BYTE
|
||||
yield QRData(sub_chunk, mode=mode, check_data=False)
|
||||
|
||||
|
||||
def _optimal_split(data, pattern):
|
||||
while data:
|
||||
match = re.search(pattern, data)
|
||||
if not match:
|
||||
break
|
||||
start, end = match.start(), match.end()
|
||||
if start:
|
||||
yield False, data[:start]
|
||||
yield True, data[start:end]
|
||||
data = data[end:]
|
||||
if data:
|
||||
yield False, data
|
||||
|
||||
|
||||
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.
|
||||
|
||||
Doesn't currently handle KANJI.
|
||||
"""
|
||||
|
||||
def __init__(self, data, mode=None, check_data=True):
|
||||
"""
|
||||
If ``mode`` isn't provided, the most compact QR data type possible is
|
||||
chosen.
|
||||
"""
|
||||
if check_data:
|
||||
data = to_bytestring(data)
|
||||
|
||||
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)
|
||||
|
||||
def write(self, buffer):
|
||||
if self.mode == MODE_NUMBER:
|
||||
for i in range(0, len(self.data), 3):
|
||||
chars = self.data[i : i + 3]
|
||||
bit_length = NUMBER_LENGTH[len(chars)]
|
||||
buffer.put(int(chars), bit_length)
|
||||
elif self.mode == MODE_ALPHA_NUM:
|
||||
for i in range(0, len(self.data), 2):
|
||||
chars = self.data[i : i + 2]
|
||||
if len(chars) > 1:
|
||||
buffer.put(
|
||||
ALPHA_NUM.find(chars[0]) * 45 + ALPHA_NUM.find(chars[1]), 11
|
||||
)
|
||||
else:
|
||||
buffer.put(ALPHA_NUM.find(chars), 6)
|
||||
else:
|
||||
# Iterating a bytestring in Python 3 returns an integer,
|
||||
# no need to ord().
|
||||
data = self.data
|
||||
for c in data:
|
||||
buffer.put(c, 8)
|
||||
|
||||
def __repr__(self):
|
||||
return repr(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 get(self, index):
|
||||
buf_index = math.floor(index / 8)
|
||||
return ((self.buffer[buf_index] >> (7 - index % 8)) & 1) == 1
|
||||
|
||||
def put(self, num, length):
|
||||
for i in range(length):
|
||||
self.put_bit(((num >> (length - i - 1)) & 1) == 1)
|
||||
|
||||
def __len__(self):
|
||||
return self.length
|
||||
|
||||
def put_bit(self, bit):
|
||||
buf_index = self.length // 8
|
||||
if len(self.buffer) <= buf_index:
|
||||
self.buffer.append(0)
|
||||
if bit:
|
||||
self.buffer[buf_index] |= 0x80 >> (self.length % 8)
|
||||
self.length += 1
|
||||
|
||||
|
||||
def create_bytes(buffer: BitBuffer, rs_blocks: List[RSBlock]):
|
||||
offset = 0
|
||||
|
||||
maxDcCount = 0
|
||||
maxEcCount = 0
|
||||
|
||||
dcdata: List[List[int]] = []
|
||||
ecdata: List[List[int]] = []
|
||||
|
||||
for rs_block in rs_blocks:
|
||||
dcCount = rs_block.data_count
|
||||
ecCount = rs_block.total_count - dcCount
|
||||
|
||||
maxDcCount = max(maxDcCount, dcCount)
|
||||
maxEcCount = max(maxEcCount, ecCount)
|
||||
|
||||
current_dc = [0xFF & buffer.buffer[i + offset] for i in range(dcCount)]
|
||||
offset += dcCount
|
||||
|
||||
# Get error correction polynomial.
|
||||
if ecCount in LUT.rsPoly_LUT:
|
||||
rsPoly = base.Polynomial(LUT.rsPoly_LUT[ecCount], 0)
|
||||
else:
|
||||
rsPoly = base.Polynomial([1], 0)
|
||||
for i in range(ecCount):
|
||||
rsPoly = rsPoly * base.Polynomial([1, base.gexp(i)], 0)
|
||||
|
||||
rawPoly = base.Polynomial(current_dc, len(rsPoly) - 1)
|
||||
|
||||
modPoly = rawPoly % rsPoly
|
||||
current_ec = []
|
||||
mod_offset = len(modPoly) - ecCount
|
||||
for i in range(ecCount):
|
||||
modIndex = i + mod_offset
|
||||
current_ec.append(modPoly[modIndex] if (modIndex >= 0) else 0)
|
||||
|
||||
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
|
||||
|
||||
|
||||
def create_data(version, error_correction, data_list):
|
||||
|
||||
buffer = BitBuffer()
|
||||
for data in data_list:
|
||||
buffer.put(data.mode, 4)
|
||||
buffer.put(len(data), length_in_bits(data.mode, version))
|
||||
data.write(buffer)
|
||||
|
||||
# Calculate the maximum number of bits for the given version.
|
||||
rs_blocks = base.rs_blocks(version, error_correction)
|
||||
bit_limit = sum(block.data_count * 8 for block in rs_blocks)
|
||||
if len(buffer) > bit_limit:
|
||||
raise exceptions.DataOverflowError(
|
||||
"Code length overflow. Data size (%s) > size available (%s)"
|
||||
% (len(buffer), bit_limit)
|
||||
)
|
||||
|
||||
# Terminate the bits (add up to four 0s).
|
||||
for _ in range(min(bit_limit - len(buffer), 4)):
|
||||
buffer.put_bit(False)
|
||||
|
||||
# Delimit the string into 8-bit words, padding with 0s if necessary.
|
||||
delimit = len(buffer) % 8
|
||||
if delimit:
|
||||
for _ in range(8 - delimit):
|
||||
buffer.put_bit(False)
|
||||
|
||||
# Add special alternating padding bitstrings until buffer is full.
|
||||
bytes_to_fill = (bit_limit - len(buffer)) // 8
|
||||
for i in range(bytes_to_fill):
|
||||
if i % 2 == 0:
|
||||
buffer.put(PAD0, 8)
|
||||
else:
|
||||
buffer.put(PAD1, 8)
|
||||
|
||||
return create_bytes(buffer, rs_blocks)
|
||||
Reference in New Issue
Block a user