ImageFile.py 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938
  1. #
  2. # The Python Imaging Library.
  3. # $Id$
  4. #
  5. # base class for image file handlers
  6. #
  7. # history:
  8. # 1995-09-09 fl Created
  9. # 1996-03-11 fl Fixed load mechanism.
  10. # 1996-04-15 fl Added pcx/xbm decoders.
  11. # 1996-04-30 fl Added encoders.
  12. # 1996-12-14 fl Added load helpers
  13. # 1997-01-11 fl Use encode_to_file where possible
  14. # 1997-08-27 fl Flush output in _save
  15. # 1998-03-05 fl Use memory mapping for some modes
  16. # 1999-02-04 fl Use memory mapping also for "I;16" and "I;16B"
  17. # 1999-05-31 fl Added image parser
  18. # 2000-10-12 fl Set readonly flag on memory-mapped images
  19. # 2002-03-20 fl Use better messages for common decoder errors
  20. # 2003-04-21 fl Fall back on mmap/map_buffer if map is not available
  21. # 2003-10-30 fl Added StubImageFile class
  22. # 2004-02-25 fl Made incremental parser more robust
  23. #
  24. # Copyright (c) 1997-2004 by Secret Labs AB
  25. # Copyright (c) 1995-2004 by Fredrik Lundh
  26. #
  27. # See the README file for information on usage and redistribution.
  28. #
  29. from __future__ import annotations
  30. import abc
  31. import io
  32. import itertools
  33. import logging
  34. import os
  35. import struct
  36. from typing import IO, Any, NamedTuple, cast
  37. from . import ExifTags, Image
  38. from ._util import DeferredError, is_path
  39. TYPE_CHECKING = False
  40. if TYPE_CHECKING:
  41. from ._typing import StrOrBytesPath
  42. logger = logging.getLogger(__name__)
  43. MAXBLOCK = 65536
  44. """
  45. By default, Pillow processes image data in blocks. This helps to prevent excessive use
  46. of resources. Codecs may disable this behaviour with ``_pulls_fd`` or ``_pushes_fd``.
  47. When reading an image, this is the number of bytes to read at once.
  48. When writing an image, this is the number of bytes to write at once.
  49. If the image width times 4 is greater, then that will be used instead.
  50. Plugins may also set a greater number.
  51. User code may set this to another number.
  52. """
  53. SAFEBLOCK = 1024 * 1024
  54. LOAD_TRUNCATED_IMAGES = False
  55. """Whether or not to load truncated image files. User code may change this."""
  56. ERRORS = {
  57. -1: "image buffer overrun error",
  58. -2: "decoding error",
  59. -3: "unknown error",
  60. -8: "bad configuration",
  61. -9: "out of memory error",
  62. }
  63. """
  64. Dict of known error codes returned from :meth:`.PyDecoder.decode`,
  65. :meth:`.PyEncoder.encode` :meth:`.PyEncoder.encode_to_pyfd` and
  66. :meth:`.PyEncoder.encode_to_file`.
  67. """
  68. #
  69. # --------------------------------------------------------------------
  70. # Helpers
  71. def _get_oserror(error: int, *, encoder: bool) -> OSError:
  72. try:
  73. msg = Image.core.getcodecstatus(error)
  74. except AttributeError:
  75. msg = ERRORS.get(error)
  76. if not msg:
  77. msg = f"{'encoder' if encoder else 'decoder'} error {error}"
  78. msg += f" when {'writing' if encoder else 'reading'} image file"
  79. return OSError(msg)
  80. def _tilesort(t: _Tile) -> int:
  81. # sort on offset
  82. return t[2]
  83. class _Tile(NamedTuple):
  84. codec_name: str
  85. extents: tuple[int, int, int, int] | None
  86. offset: int = 0
  87. args: tuple[Any, ...] | str | None = None
  88. #
  89. # --------------------------------------------------------------------
  90. # ImageFile base class
  91. class ImageFile(Image.Image):
  92. """Base class for image file format handlers."""
  93. def __init__(
  94. self, fp: StrOrBytesPath | IO[bytes], filename: str | bytes | None = None
  95. ) -> None:
  96. super().__init__()
  97. self._min_frame = 0
  98. self.custom_mimetype: str | None = None
  99. self.tile: list[_Tile] = []
  100. """ A list of tile descriptors """
  101. self.readonly = 1 # until we know better
  102. self.decoderconfig: tuple[Any, ...] = ()
  103. self.decodermaxblock = MAXBLOCK
  104. self.fp: IO[bytes] | None
  105. self._fp: IO[bytes] | DeferredError
  106. if is_path(fp):
  107. # filename
  108. self.fp = open(fp, "rb")
  109. self.filename = os.fspath(fp)
  110. self._exclusive_fp = True
  111. else:
  112. # stream
  113. self.fp = cast(IO[bytes], fp)
  114. self.filename = filename if filename is not None else ""
  115. # can be overridden
  116. self._exclusive_fp = False
  117. try:
  118. try:
  119. self._open()
  120. except (
  121. IndexError, # end of data
  122. TypeError, # end of data (ord)
  123. KeyError, # unsupported mode
  124. EOFError, # got header but not the first frame
  125. struct.error,
  126. ) as v:
  127. raise SyntaxError(v) from v
  128. if not self.mode or self.size[0] <= 0 or self.size[1] <= 0:
  129. msg = "not identified by this driver"
  130. raise SyntaxError(msg)
  131. except BaseException:
  132. # close the file only if we have opened it this constructor
  133. if self._exclusive_fp:
  134. self.fp.close()
  135. raise
  136. def _open(self) -> None:
  137. pass
  138. # Context manager support
  139. def __enter__(self) -> ImageFile:
  140. return self
  141. def _close_fp(self) -> None:
  142. if getattr(self, "_fp", False) and not isinstance(self._fp, DeferredError):
  143. if self._fp != self.fp:
  144. self._fp.close()
  145. self._fp = DeferredError(ValueError("Operation on closed image"))
  146. if self.fp:
  147. self.fp.close()
  148. def __exit__(self, *args: object) -> None:
  149. if getattr(self, "_exclusive_fp", False):
  150. self._close_fp()
  151. self.fp = None
  152. def close(self) -> None:
  153. """
  154. Closes the file pointer, if possible.
  155. This operation will destroy the image core and release its memory.
  156. The image data will be unusable afterward.
  157. This function is required to close images that have multiple frames or
  158. have not had their file read and closed by the
  159. :py:meth:`~PIL.Image.Image.load` method. See :ref:`file-handling` for
  160. more information.
  161. """
  162. try:
  163. self._close_fp()
  164. self.fp = None
  165. except Exception as msg:
  166. logger.debug("Error closing: %s", msg)
  167. super().close()
  168. def get_child_images(self) -> list[ImageFile]:
  169. child_images = []
  170. exif = self.getexif()
  171. ifds = []
  172. if ExifTags.Base.SubIFDs in exif:
  173. subifd_offsets = exif[ExifTags.Base.SubIFDs]
  174. if subifd_offsets:
  175. if not isinstance(subifd_offsets, tuple):
  176. subifd_offsets = (subifd_offsets,)
  177. for subifd_offset in subifd_offsets:
  178. ifds.append((exif._get_ifd_dict(subifd_offset), subifd_offset))
  179. ifd1 = exif.get_ifd(ExifTags.IFD.IFD1)
  180. if ifd1 and ifd1.get(ExifTags.Base.JpegIFOffset):
  181. assert exif._info is not None
  182. ifds.append((ifd1, exif._info.next))
  183. offset = None
  184. for ifd, ifd_offset in ifds:
  185. assert self.fp is not None
  186. current_offset = self.fp.tell()
  187. if offset is None:
  188. offset = current_offset
  189. fp = self.fp
  190. if ifd is not None:
  191. thumbnail_offset = ifd.get(ExifTags.Base.JpegIFOffset)
  192. if thumbnail_offset is not None:
  193. thumbnail_offset += getattr(self, "_exif_offset", 0)
  194. self.fp.seek(thumbnail_offset)
  195. length = ifd.get(ExifTags.Base.JpegIFByteCount)
  196. assert isinstance(length, int)
  197. data = self.fp.read(length)
  198. fp = io.BytesIO(data)
  199. with Image.open(fp) as im:
  200. from . import TiffImagePlugin
  201. if thumbnail_offset is None and isinstance(
  202. im, TiffImagePlugin.TiffImageFile
  203. ):
  204. im._frame_pos = [ifd_offset]
  205. im._seek(0)
  206. im.load()
  207. child_images.append(im)
  208. if offset is not None:
  209. assert self.fp is not None
  210. self.fp.seek(offset)
  211. return child_images
  212. def get_format_mimetype(self) -> str | None:
  213. if self.custom_mimetype:
  214. return self.custom_mimetype
  215. if self.format is not None:
  216. return Image.MIME.get(self.format.upper())
  217. return None
  218. def __getstate__(self) -> list[Any]:
  219. return super().__getstate__() + [self.filename]
  220. def __setstate__(self, state: list[Any]) -> None:
  221. self.tile = []
  222. if len(state) > 5:
  223. self.filename = state[5]
  224. super().__setstate__(state)
  225. def verify(self) -> None:
  226. """Check file integrity"""
  227. # raise exception if something's wrong. must be called
  228. # directly after open, and closes file when finished.
  229. if self._exclusive_fp and self.fp:
  230. self.fp.close()
  231. self.fp = None
  232. def load(self) -> Image.core.PixelAccess | None:
  233. """Load image data based on tile list"""
  234. if not self.tile and self._im is None:
  235. msg = "cannot load this image"
  236. raise OSError(msg)
  237. pixel = Image.Image.load(self)
  238. if not self.tile:
  239. return pixel
  240. self.map: mmap.mmap | None = None
  241. use_mmap = self.filename and len(self.tile) == 1
  242. assert self.fp is not None
  243. readonly = 0
  244. # look for read/seek overrides
  245. if hasattr(self, "load_read"):
  246. read = self.load_read
  247. # don't use mmap if there are custom read/seek functions
  248. use_mmap = False
  249. else:
  250. read = self.fp.read
  251. if hasattr(self, "load_seek"):
  252. seek = self.load_seek
  253. use_mmap = False
  254. else:
  255. seek = self.fp.seek
  256. if use_mmap:
  257. # try memory mapping
  258. decoder_name, extents, offset, args = self.tile[0]
  259. if isinstance(args, str):
  260. args = (args, 0, 1)
  261. if (
  262. decoder_name == "raw"
  263. and isinstance(args, tuple)
  264. and len(args) >= 3
  265. and args[0] == self.mode
  266. and args[0] in Image._MAPMODES
  267. ):
  268. if offset < 0:
  269. msg = "Tile offset cannot be negative"
  270. raise ValueError(msg)
  271. try:
  272. # use mmap, if possible
  273. import mmap
  274. with open(self.filename) as fp:
  275. self.map = mmap.mmap(fp.fileno(), 0, access=mmap.ACCESS_READ)
  276. if offset + self.size[1] * args[1] > self.map.size():
  277. msg = "buffer is not large enough"
  278. raise OSError(msg)
  279. self.im = Image.core.map_buffer(
  280. self.map, self.size, decoder_name, offset, args
  281. )
  282. readonly = 1
  283. # After trashing self.im,
  284. # we might need to reload the palette data.
  285. if self.palette:
  286. self.palette.dirty = 1
  287. except (AttributeError, OSError, ImportError):
  288. self.map = None
  289. self.load_prepare()
  290. err_code = -3 # initialize to unknown error
  291. if not self.map:
  292. # sort tiles in file order
  293. self.tile.sort(key=_tilesort)
  294. # FIXME: This is a hack to handle TIFF's JpegTables tag.
  295. prefix = getattr(self, "tile_prefix", b"")
  296. # Remove consecutive duplicates that only differ by their offset
  297. self.tile = [
  298. list(tiles)[-1]
  299. for _, tiles in itertools.groupby(
  300. self.tile, lambda tile: (tile[0], tile[1], tile[3])
  301. )
  302. ]
  303. for i, (decoder_name, extents, offset, args) in enumerate(self.tile):
  304. seek(offset)
  305. decoder = Image._getdecoder(
  306. self.mode, decoder_name, args, self.decoderconfig
  307. )
  308. try:
  309. decoder.setimage(self.im, extents)
  310. if decoder.pulls_fd:
  311. decoder.setfd(self.fp)
  312. err_code = decoder.decode(b"")[1]
  313. else:
  314. b = prefix
  315. while True:
  316. read_bytes = self.decodermaxblock
  317. if i + 1 < len(self.tile):
  318. next_offset = self.tile[i + 1].offset
  319. if next_offset > offset:
  320. read_bytes = next_offset - offset
  321. try:
  322. s = read(read_bytes)
  323. except (IndexError, struct.error) as e:
  324. # truncated png/gif
  325. if LOAD_TRUNCATED_IMAGES:
  326. break
  327. else:
  328. msg = "image file is truncated"
  329. raise OSError(msg) from e
  330. if not s: # truncated jpeg
  331. if LOAD_TRUNCATED_IMAGES:
  332. break
  333. else:
  334. msg = (
  335. "image file is truncated "
  336. f"({len(b)} bytes not processed)"
  337. )
  338. raise OSError(msg)
  339. b = b + s
  340. n, err_code = decoder.decode(b)
  341. if n < 0:
  342. break
  343. b = b[n:]
  344. finally:
  345. # Need to cleanup here to prevent leaks
  346. decoder.cleanup()
  347. self.tile = []
  348. self.readonly = readonly
  349. self.load_end()
  350. if self._exclusive_fp and self._close_exclusive_fp_after_loading:
  351. self.fp.close()
  352. self.fp = None
  353. if not self.map and not LOAD_TRUNCATED_IMAGES and err_code < 0:
  354. # still raised if decoder fails to return anything
  355. raise _get_oserror(err_code, encoder=False)
  356. return Image.Image.load(self)
  357. def load_prepare(self) -> None:
  358. # create image memory if necessary
  359. if self._im is None:
  360. self.im = Image.core.new(self.mode, self.size)
  361. # create palette (optional)
  362. if self.mode == "P":
  363. Image.Image.load(self)
  364. def load_end(self) -> None:
  365. # may be overridden
  366. pass
  367. # may be defined for contained formats
  368. # def load_seek(self, pos: int) -> None:
  369. # pass
  370. # may be defined for blocked formats (e.g. PNG)
  371. # def load_read(self, read_bytes: int) -> bytes:
  372. # pass
  373. def _seek_check(self, frame: int) -> bool:
  374. if (
  375. frame < self._min_frame
  376. # Only check upper limit on frames if additional seek operations
  377. # are not required to do so
  378. or (
  379. not (hasattr(self, "_n_frames") and self._n_frames is None)
  380. and frame >= getattr(self, "n_frames") + self._min_frame
  381. )
  382. ):
  383. msg = "attempt to seek outside sequence"
  384. raise EOFError(msg)
  385. return self.tell() != frame
  386. class StubHandler(abc.ABC):
  387. def open(self, im: StubImageFile) -> None:
  388. pass
  389. @abc.abstractmethod
  390. def load(self, im: StubImageFile) -> Image.Image:
  391. pass
  392. class StubImageFile(ImageFile, metaclass=abc.ABCMeta):
  393. """
  394. Base class for stub image loaders.
  395. A stub loader is an image loader that can identify files of a
  396. certain format, but relies on external code to load the file.
  397. """
  398. @abc.abstractmethod
  399. def _open(self) -> None:
  400. pass
  401. def load(self) -> Image.core.PixelAccess | None:
  402. loader = self._load()
  403. if loader is None:
  404. msg = f"cannot find loader for this {self.format} file"
  405. raise OSError(msg)
  406. image = loader.load(self)
  407. assert image is not None
  408. # become the other object (!)
  409. self.__class__ = image.__class__ # type: ignore[assignment]
  410. self.__dict__ = image.__dict__
  411. return image.load()
  412. @abc.abstractmethod
  413. def _load(self) -> StubHandler | None:
  414. """(Hook) Find actual image loader."""
  415. pass
  416. class Parser:
  417. """
  418. Incremental image parser. This class implements the standard
  419. feed/close consumer interface.
  420. """
  421. incremental = None
  422. image: Image.Image | None = None
  423. data: bytes | None = None
  424. decoder: Image.core.ImagingDecoder | PyDecoder | None = None
  425. offset = 0
  426. finished = 0
  427. def reset(self) -> None:
  428. """
  429. (Consumer) Reset the parser. Note that you can only call this
  430. method immediately after you've created a parser; parser
  431. instances cannot be reused.
  432. """
  433. assert self.data is None, "cannot reuse parsers"
  434. def feed(self, data: bytes) -> None:
  435. """
  436. (Consumer) Feed data to the parser.
  437. :param data: A string buffer.
  438. :exception OSError: If the parser failed to parse the image file.
  439. """
  440. # collect data
  441. if self.finished:
  442. return
  443. if self.data is None:
  444. self.data = data
  445. else:
  446. self.data = self.data + data
  447. # parse what we have
  448. if self.decoder:
  449. if self.offset > 0:
  450. # skip header
  451. skip = min(len(self.data), self.offset)
  452. self.data = self.data[skip:]
  453. self.offset = self.offset - skip
  454. if self.offset > 0 or not self.data:
  455. return
  456. n, e = self.decoder.decode(self.data)
  457. if n < 0:
  458. # end of stream
  459. self.data = None
  460. self.finished = 1
  461. if e < 0:
  462. # decoding error
  463. self.image = None
  464. raise _get_oserror(e, encoder=False)
  465. else:
  466. # end of image
  467. return
  468. self.data = self.data[n:]
  469. elif self.image:
  470. # if we end up here with no decoder, this file cannot
  471. # be incrementally parsed. wait until we've gotten all
  472. # available data
  473. pass
  474. else:
  475. # attempt to open this file
  476. try:
  477. with io.BytesIO(self.data) as fp:
  478. im = Image.open(fp)
  479. except OSError:
  480. pass # not enough data
  481. else:
  482. flag = hasattr(im, "load_seek") or hasattr(im, "load_read")
  483. if flag or len(im.tile) != 1:
  484. # custom load code, or multiple tiles
  485. self.decode = None
  486. else:
  487. # initialize decoder
  488. im.load_prepare()
  489. d, e, o, a = im.tile[0]
  490. im.tile = []
  491. self.decoder = Image._getdecoder(im.mode, d, a, im.decoderconfig)
  492. self.decoder.setimage(im.im, e)
  493. # calculate decoder offset
  494. self.offset = o
  495. if self.offset <= len(self.data):
  496. self.data = self.data[self.offset :]
  497. self.offset = 0
  498. self.image = im
  499. def __enter__(self) -> Parser:
  500. return self
  501. def __exit__(self, *args: object) -> None:
  502. self.close()
  503. def close(self) -> Image.Image:
  504. """
  505. (Consumer) Close the stream.
  506. :returns: An image object.
  507. :exception OSError: If the parser failed to parse the image file either
  508. because it cannot be identified or cannot be
  509. decoded.
  510. """
  511. # finish decoding
  512. if self.decoder:
  513. # get rid of what's left in the buffers
  514. self.feed(b"")
  515. self.data = self.decoder = None
  516. if not self.finished:
  517. msg = "image was incomplete"
  518. raise OSError(msg)
  519. if not self.image:
  520. msg = "cannot parse this image"
  521. raise OSError(msg)
  522. if self.data:
  523. # incremental parsing not possible; reopen the file
  524. # not that we have all data
  525. with io.BytesIO(self.data) as fp:
  526. try:
  527. self.image = Image.open(fp)
  528. finally:
  529. self.image.load()
  530. return self.image
  531. # --------------------------------------------------------------------
  532. def _save(im: Image.Image, fp: IO[bytes], tile: list[_Tile], bufsize: int = 0) -> None:
  533. """Helper to save image based on tile list
  534. :param im: Image object.
  535. :param fp: File object.
  536. :param tile: Tile list.
  537. :param bufsize: Optional buffer size
  538. """
  539. im.load()
  540. if not hasattr(im, "encoderconfig"):
  541. im.encoderconfig = ()
  542. tile.sort(key=_tilesort)
  543. # FIXME: make MAXBLOCK a configuration parameter
  544. # It would be great if we could have the encoder specify what it needs
  545. # But, it would need at least the image size in most cases. RawEncode is
  546. # a tricky case.
  547. bufsize = max(MAXBLOCK, bufsize, im.size[0] * 4) # see RawEncode.c
  548. try:
  549. fh = fp.fileno()
  550. fp.flush()
  551. _encode_tile(im, fp, tile, bufsize, fh)
  552. except (AttributeError, io.UnsupportedOperation) as exc:
  553. _encode_tile(im, fp, tile, bufsize, None, exc)
  554. if hasattr(fp, "flush"):
  555. fp.flush()
  556. def _encode_tile(
  557. im: Image.Image,
  558. fp: IO[bytes],
  559. tile: list[_Tile],
  560. bufsize: int,
  561. fh: int | None,
  562. exc: BaseException | None = None,
  563. ) -> None:
  564. for encoder_name, extents, offset, args in tile:
  565. if offset > 0:
  566. fp.seek(offset)
  567. encoder = Image._getencoder(im.mode, encoder_name, args, im.encoderconfig)
  568. try:
  569. encoder.setimage(im.im, extents)
  570. if encoder.pushes_fd:
  571. encoder.setfd(fp)
  572. errcode = encoder.encode_to_pyfd()[1]
  573. else:
  574. if exc:
  575. # compress to Python file-compatible object
  576. while True:
  577. errcode, data = encoder.encode(bufsize)[1:]
  578. fp.write(data)
  579. if errcode:
  580. break
  581. else:
  582. # slight speedup: compress to real file object
  583. assert fh is not None
  584. errcode = encoder.encode_to_file(fh, bufsize)
  585. if errcode < 0:
  586. raise _get_oserror(errcode, encoder=True) from exc
  587. finally:
  588. encoder.cleanup()
  589. def _safe_read(fp: IO[bytes], size: int) -> bytes:
  590. """
  591. Reads large blocks in a safe way. Unlike fp.read(n), this function
  592. doesn't trust the user. If the requested size is larger than
  593. SAFEBLOCK, the file is read block by block.
  594. :param fp: File handle. Must implement a <b>read</b> method.
  595. :param size: Number of bytes to read.
  596. :returns: A string containing <i>size</i> bytes of data.
  597. Raises an OSError if the file is truncated and the read cannot be completed
  598. """
  599. if size <= 0:
  600. return b""
  601. if size <= SAFEBLOCK:
  602. data = fp.read(size)
  603. if len(data) < size:
  604. msg = "Truncated File Read"
  605. raise OSError(msg)
  606. return data
  607. blocks: list[bytes] = []
  608. remaining_size = size
  609. while remaining_size > 0:
  610. block = fp.read(min(remaining_size, SAFEBLOCK))
  611. if not block:
  612. break
  613. blocks.append(block)
  614. remaining_size -= len(block)
  615. if sum(len(block) for block in blocks) < size:
  616. msg = "Truncated File Read"
  617. raise OSError(msg)
  618. return b"".join(blocks)
  619. class PyCodecState:
  620. def __init__(self) -> None:
  621. self.xsize = 0
  622. self.ysize = 0
  623. self.xoff = 0
  624. self.yoff = 0
  625. def extents(self) -> tuple[int, int, int, int]:
  626. return self.xoff, self.yoff, self.xoff + self.xsize, self.yoff + self.ysize
  627. class PyCodec:
  628. fd: IO[bytes] | None
  629. def __init__(self, mode: str, *args: Any) -> None:
  630. self.im: Image.core.ImagingCore | None = None
  631. self.state = PyCodecState()
  632. self.fd = None
  633. self.mode = mode
  634. self.init(args)
  635. def init(self, args: tuple[Any, ...]) -> None:
  636. """
  637. Override to perform codec specific initialization
  638. :param args: Tuple of arg items from the tile entry
  639. :returns: None
  640. """
  641. self.args = args
  642. def cleanup(self) -> None:
  643. """
  644. Override to perform codec specific cleanup
  645. :returns: None
  646. """
  647. pass
  648. def setfd(self, fd: IO[bytes]) -> None:
  649. """
  650. Called from ImageFile to set the Python file-like object
  651. :param fd: A Python file-like object
  652. :returns: None
  653. """
  654. self.fd = fd
  655. def setimage(
  656. self,
  657. im: Image.core.ImagingCore,
  658. extents: tuple[int, int, int, int] | None = None,
  659. ) -> None:
  660. """
  661. Called from ImageFile to set the core output image for the codec
  662. :param im: A core image object
  663. :param extents: a 4 tuple of (x0, y0, x1, y1) defining the rectangle
  664. for this tile
  665. :returns: None
  666. """
  667. # following c code
  668. self.im = im
  669. if extents:
  670. (x0, y0, x1, y1) = extents
  671. else:
  672. (x0, y0, x1, y1) = (0, 0, 0, 0)
  673. if x0 == 0 and x1 == 0:
  674. self.state.xsize, self.state.ysize = self.im.size
  675. else:
  676. self.state.xoff = x0
  677. self.state.yoff = y0
  678. self.state.xsize = x1 - x0
  679. self.state.ysize = y1 - y0
  680. if self.state.xsize <= 0 or self.state.ysize <= 0:
  681. msg = "Size cannot be negative"
  682. raise ValueError(msg)
  683. if (
  684. self.state.xsize + self.state.xoff > self.im.size[0]
  685. or self.state.ysize + self.state.yoff > self.im.size[1]
  686. ):
  687. msg = "Tile cannot extend outside image"
  688. raise ValueError(msg)
  689. class PyDecoder(PyCodec):
  690. """
  691. Python implementation of a format decoder. Override this class and
  692. add the decoding logic in the :meth:`decode` method.
  693. See :ref:`Writing Your Own File Codec in Python<file-codecs-py>`
  694. """
  695. _pulls_fd = False
  696. @property
  697. def pulls_fd(self) -> bool:
  698. return self._pulls_fd
  699. def decode(self, buffer: bytes | Image.SupportsArrayInterface) -> tuple[int, int]:
  700. """
  701. Override to perform the decoding process.
  702. :param buffer: A bytes object with the data to be decoded.
  703. :returns: A tuple of ``(bytes consumed, errcode)``.
  704. If finished with decoding return -1 for the bytes consumed.
  705. Err codes are from :data:`.ImageFile.ERRORS`.
  706. """
  707. msg = "unavailable in base decoder"
  708. raise NotImplementedError(msg)
  709. def set_as_raw(
  710. self, data: bytes, rawmode: str | None = None, extra: tuple[Any, ...] = ()
  711. ) -> None:
  712. """
  713. Convenience method to set the internal image from a stream of raw data
  714. :param data: Bytes to be set
  715. :param rawmode: The rawmode to be used for the decoder.
  716. If not specified, it will default to the mode of the image
  717. :param extra: Extra arguments for the decoder.
  718. :returns: None
  719. """
  720. if not rawmode:
  721. rawmode = self.mode
  722. d = Image._getdecoder(self.mode, "raw", rawmode, extra)
  723. assert self.im is not None
  724. d.setimage(self.im, self.state.extents())
  725. s = d.decode(data)
  726. if s[0] >= 0:
  727. msg = "not enough image data"
  728. raise ValueError(msg)
  729. if s[1] != 0:
  730. msg = "cannot decode image data"
  731. raise ValueError(msg)
  732. class PyEncoder(PyCodec):
  733. """
  734. Python implementation of a format encoder. Override this class and
  735. add the decoding logic in the :meth:`encode` method.
  736. See :ref:`Writing Your Own File Codec in Python<file-codecs-py>`
  737. """
  738. _pushes_fd = False
  739. @property
  740. def pushes_fd(self) -> bool:
  741. return self._pushes_fd
  742. def encode(self, bufsize: int) -> tuple[int, int, bytes]:
  743. """
  744. Override to perform the encoding process.
  745. :param bufsize: Buffer size.
  746. :returns: A tuple of ``(bytes encoded, errcode, bytes)``.
  747. If finished with encoding return 1 for the error code.
  748. Err codes are from :data:`.ImageFile.ERRORS`.
  749. """
  750. msg = "unavailable in base encoder"
  751. raise NotImplementedError(msg)
  752. def encode_to_pyfd(self) -> tuple[int, int]:
  753. """
  754. If ``pushes_fd`` is ``True``, then this method will be used,
  755. and ``encode()`` will only be called once.
  756. :returns: A tuple of ``(bytes consumed, errcode)``.
  757. Err codes are from :data:`.ImageFile.ERRORS`.
  758. """
  759. if not self.pushes_fd:
  760. return 0, -8 # bad configuration
  761. bytes_consumed, errcode, data = self.encode(0)
  762. if data:
  763. assert self.fd is not None
  764. self.fd.write(data)
  765. return bytes_consumed, errcode
  766. def encode_to_file(self, fh: int, bufsize: int) -> int:
  767. """
  768. :param fh: File handle.
  769. :param bufsize: Buffer size.
  770. :returns: If finished successfully, return 0.
  771. Otherwise, return an error code. Err codes are from
  772. :data:`.ImageFile.ERRORS`.
  773. """
  774. errcode = 0
  775. while errcode == 0:
  776. status, errcode, buf = self.encode(bufsize)
  777. if status > 0:
  778. os.write(fh, buf[status:])
  779. return errcode