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

1from qrcode import constants, exceptions, util 

2from qrcode.image.base import BaseImage 

3 

4import sys 

5from bisect import bisect_left 

6 

7# Cache modules generated just based on the QR Code version 

8precomputed_qr_blanks = {} 

9 

10def make(data=None, **kwargs): 

11 qr = QRCode(**kwargs) 

12 qr.add_data(data) 

13 return qr.make_image() 

14 

15 

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

20 

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) 

24 

25 

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

35 

36def copy_2d_array(x): 

37 return [row[:] for row in x] 

38 

39class QRCode: 

40 

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

59 

60 @property 

61 def mask_pattern(self): 

62 return self._mask_pattern 

63 

64 @mask_pattern.setter 

65 def mask_pattern(self, pattern): 

66 _check_mask_pattern(pattern) 

67 self._mask_pattern = pattern 

68 

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 = [] 

77 

78 def add_data(self, data, optimize=20): 

79 """ 

80 Add data to this QR Code. 

81 

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 

94 

95 def make(self, fit=True): 

96 """ 

97 Compile the data into a QR Code array. 

98 

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) 

108 

109 def makeImpl(self, test, mask_pattern): 

110 util.check_version(self.version) 

111 self.modules_count = self.version * 4 + 17 

112 

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 

117 

118 for row in range(self.modules_count): 

119 self.modules[row] = [None] * self.modules_count 

120 

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

126 

127 precomputed_qr_blanks[self.version] = copy_2d_array(self.modules) 

128 

129 self.setup_type_info(test, mask_pattern) 

130 

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) 

133 

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) 

138 

139 def setup_position_probe_pattern(self, row, col): 

140 for r in range(-1, 8): 

141 

142 if row + r <= -1 or self.modules_count <= row + r: 

143 continue 

144 

145 for c in range(-1, 8): 

146 

147 if col + c <= -1 or self.modules_count <= col + c: 

148 continue 

149 

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 

158 

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) 

166 

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) 

175 

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

181 

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 

187 

188 def best_mask_pattern(self): 

189 """ 

190 Find the most efficient mask pattern. 

191 """ 

192 min_lost_point = 0 

193 pattern = 0 

194 

195 for i in range(8): 

196 self.makeImpl(True, i) 

197 

198 lost_point = util.lost_point(self.modules) 

199 

200 if i == 0 or min_lost_point > lost_point: 

201 min_lost_point = lost_point 

202 pattern = i 

203 

204 return pattern 

205 

206 def print_tty(self, out=None): 

207 """ 

208 Output the QR Code only using TTY colors. 

209 

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 

215 

216 if not out.isatty(): 

217 raise OSError("Not a tty") 

218 

219 if self.data_cache is None: 

220 self.make() 

221 

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

234 

235 def print_ascii(self, out=None, tty=False, invert=False): 

236 """ 

237 Output the QR Code using ASCII characters. 

238 

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 

244 

245 if tty and not out.isatty(): 

246 raise OSError("Not a tty") 

247 

248 if self.data_cache is None: 

249 self.make() 

250 

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

258 

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] 

266 

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

279 

280 def make_image(self, image_factory=None, **kwargs): 

281 """ 

282 Make an image from the QR Code data. 

283 

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

289 

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 

298 

299 im = image_factory( 

300 self.border, self.modules_count, self.box_size, **kwargs) 

301 

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

310 

311 return im 

312 

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

316 

317 def get_module_context(self, row, col): 

318 context = [] 

319 

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 

325 

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) 

331 

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) 

336 

337 def setup_position_adjust_pattern(self): 

338 pos = util.pattern_position(self.version) 

339 

340 for i in range(len(pos)): 

341 

342 row = pos[i] 

343 

344 for j in range(len(pos)): 

345 

346 col = pos[j] 

347 

348 if self.modules[row][col] is not None: 

349 continue 

350 

351 for r in range(-2, 3): 

352 

353 for c in range(-2, 3): 

354 

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 

360 

361 def setup_type_number(self, test): 

362 bits = util.BCH_type_number(self.version) 

363 

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 

367 

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 

371 

372 def setup_type_info(self, test, mask_pattern): 

373 data = (self.error_correction << 3) | mask_pattern 

374 bits = util.BCH_type_info(data) 

375 

376 # vertical 

377 for i in range(15): 

378 

379 mod = (not test and ((bits >> i) & 1) == 1) 

380 

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 

387 

388 # horizontal 

389 for i in range(15): 

390 

391 mod = (not test and ((bits >> i) & 1) == 1) 

392 

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 

399 

400 # fixed module 

401 self.modules[self.modules_count - 8][8] = (not test) 

402 

403 def map_data(self, data, mask_pattern): 

404 inc = -1 

405 row = self.modules_count - 1 

406 bitIndex = 7 

407 byteIndex = 0 

408 

409 mask_func = util.mask_func(mask_pattern) 

410 

411 data_len = len(data) 

412 

413 for col in range(self.modules_count - 1, 0, -2): 

414 

415 if col <= 6: 

416 col -= 1 

417 

418 col_range = (col, col-1) 

419 

420 while True: 

421 

422 for c in col_range: 

423 

424 if self.modules[row][c] is None: 

425 

426 dark = False 

427 

428 if byteIndex < data_len: 

429 dark = (((data[byteIndex] >> bitIndex) & 1) == 1) 

430 

431 if mask_func(row, c): 

432 dark = not dark 

433 

434 self.modules[row][c] = dark 

435 bitIndex -= 1 

436 

437 if bitIndex == -1: 

438 byteIndex += 1 

439 bitIndex = 7 

440 

441 row += inc 

442 

443 if row < 0 or self.modules_count <= row: 

444 row -= inc 

445 inc = -inc 

446 break 

447 

448 def get_matrix(self): 

449 """ 

450 Return the QR Code as a multidimensional array, including the border. 

451 

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

456 

457 if not self.border: 

458 return self.modules 

459 

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 

466 

467 return code