Coverage for /var/srv/projects/api.amasfac.comuna18.com/tmp/venv/lib/python3.9/site-packages/qrcode/util.py: 82%
307 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
1import re
2import math
4from qrcode import base, exceptions, LUT
6# QR encoding modes.
7MODE_NUMBER = 1 << 0
8MODE_ALPHA_NUM = 1 << 1
9MODE_8BIT_BYTE = 1 << 2
10MODE_KANJI = 1 << 3
12# Encoding mode sizes.
13MODE_SIZE_SMALL = {
14 MODE_NUMBER: 10,
15 MODE_ALPHA_NUM: 9,
16 MODE_8BIT_BYTE: 8,
17 MODE_KANJI: 8,
18}
19MODE_SIZE_MEDIUM = {
20 MODE_NUMBER: 12,
21 MODE_ALPHA_NUM: 11,
22 MODE_8BIT_BYTE: 16,
23 MODE_KANJI: 10,
24}
25MODE_SIZE_LARGE = {
26 MODE_NUMBER: 14,
27 MODE_ALPHA_NUM: 13,
28 MODE_8BIT_BYTE: 16,
29 MODE_KANJI: 12,
30}
32ALPHA_NUM = b'0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ $%*+-./:'
33RE_ALPHA_NUM = re.compile(b'^[' + re.escape(ALPHA_NUM) + br']*\Z')
35# The number of bits for numeric delimited data lengths.
36NUMBER_LENGTH = {3: 10, 2: 7, 1: 4}
38PATTERN_POSITION_TABLE = [
39 [],
40 [6, 18],
41 [6, 22],
42 [6, 26],
43 [6, 30],
44 [6, 34],
45 [6, 22, 38],
46 [6, 24, 42],
47 [6, 26, 46],
48 [6, 28, 50],
49 [6, 30, 54],
50 [6, 32, 58],
51 [6, 34, 62],
52 [6, 26, 46, 66],
53 [6, 26, 48, 70],
54 [6, 26, 50, 74],
55 [6, 30, 54, 78],
56 [6, 30, 56, 82],
57 [6, 30, 58, 86],
58 [6, 34, 62, 90],
59 [6, 28, 50, 72, 94],
60 [6, 26, 50, 74, 98],
61 [6, 30, 54, 78, 102],
62 [6, 28, 54, 80, 106],
63 [6, 32, 58, 84, 110],
64 [6, 30, 58, 86, 114],
65 [6, 34, 62, 90, 118],
66 [6, 26, 50, 74, 98, 122],
67 [6, 30, 54, 78, 102, 126],
68 [6, 26, 52, 78, 104, 130],
69 [6, 30, 56, 82, 108, 134],
70 [6, 34, 60, 86, 112, 138],
71 [6, 30, 58, 86, 114, 142],
72 [6, 34, 62, 90, 118, 146],
73 [6, 30, 54, 78, 102, 126, 150],
74 [6, 24, 50, 76, 102, 128, 154],
75 [6, 28, 54, 80, 106, 132, 158],
76 [6, 32, 58, 84, 110, 136, 162],
77 [6, 26, 54, 82, 110, 138, 166],
78 [6, 30, 58, 86, 114, 142, 170]
79]
81G15 = (
82 (1 << 10) | (1 << 8) | (1 << 5) | (1 << 4) | (1 << 2) | (1 << 1) |
83 (1 << 0))
84G18 = (
85 (1 << 12) | (1 << 11) | (1 << 10) | (1 << 9) | (1 << 8) | (1 << 5) |
86 (1 << 2) | (1 << 0))
87G15_MASK = (1 << 14) | (1 << 12) | (1 << 10) | (1 << 4) | (1 << 1)
89PAD0 = 0xEC
90PAD1 = 0x11
92# Precompute bit count limits, indexed by error correction level and code size
93_data_count = lambda block: block.data_count
94BIT_LIMIT_TABLE = [
95 [0] + [8*sum(map(_data_count, base.rs_blocks(version, error_correction)))
96 for version in range(1, 41)]
97 for error_correction in range(4)
98]
101def BCH_type_info(data):
102 d = data << 10
103 while BCH_digit(d) - BCH_digit(G15) >= 0:
104 d ^= (G15 << (BCH_digit(d) - BCH_digit(G15)))
106 return ((data << 10) | d) ^ G15_MASK
109def BCH_type_number(data):
110 d = data << 12
111 while BCH_digit(d) - BCH_digit(G18) >= 0:
112 d ^= (G18 << (BCH_digit(d) - BCH_digit(G18)))
113 return (data << 12) | d
116def BCH_digit(data):
117 digit = 0
118 while data != 0:
119 digit += 1
120 data >>= 1
121 return digit
124def pattern_position(version):
125 return PATTERN_POSITION_TABLE[version - 1]
128def mask_func(pattern):
129 """
130 Return the mask function for the given mask pattern.
131 """
132 if pattern == 0: # 000
133 return lambda i, j: (i + j) % 2 == 0
134 if pattern == 1: # 001
135 return lambda i, j: i % 2 == 0
136 if pattern == 2: # 010
137 return lambda i, j: j % 3 == 0
138 if pattern == 3: # 011
139 return lambda i, j: (i + j) % 3 == 0
140 if pattern == 4: # 100
141 return lambda i, j: (math.floor(i / 2) + math.floor(j / 3)) % 2 == 0
142 if pattern == 5: # 101
143 return lambda i, j: (i * j) % 2 + (i * j) % 3 == 0
144 if pattern == 6: # 110
145 return lambda i, j: ((i * j) % 2 + (i * j) % 3) % 2 == 0
146 if pattern == 7: # 111 146 ↛ 148line 146 didn't jump to line 148, because the condition on line 146 was never false
147 return lambda i, j: ((i * j) % 3 + (i + j) % 2) % 2 == 0
148 raise TypeError("Bad mask pattern: " + pattern) # pragma: no cover
151def mode_sizes_for_version(version):
152 if version < 10: 152 ↛ 154line 152 didn't jump to line 154, because the condition on line 152 was never false
153 return MODE_SIZE_SMALL
154 elif version < 27:
155 return MODE_SIZE_MEDIUM
156 else:
157 return MODE_SIZE_LARGE
160def length_in_bits(mode, version):
161 if mode not in ( 161 ↛ 163line 161 didn't jump to line 163, because the condition on line 161 was never true
162 MODE_NUMBER, MODE_ALPHA_NUM, MODE_8BIT_BYTE, MODE_KANJI):
163 raise TypeError(f"Invalid mode ({mode})") # pragma: no cover
165 check_version(version)
167 return mode_sizes_for_version(version)[mode]
170def check_version(version):
171 if version < 1 or version > 40: 171 ↛ 172line 171 didn't jump to line 172, because the condition on line 171 was never true
172 raise ValueError(
173 f"Invalid version (was {version}, expected 1 to 40)")
176def lost_point(modules):
177 modules_count = len(modules)
179 lost_point = 0
181 lost_point = _lost_point_level1(modules, modules_count)
182 lost_point += _lost_point_level2(modules, modules_count)
183 lost_point += _lost_point_level3(modules, modules_count)
184 lost_point += _lost_point_level4(modules, modules_count)
186 return lost_point
189def _lost_point_level1(modules, modules_count):
190 lost_point = 0
192 modules_range = range(modules_count)
193 container = [0] * (modules_count + 1)
195 for row in modules_range:
196 this_row = modules[row]
197 previous_color = this_row[0]
198 length = 0
199 for col in modules_range:
200 if this_row[col] == previous_color:
201 length += 1
202 else:
203 if length >= 5:
204 container[length] += 1
205 length = 1
206 previous_color = this_row[col]
207 if length >= 5:
208 container[length] += 1
210 for col in modules_range:
211 previous_color = modules[0][col]
212 length = 0
213 for row in modules_range:
214 if modules[row][col] == previous_color:
215 length += 1
216 else:
217 if length >= 5:
218 container[length] += 1
219 length = 1
220 previous_color = modules[row][col]
221 if length >= 5:
222 container[length] += 1
224 lost_point += sum(container[each_length] * (each_length - 2)
225 for each_length in range(5, modules_count + 1))
227 return lost_point
230def _lost_point_level2(modules, modules_count):
231 lost_point = 0
233 modules_range = range(modules_count - 1)
234 for row in modules_range:
235 this_row = modules[row]
236 next_row = modules[row + 1]
237 # use iter() and next() to skip next four-block. e.g.
238 # d a f if top-right a != b botton-right,
239 # c b e then both abcd and abef won't lost any point.
240 modules_range_iter = iter(modules_range)
241 for col in modules_range_iter:
242 top_right = this_row[col + 1]
243 if top_right != next_row[col + 1]:
244 # reduce 33.3% of runtime via next().
245 # None: raise nothing if there is no next item.
246 next(modules_range_iter, None)
247 elif top_right != this_row[col]:
248 continue
249 elif top_right != next_row[col]:
250 continue
251 else:
252 lost_point += 3
254 return lost_point
257def _lost_point_level3(modules, modules_count):
258 # 1 : 1 : 3 : 1 : 1 ratio (dark:light:dark:light:dark) pattern in
259 # row/column, preceded or followed by light area 4 modules wide. From ISOIEC.
260 # pattern1: 10111010000
261 # pattern2: 00001011101
262 modules_range = range(modules_count)
263 modules_range_short = range(modules_count-10)
264 lost_point = 0
266 for row in modules_range:
267 this_row = modules[row]
268 modules_range_short_iter = iter(modules_range_short)
269 col = 0
270 for col in modules_range_short_iter:
271 if (
272 not this_row[col + 1]
273 and this_row[col + 4]
274 and not this_row[col + 5]
275 and this_row[col + 6]
276 and not this_row[col + 9]
277 and (
278 this_row[col + 0]
279 and this_row[col + 2]
280 and this_row[col + 3]
281 and not this_row[col + 7]
282 and not this_row[col + 8]
283 and not this_row[col + 10]
284 or
285 not this_row[col + 0]
286 and not this_row[col + 2]
287 and not this_row[col + 3]
288 and this_row[col + 7]
289 and this_row[col + 8]
290 and this_row[col + 10]
291 )
292 ):
293 lost_point += 40
294# horspool algorithm.
295# if this_row[col + 10] == True, pattern1 shift 4, pattern2 shift 2. So min=2.
296# if this_row[col + 10] == False, pattern1 shift 1, pattern2 shift 1. So min=1.
297 if this_row[col + 10]:
298 next(modules_range_short_iter, None)
300 for col in modules_range:
301 modules_range_short_iter = iter(modules_range_short)
302 row = 0
303 for row in modules_range_short_iter:
304 if (
305 not modules[row + 1][col]
306 and modules[row + 4][col]
307 and not modules[row + 5][col]
308 and modules[row + 6][col]
309 and not modules[row + 9][col]
310 and (
311 modules[row + 0][col]
312 and modules[row + 2][col]
313 and modules[row + 3][col]
314 and not modules[row + 7][col]
315 and not modules[row + 8][col]
316 and not modules[row + 10][col]
317 or
318 not modules[row + 0][col]
319 and not modules[row + 2][col]
320 and not modules[row + 3][col]
321 and modules[row + 7][col]
322 and modules[row + 8][col]
323 and modules[row + 10][col]
324 )
325 ):
326 lost_point += 40
327 if modules[row + 10][col]:
328 next(modules_range_short_iter, None)
330 return lost_point
333def _lost_point_level4(modules, modules_count):
334 dark_count = sum(map(sum, modules))
335 percent = float(dark_count) / (modules_count**2)
336 # Every 5% departure from 50%, rating++
337 rating = int(abs(percent * 100 - 50) / 5)
338 return rating * 10
341def optimal_data_chunks(data, minimum=4):
342 """
343 An iterator returning QRData chunks optimized to the data content.
345 :param minimum: The minimum number of bytes in a row to split as a chunk.
346 """
347 data = to_bytestring(data)
348 num_pattern = br'\d'
349 alpha_pattern = b'[' + re.escape(ALPHA_NUM) + b']'
350 if len(data) <= minimum: 350 ↛ 351line 350 didn't jump to line 351, because the condition on line 350 was never true
351 num_pattern = re.compile(b'^' + num_pattern + b'+$')
352 alpha_pattern = re.compile(b'^' + alpha_pattern + b'+$')
353 else:
354 re_repeat = (
355 b'{' + str(minimum).encode('ascii') + b',}')
356 num_pattern = re.compile(num_pattern + re_repeat)
357 alpha_pattern = re.compile(alpha_pattern + re_repeat)
358 num_bits = _optimal_split(data, num_pattern)
359 for is_num, chunk in num_bits:
360 if is_num: 360 ↛ 361line 360 didn't jump to line 361, because the condition on line 360 was never true
361 yield QRData(chunk, mode=MODE_NUMBER, check_data=False)
362 else:
363 for is_alpha, sub_chunk in _optimal_split(chunk, alpha_pattern):
364 mode = MODE_ALPHA_NUM if is_alpha else MODE_8BIT_BYTE
365 yield QRData(sub_chunk, mode=mode, check_data=False)
368def _optimal_split(data, pattern):
369 while data: 369 ↛ 378line 369 didn't jump to line 378, because the condition on line 369 was never false
370 match = re.search(pattern, data)
371 if not match: 371 ↛ 373line 371 didn't jump to line 373, because the condition on line 371 was never false
372 break
373 start, end = match.start(), match.end()
374 if start:
375 yield False, data[:start]
376 yield True, data[start:end]
377 data = data[end:]
378 if data: 378 ↛ exitline 378 didn't return from function '_optimal_split', because the condition on line 378 was never false
379 yield False, data
382def to_bytestring(data):
383 """
384 Convert data to a (utf-8 encoded) byte-string if it isn't a byte-string
385 already.
386 """
387 if not isinstance(data, bytes): 387 ↛ 389line 387 didn't jump to line 389, because the condition on line 387 was never false
388 data = str(data).encode('utf-8')
389 return data
392def optimal_mode(data):
393 """
394 Calculate the optimal mode for this chunk of data.
395 """
396 if data.isdigit():
397 return MODE_NUMBER
398 if RE_ALPHA_NUM.match(data):
399 return MODE_ALPHA_NUM
400 return MODE_8BIT_BYTE
403class QRData:
404 """
405 Data held in a QR compatible format.
407 Doesn't currently handle KANJI.
408 """
410 def __init__(self, data, mode=None, check_data=True):
411 """
412 If ``mode`` isn't provided, the most compact QR data type possible is
413 chosen.
414 """
415 if check_data: 415 ↛ 416line 415 didn't jump to line 416, because the condition on line 415 was never true
416 data = to_bytestring(data)
418 if mode is None: 418 ↛ 419line 418 didn't jump to line 419, because the condition on line 418 was never true
419 self.mode = optimal_mode(data)
420 else:
421 self.mode = mode
422 if mode not in (MODE_NUMBER, MODE_ALPHA_NUM, MODE_8BIT_BYTE): 422 ↛ 423line 422 didn't jump to line 423, because the condition on line 422 was never true
423 raise TypeError(f"Invalid mode ({mode})") # pragma: no cover
424 if check_data and mode < optimal_mode(data): # pragma: no cover 424 ↛ 425line 424 didn't jump to line 425, because the condition on line 424 was never true
425 raise ValueError(
426 f"Provided data can not be represented in mode {mode}")
428 self.data = data
430 def __len__(self):
431 return len(self.data)
433 def write(self, buffer):
434 if self.mode == MODE_NUMBER: 434 ↛ 435line 434 didn't jump to line 435, because the condition on line 434 was never true
435 for i in range(0, len(self.data), 3):
436 chars = self.data[i:i + 3]
437 bit_length = NUMBER_LENGTH[len(chars)]
438 buffer.put(int(chars), bit_length)
439 elif self.mode == MODE_ALPHA_NUM: 439 ↛ 440line 439 didn't jump to line 440, because the condition on line 439 was never true
440 for i in range(0, len(self.data), 2):
441 chars = self.data[i:i + 2]
442 if len(chars) > 1:
443 buffer.put(
444 ALPHA_NUM.find(chars[0]) * 45 +
445 ALPHA_NUM.find(chars[1]), 11)
446 else:
447 buffer.put(ALPHA_NUM.find(chars), 6)
448 else:
449 # Iterating a bytestring in Python 3 returns an integer,
450 # no need to ord().
451 data = self.data
452 for c in data:
453 buffer.put(c, 8)
455 def __repr__(self):
456 return repr(self.data)
459class BitBuffer:
461 def __init__(self):
462 self.buffer = []
463 self.length = 0
465 def __repr__(self):
466 return ".".join([str(n) for n in self.buffer])
468 def get(self, index):
469 buf_index = math.floor(index / 8)
470 return ((self.buffer[buf_index] >> (7 - index % 8)) & 1) == 1
472 def put(self, num, length):
473 for i in range(length):
474 self.put_bit(((num >> (length - i - 1)) & 1) == 1)
476 def __len__(self):
477 return self.length
479 def put_bit(self, bit):
480 buf_index = self.length // 8
481 if len(self.buffer) <= buf_index:
482 self.buffer.append(0)
483 if bit:
484 self.buffer[buf_index] |= (0x80 >> (self.length % 8))
485 self.length += 1
488def create_bytes(buffer, rs_blocks):
489 offset = 0
491 maxDcCount = 0
492 maxEcCount = 0
494 dcdata = [0] * len(rs_blocks)
495 ecdata = [0] * len(rs_blocks)
497 for r in range(len(rs_blocks)):
499 dcCount = rs_blocks[r].data_count
500 ecCount = rs_blocks[r].total_count - dcCount
502 maxDcCount = max(maxDcCount, dcCount)
503 maxEcCount = max(maxEcCount, ecCount)
505 dcdata[r] = [0] * dcCount
507 for i in range(len(dcdata[r])):
508 dcdata[r][i] = 0xff & buffer.buffer[i + offset]
509 offset += dcCount
511 # Get error correction polynomial.
512 if ecCount in LUT.rsPoly_LUT: 512 ↛ 515line 512 didn't jump to line 515, because the condition on line 512 was never false
513 rsPoly = base.Polynomial(LUT.rsPoly_LUT[ecCount], 0)
514 else:
515 rsPoly = base.Polynomial([1], 0)
516 for i in range(ecCount):
517 rsPoly = rsPoly * base.Polynomial([1, base.gexp(i)], 0)
519 rawPoly = base.Polynomial(dcdata[r], len(rsPoly) - 1)
521 modPoly = rawPoly % rsPoly
522 ecdata[r] = [0] * (len(rsPoly) - 1)
523 for i in range(len(ecdata[r])):
524 modIndex = i + len(modPoly) - len(ecdata[r])
525 ecdata[r][i] = modPoly[modIndex] if (modIndex >= 0) else 0
526 totalCodeCount = sum(rs_block.total_count for rs_block in rs_blocks)
527 data = [None] * totalCodeCount
528 index = 0
530 for i in range(maxDcCount):
531 for r in range(len(rs_blocks)):
532 if i < len(dcdata[r]): 532 ↛ 531line 532 didn't jump to line 531, because the condition on line 532 was never false
533 data[index] = dcdata[r][i]
534 index += 1
536 for i in range(maxEcCount):
537 for r in range(len(rs_blocks)):
538 if i < len(ecdata[r]): 538 ↛ 537line 538 didn't jump to line 537, because the condition on line 538 was never false
539 data[index] = ecdata[r][i]
540 index += 1
542 return data
545def create_data(version, error_correction, data_list):
547 buffer = BitBuffer()
548 for data in data_list:
549 buffer.put(data.mode, 4)
550 buffer.put(len(data), length_in_bits(data.mode, version))
551 data.write(buffer)
553 # Calculate the maximum number of bits for the given version.
554 rs_blocks = base.rs_blocks(version, error_correction)
555 bit_limit = sum(block.data_count * 8 for block in rs_blocks)
556 if len(buffer) > bit_limit: 556 ↛ 557line 556 didn't jump to line 557, because the condition on line 556 was never true
557 raise exceptions.DataOverflowError(
558 "Code length overflow. Data size (%s) > size available (%s)" %
559 (len(buffer), bit_limit))
561 # Terminate the bits (add up to four 0s).
562 for _ in range(min(bit_limit - len(buffer), 4)):
563 buffer.put_bit(False)
565 # Delimit the string into 8-bit words, padding with 0s if necessary.
566 delimit = len(buffer) % 8
567 if delimit: 567 ↛ 568line 567 didn't jump to line 568, because the condition on line 567 was never true
568 for _ in range(8 - delimit):
569 buffer.put_bit(False)
571 # Add special alternating padding bitstrings until buffer is full.
572 bytes_to_fill = (bit_limit - len(buffer)) // 8
573 for i in range(bytes_to_fill):
574 if i % 2 == 0:
575 buffer.put(PAD0, 8)
576 else:
577 buffer.put(PAD1, 8)
579 return create_bytes(buffer, rs_blocks)