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 typing import ( 

16 Any, 

17 Callable, 

18 Dict, 

19 Iterator, 

20 List, 

21 MutableSequence, 

22 Optional, 

23 Sequence, 

24 Union, 

25 cast, 

26) 

27 

28from pdfminer.pdfexceptions import PDFException, PDFValueError 

29 

30 

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

32 yield from data 

33 

34 

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

36BitParserState = MutableSequence[Any] 

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

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

39 

40 

41class BitParser: 

42 _state: BitParserState 

43 

44 # _accept is declared Optional solely as a workaround for 

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

46 _accept: Optional[Callable[[Any], BitParserState]] 

47 

48 def __init__(self) -> None: 

49 self._pos = 0 

50 

51 @classmethod 

52 def add(cls, root: BitParserState, v: Union[int, str], bits: str) -> None: 

53 p: BitParserState = root 

54 b = None 

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

56 if i > 0: 

57 assert b is not None 

58 if p[b] is None: 

59 p[b] = [None, None] 

60 p = p[b] 

61 if bits[i] == "1": 

62 b = 1 

63 else: 

64 b = 0 

65 assert b is not None 

66 p[b] = v 

67 

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

69 for byte in get_bytes(data): 

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

71 self._parse_bit(byte & m) 

72 

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

74 if x: 

75 v = self._state[1] 

76 else: 

77 v = self._state[0] 

78 self._pos += 1 

79 if isinstance(v, list): 

80 self._state = v 

81 else: 

82 assert self._accept is not None 

83 self._state = self._accept(v) 

84 

85 

86class CCITTG4Parser(BitParser): 

87 MODE = [None, None] 

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

106 

107 WHITE = [None, None] 

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

212 

213 BLACK = [None, None] 

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

318 

319 UNCOMPRESSED = [None, None] 

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

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

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

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

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

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

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

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

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

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

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

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

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

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

334 

335 class CCITTException(PDFException): 

336 pass 

337 

338 class EOFB(CCITTException): 

339 pass 

340 

341 class InvalidData(CCITTException): 

342 pass 

343 

344 class ByteSkip(CCITTException): 

345 pass 

346 

347 _color: int 

348 

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

350 BitParser.__init__(self) 

351 self.width = width 

352 self.bytealign = bytealign 

353 self.reset() 

354 

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

356 for byte in get_bytes(data): 

357 try: 

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

359 self._parse_bit(byte & m) 

360 except self.ByteSkip: 

361 self._accept = self._parse_mode 

362 self._state = self.MODE 

363 except self.EOFB: 

364 break 

365 

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

367 if mode == "p": 

368 self._do_pass() 

369 self._flush_line() 

370 return self.MODE 

371 elif mode == "h": 

372 self._n1 = 0 

373 self._accept = self._parse_horiz1 

374 if self._color: 

375 return self.WHITE 

376 else: 

377 return self.BLACK 

378 elif mode == "u": 

379 self._accept = self._parse_uncompressed 

380 return self.UNCOMPRESSED 

381 elif mode == "e": 

382 raise self.EOFB 

383 elif isinstance(mode, int): 

384 self._do_vertical(mode) 

385 self._flush_line() 

386 return self.MODE 

387 else: 

388 raise self.InvalidData(mode) 

389 

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

391 if n is None: 

392 raise self.InvalidData 

393 self._n1 += n 

394 if n < 64: 

395 self._n2 = 0 

396 self._color = 1 - self._color 

397 self._accept = self._parse_horiz2 

398 if self._color: 

399 return self.WHITE 

400 else: 

401 return self.BLACK 

402 

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

404 if n is None: 

405 raise self.InvalidData 

406 self._n2 += n 

407 if n < 64: 

408 self._color = 1 - self._color 

409 self._accept = self._parse_mode 

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

411 self._flush_line() 

412 return self.MODE 

413 elif self._color: 

414 return self.WHITE 

415 else: 

416 return self.BLACK 

417 

418 def _parse_uncompressed(self, bits: Optional[str]) -> BitParserState: 

419 if not bits: 

420 raise self.InvalidData 

421 if bits.startswith("T"): 

422 self._accept = self._parse_mode 

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

424 self._do_uncompressed(bits[2:]) 

425 return self.MODE 

426 else: 

427 self._do_uncompressed(bits) 

428 return self.UNCOMPRESSED 

429 

430 def _get_bits(self) -> str: 

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

432 

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

434 if i < 0: 

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

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

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

438 else: 

439 return ( 

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

441 + "[" 

442 + str(self._refline[i]) 

443 + "]" 

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

445 ) 

446 

447 def reset(self) -> None: 

448 self._y = 0 

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

450 self._reset_line() 

451 self._accept = self._parse_mode 

452 self._state = self.MODE 

453 

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

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

456 

457 def _reset_line(self) -> None: 

458 self._refline = self._curline 

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

460 self._curpos = -1 

461 self._color = 1 

462 

463 def _flush_line(self) -> None: 

464 if self.width <= self._curpos: 

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

466 self._y += 1 

467 self._reset_line() 

468 if self.bytealign: 

469 raise self.ByteSkip 

470 

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

472 x1 = self._curpos + 1 

473 while 1: 

474 if x1 == 0: 

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

476 break 

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

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

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

480 ): 

481 break 

482 x1 += 1 

483 x1 += dx 

484 x0 = max(0, self._curpos) 

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

486 if x1 < x0: 

487 for x in range(x1, x0): 

488 self._curline[x] = self._color 

489 elif x0 < x1: 

490 for x in range(x0, x1): 

491 self._curline[x] = self._color 

492 self._curpos = x1 

493 self._color = 1 - self._color 

494 

495 def _do_pass(self) -> None: 

496 x1 = self._curpos + 1 

497 while 1: 

498 if x1 == 0: 

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

500 break 

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

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

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

504 ): 

505 break 

506 x1 += 1 

507 while 1: 

508 if x1 == 0: 

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

510 break 

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

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

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

514 ): 

515 break 

516 x1 += 1 

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

518 self._curline[x] = self._color 

519 self._curpos = x1 

520 

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

522 if self._curpos < 0: 

523 self._curpos = 0 

524 x = self._curpos 

525 for _ in range(n1): 

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

527 break 

528 self._curline[x] = self._color 

529 x += 1 

530 for _ in range(n2): 

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

532 break 

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

534 x += 1 

535 self._curpos = x 

536 

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

538 for c in bits: 

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

540 self._curpos += 1 

541 self._flush_line() 

542 

543 

544class CCITTFaxDecoder(CCITTG4Parser): 

545 def __init__( 

546 self, 

547 width: int, 

548 bytealign: bool = False, 

549 reversed: bool = False, 

550 ) -> None: 

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

552 self.reversed = reversed 

553 self._buf = b"" 

554 

555 def close(self) -> bytes: 

556 return self._buf 

557 

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

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

560 if self.reversed: 

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

562 for i, b in enumerate(bits): 

563 if b: 

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

565 self._buf += arr.tobytes() 

566 

567 

568def ccittfaxdecode(data: bytes, params: Dict[str, object]) -> bytes: 

569 K = params.get("K") 

570 if K == -1: 

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

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

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

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

575 else: 

576 raise PDFValueError(K) 

577 parser.feedbytes(data) 

578 return parser.close() 

579 

580 

581# test 

582def main(argv: List[str]) -> None: 

583 if not argv[1:]: 

584 import unittest 

585 

586 unittest.main() 

587 return 

588 

589 class Parser(CCITTG4Parser): 

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

591 import pygame # type: ignore[import] 

592 

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

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

595 

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

597 for x, b in enumerate(bits): 

598 if b: 

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

600 else: 

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

602 

603 def close(self) -> None: 

604 import pygame 

605 

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

607 

608 for path in argv[1:]: 

609 fp = open(path, "rb") 

610 (_, _, k, w, h, _) = path.split(".") 

611 parser = Parser(int(w)) 

612 parser.feedbytes(fp.read()) 

613 parser.close() 

614 fp.close()