import os import tempfile from io import BytesIO from typing import * import defusedxml.cElementTree from glymur import jp2box, Jp2k # Replace glymur's ElementTree with a safe one jp2box.ET = defusedxml.cElementTree SL_DEFAULT_ENCODE = { "cratios": (1920.0, 480.0, 120.0, 30.0, 10.0), "irreversible": True, } class BufferedJp2k(Jp2k): """ For manipulating JP2K from within a binary buffer. For many operations glymur expects to be able to re-read from a file based on filename, so this is the least brittle approach. """ def __init__(self, contents: bytes, encode_kwargs: Optional[Dict] = None): if encode_kwargs is None: self.encode_kwargs = SL_DEFAULT_ENCODE.copy() else: self.encode_kwargs = encode_kwargs stream = BytesIO(contents) self.temp_file = tempfile.NamedTemporaryFile(delete=False) stream.seek(0) self.temp_file.write(stream.read()) # Windows NT can't handle two FHs open on a tempfile at once self.temp_file.close() super().__init__(self.temp_file.name) def __del__(self): if self.temp_file is not None: os.remove(self.temp_file.name) self.temp_file = None def _write(self, img_array, verbose=False, **kwargs): # Glymur normally only lets you control encode params when a write happens within # the constructor. Keep around the encode params from the constructor and pass # them to successive write calls. return super()._write(img_array, verbose=False, **self.encode_kwargs, **kwargs) def __bytes__(self): with open(self.temp_file.name, "rb") as f: return f.read()