Coverage for /var/srv/projects/api.amasfac.comuna18.com/tmp/venv/lib/python3.9/site-packages/qrcode/main.py: 62%
286 statements
« prev ^ index » next coverage.py v6.4.4, created at 2023-07-17 14:22 -0600
« prev ^ index » next coverage.py v6.4.4, created at 2023-07-17 14:22 -0600
1from qrcode import constants, exceptions, util
2from qrcode.image.base import BaseImage
4import sys
5from bisect import bisect_left
7# Cache modules generated just based on the QR Code version
8precomputed_qr_blanks = {}
10def make(data=None, **kwargs):
11 qr = QRCode(**kwargs)
12 qr.add_data(data)
13 return qr.make_image()
16def _check_box_size(size):
17 if int(size) <= 0: 17 ↛ 18line 17 didn't jump to line 18, because the condition on line 17 was never true
18 raise ValueError(
19 f"Invalid box size (was {size}, expected larger than 0)")
21def _check_border(size):
22 if int(size) < 0: 22 ↛ 23line 22 didn't jump to line 23, because the condition on line 22 was never true
23 raise ValueError("Invalid border value (was %s, expected 0 or larger than that)" % size)
26def _check_mask_pattern(mask_pattern):
27 if mask_pattern is None: 27 ↛ 29line 27 didn't jump to line 29, because the condition on line 27 was never false
28 return
29 if not isinstance(mask_pattern, int):
30 raise TypeError(
31 f"Invalid mask pattern (was {type(mask_pattern)}, expected int)")
32 if mask_pattern < 0 or mask_pattern > 7:
33 raise ValueError(
34 f"Mask pattern should be in range(8) (got {mask_pattern})")
36def copy_2d_array(x):
37 return [row[:] for row in x]
39class QRCode:
41 def __init__(self, version=None,
42 error_correction=constants.ERROR_CORRECT_M,
43 box_size=10, border=4,
44 image_factory=None,
45 mask_pattern=None):
46 _check_box_size(box_size)
47 _check_border(border)
48 self.version = version and int(version)
49 self.error_correction = int(error_correction)
50 self.box_size = int(box_size)
51 # Spec says border should be at least four boxes wide, but allow for
52 # any (e.g. for producing printable QR codes).
53 self.border = int(border)
54 self.mask_pattern = mask_pattern
55 self.image_factory = image_factory
56 if image_factory is not None: 56 ↛ 57line 56 didn't jump to line 57, because the condition on line 56 was never true
57 assert issubclass(image_factory, BaseImage)
58 self.clear()
60 @property
61 def mask_pattern(self):
62 return self._mask_pattern
64 @mask_pattern.setter
65 def mask_pattern(self, pattern):
66 _check_mask_pattern(pattern)
67 self._mask_pattern = pattern
69 def clear(self):
70 """
71 Reset the internal data.
72 """
73 self.modules = None
74 self.modules_count = 0
75 self.data_cache = None
76 self.data_list = []
78 def add_data(self, data, optimize=20):
79 """
80 Add data to this QR Code.
82 :param optimize: Data will be split into multiple chunks to optimize
83 the QR size by finding to more compressed modes of at least this
84 length. Set to ``0`` to avoid optimizing at all.
85 """
86 if isinstance(data, util.QRData): 86 ↛ 87line 86 didn't jump to line 87, because the condition on line 86 was never true
87 self.data_list.append(data)
88 elif optimize: 88 ↛ 92line 88 didn't jump to line 92, because the condition on line 88 was never false
89 self.data_list.extend(
90 util.optimal_data_chunks(data, minimum=optimize))
91 else:
92 self.data_list.append(util.QRData(data))
93 self.data_cache = None
95 def make(self, fit=True):
96 """
97 Compile the data into a QR Code array.
99 :param fit: If ``True`` (or if a size has not been provided), find the
100 best fit for the data to avoid data overflow errors.
101 """
102 if fit or (self.version is None): 102 ↛ 104line 102 didn't jump to line 104, because the condition on line 102 was never false
103 self.best_fit(start=self.version)
104 if self.mask_pattern is None: 104 ↛ 107line 104 didn't jump to line 107, because the condition on line 104 was never false
105 self.makeImpl(False, self.best_mask_pattern())
106 else:
107 self.makeImpl(False, self.mask_pattern)
109 def makeImpl(self, test, mask_pattern):
110 util.check_version(self.version)
111 self.modules_count = self.version * 4 + 17
113 if self.version in precomputed_qr_blanks:
114 self.modules = copy_2d_array(precomputed_qr_blanks[self.version])
115 else:
116 self.modules = [None] * self.modules_count
118 for row in range(self.modules_count):
119 self.modules[row] = [None] * self.modules_count
121 self.setup_position_probe_pattern(0, 0)
122 self.setup_position_probe_pattern(self.modules_count - 7, 0)
123 self.setup_position_probe_pattern(0, self.modules_count - 7)
124 self.setup_position_adjust_pattern()
125 self.setup_timing_pattern()
127 precomputed_qr_blanks[self.version] = copy_2d_array(self.modules)
129 self.setup_type_info(test, mask_pattern)
131 if self.version >= 7: 131 ↛ 132line 131 didn't jump to line 132, because the condition on line 131 was never true
132 self.setup_type_number(test)
134 if self.data_cache is None:
135 self.data_cache = util.create_data(
136 self.version, self.error_correction, self.data_list)
137 self.map_data(self.data_cache, mask_pattern)
139 def setup_position_probe_pattern(self, row, col):
140 for r in range(-1, 8):
142 if row + r <= -1 or self.modules_count <= row + r:
143 continue
145 for c in range(-1, 8):
147 if col + c <= -1 or self.modules_count <= col + c:
148 continue
150 if (
151 (0 <= r <= 6 and c in {0, 6})
152 or (0 <= c <= 6 and r in {0, 6})
153 or (2 <= r <= 4 and 2 <= c <= 4)
154 ):
155 self.modules[row + r][col + c] = True
156 else:
157 self.modules[row + r][col + c] = False
159 def best_fit(self, start=None):
160 """
161 Find the minimum size required to fit in the data.
162 """
163 if start is None: 163 ↛ 164line 163 didn't jump to line 164, because the condition on line 163 was never true
164 start = 1
165 util.check_version(start)
167 # Corresponds to the code in util.create_data, except we don't yet know
168 # version, so optimistically assume start and check later
169 mode_sizes = util.mode_sizes_for_version(start)
170 buffer = util.BitBuffer()
171 for data in self.data_list:
172 buffer.put(data.mode, 4)
173 buffer.put(len(data), mode_sizes[data.mode])
174 data.write(buffer)
176 needed_bits = len(buffer)
177 self.version = bisect_left(util.BIT_LIMIT_TABLE[self.error_correction],
178 needed_bits, start)
179 if self.version == 41: 179 ↛ 180line 179 didn't jump to line 180, because the condition on line 179 was never true
180 raise exceptions.DataOverflowError()
182 # Now check whether we need more bits for the mode sizes, recursing if
183 # our guess was too low
184 if mode_sizes is not util.mode_sizes_for_version(self.version): 184 ↛ 185line 184 didn't jump to line 185, because the condition on line 184 was never true
185 self.best_fit(start=self.version)
186 return self.version
188 def best_mask_pattern(self):
189 """
190 Find the most efficient mask pattern.
191 """
192 min_lost_point = 0
193 pattern = 0
195 for i in range(8):
196 self.makeImpl(True, i)
198 lost_point = util.lost_point(self.modules)
200 if i == 0 or min_lost_point > lost_point:
201 min_lost_point = lost_point
202 pattern = i
204 return pattern
206 def print_tty(self, out=None):
207 """
208 Output the QR Code only using TTY colors.
210 If the data has not been compiled yet, make it first.
211 """
212 if out is None:
213 import sys
214 out = sys.stdout
216 if not out.isatty():
217 raise OSError("Not a tty")
219 if self.data_cache is None:
220 self.make()
222 modcount = self.modules_count
223 out.write("\x1b[1;47m" + (" " * (modcount * 2 + 4)) + "\x1b[0m\n")
224 for r in range(modcount):
225 out.write("\x1b[1;47m \x1b[40m")
226 for c in range(modcount):
227 if self.modules[r][c]:
228 out.write(" ")
229 else:
230 out.write("\x1b[1;47m \x1b[40m")
231 out.write("\x1b[1;47m \x1b[0m\n")
232 out.write("\x1b[1;47m" + (" " * (modcount * 2 + 4)) + "\x1b[0m\n")
233 out.flush()
235 def print_ascii(self, out=None, tty=False, invert=False):
236 """
237 Output the QR Code using ASCII characters.
239 :param tty: use fixed TTY color codes (forces invert=True)
240 :param invert: invert the ASCII characters (solid <-> transparent)
241 """
242 if out is None:
243 out = sys.stdout
245 if tty and not out.isatty():
246 raise OSError("Not a tty")
248 if self.data_cache is None:
249 self.make()
251 modcount = self.modules_count
252 codes = [bytes((code,)).decode('cp437')
253 for code in (255, 223, 220, 219)]
254 if tty:
255 invert = True
256 if invert:
257 codes.reverse()
259 def get_module(x, y):
260 if (invert and self.border and
261 max(x, y) >= modcount+self.border):
262 return 1
263 if min(x, y) < 0 or max(x, y) >= modcount:
264 return 0
265 return self.modules[x][y]
267 for r in range(-self.border, modcount+self.border, 2):
268 if tty:
269 if not invert or r < modcount+self.border-1:
270 out.write('\x1b[48;5;232m') # Background black
271 out.write('\x1b[38;5;255m') # Foreground white
272 for c in range(-self.border, modcount+self.border):
273 pos = get_module(r, c) + (get_module(r+1, c) << 1)
274 out.write(codes[pos])
275 if tty:
276 out.write('\x1b[0m')
277 out.write('\n')
278 out.flush()
280 def make_image(self, image_factory=None, **kwargs):
281 """
282 Make an image from the QR Code data.
284 If the data has not been compiled yet, make it first.
285 """
286 _check_box_size(self.box_size)
287 if self.data_cache is None: 287 ↛ 288line 287 didn't jump to line 288, because the condition on line 287 was never true
288 self.make()
290 if image_factory is not None: 290 ↛ 291line 290 didn't jump to line 291, because the condition on line 290 was never true
291 assert issubclass(image_factory, BaseImage)
292 else:
293 image_factory = self.image_factory
294 if image_factory is None: 294 ↛ 299line 294 didn't jump to line 299, because the condition on line 294 was never false
295 # Use PIL by default
296 from qrcode.image.pil import PilImage
297 image_factory = PilImage
299 im = image_factory(
300 self.border, self.modules_count, self.box_size, **kwargs)
302 for r in range(self.modules_count):
303 for c in range(self.modules_count):
304 if im.needs_context: 304 ↛ 305line 304 didn't jump to line 305, because the condition on line 304 was never true
305 im.drawrect_context(r, c, self.modules[r][c], self.get_module_context(r,c))
306 elif self.modules[r][c]:
307 im.drawrect(r,c)
308 if im.needs_processing: 308 ↛ 309line 308 didn't jump to line 309, because the condition on line 308 was never true
309 im.process()
311 return im
313 # return true if and only if (row, col) is in the module
314 def is_constrained(self, row, col):
315 return row >= 0 and row < len(self.modules) and col >= 0 and col < len(self.modules[row])
317 def get_module_context(self, row, col):
318 context = []
320 for r in range(row-1,row + 2):
321 for c in range(col - 1, col + 2):
322 if r != row or c != col:
323 context.append(self.is_constrained(r,c) and self.modules[r][c])
324 return context
326 def setup_timing_pattern(self):
327 for r in range(8, self.modules_count - 8):
328 if self.modules[r][6] is not None: 328 ↛ 329line 328 didn't jump to line 329, because the condition on line 328 was never true
329 continue
330 self.modules[r][6] = (r % 2 == 0)
332 for c in range(8, self.modules_count - 8):
333 if self.modules[6][c] is not None: 333 ↛ 334line 333 didn't jump to line 334, because the condition on line 333 was never true
334 continue
335 self.modules[6][c] = (c % 2 == 0)
337 def setup_position_adjust_pattern(self):
338 pos = util.pattern_position(self.version)
340 for i in range(len(pos)):
342 row = pos[i]
344 for j in range(len(pos)):
346 col = pos[j]
348 if self.modules[row][col] is not None:
349 continue
351 for r in range(-2, 3):
353 for c in range(-2, 3):
355 if (r == -2 or r == 2 or c == -2 or c == 2 or
356 (r == 0 and c == 0)):
357 self.modules[row + r][col + c] = True
358 else:
359 self.modules[row + r][col + c] = False
361 def setup_type_number(self, test):
362 bits = util.BCH_type_number(self.version)
364 for i in range(18):
365 mod = (not test and ((bits >> i) & 1) == 1)
366 self.modules[i // 3][i % 3 + self.modules_count - 8 - 3] = mod
368 for i in range(18):
369 mod = (not test and ((bits >> i) & 1) == 1)
370 self.modules[i % 3 + self.modules_count - 8 - 3][i // 3] = mod
372 def setup_type_info(self, test, mask_pattern):
373 data = (self.error_correction << 3) | mask_pattern
374 bits = util.BCH_type_info(data)
376 # vertical
377 for i in range(15):
379 mod = (not test and ((bits >> i) & 1) == 1)
381 if i < 6:
382 self.modules[i][8] = mod
383 elif i < 8:
384 self.modules[i + 1][8] = mod
385 else:
386 self.modules[self.modules_count - 15 + i][8] = mod
388 # horizontal
389 for i in range(15):
391 mod = (not test and ((bits >> i) & 1) == 1)
393 if i < 8:
394 self.modules[8][self.modules_count - i - 1] = mod
395 elif i < 9:
396 self.modules[8][15 - i - 1 + 1] = mod
397 else:
398 self.modules[8][15 - i - 1] = mod
400 # fixed module
401 self.modules[self.modules_count - 8][8] = (not test)
403 def map_data(self, data, mask_pattern):
404 inc = -1
405 row = self.modules_count - 1
406 bitIndex = 7
407 byteIndex = 0
409 mask_func = util.mask_func(mask_pattern)
411 data_len = len(data)
413 for col in range(self.modules_count - 1, 0, -2):
415 if col <= 6:
416 col -= 1
418 col_range = (col, col-1)
420 while True:
422 for c in col_range:
424 if self.modules[row][c] is None:
426 dark = False
428 if byteIndex < data_len:
429 dark = (((data[byteIndex] >> bitIndex) & 1) == 1)
431 if mask_func(row, c):
432 dark = not dark
434 self.modules[row][c] = dark
435 bitIndex -= 1
437 if bitIndex == -1:
438 byteIndex += 1
439 bitIndex = 7
441 row += inc
443 if row < 0 or self.modules_count <= row:
444 row -= inc
445 inc = -inc
446 break
448 def get_matrix(self):
449 """
450 Return the QR Code as a multidimensional array, including the border.
452 To return the array without a border, set ``self.border`` to 0 first.
453 """
454 if self.data_cache is None:
455 self.make()
457 if not self.border:
458 return self.modules
460 width = len(self.modules) + self.border*2
461 code = [[False]*width] * self.border
462 x_border = [False]*self.border
463 for module in self.modules:
464 code.append(x_border + module + x_border)
465 code += [[False]*width] * self.border
467 return code