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

1import re 

2import math 

3 

4from qrcode import base, exceptions, LUT 

5 

6# QR encoding modes. 

7MODE_NUMBER = 1 << 0 

8MODE_ALPHA_NUM = 1 << 1 

9MODE_8BIT_BYTE = 1 << 2 

10MODE_KANJI = 1 << 3 

11 

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} 

31 

32ALPHA_NUM = b'0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ $%*+-./:' 

33RE_ALPHA_NUM = re.compile(b'^[' + re.escape(ALPHA_NUM) + br']*\Z') 

34 

35# The number of bits for numeric delimited data lengths. 

36NUMBER_LENGTH = {3: 10, 2: 7, 1: 4} 

37 

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] 

80 

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) 

88 

89PAD0 = 0xEC 

90PAD1 = 0x11 

91 

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] 

99 

100 

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))) 

105 

106 return ((data << 10) | d) ^ G15_MASK 

107 

108 

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 

114 

115 

116def BCH_digit(data): 

117 digit = 0 

118 while data != 0: 

119 digit += 1 

120 data >>= 1 

121 return digit 

122 

123 

124def pattern_position(version): 

125 return PATTERN_POSITION_TABLE[version - 1] 

126 

127 

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 

149 

150 

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 

158 

159 

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 

164 

165 check_version(version) 

166 

167 return mode_sizes_for_version(version)[mode] 

168 

169 

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)") 

174 

175 

176def lost_point(modules): 

177 modules_count = len(modules) 

178 

179 lost_point = 0 

180 

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) 

185 

186 return lost_point 

187 

188 

189def _lost_point_level1(modules, modules_count): 

190 lost_point = 0 

191 

192 modules_range = range(modules_count) 

193 container = [0] * (modules_count + 1) 

194 

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 

209 

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 

223 

224 lost_point += sum(container[each_length] * (each_length - 2) 

225 for each_length in range(5, modules_count + 1)) 

226 

227 return lost_point 

228 

229 

230def _lost_point_level2(modules, modules_count): 

231 lost_point = 0 

232 

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 

253 

254 return lost_point 

255 

256 

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 

265 

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) 

299 

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) 

329 

330 return lost_point 

331 

332 

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 

339 

340 

341def optimal_data_chunks(data, minimum=4): 

342 """ 

343 An iterator returning QRData chunks optimized to the data content. 

344 

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) 

366 

367 

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 

380 

381 

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 

390 

391 

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 

401 

402 

403class QRData: 

404 """ 

405 Data held in a QR compatible format. 

406 

407 Doesn't currently handle KANJI. 

408 """ 

409 

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) 

417 

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}") 

427 

428 self.data = data 

429 

430 def __len__(self): 

431 return len(self.data) 

432 

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) 

454 

455 def __repr__(self): 

456 return repr(self.data) 

457 

458 

459class BitBuffer: 

460 

461 def __init__(self): 

462 self.buffer = [] 

463 self.length = 0 

464 

465 def __repr__(self): 

466 return ".".join([str(n) for n in self.buffer]) 

467 

468 def get(self, index): 

469 buf_index = math.floor(index / 8) 

470 return ((self.buffer[buf_index] >> (7 - index % 8)) & 1) == 1 

471 

472 def put(self, num, length): 

473 for i in range(length): 

474 self.put_bit(((num >> (length - i - 1)) & 1) == 1) 

475 

476 def __len__(self): 

477 return self.length 

478 

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 

486 

487 

488def create_bytes(buffer, rs_blocks): 

489 offset = 0 

490 

491 maxDcCount = 0 

492 maxEcCount = 0 

493 

494 dcdata = [0] * len(rs_blocks) 

495 ecdata = [0] * len(rs_blocks) 

496 

497 for r in range(len(rs_blocks)): 

498 

499 dcCount = rs_blocks[r].data_count 

500 ecCount = rs_blocks[r].total_count - dcCount 

501 

502 maxDcCount = max(maxDcCount, dcCount) 

503 maxEcCount = max(maxEcCount, ecCount) 

504 

505 dcdata[r] = [0] * dcCount 

506 

507 for i in range(len(dcdata[r])): 

508 dcdata[r][i] = 0xff & buffer.buffer[i + offset] 

509 offset += dcCount 

510 

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) 

518 

519 rawPoly = base.Polynomial(dcdata[r], len(rsPoly) - 1) 

520 

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 

529 

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 

535 

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 

541 

542 return data 

543 

544 

545def create_data(version, error_correction, data_list): 

546 

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) 

552 

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)) 

560 

561 # Terminate the bits (add up to four 0s). 

562 for _ in range(min(bit_limit - len(buffer), 4)): 

563 buffer.put_bit(False) 

564 

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) 

570 

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) 

578 

579 return create_bytes(buffer, rs_blocks)