diff options
Diffstat (limited to 'env/lib/python3.10/site-packages/pikepdf/models/_transcoding.py')
-rw-r--r-- | env/lib/python3.10/site-packages/pikepdf/models/_transcoding.py | 243 |
1 files changed, 0 insertions, 243 deletions
diff --git a/env/lib/python3.10/site-packages/pikepdf/models/_transcoding.py b/env/lib/python3.10/site-packages/pikepdf/models/_transcoding.py deleted file mode 100644 index e54facf..0000000 --- a/env/lib/python3.10/site-packages/pikepdf/models/_transcoding.py +++ /dev/null @@ -1,243 +0,0 @@ -# SPDX-FileCopyrightText: 2022 James R. Barlow -# SPDX-License-Identifier: MPL-2.0 - -from __future__ import annotations - -import struct -from typing import Any, Callable, NamedTuple, Union - -from PIL import Image -from PIL.TiffTags import TAGS_V2 as TIFF_TAGS - -BytesLike = Union[bytes, memoryview] -MutableBytesLike = Union[bytearray, memoryview] - - -def _next_multiple(n: int, k: int) -> int: - """Return the multiple of k that is greater than or equal n. - - >>> _next_multiple(101, 4) - 104 - >>> _next_multiple(100, 4) - 100 - """ - div, mod = divmod(n, k) - if mod > 0: - div += 1 - return div * k - - -def unpack_subbyte_pixels( - packed: BytesLike, size: tuple[int, int], bits: int, scale: int = 0 -) -> tuple[BytesLike, int]: - """Unpack subbyte *bits* pixels into full bytes and rescale. - - When scale is 0, the appropriate scale is calculated. - e.g. for 2-bit, the scale is adjusted so that - 0b00 = 0.00 = 0x00 - 0b01 = 0.33 = 0x55 - 0b10 = 0.66 = 0xaa - 0b11 = 1.00 = 0xff - When scale is 1, no scaling is applied, appropriate when - the bytes are palette indexes. - """ - width, height = size - bits_per_byte = 8 // bits - stride = _next_multiple(width, bits_per_byte) - buffer = bytearray(bits_per_byte * stride * height) - max_read = len(buffer) // bits_per_byte - if scale == 0: - scale = 255 / ((2**bits) - 1) - if bits == 4: - _4bit_inner_loop(packed[:max_read], buffer, scale) - elif bits == 2: - _2bit_inner_loop(packed[:max_read], buffer, scale) - # elif bits == 1: - # _1bit_inner_loop(packed[:max_read], buffer, scale) - else: - raise NotImplementedError(bits) - return memoryview(buffer), stride - - -# def _1bit_inner_loop(in_: BytesLike, out: MutableBytesLike, scale: int) -> None: -# """Unpack 1-bit values to their 8-bit equivalents. - -# Thus *out* must be 8x at long as *in*. -# """ -# for n, val in enumerate(in_): -# out[8 * n + 0] = int((val >> 7) & 0b1) * scale -# out[8 * n + 1] = int((val >> 6) & 0b1) * scale -# out[8 * n + 2] = int((val >> 5) & 0b1) * scale -# out[8 * n + 3] = int((val >> 4) & 0b1) * scale -# out[8 * n + 4] = int((val >> 3) & 0b1) * scale -# out[8 * n + 5] = int((val >> 2) & 0b1) * scale -# out[8 * n + 6] = int((val >> 1) & 0b1) * scale -# out[8 * n + 7] = int((val >> 0) & 0b1) * scale - - -def _2bit_inner_loop(in_: BytesLike, out: MutableBytesLike, scale: int) -> None: - """Unpack 2-bit values to their 8-bit equivalents. - - Thus *out* must be 4x at long as *in*. - """ - for n, val in enumerate(in_): - out[4 * n] = int((val >> 6) * scale) - out[4 * n + 1] = int(((val >> 4) & 0b11) * scale) - out[4 * n + 2] = int(((val >> 2) & 0b11) * scale) - out[4 * n + 3] = int((val & 0b11) * scale) - - -def _4bit_inner_loop(in_: BytesLike, out: MutableBytesLike, scale: int) -> None: - """Unpack 4-bit values to their 8-bit equivalents. - - Thus *out* must be 2x at long as *in*. - """ - for n, val in enumerate(in_): - out[2 * n] = int((val >> 4) * scale) - out[2 * n + 1] = int((val & 0b1111) * scale) - - -def image_from_byte_buffer(buffer: BytesLike, size: tuple[int, int], stride: int): - """Use Pillow to create one-component image from a byte buffer. - - *stride* is the number of bytes per row, and is essential for packed bits - with odd image widths. - """ - ystep = 1 # image is top to bottom in memory - return Image.frombuffer('L', size, buffer, "raw", 'L', stride, ystep) - - -def _make_rgb_palette(gray_palette: bytes) -> bytes: - palette = b'' - for entry in gray_palette: - palette += bytes([entry]) * 3 - return palette - - -def _depalettize_cmyk(buffer: BytesLike, palette: BytesLike): - with memoryview(buffer) as mv: - output = bytearray(4 * len(mv)) - for n, pal_idx in enumerate(mv): - output[4 * n : 4 * (n + 1)] = palette[4 * pal_idx : 4 * (pal_idx + 1)] - return output - - -def image_from_buffer_and_palette( - buffer: BytesLike, - size: tuple[int, int], - stride: int, - base_mode: str, - palette: BytesLike, -) -> Image.Image: - """Construct an image from a byte buffer and apply the palette. - - 1/2/4-bit images must be unpacked (no scaling!) to byte buffers first, such - that every 8-bit integer is an index into the palette. - """ - # Reminder Pillow palette byte order unintentionally changed in 8.3.0 - # https://github.com/python-pillow/Pillow/issues/5595 - # 8.2.0: all aligned by channel (very nonstandard) - # 8.3.0: all channels for one color followed by the next color (e.g. RGBRGBRGB) - - if base_mode == 'RGB': - im = image_from_byte_buffer(buffer, size, stride) - im.putpalette(palette, rawmode=base_mode) - elif base_mode == 'L': - # Pillow does not fully support palettes with rawmode='L'. - # Convert to RGB palette. - gray_palette = _make_rgb_palette(palette) - im = image_from_byte_buffer(buffer, size, stride) - im.putpalette(gray_palette, rawmode='RGB') - elif base_mode == 'CMYK': - # Pillow does not support CMYK with palettes; convert manually - output = _depalettize_cmyk(buffer, palette) - im = Image.frombuffer('CMYK', size, data=output, decoder_name='raw') - else: - raise NotImplementedError(f'palette with {base_mode}') - return im - - -def fix_1bit_palette_image( - im: Image.Image, base_mode: str, palette: BytesLike -) -> Image.Image: - """Apply palettes to 1-bit images.""" - im = im.convert('P') - if base_mode == 'RGB' and len(palette) == 6: - # rgbrgb -> rgb000000...rgb - palette = palette[0:3] + (b'\x00\x00\x00' * (256 - 2)) + palette[3:6] - im.putpalette(palette, rawmode='RGB') - elif base_mode == 'L': - try: - im.putpalette(palette, rawmode='L') - except ValueError as e: - if 'unrecognized raw mode' in str(e): - rgb_palette = _make_rgb_palette(palette) - im.putpalette(rgb_palette, rawmode='RGB') - return im - - -def generate_ccitt_header( - size: tuple[int, int], - data_length: int, - ccitt_group: int, - photometry: int, - icc: bytes, -) -> bytes: - """Generate binary CCITT header for image with given parameters.""" - tiff_header_struct = '<' + '2s' + 'H' + 'L' + 'H' - - tag_keys = {tag.name: key for key, tag in TIFF_TAGS.items()} # type: ignore - ifd_struct = '<HHLL' - - class IFD(NamedTuple): - key: int - typecode: Any - count_: int - data: int | Callable[[], int | None] - - ifds: list[IFD] = [] - - def header_length(ifd_count) -> int: - return ( - struct.calcsize(tiff_header_struct) - + struct.calcsize(ifd_struct) * ifd_count - + 4 - ) - - def add_ifd(tag_name: str, data: int | Callable[[], int | None], count: int = 1): - key = tag_keys[tag_name] - typecode = TIFF_TAGS[key].type # type: ignore - ifds.append(IFD(key, typecode, count, data)) - - image_offset = None - width, height = size - add_ifd('ImageWidth', width) - add_ifd('ImageLength', height) - add_ifd('BitsPerSample', 1) - add_ifd('Compression', ccitt_group) - add_ifd('PhotometricInterpretation', int(photometry)) - add_ifd('StripOffsets', lambda: image_offset) - add_ifd('RowsPerStrip', height) - add_ifd('StripByteCounts', data_length) - - icc_offset = 0 - if icc: - add_ifd('ICCProfile', lambda: icc_offset, count=len(icc)) - - icc_offset = header_length(len(ifds)) - image_offset = icc_offset + len(icc) - - ifd_args = [(arg() if callable(arg) else arg) for ifd in ifds for arg in ifd] - tiff_header = struct.pack( - (tiff_header_struct + ifd_struct[1:] * len(ifds) + 'L'), - b'II', # Byte order indication: Little endian - 42, # Version number (always 42) - 8, # Offset to first IFD - len(ifds), # Number of tags in IFD - *ifd_args, - 0, # Last IFD - ) - - if icc: - tiff_header += icc - return tiff_header |