Coverage for /pythoncovmergedfiles/medio/medio/src/pdfminer.six/pdfminer/ccitt.py: 61%

Shortcuts on this page

r m x   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

497 statements  

1# CCITT Fax decoder 

2# 

3# Bugs: uncompressed mode untested. 

4# 

5# cf. 

6# ITU-T Recommendation T.4 

7# "Standardization of Group 3 facsimile terminals 

8# for document transmission" 

9# ITU-T Recommendation T.6 

10# "FACSIMILE CODING SCHEMES AND CODING CONTROL FUNCTIONS 

11# FOR GROUP 4 FACSIMILE APPARATUS" 

12 

13 

14import array 

15from collections.abc import Callable, Iterator, MutableSequence, Sequence 

16from typing import ( 

17 Any, 

18 ClassVar, 

19 cast, 

20) 

21 

22from pdfminer.pdfexceptions import PDFException, PDFValueError 

23 

24 

25def get_bytes(data: bytes) -> Iterator[int]: 

26 yield from data 

27 

28 

29# Workaround https://github.com/python/mypy/issues/731 

30BitParserState = MutableSequence[Any] 

31# A better definition (not supported by mypy) would be: 

32# BitParserState = MutableSequence[Union["BitParserState", int, str, None]] 

33 

34 

35class BitParser: 

36 _state: BitParserState 

37 

38 # _accept is declared Optional solely as a workaround for 

39 # https://github.com/python/mypy/issues/708 

40 _accept: Callable[[Any], BitParserState] | None 

41 

42 def __init__(self) -> None: 

43 self._pos = 0 

44 

45 @classmethod 

46 def add(cls, root: BitParserState, v: int | str, bits: str) -> None: 

47 p: BitParserState = root 

48 b = None 

49 for i in range(len(bits)): 

50 if i > 0: 

51 assert b is not None 

52 if p[b] is None: 

53 p[b] = [None, None] 

54 p = p[b] 

55 b = 1 if bits[i] == "1" else 0 

56 assert b is not None 

57 p[b] = v 

58 

59 def feedbytes(self, data: bytes) -> None: 

60 for byte in get_bytes(data): 

61 for m in (128, 64, 32, 16, 8, 4, 2, 1): 

62 self._parse_bit(byte & m) 

63 

64 def _parse_bit(self, x: object) -> None: 

65 v = self._state[1] if x else self._state[0] 

66 self._pos += 1 

67 if isinstance(v, list): 

68 self._state = v 

69 else: 

70 assert self._accept is not None 

71 self._state = self._accept(v) 

72 

73 

74class CCITTG4Parser(BitParser): 

75 MODE: ClassVar[BitParserState] = [None, None] 

76 BitParser.add(MODE, 0, "1") 

77 BitParser.add(MODE, +1, "011") 

78 BitParser.add(MODE, -1, "010") 

79 BitParser.add(MODE, "h", "001") 

80 BitParser.add(MODE, "p", "0001") 

81 BitParser.add(MODE, +2, "000011") 

82 BitParser.add(MODE, -2, "000010") 

83 BitParser.add(MODE, +3, "0000011") 

84 BitParser.add(MODE, -3, "0000010") 

85 BitParser.add(MODE, "u", "0000001111") 

86 BitParser.add(MODE, "x1", "0000001000") 

87 BitParser.add(MODE, "x2", "0000001001") 

88 BitParser.add(MODE, "x3", "0000001010") 

89 BitParser.add(MODE, "x4", "0000001011") 

90 BitParser.add(MODE, "x5", "0000001100") 

91 BitParser.add(MODE, "x6", "0000001101") 

92 BitParser.add(MODE, "x7", "0000001110") 

93 BitParser.add(MODE, "e", "000000000001000000000001") 

94 

95 WHITE: ClassVar[BitParserState] = [None, None] 

96 BitParser.add(WHITE, 0, "00110101") 

97 BitParser.add(WHITE, 1, "000111") 

98 BitParser.add(WHITE, 2, "0111") 

99 BitParser.add(WHITE, 3, "1000") 

100 BitParser.add(WHITE, 4, "1011") 

101 BitParser.add(WHITE, 5, "1100") 

102 BitParser.add(WHITE, 6, "1110") 

103 BitParser.add(WHITE, 7, "1111") 

104 BitParser.add(WHITE, 8, "10011") 

105 BitParser.add(WHITE, 9, "10100") 

106 BitParser.add(WHITE, 10, "00111") 

107 BitParser.add(WHITE, 11, "01000") 

108 BitParser.add(WHITE, 12, "001000") 

109 BitParser.add(WHITE, 13, "000011") 

110 BitParser.add(WHITE, 14, "110100") 

111 BitParser.add(WHITE, 15, "110101") 

112 BitParser.add(WHITE, 16, "101010") 

113 BitParser.add(WHITE, 17, "101011") 

114 BitParser.add(WHITE, 18, "0100111") 

115 BitParser.add(WHITE, 19, "0001100") 

116 BitParser.add(WHITE, 20, "0001000") 

117 BitParser.add(WHITE, 21, "0010111") 

118 BitParser.add(WHITE, 22, "0000011") 

119 BitParser.add(WHITE, 23, "0000100") 

120 BitParser.add(WHITE, 24, "0101000") 

121 BitParser.add(WHITE, 25, "0101011") 

122 BitParser.add(WHITE, 26, "0010011") 

123 BitParser.add(WHITE, 27, "0100100") 

124 BitParser.add(WHITE, 28, "0011000") 

125 BitParser.add(WHITE, 29, "00000010") 

126 BitParser.add(WHITE, 30, "00000011") 

127 BitParser.add(WHITE, 31, "00011010") 

128 BitParser.add(WHITE, 32, "00011011") 

129 BitParser.add(WHITE, 33, "00010010") 

130 BitParser.add(WHITE, 34, "00010011") 

131 BitParser.add(WHITE, 35, "00010100") 

132 BitParser.add(WHITE, 36, "00010101") 

133 BitParser.add(WHITE, 37, "00010110") 

134 BitParser.add(WHITE, 38, "00010111") 

135 BitParser.add(WHITE, 39, "00101000") 

136 BitParser.add(WHITE, 40, "00101001") 

137 BitParser.add(WHITE, 41, "00101010") 

138 BitParser.add(WHITE, 42, "00101011") 

139 BitParser.add(WHITE, 43, "00101100") 

140 BitParser.add(WHITE, 44, "00101101") 

141 BitParser.add(WHITE, 45, "00000100") 

142 BitParser.add(WHITE, 46, "00000101") 

143 BitParser.add(WHITE, 47, "00001010") 

144 BitParser.add(WHITE, 48, "00001011") 

145 BitParser.add(WHITE, 49, "01010010") 

146 BitParser.add(WHITE, 50, "01010011") 

147 BitParser.add(WHITE, 51, "01010100") 

148 BitParser.add(WHITE, 52, "01010101") 

149 BitParser.add(WHITE, 53, "00100100") 

150 BitParser.add(WHITE, 54, "00100101") 

151 BitParser.add(WHITE, 55, "01011000") 

152 BitParser.add(WHITE, 56, "01011001") 

153 BitParser.add(WHITE, 57, "01011010") 

154 BitParser.add(WHITE, 58, "01011011") 

155 BitParser.add(WHITE, 59, "01001010") 

156 BitParser.add(WHITE, 60, "01001011") 

157 BitParser.add(WHITE, 61, "00110010") 

158 BitParser.add(WHITE, 62, "00110011") 

159 BitParser.add(WHITE, 63, "00110100") 

160 BitParser.add(WHITE, 64, "11011") 

161 BitParser.add(WHITE, 128, "10010") 

162 BitParser.add(WHITE, 192, "010111") 

163 BitParser.add(WHITE, 256, "0110111") 

164 BitParser.add(WHITE, 320, "00110110") 

165 BitParser.add(WHITE, 384, "00110111") 

166 BitParser.add(WHITE, 448, "01100100") 

167 BitParser.add(WHITE, 512, "01100101") 

168 BitParser.add(WHITE, 576, "01101000") 

169 BitParser.add(WHITE, 640, "01100111") 

170 BitParser.add(WHITE, 704, "011001100") 

171 BitParser.add(WHITE, 768, "011001101") 

172 BitParser.add(WHITE, 832, "011010010") 

173 BitParser.add(WHITE, 896, "011010011") 

174 BitParser.add(WHITE, 960, "011010100") 

175 BitParser.add(WHITE, 1024, "011010101") 

176 BitParser.add(WHITE, 1088, "011010110") 

177 BitParser.add(WHITE, 1152, "011010111") 

178 BitParser.add(WHITE, 1216, "011011000") 

179 BitParser.add(WHITE, 1280, "011011001") 

180 BitParser.add(WHITE, 1344, "011011010") 

181 BitParser.add(WHITE, 1408, "011011011") 

182 BitParser.add(WHITE, 1472, "010011000") 

183 BitParser.add(WHITE, 1536, "010011001") 

184 BitParser.add(WHITE, 1600, "010011010") 

185 BitParser.add(WHITE, 1664, "011000") 

186 BitParser.add(WHITE, 1728, "010011011") 

187 BitParser.add(WHITE, 1792, "00000001000") 

188 BitParser.add(WHITE, 1856, "00000001100") 

189 BitParser.add(WHITE, 1920, "00000001101") 

190 BitParser.add(WHITE, 1984, "000000010010") 

191 BitParser.add(WHITE, 2048, "000000010011") 

192 BitParser.add(WHITE, 2112, "000000010100") 

193 BitParser.add(WHITE, 2176, "000000010101") 

194 BitParser.add(WHITE, 2240, "000000010110") 

195 BitParser.add(WHITE, 2304, "000000010111") 

196 BitParser.add(WHITE, 2368, "000000011100") 

197 BitParser.add(WHITE, 2432, "000000011101") 

198 BitParser.add(WHITE, 2496, "000000011110") 

199 BitParser.add(WHITE, 2560, "000000011111") 

200 

201 BLACK: ClassVar[BitParserState] = [None, None] 

202 BitParser.add(BLACK, 0, "0000110111") 

203 BitParser.add(BLACK, 1, "010") 

204 BitParser.add(BLACK, 2, "11") 

205 BitParser.add(BLACK, 3, "10") 

206 BitParser.add(BLACK, 4, "011") 

207 BitParser.add(BLACK, 5, "0011") 

208 BitParser.add(BLACK, 6, "0010") 

209 BitParser.add(BLACK, 7, "00011") 

210 BitParser.add(BLACK, 8, "000101") 

211 BitParser.add(BLACK, 9, "000100") 

212 BitParser.add(BLACK, 10, "0000100") 

213 BitParser.add(BLACK, 11, "0000101") 

214 BitParser.add(BLACK, 12, "0000111") 

215 BitParser.add(BLACK, 13, "00000100") 

216 BitParser.add(BLACK, 14, "00000111") 

217 BitParser.add(BLACK, 15, "000011000") 

218 BitParser.add(BLACK, 16, "0000010111") 

219 BitParser.add(BLACK, 17, "0000011000") 

220 BitParser.add(BLACK, 18, "0000001000") 

221 BitParser.add(BLACK, 19, "00001100111") 

222 BitParser.add(BLACK, 20, "00001101000") 

223 BitParser.add(BLACK, 21, "00001101100") 

224 BitParser.add(BLACK, 22, "00000110111") 

225 BitParser.add(BLACK, 23, "00000101000") 

226 BitParser.add(BLACK, 24, "00000010111") 

227 BitParser.add(BLACK, 25, "00000011000") 

228 BitParser.add(BLACK, 26, "000011001010") 

229 BitParser.add(BLACK, 27, "000011001011") 

230 BitParser.add(BLACK, 28, "000011001100") 

231 BitParser.add(BLACK, 29, "000011001101") 

232 BitParser.add(BLACK, 30, "000001101000") 

233 BitParser.add(BLACK, 31, "000001101001") 

234 BitParser.add(BLACK, 32, "000001101010") 

235 BitParser.add(BLACK, 33, "000001101011") 

236 BitParser.add(BLACK, 34, "000011010010") 

237 BitParser.add(BLACK, 35, "000011010011") 

238 BitParser.add(BLACK, 36, "000011010100") 

239 BitParser.add(BLACK, 37, "000011010101") 

240 BitParser.add(BLACK, 38, "000011010110") 

241 BitParser.add(BLACK, 39, "000011010111") 

242 BitParser.add(BLACK, 40, "000001101100") 

243 BitParser.add(BLACK, 41, "000001101101") 

244 BitParser.add(BLACK, 42, "000011011010") 

245 BitParser.add(BLACK, 43, "000011011011") 

246 BitParser.add(BLACK, 44, "000001010100") 

247 BitParser.add(BLACK, 45, "000001010101") 

248 BitParser.add(BLACK, 46, "000001010110") 

249 BitParser.add(BLACK, 47, "000001010111") 

250 BitParser.add(BLACK, 48, "000001100100") 

251 BitParser.add(BLACK, 49, "000001100101") 

252 BitParser.add(BLACK, 50, "000001010010") 

253 BitParser.add(BLACK, 51, "000001010011") 

254 BitParser.add(BLACK, 52, "000000100100") 

255 BitParser.add(BLACK, 53, "000000110111") 

256 BitParser.add(BLACK, 54, "000000111000") 

257 BitParser.add(BLACK, 55, "000000100111") 

258 BitParser.add(BLACK, 56, "000000101000") 

259 BitParser.add(BLACK, 57, "000001011000") 

260 BitParser.add(BLACK, 58, "000001011001") 

261 BitParser.add(BLACK, 59, "000000101011") 

262 BitParser.add(BLACK, 60, "000000101100") 

263 BitParser.add(BLACK, 61, "000001011010") 

264 BitParser.add(BLACK, 62, "000001100110") 

265 BitParser.add(BLACK, 63, "000001100111") 

266 BitParser.add(BLACK, 64, "0000001111") 

267 BitParser.add(BLACK, 128, "000011001000") 

268 BitParser.add(BLACK, 192, "000011001001") 

269 BitParser.add(BLACK, 256, "000001011011") 

270 BitParser.add(BLACK, 320, "000000110011") 

271 BitParser.add(BLACK, 384, "000000110100") 

272 BitParser.add(BLACK, 448, "000000110101") 

273 BitParser.add(BLACK, 512, "0000001101100") 

274 BitParser.add(BLACK, 576, "0000001101101") 

275 BitParser.add(BLACK, 640, "0000001001010") 

276 BitParser.add(BLACK, 704, "0000001001011") 

277 BitParser.add(BLACK, 768, "0000001001100") 

278 BitParser.add(BLACK, 832, "0000001001101") 

279 BitParser.add(BLACK, 896, "0000001110010") 

280 BitParser.add(BLACK, 960, "0000001110011") 

281 BitParser.add(BLACK, 1024, "0000001110100") 

282 BitParser.add(BLACK, 1088, "0000001110101") 

283 BitParser.add(BLACK, 1152, "0000001110110") 

284 BitParser.add(BLACK, 1216, "0000001110111") 

285 BitParser.add(BLACK, 1280, "0000001010010") 

286 BitParser.add(BLACK, 1344, "0000001010011") 

287 BitParser.add(BLACK, 1408, "0000001010100") 

288 BitParser.add(BLACK, 1472, "0000001010101") 

289 BitParser.add(BLACK, 1536, "0000001011010") 

290 BitParser.add(BLACK, 1600, "0000001011011") 

291 BitParser.add(BLACK, 1664, "0000001100100") 

292 BitParser.add(BLACK, 1728, "0000001100101") 

293 BitParser.add(BLACK, 1792, "00000001000") 

294 BitParser.add(BLACK, 1856, "00000001100") 

295 BitParser.add(BLACK, 1920, "00000001101") 

296 BitParser.add(BLACK, 1984, "000000010010") 

297 BitParser.add(BLACK, 2048, "000000010011") 

298 BitParser.add(BLACK, 2112, "000000010100") 

299 BitParser.add(BLACK, 2176, "000000010101") 

300 BitParser.add(BLACK, 2240, "000000010110") 

301 BitParser.add(BLACK, 2304, "000000010111") 

302 BitParser.add(BLACK, 2368, "000000011100") 

303 BitParser.add(BLACK, 2432, "000000011101") 

304 BitParser.add(BLACK, 2496, "000000011110") 

305 BitParser.add(BLACK, 2560, "000000011111") 

306 

307 UNCOMPRESSED: ClassVar[BitParserState] = [None, None] 

308 BitParser.add(UNCOMPRESSED, "1", "1") 

309 BitParser.add(UNCOMPRESSED, "01", "01") 

310 BitParser.add(UNCOMPRESSED, "001", "001") 

311 BitParser.add(UNCOMPRESSED, "0001", "0001") 

312 BitParser.add(UNCOMPRESSED, "00001", "00001") 

313 BitParser.add(UNCOMPRESSED, "00000", "000001") 

314 BitParser.add(UNCOMPRESSED, "T00", "00000011") 

315 BitParser.add(UNCOMPRESSED, "T10", "00000010") 

316 BitParser.add(UNCOMPRESSED, "T000", "000000011") 

317 BitParser.add(UNCOMPRESSED, "T100", "000000010") 

318 BitParser.add(UNCOMPRESSED, "T0000", "0000000011") 

319 BitParser.add(UNCOMPRESSED, "T1000", "0000000010") 

320 BitParser.add(UNCOMPRESSED, "T00000", "00000000011") 

321 BitParser.add(UNCOMPRESSED, "T10000", "00000000010") 

322 

323 class CCITTException(PDFException): 

324 pass 

325 

326 class EOFB(CCITTException): 

327 pass 

328 

329 class InvalidData(CCITTException): 

330 pass 

331 

332 class ByteSkip(CCITTException): 

333 pass 

334 

335 _color: int 

336 

337 def __init__(self, width: int, bytealign: bool = False) -> None: 

338 BitParser.__init__(self) 

339 self.width = width 

340 self.bytealign = bytealign 

341 self.reset() 

342 

343 def feedbytes(self, data: bytes) -> None: 

344 for byte in get_bytes(data): 

345 try: 

346 for m in (128, 64, 32, 16, 8, 4, 2, 1): 

347 self._parse_bit(byte & m) 

348 except self.ByteSkip: 

349 self._accept = self._parse_mode 

350 self._state = self.MODE 

351 except self.EOFB: 

352 break 

353 

354 def _parse_mode(self, mode: object) -> BitParserState: 

355 if mode == "p": 

356 self._do_pass() 

357 self._flush_line() 

358 return self.MODE 

359 elif mode == "h": 

360 self._n1 = 0 

361 self._accept = self._parse_horiz1 

362 if self._color: 

363 return self.WHITE 

364 else: 

365 return self.BLACK 

366 elif mode == "u": 

367 self._accept = self._parse_uncompressed 

368 return self.UNCOMPRESSED 

369 elif mode == "e": 

370 raise self.EOFB 

371 elif isinstance(mode, int): 

372 self._do_vertical(mode) 

373 self._flush_line() 

374 return self.MODE 

375 else: 

376 raise self.InvalidData(mode) 

377 

378 def _parse_horiz1(self, n: Any) -> BitParserState: 

379 if n is None: 

380 raise self.InvalidData 

381 self._n1 += n 

382 if n < 64: 

383 self._n2 = 0 

384 self._color = 1 - self._color 

385 self._accept = self._parse_horiz2 

386 if self._color: 

387 return self.WHITE 

388 else: 

389 return self.BLACK 

390 

391 def _parse_horiz2(self, n: Any) -> BitParserState: 

392 if n is None: 

393 raise self.InvalidData 

394 self._n2 += n 

395 if n < 64: 

396 self._color = 1 - self._color 

397 self._accept = self._parse_mode 

398 self._do_horizontal(self._n1, self._n2) 

399 self._flush_line() 

400 return self.MODE 

401 elif self._color: 

402 return self.WHITE 

403 else: 

404 return self.BLACK 

405 

406 def _parse_uncompressed(self, bits: str | None) -> BitParserState: 

407 if not bits: 

408 raise self.InvalidData 

409 if bits.startswith("T"): 

410 self._accept = self._parse_mode 

411 self._color = int(bits[1]) 

412 self._do_uncompressed(bits[2:]) 

413 return self.MODE 

414 else: 

415 self._do_uncompressed(bits) 

416 return self.UNCOMPRESSED 

417 

418 def _get_bits(self) -> str: 

419 return "".join(str(b) for b in self._curline[: self._curpos]) 

420 

421 def _get_refline(self, i: int) -> str: 

422 if i < 0: 

423 return "[]" + "".join(str(b) for b in self._refline) 

424 elif len(self._refline) <= i: 

425 return "".join(str(b) for b in self._refline) + "[]" 

426 else: 

427 return ( 

428 "".join(str(b) for b in self._refline[:i]) 

429 + "[" 

430 + str(self._refline[i]) 

431 + "]" 

432 + "".join(str(b) for b in self._refline[i + 1 :]) 

433 ) 

434 

435 def reset(self) -> None: 

436 self._y = 0 

437 self._curline = array.array("b", [1] * self.width) 

438 self._reset_line() 

439 self._accept = self._parse_mode 

440 self._state = self.MODE 

441 

442 def output_line(self, y: int, bits: Sequence[int]) -> None: 

443 print(y, "".join(str(b) for b in bits)) 

444 

445 def _reset_line(self) -> None: 

446 self._refline = self._curline 

447 self._curline = array.array("b", [1] * self.width) 

448 self._curpos = -1 

449 self._color = 1 

450 

451 def _flush_line(self) -> None: 

452 if self.width <= self._curpos: 

453 self.output_line(self._y, self._curline) 

454 self._y += 1 

455 self._reset_line() 

456 if self.bytealign: 

457 raise self.ByteSkip 

458 

459 def _do_vertical(self, dx: int) -> None: 

460 x1 = self._curpos + 1 

461 while 1: 

462 if x1 == 0: 

463 if self._color == 1 and self._refline[x1] != self._color: 

464 break 

465 elif x1 == len(self._refline) or ( 

466 self._refline[x1 - 1] == self._color 

467 and self._refline[x1] != self._color 

468 ): 

469 break 

470 x1 += 1 

471 x1 += dx 

472 x0 = max(0, self._curpos) 

473 x1 = max(0, min(self.width, x1)) 

474 if x1 < x0: 

475 for x in range(x1, x0): 

476 self._curline[x] = self._color 

477 elif x0 < x1: 

478 for x in range(x0, x1): 

479 self._curline[x] = self._color 

480 self._curpos = x1 

481 self._color = 1 - self._color 

482 

483 def _do_pass(self) -> None: 

484 x1 = self._curpos + 1 

485 while 1: 

486 if x1 == 0: 

487 if self._color == 1 and self._refline[x1] != self._color: 

488 break 

489 elif x1 == len(self._refline) or ( 

490 self._refline[x1 - 1] == self._color 

491 and self._refline[x1] != self._color 

492 ): 

493 break 

494 x1 += 1 

495 while 1: 

496 if x1 == 0: 

497 if self._color == 0 and self._refline[x1] == self._color: 

498 break 

499 elif x1 == len(self._refline) or ( 

500 self._refline[x1 - 1] != self._color 

501 and self._refline[x1] == self._color 

502 ): 

503 break 

504 x1 += 1 

505 for x in range(self._curpos, x1): 

506 self._curline[x] = self._color 

507 self._curpos = x1 

508 

509 def _do_horizontal(self, n1: int, n2: int) -> None: 

510 if self._curpos < 0: 

511 self._curpos = 0 

512 x = self._curpos 

513 for _ in range(n1): 

514 if len(self._curline) <= x: 

515 break 

516 self._curline[x] = self._color 

517 x += 1 

518 for _ in range(n2): 

519 if len(self._curline) <= x: 

520 break 

521 self._curline[x] = 1 - self._color 

522 x += 1 

523 self._curpos = x 

524 

525 def _do_uncompressed(self, bits: str) -> None: 

526 for c in bits: 

527 self._curline[self._curpos] = int(c) 

528 self._curpos += 1 

529 self._flush_line() 

530 

531 

532class CCITTFaxDecoder(CCITTG4Parser): 

533 def __init__( 

534 self, 

535 width: int, 

536 bytealign: bool = False, 

537 reversed: bool = False, 

538 ) -> None: 

539 CCITTG4Parser.__init__(self, width, bytealign=bytealign) 

540 self.reversed = reversed 

541 self._buf = b"" 

542 

543 def close(self) -> bytes: 

544 return self._buf 

545 

546 def output_line(self, y: int, bits: Sequence[int]) -> None: 

547 arr = array.array("B", [0] * ((len(bits) + 7) // 8)) 

548 if self.reversed: 

549 bits = [1 - b for b in bits] 

550 for i, b in enumerate(bits): 

551 if b: 

552 arr[i // 8] += (128, 64, 32, 16, 8, 4, 2, 1)[i % 8] 

553 self._buf += arr.tobytes() 

554 

555 

556def ccittfaxdecode(data: bytes, params: dict[str, object]) -> bytes: 

557 K = params.get("K") 

558 if K == -1: 

559 cols = cast(int, params.get("Columns")) 

560 bytealign = cast(bool, params.get("EncodedByteAlign")) 

561 reversed = cast(bool, params.get("BlackIs1")) 

562 parser = CCITTFaxDecoder(cols, bytealign=bytealign, reversed=reversed) 

563 else: 

564 raise PDFValueError(K) 

565 parser.feedbytes(data) 

566 return parser.close() 

567 

568 

569# test 

570def main(argv: list[str]) -> None: 

571 if not argv[1:]: 

572 import unittest 

573 

574 unittest.main() 

575 return 

576 

577 class Parser(CCITTG4Parser): 

578 def __init__(self, width: int, bytealign: bool = False) -> None: 

579 import pygame # type: ignore[import] 

580 

581 CCITTG4Parser.__init__(self, width, bytealign=bytealign) 

582 self.img = pygame.Surface((self.width, 1000)) 

583 

584 def output_line(self, y: int, bits: Sequence[int]) -> None: 

585 for x, b in enumerate(bits): 

586 if b: 

587 self.img.set_at((x, y), (255, 255, 255)) 

588 else: 

589 self.img.set_at((x, y), (0, 0, 0)) 

590 

591 def close(self) -> None: 

592 import pygame 

593 

594 pygame.image.save(self.img, "out.bmp") 

595 

596 for path in argv[1:]: 

597 with open(path, "rb") as fp: 

598 (_, _, _k, w, _h, _) = path.split(".") 

599 parser = Parser(int(w)) 

600 parser.feedbytes(fp.read()) 

601 parser.close()