Initial commit of Roster

This commit is contained in:
Media
2025-06-28 01:12:46 -05:00
commit c23d3a7676
4367 changed files with 890519 additions and 0 deletions
@@ -0,0 +1,10 @@
from importlib.metadata import PackageNotFoundError, version
try:
__version__ = version("pdfminer.six")
except PackageNotFoundError:
# package is not installed, return default
__version__ = "0.0"
if __name__ == "__main__":
print(__version__)
@@ -0,0 +1,101 @@
# Copyright 2016-present MongoDB, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# Some changes copyright 2021-present Matthias Valvekens,
# licensed under the license of the pyHanko project (see LICENSE file).
"""An implementation of RFC4013 SASLprep."""
__all__ = ["saslprep"]
import stringprep
import unicodedata
from typing import Callable, Tuple
from pdfminer.pdfexceptions import PDFValueError
# RFC4013 section 2.3 prohibited output.
_PROHIBITED: Tuple[Callable[[str], bool], ...] = (
# A strict reading of RFC 4013 requires table c12 here, but
# characters from it are mapped to SPACE in the Map step. Can
# normalization reintroduce them somehow?
stringprep.in_table_c12,
stringprep.in_table_c21_c22,
stringprep.in_table_c3,
stringprep.in_table_c4,
stringprep.in_table_c5,
stringprep.in_table_c6,
stringprep.in_table_c7,
stringprep.in_table_c8,
stringprep.in_table_c9,
)
def saslprep(data: str, prohibit_unassigned_code_points: bool = True) -> str:
"""An implementation of RFC4013 SASLprep.
:param data:
The string to SASLprep.
:param prohibit_unassigned_code_points:
RFC 3454 and RFCs for various SASL mechanisms distinguish between
`queries` (unassigned code points allowed) and
`stored strings` (unassigned code points prohibited). Defaults
to ``True`` (unassigned code points are prohibited).
:return: The SASLprep'ed version of `data`.
"""
if prohibit_unassigned_code_points:
prohibited = _PROHIBITED + (stringprep.in_table_a1,)
else:
prohibited = _PROHIBITED
# RFC3454 section 2, step 1 - Map
# RFC4013 section 2.1 mappings
# Map Non-ASCII space characters to SPACE (U+0020). Map
# commonly mapped to nothing characters to, well, nothing.
in_table_c12 = stringprep.in_table_c12
in_table_b1 = stringprep.in_table_b1
data = "".join(
[
"\u0020" if in_table_c12(elt) else elt
for elt in data
if not in_table_b1(elt)
],
)
# RFC3454 section 2, step 2 - Normalize
# RFC4013 section 2.2 normalization
data = unicodedata.ucd_3_2_0.normalize("NFKC", data)
in_table_d1 = stringprep.in_table_d1
if in_table_d1(data[0]):
if not in_table_d1(data[-1]):
# RFC3454, Section 6, #3. If a string contains any
# RandALCat character, the first and last characters
# MUST be RandALCat characters.
raise PDFValueError("SASLprep: failed bidirectional check")
# RFC3454, Section 6, #2. If a string contains any RandALCat
# character, it MUST NOT contain any LCat character.
prohibited = prohibited + (stringprep.in_table_d2,)
else:
# RFC3454, Section 6, #3. Following the logic of #3, if
# the first character is not a RandALCat, no other character
# can be either.
prohibited = prohibited + (in_table_d1,)
# RFC3454 section 2, step 3 and 4 - Prohibit and check bidi
for char in data:
if any(in_table(char) for in_table in prohibited):
raise PDFValueError("SASLprep: failed prohibited character check")
return data
@@ -0,0 +1,35 @@
"""Python implementation of Arcfour encryption algorithm.
See https://en.wikipedia.org/wiki/RC4
This code is in the public domain.
"""
from typing import Sequence
class Arcfour:
def __init__(self, key: Sequence[int]) -> None:
# because Py3 range is not indexable
s = [i for i in range(256)]
j = 0
klen = len(key)
for i in range(256):
j = (j + s[i] + key[i % klen]) % 256
(s[i], s[j]) = (s[j], s[i])
self.s = s
(self.i, self.j) = (0, 0)
def process(self, data: bytes) -> bytes:
(i, j) = (self.i, self.j)
s = self.s
r = b""
for c in iter(data):
i = (i + 1) % 256
j = (j + s[i]) % 256
(s[i], s[j]) = (s[j], s[i])
k = s[(s[i] + s[j]) % 256]
r += bytes((c ^ k,))
(self.i, self.j) = (i, j)
return r
encrypt = decrypt = process
@@ -0,0 +1,48 @@
"""Python implementation of ASCII85/ASCIIHex decoder (Adobe version)."""
import re
from base64 import a85decode
from binascii import unhexlify
start_re = re.compile(rb"^\s*<?\s*~\s*")
end_re = re.compile(rb"\s*~\s*>?\s*$")
def ascii85decode(data: bytes) -> bytes:
"""In ASCII85 encoding, every four bytes are encoded with five ASCII
letters, using 85 different types of characters (as 256**4 < 85**5).
When the length of the original bytes is not a multiple of 4, a special
rule is used for round up.
Adobe's ASCII85 implementation expects the input to be terminated
by `b"~>"`, and (though this is absent from the PDF spec) it can
also begin with `b"<~"`. We can't reliably expect this to be the
case, and there can be off-by-one errors in stream lengths which
mean we only see `~` at the end. Worse yet, `<` and `>` are
ASCII85 digits, so we can't strip them. We settle on a compromise
where we strip leading `<~` or `~` and trailing `~` or `~>`.
"""
data = start_re.sub(b"", data)
data = end_re.sub(b"", data)
return a85decode(data)
bws_re = re.compile(rb"\s")
def asciihexdecode(data: bytes) -> bytes:
"""ASCIIHexDecode filter: PDFReference v1.4 section 3.3.1
For each pair of ASCII hexadecimal digits (0-9 and A-F or a-f), the
ASCIIHexDecode filter produces one byte of binary data. All white-space
characters are ignored. A right angle bracket character (>) indicates
EOD. Any other characters will cause an error. If the filter encounters
the EOD marker after reading an odd number of hexadecimal digits, it
will behave as if a 0 followed the last digit.
"""
data = bws_re.sub(b"", data)
idx = data.find(b">")
if idx != -1:
data = data[:idx]
if idx % 2 == 1:
data += b"0"
return unhexlify(data)
@@ -0,0 +1,91 @@
import itertools
from typing import Any, Optional, Tuple
from pdfminer.utils import Matrix, Rect
_FloatTriple = Tuple[float, float, float]
_FloatQuadruple = Tuple[float, float, float, float]
def safe_int(o: Any) -> Optional[int]:
try:
return int(o)
except (TypeError, ValueError):
return None
def safe_float(o: Any) -> Optional[float]:
try:
return float(o)
except (TypeError, ValueError, OverflowError):
return None
def safe_matrix(a: Any, b: Any, c: Any, d: Any, e: Any, f: Any) -> Optional[Matrix]:
a_f = safe_float(a)
b_f = safe_float(b)
c_f = safe_float(c)
d_f = safe_float(d)
e_f = safe_float(e)
f_f = safe_float(f)
if (
a_f is None
or b_f is None
or c_f is None
or d_f is None
or e_f is None
or f_f is None
):
return None
return a_f, b_f, c_f, d_f, e_f, f_f
def safe_rgb(r: Any, g: Any, b: Any) -> Optional[Tuple[float, float, float]]:
return _safe_float_triple(r, g, b)
def safe_cmyk(
c: Any, m: Any, y: Any, k: Any
) -> Optional[Tuple[float, float, float, float]]:
return _safe_float_quadruple(c, m, y, k)
def safe_rect_list(value: Any) -> Optional[Rect]:
try:
values = list(itertools.islice(value, 4))
except TypeError:
return None
if len(values) != 4:
return None
return safe_rect(*values)
def safe_rect(a: Any, b: Any, c: Any, d: Any) -> Optional[Rect]:
return _safe_float_quadruple(a, b, c, d)
def _safe_float_triple(a: Any, b: Any, c: Any) -> Optional[_FloatTriple]:
a_f = safe_float(a)
b_f = safe_float(b)
c_f = safe_float(c)
if a_f is None or b_f is None or c_f is None:
return None
return a_f, b_f, c_f
def _safe_float_quadruple(a: Any, b: Any, c: Any, d: Any) -> Optional[_FloatQuadruple]:
a_f = safe_float(a)
b_f = safe_float(b)
c_f = safe_float(c)
d_f = safe_float(d)
if a_f is None or b_f is None or c_f is None or d_f is None:
return None
return a_f, b_f, c_f, d_f
@@ -0,0 +1,614 @@
# CCITT Fax decoder
#
# Bugs: uncompressed mode untested.
#
# cf.
# ITU-T Recommendation T.4
# "Standardization of Group 3 facsimile terminals
# for document transmission"
# ITU-T Recommendation T.6
# "FACSIMILE CODING SCHEMES AND CODING CONTROL FUNCTIONS
# FOR GROUP 4 FACSIMILE APPARATUS"
import array
from typing import (
Any,
Callable,
Dict,
Iterator,
List,
MutableSequence,
Optional,
Sequence,
Union,
cast,
)
from pdfminer.pdfexceptions import PDFException, PDFValueError
def get_bytes(data: bytes) -> Iterator[int]:
yield from data
# Workaround https://github.com/python/mypy/issues/731
BitParserState = MutableSequence[Any]
# A better definition (not supported by mypy) would be:
# BitParserState = MutableSequence[Union["BitParserState", int, str, None]]
class BitParser:
_state: BitParserState
# _accept is declared Optional solely as a workaround for
# https://github.com/python/mypy/issues/708
_accept: Optional[Callable[[Any], BitParserState]]
def __init__(self) -> None:
self._pos = 0
@classmethod
def add(cls, root: BitParserState, v: Union[int, str], bits: str) -> None:
p: BitParserState = root
b = None
for i in range(len(bits)):
if i > 0:
assert b is not None
if p[b] is None:
p[b] = [None, None]
p = p[b]
if bits[i] == "1":
b = 1
else:
b = 0
assert b is not None
p[b] = v
def feedbytes(self, data: bytes) -> None:
for byte in get_bytes(data):
for m in (128, 64, 32, 16, 8, 4, 2, 1):
self._parse_bit(byte & m)
def _parse_bit(self, x: object) -> None:
if x:
v = self._state[1]
else:
v = self._state[0]
self._pos += 1
if isinstance(v, list):
self._state = v
else:
assert self._accept is not None
self._state = self._accept(v)
class CCITTG4Parser(BitParser):
MODE = [None, None]
BitParser.add(MODE, 0, "1")
BitParser.add(MODE, +1, "011")
BitParser.add(MODE, -1, "010")
BitParser.add(MODE, "h", "001")
BitParser.add(MODE, "p", "0001")
BitParser.add(MODE, +2, "000011")
BitParser.add(MODE, -2, "000010")
BitParser.add(MODE, +3, "0000011")
BitParser.add(MODE, -3, "0000010")
BitParser.add(MODE, "u", "0000001111")
BitParser.add(MODE, "x1", "0000001000")
BitParser.add(MODE, "x2", "0000001001")
BitParser.add(MODE, "x3", "0000001010")
BitParser.add(MODE, "x4", "0000001011")
BitParser.add(MODE, "x5", "0000001100")
BitParser.add(MODE, "x6", "0000001101")
BitParser.add(MODE, "x7", "0000001110")
BitParser.add(MODE, "e", "000000000001000000000001")
WHITE = [None, None]
BitParser.add(WHITE, 0, "00110101")
BitParser.add(WHITE, 1, "000111")
BitParser.add(WHITE, 2, "0111")
BitParser.add(WHITE, 3, "1000")
BitParser.add(WHITE, 4, "1011")
BitParser.add(WHITE, 5, "1100")
BitParser.add(WHITE, 6, "1110")
BitParser.add(WHITE, 7, "1111")
BitParser.add(WHITE, 8, "10011")
BitParser.add(WHITE, 9, "10100")
BitParser.add(WHITE, 10, "00111")
BitParser.add(WHITE, 11, "01000")
BitParser.add(WHITE, 12, "001000")
BitParser.add(WHITE, 13, "000011")
BitParser.add(WHITE, 14, "110100")
BitParser.add(WHITE, 15, "110101")
BitParser.add(WHITE, 16, "101010")
BitParser.add(WHITE, 17, "101011")
BitParser.add(WHITE, 18, "0100111")
BitParser.add(WHITE, 19, "0001100")
BitParser.add(WHITE, 20, "0001000")
BitParser.add(WHITE, 21, "0010111")
BitParser.add(WHITE, 22, "0000011")
BitParser.add(WHITE, 23, "0000100")
BitParser.add(WHITE, 24, "0101000")
BitParser.add(WHITE, 25, "0101011")
BitParser.add(WHITE, 26, "0010011")
BitParser.add(WHITE, 27, "0100100")
BitParser.add(WHITE, 28, "0011000")
BitParser.add(WHITE, 29, "00000010")
BitParser.add(WHITE, 30, "00000011")
BitParser.add(WHITE, 31, "00011010")
BitParser.add(WHITE, 32, "00011011")
BitParser.add(WHITE, 33, "00010010")
BitParser.add(WHITE, 34, "00010011")
BitParser.add(WHITE, 35, "00010100")
BitParser.add(WHITE, 36, "00010101")
BitParser.add(WHITE, 37, "00010110")
BitParser.add(WHITE, 38, "00010111")
BitParser.add(WHITE, 39, "00101000")
BitParser.add(WHITE, 40, "00101001")
BitParser.add(WHITE, 41, "00101010")
BitParser.add(WHITE, 42, "00101011")
BitParser.add(WHITE, 43, "00101100")
BitParser.add(WHITE, 44, "00101101")
BitParser.add(WHITE, 45, "00000100")
BitParser.add(WHITE, 46, "00000101")
BitParser.add(WHITE, 47, "00001010")
BitParser.add(WHITE, 48, "00001011")
BitParser.add(WHITE, 49, "01010010")
BitParser.add(WHITE, 50, "01010011")
BitParser.add(WHITE, 51, "01010100")
BitParser.add(WHITE, 52, "01010101")
BitParser.add(WHITE, 53, "00100100")
BitParser.add(WHITE, 54, "00100101")
BitParser.add(WHITE, 55, "01011000")
BitParser.add(WHITE, 56, "01011001")
BitParser.add(WHITE, 57, "01011010")
BitParser.add(WHITE, 58, "01011011")
BitParser.add(WHITE, 59, "01001010")
BitParser.add(WHITE, 60, "01001011")
BitParser.add(WHITE, 61, "00110010")
BitParser.add(WHITE, 62, "00110011")
BitParser.add(WHITE, 63, "00110100")
BitParser.add(WHITE, 64, "11011")
BitParser.add(WHITE, 128, "10010")
BitParser.add(WHITE, 192, "010111")
BitParser.add(WHITE, 256, "0110111")
BitParser.add(WHITE, 320, "00110110")
BitParser.add(WHITE, 384, "00110111")
BitParser.add(WHITE, 448, "01100100")
BitParser.add(WHITE, 512, "01100101")
BitParser.add(WHITE, 576, "01101000")
BitParser.add(WHITE, 640, "01100111")
BitParser.add(WHITE, 704, "011001100")
BitParser.add(WHITE, 768, "011001101")
BitParser.add(WHITE, 832, "011010010")
BitParser.add(WHITE, 896, "011010011")
BitParser.add(WHITE, 960, "011010100")
BitParser.add(WHITE, 1024, "011010101")
BitParser.add(WHITE, 1088, "011010110")
BitParser.add(WHITE, 1152, "011010111")
BitParser.add(WHITE, 1216, "011011000")
BitParser.add(WHITE, 1280, "011011001")
BitParser.add(WHITE, 1344, "011011010")
BitParser.add(WHITE, 1408, "011011011")
BitParser.add(WHITE, 1472, "010011000")
BitParser.add(WHITE, 1536, "010011001")
BitParser.add(WHITE, 1600, "010011010")
BitParser.add(WHITE, 1664, "011000")
BitParser.add(WHITE, 1728, "010011011")
BitParser.add(WHITE, 1792, "00000001000")
BitParser.add(WHITE, 1856, "00000001100")
BitParser.add(WHITE, 1920, "00000001101")
BitParser.add(WHITE, 1984, "000000010010")
BitParser.add(WHITE, 2048, "000000010011")
BitParser.add(WHITE, 2112, "000000010100")
BitParser.add(WHITE, 2176, "000000010101")
BitParser.add(WHITE, 2240, "000000010110")
BitParser.add(WHITE, 2304, "000000010111")
BitParser.add(WHITE, 2368, "000000011100")
BitParser.add(WHITE, 2432, "000000011101")
BitParser.add(WHITE, 2496, "000000011110")
BitParser.add(WHITE, 2560, "000000011111")
BLACK = [None, None]
BitParser.add(BLACK, 0, "0000110111")
BitParser.add(BLACK, 1, "010")
BitParser.add(BLACK, 2, "11")
BitParser.add(BLACK, 3, "10")
BitParser.add(BLACK, 4, "011")
BitParser.add(BLACK, 5, "0011")
BitParser.add(BLACK, 6, "0010")
BitParser.add(BLACK, 7, "00011")
BitParser.add(BLACK, 8, "000101")
BitParser.add(BLACK, 9, "000100")
BitParser.add(BLACK, 10, "0000100")
BitParser.add(BLACK, 11, "0000101")
BitParser.add(BLACK, 12, "0000111")
BitParser.add(BLACK, 13, "00000100")
BitParser.add(BLACK, 14, "00000111")
BitParser.add(BLACK, 15, "000011000")
BitParser.add(BLACK, 16, "0000010111")
BitParser.add(BLACK, 17, "0000011000")
BitParser.add(BLACK, 18, "0000001000")
BitParser.add(BLACK, 19, "00001100111")
BitParser.add(BLACK, 20, "00001101000")
BitParser.add(BLACK, 21, "00001101100")
BitParser.add(BLACK, 22, "00000110111")
BitParser.add(BLACK, 23, "00000101000")
BitParser.add(BLACK, 24, "00000010111")
BitParser.add(BLACK, 25, "00000011000")
BitParser.add(BLACK, 26, "000011001010")
BitParser.add(BLACK, 27, "000011001011")
BitParser.add(BLACK, 28, "000011001100")
BitParser.add(BLACK, 29, "000011001101")
BitParser.add(BLACK, 30, "000001101000")
BitParser.add(BLACK, 31, "000001101001")
BitParser.add(BLACK, 32, "000001101010")
BitParser.add(BLACK, 33, "000001101011")
BitParser.add(BLACK, 34, "000011010010")
BitParser.add(BLACK, 35, "000011010011")
BitParser.add(BLACK, 36, "000011010100")
BitParser.add(BLACK, 37, "000011010101")
BitParser.add(BLACK, 38, "000011010110")
BitParser.add(BLACK, 39, "000011010111")
BitParser.add(BLACK, 40, "000001101100")
BitParser.add(BLACK, 41, "000001101101")
BitParser.add(BLACK, 42, "000011011010")
BitParser.add(BLACK, 43, "000011011011")
BitParser.add(BLACK, 44, "000001010100")
BitParser.add(BLACK, 45, "000001010101")
BitParser.add(BLACK, 46, "000001010110")
BitParser.add(BLACK, 47, "000001010111")
BitParser.add(BLACK, 48, "000001100100")
BitParser.add(BLACK, 49, "000001100101")
BitParser.add(BLACK, 50, "000001010010")
BitParser.add(BLACK, 51, "000001010011")
BitParser.add(BLACK, 52, "000000100100")
BitParser.add(BLACK, 53, "000000110111")
BitParser.add(BLACK, 54, "000000111000")
BitParser.add(BLACK, 55, "000000100111")
BitParser.add(BLACK, 56, "000000101000")
BitParser.add(BLACK, 57, "000001011000")
BitParser.add(BLACK, 58, "000001011001")
BitParser.add(BLACK, 59, "000000101011")
BitParser.add(BLACK, 60, "000000101100")
BitParser.add(BLACK, 61, "000001011010")
BitParser.add(BLACK, 62, "000001100110")
BitParser.add(BLACK, 63, "000001100111")
BitParser.add(BLACK, 64, "0000001111")
BitParser.add(BLACK, 128, "000011001000")
BitParser.add(BLACK, 192, "000011001001")
BitParser.add(BLACK, 256, "000001011011")
BitParser.add(BLACK, 320, "000000110011")
BitParser.add(BLACK, 384, "000000110100")
BitParser.add(BLACK, 448, "000000110101")
BitParser.add(BLACK, 512, "0000001101100")
BitParser.add(BLACK, 576, "0000001101101")
BitParser.add(BLACK, 640, "0000001001010")
BitParser.add(BLACK, 704, "0000001001011")
BitParser.add(BLACK, 768, "0000001001100")
BitParser.add(BLACK, 832, "0000001001101")
BitParser.add(BLACK, 896, "0000001110010")
BitParser.add(BLACK, 960, "0000001110011")
BitParser.add(BLACK, 1024, "0000001110100")
BitParser.add(BLACK, 1088, "0000001110101")
BitParser.add(BLACK, 1152, "0000001110110")
BitParser.add(BLACK, 1216, "0000001110111")
BitParser.add(BLACK, 1280, "0000001010010")
BitParser.add(BLACK, 1344, "0000001010011")
BitParser.add(BLACK, 1408, "0000001010100")
BitParser.add(BLACK, 1472, "0000001010101")
BitParser.add(BLACK, 1536, "0000001011010")
BitParser.add(BLACK, 1600, "0000001011011")
BitParser.add(BLACK, 1664, "0000001100100")
BitParser.add(BLACK, 1728, "0000001100101")
BitParser.add(BLACK, 1792, "00000001000")
BitParser.add(BLACK, 1856, "00000001100")
BitParser.add(BLACK, 1920, "00000001101")
BitParser.add(BLACK, 1984, "000000010010")
BitParser.add(BLACK, 2048, "000000010011")
BitParser.add(BLACK, 2112, "000000010100")
BitParser.add(BLACK, 2176, "000000010101")
BitParser.add(BLACK, 2240, "000000010110")
BitParser.add(BLACK, 2304, "000000010111")
BitParser.add(BLACK, 2368, "000000011100")
BitParser.add(BLACK, 2432, "000000011101")
BitParser.add(BLACK, 2496, "000000011110")
BitParser.add(BLACK, 2560, "000000011111")
UNCOMPRESSED = [None, None]
BitParser.add(UNCOMPRESSED, "1", "1")
BitParser.add(UNCOMPRESSED, "01", "01")
BitParser.add(UNCOMPRESSED, "001", "001")
BitParser.add(UNCOMPRESSED, "0001", "0001")
BitParser.add(UNCOMPRESSED, "00001", "00001")
BitParser.add(UNCOMPRESSED, "00000", "000001")
BitParser.add(UNCOMPRESSED, "T00", "00000011")
BitParser.add(UNCOMPRESSED, "T10", "00000010")
BitParser.add(UNCOMPRESSED, "T000", "000000011")
BitParser.add(UNCOMPRESSED, "T100", "000000010")
BitParser.add(UNCOMPRESSED, "T0000", "0000000011")
BitParser.add(UNCOMPRESSED, "T1000", "0000000010")
BitParser.add(UNCOMPRESSED, "T00000", "00000000011")
BitParser.add(UNCOMPRESSED, "T10000", "00000000010")
class CCITTException(PDFException):
pass
class EOFB(CCITTException):
pass
class InvalidData(CCITTException):
pass
class ByteSkip(CCITTException):
pass
_color: int
def __init__(self, width: int, bytealign: bool = False) -> None:
BitParser.__init__(self)
self.width = width
self.bytealign = bytealign
self.reset()
def feedbytes(self, data: bytes) -> None:
for byte in get_bytes(data):
try:
for m in (128, 64, 32, 16, 8, 4, 2, 1):
self._parse_bit(byte & m)
except self.ByteSkip:
self._accept = self._parse_mode
self._state = self.MODE
except self.EOFB:
break
def _parse_mode(self, mode: object) -> BitParserState:
if mode == "p":
self._do_pass()
self._flush_line()
return self.MODE
elif mode == "h":
self._n1 = 0
self._accept = self._parse_horiz1
if self._color:
return self.WHITE
else:
return self.BLACK
elif mode == "u":
self._accept = self._parse_uncompressed
return self.UNCOMPRESSED
elif mode == "e":
raise self.EOFB
elif isinstance(mode, int):
self._do_vertical(mode)
self._flush_line()
return self.MODE
else:
raise self.InvalidData(mode)
def _parse_horiz1(self, n: Any) -> BitParserState:
if n is None:
raise self.InvalidData
self._n1 += n
if n < 64:
self._n2 = 0
self._color = 1 - self._color
self._accept = self._parse_horiz2
if self._color:
return self.WHITE
else:
return self.BLACK
def _parse_horiz2(self, n: Any) -> BitParserState:
if n is None:
raise self.InvalidData
self._n2 += n
if n < 64:
self._color = 1 - self._color
self._accept = self._parse_mode
self._do_horizontal(self._n1, self._n2)
self._flush_line()
return self.MODE
elif self._color:
return self.WHITE
else:
return self.BLACK
def _parse_uncompressed(self, bits: Optional[str]) -> BitParserState:
if not bits:
raise self.InvalidData
if bits.startswith("T"):
self._accept = self._parse_mode
self._color = int(bits[1])
self._do_uncompressed(bits[2:])
return self.MODE
else:
self._do_uncompressed(bits)
return self.UNCOMPRESSED
def _get_bits(self) -> str:
return "".join(str(b) for b in self._curline[: self._curpos])
def _get_refline(self, i: int) -> str:
if i < 0:
return "[]" + "".join(str(b) for b in self._refline)
elif len(self._refline) <= i:
return "".join(str(b) for b in self._refline) + "[]"
else:
return (
"".join(str(b) for b in self._refline[:i])
+ "["
+ str(self._refline[i])
+ "]"
+ "".join(str(b) for b in self._refline[i + 1 :])
)
def reset(self) -> None:
self._y = 0
self._curline = array.array("b", [1] * self.width)
self._reset_line()
self._accept = self._parse_mode
self._state = self.MODE
def output_line(self, y: int, bits: Sequence[int]) -> None:
print(y, "".join(str(b) for b in bits))
def _reset_line(self) -> None:
self._refline = self._curline
self._curline = array.array("b", [1] * self.width)
self._curpos = -1
self._color = 1
def _flush_line(self) -> None:
if self.width <= self._curpos:
self.output_line(self._y, self._curline)
self._y += 1
self._reset_line()
if self.bytealign:
raise self.ByteSkip
def _do_vertical(self, dx: int) -> None:
x1 = self._curpos + 1
while 1:
if x1 == 0:
if self._color == 1 and self._refline[x1] != self._color:
break
elif x1 == len(self._refline) or (
self._refline[x1 - 1] == self._color
and self._refline[x1] != self._color
):
break
x1 += 1
x1 += dx
x0 = max(0, self._curpos)
x1 = max(0, min(self.width, x1))
if x1 < x0:
for x in range(x1, x0):
self._curline[x] = self._color
elif x0 < x1:
for x in range(x0, x1):
self._curline[x] = self._color
self._curpos = x1
self._color = 1 - self._color
def _do_pass(self) -> None:
x1 = self._curpos + 1
while 1:
if x1 == 0:
if self._color == 1 and self._refline[x1] != self._color:
break
elif x1 == len(self._refline) or (
self._refline[x1 - 1] == self._color
and self._refline[x1] != self._color
):
break
x1 += 1
while 1:
if x1 == 0:
if self._color == 0 and self._refline[x1] == self._color:
break
elif x1 == len(self._refline) or (
self._refline[x1 - 1] != self._color
and self._refline[x1] == self._color
):
break
x1 += 1
for x in range(self._curpos, x1):
self._curline[x] = self._color
self._curpos = x1
def _do_horizontal(self, n1: int, n2: int) -> None:
if self._curpos < 0:
self._curpos = 0
x = self._curpos
for _ in range(n1):
if len(self._curline) <= x:
break
self._curline[x] = self._color
x += 1
for _ in range(n2):
if len(self._curline) <= x:
break
self._curline[x] = 1 - self._color
x += 1
self._curpos = x
def _do_uncompressed(self, bits: str) -> None:
for c in bits:
self._curline[self._curpos] = int(c)
self._curpos += 1
self._flush_line()
class CCITTFaxDecoder(CCITTG4Parser):
def __init__(
self,
width: int,
bytealign: bool = False,
reversed: bool = False,
) -> None:
CCITTG4Parser.__init__(self, width, bytealign=bytealign)
self.reversed = reversed
self._buf = b""
def close(self) -> bytes:
return self._buf
def output_line(self, y: int, bits: Sequence[int]) -> None:
arr = array.array("B", [0] * ((len(bits) + 7) // 8))
if self.reversed:
bits = [1 - b for b in bits]
for i, b in enumerate(bits):
if b:
arr[i // 8] += (128, 64, 32, 16, 8, 4, 2, 1)[i % 8]
self._buf += arr.tobytes()
def ccittfaxdecode(data: bytes, params: Dict[str, object]) -> bytes:
K = params.get("K")
if K == -1:
cols = cast(int, params.get("Columns"))
bytealign = cast(bool, params.get("EncodedByteAlign"))
reversed = cast(bool, params.get("BlackIs1"))
parser = CCITTFaxDecoder(cols, bytealign=bytealign, reversed=reversed)
else:
raise PDFValueError(K)
parser.feedbytes(data)
return parser.close()
# test
def main(argv: List[str]) -> None:
if not argv[1:]:
import unittest
unittest.main()
return
class Parser(CCITTG4Parser):
def __init__(self, width: int, bytealign: bool = False) -> None:
import pygame # type: ignore[import]
CCITTG4Parser.__init__(self, width, bytealign=bytealign)
self.img = pygame.Surface((self.width, 1000))
def output_line(self, y: int, bits: Sequence[int]) -> None:
for x, b in enumerate(bits):
if b:
self.img.set_at((x, y), (255, 255, 255))
else:
self.img.set_at((x, y), (0, 0, 0))
def close(self) -> None:
import pygame
pygame.image.save(self.img, "out.bmp")
for path in argv[1:]:
fp = open(path, "rb")
(_, _, k, w, h, _) = path.split(".")
parser = Parser(int(w))
parser.feedbytes(fp.read())
parser.close()
fp.close()

Some files were not shown because too many files have changed in this diff Show More