Files
removeads/src/tscut/tools/ppm.py
T

84 lines
2.8 KiB
Python

# SPDX-License-Identifier: GPL-2.0-or-later
#
# Copyright (C) 2026 Frédéric Tronel
import logging
import re
from math import ceil, log
from os import write
from typing import IO
from io import BytesIO
from typeguard import typechecked
@typechecked
def dump_ppm(pictures: bytes, prefix: str, temporaries: list[IO[bytes]]) -> None:
"""
Dump PPM pictures from a bytes buffer to files.
This function takes a bytes buffer containing PPM pictures, a prefix for the output file names,
and a list of temporary files.
It extracts each PPM picture from the buffer, checks its validity, and writes it to a file.
The output files are named according to the prefix and a zero-padded three-digit number.
Args:
pictures (bytes): The bytes buffer containing the PPM pictures.
prefix (str): The prefix for the output file names.
temporaries (list[IO[bytes]]): A list of temporary files that will be used to store
the output files.
Returns:
None
Raises:
None, but logs errors if:
- the PPM picture is not valid (e.g. wrong magic number, dimensions, or color encoding)
- an I/O error occurs while creating or writing to an output file
"""
logger = logging.getLogger(__name__)
# "P6\nWIDTH HEIGHT\n255\n"
pos = 0
picture = 0
logger.debug('Dumping %d pictures: %s', len(pictures),prefix)
while pos<len(pictures):
filename = f'{prefix}-{picture:03d}.ppm'
header = BytesIO(pictures[pos:])
magic = header.readline().decode('utf8')
dimensions = header.readline().decode('utf8')
max_value = int(header.readline().decode('utf8'))
if magic == 'P6\n':
pattern = re.compile('^(?P<width>[0-9]+) (?P<height>[0-9]+)\n$')
m = pattern.match(dimensions)
if m is not None:
width = int(m['width'])
height = int(m['height'])
else:
logger.error('Impossible to parse dimensions of picture')
return
else:
logger.error('Not a PPM picture')
return
if max_value != 255:
logger.error('Not a valid PPM picture. Color are not encoded on byte. Max value: %d',
max_value)
header_len=2+1+ceil(log(width, 10))+1+ceil(log(height, 10))+1+3+1
try:
with open(filename, 'wb') as out:
temporaries.append(out)
outfd = out.fileno()
length=header_len+3*width*height
nb_bytes = 0
while nb_bytes < length:
nb_bytes+=write(outfd, pictures[pos+nb_bytes:pos+length])
pos+=length
picture+=1
except OSError:
logger.error('Impossible to create file: %s', filename)