@ai-ecoverse/py-pillow 12.3.0-2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +30 -0
- package/README.md +4 -0
- package/lib/python3.14/site-packages/PIL/AvifImagePlugin.py +295 -0
- package/lib/python3.14/site-packages/PIL/BdfFontFile.py +124 -0
- package/lib/python3.14/site-packages/PIL/BlpImagePlugin.py +498 -0
- package/lib/python3.14/site-packages/PIL/BmpImagePlugin.py +514 -0
- package/lib/python3.14/site-packages/PIL/BufrStubImagePlugin.py +72 -0
- package/lib/python3.14/site-packages/PIL/ContainerIO.py +173 -0
- package/lib/python3.14/site-packages/PIL/CurImagePlugin.py +103 -0
- package/lib/python3.14/site-packages/PIL/DcxImagePlugin.py +84 -0
- package/lib/python3.14/site-packages/PIL/DdsImagePlugin.py +632 -0
- package/lib/python3.14/site-packages/PIL/EpsImagePlugin.py +484 -0
- package/lib/python3.14/site-packages/PIL/ExifTags.py +384 -0
- package/lib/python3.14/site-packages/PIL/FitsImagePlugin.py +153 -0
- package/lib/python3.14/site-packages/PIL/FliImagePlugin.py +184 -0
- package/lib/python3.14/site-packages/PIL/FontFile.py +160 -0
- package/lib/python3.14/site-packages/PIL/FpxImagePlugin.py +262 -0
- package/lib/python3.14/site-packages/PIL/FtexImagePlugin.py +115 -0
- package/lib/python3.14/site-packages/PIL/GbrImagePlugin.py +103 -0
- package/lib/python3.14/site-packages/PIL/GdImageFile.py +104 -0
- package/lib/python3.14/site-packages/PIL/GifImagePlugin.py +1223 -0
- package/lib/python3.14/site-packages/PIL/GimpGradientFile.py +154 -0
- package/lib/python3.14/site-packages/PIL/GimpPaletteFile.py +75 -0
- package/lib/python3.14/site-packages/PIL/GribStubImagePlugin.py +72 -0
- package/lib/python3.14/site-packages/PIL/Hdf5StubImagePlugin.py +72 -0
- package/lib/python3.14/site-packages/PIL/IcnsImagePlugin.py +401 -0
- package/lib/python3.14/site-packages/PIL/IcoImagePlugin.py +396 -0
- package/lib/python3.14/site-packages/PIL/ImImagePlugin.py +390 -0
- package/lib/python3.14/site-packages/PIL/Image.py +4385 -0
- package/lib/python3.14/site-packages/PIL/ImageChops.py +311 -0
- package/lib/python3.14/site-packages/PIL/ImageCms.py +1078 -0
- package/lib/python3.14/site-packages/PIL/ImageColor.py +320 -0
- package/lib/python3.14/site-packages/PIL/ImageDraw.py +1002 -0
- package/lib/python3.14/site-packages/PIL/ImageDraw2.py +244 -0
- package/lib/python3.14/site-packages/PIL/ImageEnhance.py +113 -0
- package/lib/python3.14/site-packages/PIL/ImageFile.py +938 -0
- package/lib/python3.14/site-packages/PIL/ImageFilter.py +617 -0
- package/lib/python3.14/site-packages/PIL/ImageFont.py +1330 -0
- package/lib/python3.14/site-packages/PIL/ImageGrab.py +234 -0
- package/lib/python3.14/site-packages/PIL/ImageMath.py +314 -0
- package/lib/python3.14/site-packages/PIL/ImageMode.py +85 -0
- package/lib/python3.14/site-packages/PIL/ImageMorph.py +317 -0
- package/lib/python3.14/site-packages/PIL/ImageOps.py +749 -0
- package/lib/python3.14/site-packages/PIL/ImagePalette.py +290 -0
- package/lib/python3.14/site-packages/PIL/ImagePath.py +20 -0
- package/lib/python3.14/site-packages/PIL/ImageQt.py +219 -0
- package/lib/python3.14/site-packages/PIL/ImageSequence.py +88 -0
- package/lib/python3.14/site-packages/PIL/ImageShow.py +367 -0
- package/lib/python3.14/site-packages/PIL/ImageStat.py +167 -0
- package/lib/python3.14/site-packages/PIL/ImageText.py +509 -0
- package/lib/python3.14/site-packages/PIL/ImageTk.py +266 -0
- package/lib/python3.14/site-packages/PIL/ImageTransform.py +136 -0
- package/lib/python3.14/site-packages/PIL/ImageWin.py +247 -0
- package/lib/python3.14/site-packages/PIL/ImtImagePlugin.py +103 -0
- package/lib/python3.14/site-packages/PIL/IptcImagePlugin.py +226 -0
- package/lib/python3.14/site-packages/PIL/Jpeg2KImagePlugin.py +466 -0
- package/lib/python3.14/site-packages/PIL/JpegImagePlugin.py +889 -0
- package/lib/python3.14/site-packages/PIL/JpegPresets.py +242 -0
- package/lib/python3.14/site-packages/PIL/McIdasImagePlugin.py +78 -0
- package/lib/python3.14/site-packages/PIL/MicImagePlugin.py +103 -0
- package/lib/python3.14/site-packages/PIL/MpegImagePlugin.py +84 -0
- package/lib/python3.14/site-packages/PIL/MpoImagePlugin.py +203 -0
- package/lib/python3.14/site-packages/PIL/MspImagePlugin.py +200 -0
- package/lib/python3.14/site-packages/PIL/PSDraw.py +238 -0
- package/lib/python3.14/site-packages/PIL/PaletteFile.py +54 -0
- package/lib/python3.14/site-packages/PIL/PalmImagePlugin.py +217 -0
- package/lib/python3.14/site-packages/PIL/PcdImagePlugin.py +68 -0
- package/lib/python3.14/site-packages/PIL/PcfFontFile.py +256 -0
- package/lib/python3.14/site-packages/PIL/PcxImagePlugin.py +232 -0
- package/lib/python3.14/site-packages/PIL/PdfImagePlugin.py +315 -0
- package/lib/python3.14/site-packages/PIL/PdfParser.py +1092 -0
- package/lib/python3.14/site-packages/PIL/PixarImagePlugin.py +72 -0
- package/lib/python3.14/site-packages/PIL/PngImagePlugin.py +1578 -0
- package/lib/python3.14/site-packages/PIL/PpmImagePlugin.py +375 -0
- package/lib/python3.14/site-packages/PIL/PsdImagePlugin.py +339 -0
- package/lib/python3.14/site-packages/PIL/QoiImagePlugin.py +235 -0
- package/lib/python3.14/site-packages/PIL/SgiImagePlugin.py +231 -0
- package/lib/python3.14/site-packages/PIL/SpiderImagePlugin.py +332 -0
- package/lib/python3.14/site-packages/PIL/SunImagePlugin.py +145 -0
- package/lib/python3.14/site-packages/PIL/TarIO.py +61 -0
- package/lib/python3.14/site-packages/PIL/TgaImagePlugin.py +284 -0
- package/lib/python3.14/site-packages/PIL/TiffImagePlugin.py +2354 -0
- package/lib/python3.14/site-packages/PIL/TiffTags.py +566 -0
- package/lib/python3.14/site-packages/PIL/WalImageFile.py +129 -0
- package/lib/python3.14/site-packages/PIL/WebPImagePlugin.py +322 -0
- package/lib/python3.14/site-packages/PIL/WmfImagePlugin.py +183 -0
- package/lib/python3.14/site-packages/PIL/XVThumbImagePlugin.py +83 -0
- package/lib/python3.14/site-packages/PIL/XbmImagePlugin.py +98 -0
- package/lib/python3.14/site-packages/PIL/XpmImagePlugin.py +157 -0
- package/lib/python3.14/site-packages/PIL/__init__.py +87 -0
- package/lib/python3.14/site-packages/PIL/__main__.py +7 -0
- package/lib/python3.14/site-packages/PIL/__pycache__/AvifImagePlugin.cpython-314.pyc +0 -0
- package/lib/python3.14/site-packages/PIL/__pycache__/BdfFontFile.cpython-314.pyc +0 -0
- package/lib/python3.14/site-packages/PIL/__pycache__/BlpImagePlugin.cpython-314.pyc +0 -0
- package/lib/python3.14/site-packages/PIL/__pycache__/BmpImagePlugin.cpython-314.pyc +0 -0
- package/lib/python3.14/site-packages/PIL/__pycache__/BufrStubImagePlugin.cpython-314.pyc +0 -0
- package/lib/python3.14/site-packages/PIL/__pycache__/ContainerIO.cpython-314.pyc +0 -0
- package/lib/python3.14/site-packages/PIL/__pycache__/CurImagePlugin.cpython-314.pyc +0 -0
- package/lib/python3.14/site-packages/PIL/__pycache__/DcxImagePlugin.cpython-314.pyc +0 -0
- package/lib/python3.14/site-packages/PIL/__pycache__/DdsImagePlugin.cpython-314.pyc +0 -0
- package/lib/python3.14/site-packages/PIL/__pycache__/EpsImagePlugin.cpython-314.pyc +0 -0
- package/lib/python3.14/site-packages/PIL/__pycache__/ExifTags.cpython-314.pyc +0 -0
- package/lib/python3.14/site-packages/PIL/__pycache__/FitsImagePlugin.cpython-314.pyc +0 -0
- package/lib/python3.14/site-packages/PIL/__pycache__/FliImagePlugin.cpython-314.pyc +0 -0
- package/lib/python3.14/site-packages/PIL/__pycache__/FontFile.cpython-314.pyc +0 -0
- package/lib/python3.14/site-packages/PIL/__pycache__/FpxImagePlugin.cpython-314.pyc +0 -0
- package/lib/python3.14/site-packages/PIL/__pycache__/FtexImagePlugin.cpython-314.pyc +0 -0
- package/lib/python3.14/site-packages/PIL/__pycache__/GbrImagePlugin.cpython-314.pyc +0 -0
- package/lib/python3.14/site-packages/PIL/__pycache__/GdImageFile.cpython-314.pyc +0 -0
- package/lib/python3.14/site-packages/PIL/__pycache__/GifImagePlugin.cpython-314.pyc +0 -0
- package/lib/python3.14/site-packages/PIL/__pycache__/GimpGradientFile.cpython-314.pyc +0 -0
- package/lib/python3.14/site-packages/PIL/__pycache__/GimpPaletteFile.cpython-314.pyc +0 -0
- package/lib/python3.14/site-packages/PIL/__pycache__/GribStubImagePlugin.cpython-314.pyc +0 -0
- package/lib/python3.14/site-packages/PIL/__pycache__/Hdf5StubImagePlugin.cpython-314.pyc +0 -0
- package/lib/python3.14/site-packages/PIL/__pycache__/IcnsImagePlugin.cpython-314.pyc +0 -0
- package/lib/python3.14/site-packages/PIL/__pycache__/IcoImagePlugin.cpython-314.pyc +0 -0
- package/lib/python3.14/site-packages/PIL/__pycache__/ImImagePlugin.cpython-314.pyc +0 -0
- package/lib/python3.14/site-packages/PIL/__pycache__/Image.cpython-314.pyc +0 -0
- package/lib/python3.14/site-packages/PIL/__pycache__/ImageChops.cpython-314.pyc +0 -0
- package/lib/python3.14/site-packages/PIL/__pycache__/ImageCms.cpython-314.pyc +0 -0
- package/lib/python3.14/site-packages/PIL/__pycache__/ImageColor.cpython-314.pyc +0 -0
- package/lib/python3.14/site-packages/PIL/__pycache__/ImageDraw.cpython-314.pyc +0 -0
- package/lib/python3.14/site-packages/PIL/__pycache__/ImageDraw2.cpython-314.pyc +0 -0
- package/lib/python3.14/site-packages/PIL/__pycache__/ImageEnhance.cpython-314.pyc +0 -0
- package/lib/python3.14/site-packages/PIL/__pycache__/ImageFile.cpython-314.pyc +0 -0
- package/lib/python3.14/site-packages/PIL/__pycache__/ImageFilter.cpython-314.pyc +0 -0
- package/lib/python3.14/site-packages/PIL/__pycache__/ImageFont.cpython-314.pyc +0 -0
- package/lib/python3.14/site-packages/PIL/__pycache__/ImageGrab.cpython-314.pyc +0 -0
- package/lib/python3.14/site-packages/PIL/__pycache__/ImageMath.cpython-314.pyc +0 -0
- package/lib/python3.14/site-packages/PIL/__pycache__/ImageMode.cpython-314.pyc +0 -0
- package/lib/python3.14/site-packages/PIL/__pycache__/ImageMorph.cpython-314.pyc +0 -0
- package/lib/python3.14/site-packages/PIL/__pycache__/ImageOps.cpython-314.pyc +0 -0
- package/lib/python3.14/site-packages/PIL/__pycache__/ImagePalette.cpython-314.pyc +0 -0
- package/lib/python3.14/site-packages/PIL/__pycache__/ImagePath.cpython-314.pyc +0 -0
- package/lib/python3.14/site-packages/PIL/__pycache__/ImageQt.cpython-314.pyc +0 -0
- package/lib/python3.14/site-packages/PIL/__pycache__/ImageSequence.cpython-314.pyc +0 -0
- package/lib/python3.14/site-packages/PIL/__pycache__/ImageShow.cpython-314.pyc +0 -0
- package/lib/python3.14/site-packages/PIL/__pycache__/ImageStat.cpython-314.pyc +0 -0
- package/lib/python3.14/site-packages/PIL/__pycache__/ImageText.cpython-314.pyc +0 -0
- package/lib/python3.14/site-packages/PIL/__pycache__/ImageTk.cpython-314.pyc +0 -0
- package/lib/python3.14/site-packages/PIL/__pycache__/ImageTransform.cpython-314.pyc +0 -0
- package/lib/python3.14/site-packages/PIL/__pycache__/ImageWin.cpython-314.pyc +0 -0
- package/lib/python3.14/site-packages/PIL/__pycache__/ImtImagePlugin.cpython-314.pyc +0 -0
- package/lib/python3.14/site-packages/PIL/__pycache__/IptcImagePlugin.cpython-314.pyc +0 -0
- package/lib/python3.14/site-packages/PIL/__pycache__/Jpeg2KImagePlugin.cpython-314.pyc +0 -0
- package/lib/python3.14/site-packages/PIL/__pycache__/JpegImagePlugin.cpython-314.pyc +0 -0
- package/lib/python3.14/site-packages/PIL/__pycache__/JpegPresets.cpython-314.pyc +0 -0
- package/lib/python3.14/site-packages/PIL/__pycache__/McIdasImagePlugin.cpython-314.pyc +0 -0
- package/lib/python3.14/site-packages/PIL/__pycache__/MicImagePlugin.cpython-314.pyc +0 -0
- package/lib/python3.14/site-packages/PIL/__pycache__/MpegImagePlugin.cpython-314.pyc +0 -0
- package/lib/python3.14/site-packages/PIL/__pycache__/MpoImagePlugin.cpython-314.pyc +0 -0
- package/lib/python3.14/site-packages/PIL/__pycache__/MspImagePlugin.cpython-314.pyc +0 -0
- package/lib/python3.14/site-packages/PIL/__pycache__/PSDraw.cpython-314.pyc +0 -0
- package/lib/python3.14/site-packages/PIL/__pycache__/PaletteFile.cpython-314.pyc +0 -0
- package/lib/python3.14/site-packages/PIL/__pycache__/PalmImagePlugin.cpython-314.pyc +0 -0
- package/lib/python3.14/site-packages/PIL/__pycache__/PcdImagePlugin.cpython-314.pyc +0 -0
- package/lib/python3.14/site-packages/PIL/__pycache__/PcfFontFile.cpython-314.pyc +0 -0
- package/lib/python3.14/site-packages/PIL/__pycache__/PcxImagePlugin.cpython-314.pyc +0 -0
- package/lib/python3.14/site-packages/PIL/__pycache__/PdfImagePlugin.cpython-314.pyc +0 -0
- package/lib/python3.14/site-packages/PIL/__pycache__/PdfParser.cpython-314.pyc +0 -0
- package/lib/python3.14/site-packages/PIL/__pycache__/PixarImagePlugin.cpython-314.pyc +0 -0
- package/lib/python3.14/site-packages/PIL/__pycache__/PngImagePlugin.cpython-314.pyc +0 -0
- package/lib/python3.14/site-packages/PIL/__pycache__/PpmImagePlugin.cpython-314.pyc +0 -0
- package/lib/python3.14/site-packages/PIL/__pycache__/PsdImagePlugin.cpython-314.pyc +0 -0
- package/lib/python3.14/site-packages/PIL/__pycache__/QoiImagePlugin.cpython-314.pyc +0 -0
- package/lib/python3.14/site-packages/PIL/__pycache__/SgiImagePlugin.cpython-314.pyc +0 -0
- package/lib/python3.14/site-packages/PIL/__pycache__/SpiderImagePlugin.cpython-314.pyc +0 -0
- package/lib/python3.14/site-packages/PIL/__pycache__/SunImagePlugin.cpython-314.pyc +0 -0
- package/lib/python3.14/site-packages/PIL/__pycache__/TarIO.cpython-314.pyc +0 -0
- package/lib/python3.14/site-packages/PIL/__pycache__/TgaImagePlugin.cpython-314.pyc +0 -0
- package/lib/python3.14/site-packages/PIL/__pycache__/TiffImagePlugin.cpython-314.pyc +0 -0
- package/lib/python3.14/site-packages/PIL/__pycache__/TiffTags.cpython-314.pyc +0 -0
- package/lib/python3.14/site-packages/PIL/__pycache__/WalImageFile.cpython-314.pyc +0 -0
- package/lib/python3.14/site-packages/PIL/__pycache__/WebPImagePlugin.cpython-314.pyc +0 -0
- package/lib/python3.14/site-packages/PIL/__pycache__/WmfImagePlugin.cpython-314.pyc +0 -0
- package/lib/python3.14/site-packages/PIL/__pycache__/XVThumbImagePlugin.cpython-314.pyc +0 -0
- package/lib/python3.14/site-packages/PIL/__pycache__/XbmImagePlugin.cpython-314.pyc +0 -0
- package/lib/python3.14/site-packages/PIL/__pycache__/XpmImagePlugin.cpython-314.pyc +0 -0
- package/lib/python3.14/site-packages/PIL/__pycache__/__init__.cpython-314.pyc +0 -0
- package/lib/python3.14/site-packages/PIL/__pycache__/__main__.cpython-314.pyc +0 -0
- package/lib/python3.14/site-packages/PIL/__pycache__/_binary.cpython-314.pyc +0 -0
- package/lib/python3.14/site-packages/PIL/__pycache__/_deprecate.cpython-314.pyc +0 -0
- package/lib/python3.14/site-packages/PIL/__pycache__/_tkinter_finder.cpython-314.pyc +0 -0
- package/lib/python3.14/site-packages/PIL/__pycache__/_typing.cpython-314.pyc +0 -0
- package/lib/python3.14/site-packages/PIL/__pycache__/_util.cpython-314.pyc +0 -0
- package/lib/python3.14/site-packages/PIL/__pycache__/_version.cpython-314.pyc +0 -0
- package/lib/python3.14/site-packages/PIL/__pycache__/features.cpython-314.pyc +0 -0
- package/lib/python3.14/site-packages/PIL/__pycache__/report.cpython-314.pyc +0 -0
- package/lib/python3.14/site-packages/PIL/_avif.pyi +3 -0
- package/lib/python3.14/site-packages/PIL/_binary.py +113 -0
- package/lib/python3.14/site-packages/PIL/_deprecate.py +72 -0
- package/lib/python3.14/site-packages/PIL/_imaging.cpython-314-wasm32-wasix.so +0 -0
- package/lib/python3.14/site-packages/PIL/_imaging.pyi +31 -0
- package/lib/python3.14/site-packages/PIL/_imagingcms.pyi +143 -0
- package/lib/python3.14/site-packages/PIL/_imagingft.cpython-314-wasm32-wasix.so +0 -0
- package/lib/python3.14/site-packages/PIL/_imagingft.pyi +70 -0
- package/lib/python3.14/site-packages/PIL/_imagingmath.cpython-314-wasm32-wasix.so +0 -0
- package/lib/python3.14/site-packages/PIL/_imagingmath.pyi +3 -0
- package/lib/python3.14/site-packages/PIL/_imagingmorph.cpython-314-wasm32-wasix.so +0 -0
- package/lib/python3.14/site-packages/PIL/_imagingmorph.pyi +3 -0
- package/lib/python3.14/site-packages/PIL/_imagingtk.cpython-314-wasm32-wasix.so +0 -0
- package/lib/python3.14/site-packages/PIL/_imagingtk.pyi +3 -0
- package/lib/python3.14/site-packages/PIL/_tkinter_finder.py +20 -0
- package/lib/python3.14/site-packages/PIL/_typing.py +45 -0
- package/lib/python3.14/site-packages/PIL/_util.py +29 -0
- package/lib/python3.14/site-packages/PIL/_version.py +4 -0
- package/lib/python3.14/site-packages/PIL/_webp.pyi +3 -0
- package/lib/python3.14/site-packages/PIL/features.py +343 -0
- package/lib/python3.14/site-packages/PIL/py.typed +0 -0
- package/lib/python3.14/site-packages/PIL/report.py +5 -0
- package/lib/python3.14/site-packages/pillow-12.3.0.dist-info/METADATA +180 -0
- package/lib/python3.14/site-packages/pillow-12.3.0.dist-info/RECORD +117 -0
- package/lib/python3.14/site-packages/pillow-12.3.0.dist-info/WHEEL +5 -0
- package/lib/python3.14/site-packages/pillow-12.3.0.dist-info/licenses/LICENSE +30 -0
- package/lib/python3.14/site-packages/pillow-12.3.0.dist-info/top_level.txt +1 -0
- package/lib/python3.14/site-packages/pillow-12.3.0.dist-info/zip-safe +1 -0
- package/package.json +46 -0
|
@@ -0,0 +1,466 @@
|
|
|
1
|
+
#
|
|
2
|
+
# The Python Imaging Library
|
|
3
|
+
# $Id$
|
|
4
|
+
#
|
|
5
|
+
# JPEG2000 file handling
|
|
6
|
+
#
|
|
7
|
+
# History:
|
|
8
|
+
# 2014-03-12 ajh Created
|
|
9
|
+
# 2021-06-30 rogermb Extract dpi information from the 'resc' header box
|
|
10
|
+
#
|
|
11
|
+
# Copyright (c) 2014 Coriolis Systems Limited
|
|
12
|
+
# Copyright (c) 2014 Alastair Houghton
|
|
13
|
+
#
|
|
14
|
+
# See the README file for information on usage and redistribution.
|
|
15
|
+
#
|
|
16
|
+
from __future__ import annotations
|
|
17
|
+
|
|
18
|
+
import io
|
|
19
|
+
import os
|
|
20
|
+
import struct
|
|
21
|
+
from typing import cast
|
|
22
|
+
|
|
23
|
+
from . import Image, ImageFile, ImagePalette, _binary
|
|
24
|
+
|
|
25
|
+
TYPE_CHECKING = False
|
|
26
|
+
if TYPE_CHECKING:
|
|
27
|
+
from collections.abc import Callable
|
|
28
|
+
from typing import IO
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
class BoxReader:
|
|
32
|
+
"""
|
|
33
|
+
A small helper class to read fields stored in JPEG2000 header boxes
|
|
34
|
+
and to easily step into and read sub-boxes.
|
|
35
|
+
"""
|
|
36
|
+
|
|
37
|
+
def __init__(self, fp: IO[bytes], length: int = -1) -> None:
|
|
38
|
+
self.fp = fp
|
|
39
|
+
self.has_length = length >= 0
|
|
40
|
+
self.length = length
|
|
41
|
+
self.remaining_in_box = -1
|
|
42
|
+
|
|
43
|
+
def _can_read(self, num_bytes: int) -> bool:
|
|
44
|
+
if self.has_length and self.fp.tell() + num_bytes > self.length:
|
|
45
|
+
# Outside box: ensure we don't read past the known file length
|
|
46
|
+
return False
|
|
47
|
+
if self.remaining_in_box >= 0:
|
|
48
|
+
# Inside box contents: ensure read does not go past box boundaries
|
|
49
|
+
return num_bytes <= self.remaining_in_box
|
|
50
|
+
else:
|
|
51
|
+
return True # No length known, just read
|
|
52
|
+
|
|
53
|
+
def _read_bytes(self, num_bytes: int) -> bytes:
|
|
54
|
+
if not self._can_read(num_bytes):
|
|
55
|
+
msg = "Not enough data in header"
|
|
56
|
+
raise SyntaxError(msg)
|
|
57
|
+
|
|
58
|
+
data = self.fp.read(num_bytes)
|
|
59
|
+
if len(data) < num_bytes:
|
|
60
|
+
msg = f"Expected to read {num_bytes} bytes but only got {len(data)}."
|
|
61
|
+
raise OSError(msg)
|
|
62
|
+
|
|
63
|
+
if self.remaining_in_box > 0:
|
|
64
|
+
self.remaining_in_box -= num_bytes
|
|
65
|
+
return data
|
|
66
|
+
|
|
67
|
+
def read_fields(self, field_format: str) -> tuple[int | bytes, ...]:
|
|
68
|
+
size = struct.calcsize(field_format)
|
|
69
|
+
data = self._read_bytes(size)
|
|
70
|
+
return struct.unpack(field_format, data)
|
|
71
|
+
|
|
72
|
+
def read_boxes(self) -> BoxReader:
|
|
73
|
+
size = self.remaining_in_box
|
|
74
|
+
data = self._read_bytes(size)
|
|
75
|
+
return BoxReader(io.BytesIO(data), size)
|
|
76
|
+
|
|
77
|
+
def has_next_box(self) -> bool:
|
|
78
|
+
if self.has_length:
|
|
79
|
+
return self.fp.tell() + self.remaining_in_box < self.length
|
|
80
|
+
else:
|
|
81
|
+
return True
|
|
82
|
+
|
|
83
|
+
def next_box_type(self) -> bytes:
|
|
84
|
+
# Skip the rest of the box if it has not been read
|
|
85
|
+
if self.remaining_in_box > 0:
|
|
86
|
+
self.fp.seek(self.remaining_in_box, os.SEEK_CUR)
|
|
87
|
+
self.remaining_in_box = -1
|
|
88
|
+
|
|
89
|
+
# Read the length and type of the next box
|
|
90
|
+
lbox, tbox = cast(tuple[int, bytes], self.read_fields(">I4s"))
|
|
91
|
+
if lbox == 1:
|
|
92
|
+
lbox = cast(int, self.read_fields(">Q")[0])
|
|
93
|
+
hlen = 16
|
|
94
|
+
else:
|
|
95
|
+
hlen = 8
|
|
96
|
+
|
|
97
|
+
if lbox < hlen or not self._can_read(lbox - hlen):
|
|
98
|
+
msg = "Invalid header length"
|
|
99
|
+
raise SyntaxError(msg)
|
|
100
|
+
|
|
101
|
+
self.remaining_in_box = lbox - hlen
|
|
102
|
+
return tbox
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
def _parse_codestream(fp: IO[bytes]) -> tuple[tuple[int, int], str]:
|
|
106
|
+
"""Parse the JPEG 2000 codestream to extract the size and component
|
|
107
|
+
count from the SIZ marker segment, returning a PIL (size, mode) tuple."""
|
|
108
|
+
|
|
109
|
+
hdr = fp.read(2)
|
|
110
|
+
lsiz = _binary.i16be(hdr)
|
|
111
|
+
if lsiz < 38:
|
|
112
|
+
msg = "SIZ marker length must be at least 38"
|
|
113
|
+
raise ValueError(msg)
|
|
114
|
+
siz = hdr + fp.read(lsiz - 2)
|
|
115
|
+
lsiz, rsiz, xsiz, ysiz, xosiz, yosiz, _, _, _, _, csiz = struct.unpack_from(
|
|
116
|
+
">HHIIIIIIIIH", siz
|
|
117
|
+
)
|
|
118
|
+
|
|
119
|
+
size = (xsiz - xosiz, ysiz - yosiz)
|
|
120
|
+
if csiz == 1:
|
|
121
|
+
ssiz = struct.unpack_from(">B", siz, 38)
|
|
122
|
+
if (ssiz[0] & 0x7F) + 1 > 8:
|
|
123
|
+
mode = "I;16"
|
|
124
|
+
else:
|
|
125
|
+
mode = "L"
|
|
126
|
+
elif csiz == 2:
|
|
127
|
+
mode = "LA"
|
|
128
|
+
elif csiz == 3:
|
|
129
|
+
mode = "RGB"
|
|
130
|
+
elif csiz == 4:
|
|
131
|
+
mode = "RGBA"
|
|
132
|
+
else:
|
|
133
|
+
msg = "unable to determine J2K image mode"
|
|
134
|
+
raise SyntaxError(msg)
|
|
135
|
+
|
|
136
|
+
return size, mode
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
def _res_to_dpi(num: int, denom: int, exp: int) -> float | None:
|
|
140
|
+
"""Convert JPEG2000's (numerator, denominator, exponent-base-10) resolution,
|
|
141
|
+
calculated as (num / denom) * 10^exp and stored in dots per meter,
|
|
142
|
+
to floating-point dots per inch."""
|
|
143
|
+
if denom == 0:
|
|
144
|
+
return None
|
|
145
|
+
return (254 * num * (10**exp)) / (10000 * denom)
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
def _parse_jp2_header(
|
|
149
|
+
fp: IO[bytes],
|
|
150
|
+
) -> tuple[
|
|
151
|
+
tuple[int, int],
|
|
152
|
+
str,
|
|
153
|
+
str | None,
|
|
154
|
+
tuple[float, float] | None,
|
|
155
|
+
ImagePalette.ImagePalette | None,
|
|
156
|
+
]:
|
|
157
|
+
"""Parse the JP2 header box to extract size, component count,
|
|
158
|
+
color space information, and optionally DPI information,
|
|
159
|
+
returning a (size, mode, mimetype, dpi) tuple."""
|
|
160
|
+
|
|
161
|
+
# Find the JP2 header box
|
|
162
|
+
reader = BoxReader(fp)
|
|
163
|
+
header = None
|
|
164
|
+
mimetype = None
|
|
165
|
+
while reader.has_next_box():
|
|
166
|
+
tbox = reader.next_box_type()
|
|
167
|
+
|
|
168
|
+
if tbox == b"jp2h":
|
|
169
|
+
header = reader.read_boxes()
|
|
170
|
+
break
|
|
171
|
+
elif tbox == b"ftyp":
|
|
172
|
+
if reader.read_fields(">4s")[0] == b"jpx ":
|
|
173
|
+
mimetype = "image/jpx"
|
|
174
|
+
assert header is not None
|
|
175
|
+
|
|
176
|
+
size = None
|
|
177
|
+
mode = None
|
|
178
|
+
bpc = None
|
|
179
|
+
nc = None
|
|
180
|
+
dpi = None # 2-tuple of DPI info, or None
|
|
181
|
+
palette = None
|
|
182
|
+
colr = None
|
|
183
|
+
|
|
184
|
+
while header.has_next_box():
|
|
185
|
+
tbox = header.next_box_type()
|
|
186
|
+
|
|
187
|
+
if tbox == b"ihdr":
|
|
188
|
+
height, width, nc, bpc = header.read_fields(">IIHB")
|
|
189
|
+
assert isinstance(height, int)
|
|
190
|
+
assert isinstance(width, int)
|
|
191
|
+
assert isinstance(bpc, int)
|
|
192
|
+
size = (width, height)
|
|
193
|
+
if nc == 1 and (bpc & 0x7F) > 8:
|
|
194
|
+
mode = "I;16"
|
|
195
|
+
elif nc == 1:
|
|
196
|
+
mode = "L"
|
|
197
|
+
elif nc == 2:
|
|
198
|
+
mode = "LA"
|
|
199
|
+
elif nc == 3:
|
|
200
|
+
mode = "RGB"
|
|
201
|
+
elif nc == 4:
|
|
202
|
+
mode = "RGBA"
|
|
203
|
+
elif tbox == b"colr":
|
|
204
|
+
meth, _, _, enumcs = header.read_fields(">BBBI")
|
|
205
|
+
if meth == 1:
|
|
206
|
+
if enumcs in (0, 15):
|
|
207
|
+
colr = "1"
|
|
208
|
+
elif enumcs == 12:
|
|
209
|
+
colr = "CMYK"
|
|
210
|
+
if nc == 4:
|
|
211
|
+
mode = "CMYK"
|
|
212
|
+
elif enumcs == 17:
|
|
213
|
+
colr = "L"
|
|
214
|
+
elif tbox == b"pclr" and mode in ("L", "LA") and colr not in ("1", "L"):
|
|
215
|
+
ne, npc = header.read_fields(">HB")
|
|
216
|
+
assert isinstance(ne, int)
|
|
217
|
+
assert isinstance(npc, int)
|
|
218
|
+
max_bitdepth = 0
|
|
219
|
+
for bitdepth in header.read_fields(">" + ("B" * npc)):
|
|
220
|
+
assert isinstance(bitdepth, int)
|
|
221
|
+
if bitdepth > max_bitdepth:
|
|
222
|
+
max_bitdepth = bitdepth
|
|
223
|
+
if max_bitdepth <= 8:
|
|
224
|
+
if npc == 4:
|
|
225
|
+
palette_mode = "CMYK" if colr == "CMYK" else "RGBA"
|
|
226
|
+
else:
|
|
227
|
+
palette_mode = "RGB"
|
|
228
|
+
palette = ImagePalette.ImagePalette(palette_mode)
|
|
229
|
+
for i in range(ne):
|
|
230
|
+
color: list[int] = []
|
|
231
|
+
for value in header.read_fields(">" + ("B" * npc)):
|
|
232
|
+
assert isinstance(value, int)
|
|
233
|
+
color.append(value)
|
|
234
|
+
palette.getcolor(tuple(color))
|
|
235
|
+
mode = "P" if mode == "L" else "PA"
|
|
236
|
+
elif tbox == b"res ":
|
|
237
|
+
res = header.read_boxes()
|
|
238
|
+
while res.has_next_box():
|
|
239
|
+
tres = res.next_box_type()
|
|
240
|
+
if tres == b"resc":
|
|
241
|
+
vrcn, vrcd, hrcn, hrcd, vrce, hrce = res.read_fields(">HHHHBB")
|
|
242
|
+
assert isinstance(vrcn, int)
|
|
243
|
+
assert isinstance(vrcd, int)
|
|
244
|
+
assert isinstance(hrcn, int)
|
|
245
|
+
assert isinstance(hrcd, int)
|
|
246
|
+
assert isinstance(vrce, int)
|
|
247
|
+
assert isinstance(hrce, int)
|
|
248
|
+
hres = _res_to_dpi(hrcn, hrcd, hrce)
|
|
249
|
+
vres = _res_to_dpi(vrcn, vrcd, vrce)
|
|
250
|
+
if hres is not None and vres is not None:
|
|
251
|
+
dpi = (hres, vres)
|
|
252
|
+
break
|
|
253
|
+
|
|
254
|
+
if size is None or mode is None:
|
|
255
|
+
msg = "Malformed JP2 header"
|
|
256
|
+
raise SyntaxError(msg)
|
|
257
|
+
|
|
258
|
+
return size, mode, mimetype, dpi, palette
|
|
259
|
+
|
|
260
|
+
|
|
261
|
+
##
|
|
262
|
+
# Image plugin for JPEG2000 images.
|
|
263
|
+
|
|
264
|
+
|
|
265
|
+
class Jpeg2KImageFile(ImageFile.ImageFile):
|
|
266
|
+
format = "JPEG2000"
|
|
267
|
+
format_description = "JPEG 2000 (ISO 15444)"
|
|
268
|
+
|
|
269
|
+
def _open(self) -> None:
|
|
270
|
+
assert self.fp is not None
|
|
271
|
+
sig = self.fp.read(4)
|
|
272
|
+
if sig == b"\xff\x4f\xff\x51":
|
|
273
|
+
self.codec = "j2k"
|
|
274
|
+
self._size, self._mode = _parse_codestream(self.fp)
|
|
275
|
+
self._parse_comment()
|
|
276
|
+
else:
|
|
277
|
+
sig = sig + self.fp.read(8)
|
|
278
|
+
|
|
279
|
+
if sig == b"\x00\x00\x00\x0cjP \x0d\x0a\x87\x0a":
|
|
280
|
+
self.codec = "jp2"
|
|
281
|
+
header = _parse_jp2_header(self.fp)
|
|
282
|
+
self._size, self._mode, self.custom_mimetype, dpi, self.palette = header
|
|
283
|
+
if dpi is not None:
|
|
284
|
+
self.info["dpi"] = dpi
|
|
285
|
+
if self.fp.read(12).endswith(b"jp2c\xff\x4f\xff\x51"):
|
|
286
|
+
hdr = self.fp.read(2)
|
|
287
|
+
length = _binary.i16be(hdr)
|
|
288
|
+
self.fp.seek(length - 2, os.SEEK_CUR)
|
|
289
|
+
self._parse_comment()
|
|
290
|
+
else:
|
|
291
|
+
msg = "not a JPEG 2000 file"
|
|
292
|
+
raise SyntaxError(msg)
|
|
293
|
+
|
|
294
|
+
self._reduce = 0
|
|
295
|
+
self.layers = 0
|
|
296
|
+
|
|
297
|
+
fd = -1
|
|
298
|
+
length = -1
|
|
299
|
+
|
|
300
|
+
try:
|
|
301
|
+
fd = self.fp.fileno()
|
|
302
|
+
length = os.fstat(fd).st_size
|
|
303
|
+
except Exception:
|
|
304
|
+
fd = -1
|
|
305
|
+
try:
|
|
306
|
+
pos = self.fp.tell()
|
|
307
|
+
self.fp.seek(0, io.SEEK_END)
|
|
308
|
+
length = self.fp.tell()
|
|
309
|
+
self.fp.seek(pos)
|
|
310
|
+
except Exception:
|
|
311
|
+
length = -1
|
|
312
|
+
|
|
313
|
+
self.tile = [
|
|
314
|
+
ImageFile._Tile(
|
|
315
|
+
"jpeg2k",
|
|
316
|
+
(0, 0) + self.size,
|
|
317
|
+
0,
|
|
318
|
+
(self.codec, self._reduce, self.layers, fd, length),
|
|
319
|
+
)
|
|
320
|
+
]
|
|
321
|
+
|
|
322
|
+
def _parse_comment(self) -> None:
|
|
323
|
+
assert self.fp is not None
|
|
324
|
+
while True:
|
|
325
|
+
marker = self.fp.read(2)
|
|
326
|
+
if not marker:
|
|
327
|
+
break
|
|
328
|
+
typ = marker[1]
|
|
329
|
+
if typ in (0x90, 0xD9):
|
|
330
|
+
# Start of tile or end of codestream
|
|
331
|
+
break
|
|
332
|
+
hdr = self.fp.read(2)
|
|
333
|
+
length = _binary.i16be(hdr)
|
|
334
|
+
if length < 2:
|
|
335
|
+
msg = "Marker length too small"
|
|
336
|
+
raise ValueError(msg)
|
|
337
|
+
if typ == 0x64:
|
|
338
|
+
# Comment
|
|
339
|
+
self.info["comment"] = self.fp.read(length - 2)[2:]
|
|
340
|
+
break
|
|
341
|
+
else:
|
|
342
|
+
self.fp.seek(length - 2, os.SEEK_CUR)
|
|
343
|
+
|
|
344
|
+
@property # type: ignore[override]
|
|
345
|
+
def reduce(
|
|
346
|
+
self,
|
|
347
|
+
) -> (
|
|
348
|
+
Callable[[int | tuple[int, int], tuple[int, int, int, int] | None], Image.Image]
|
|
349
|
+
| int
|
|
350
|
+
):
|
|
351
|
+
# https://github.com/python-pillow/Pillow/issues/4343 found that the
|
|
352
|
+
# new Image 'reduce' method was shadowed by this plugin's 'reduce'
|
|
353
|
+
# property. This attempts to allow for both scenarios
|
|
354
|
+
return self._reduce or super().reduce
|
|
355
|
+
|
|
356
|
+
@reduce.setter
|
|
357
|
+
def reduce(self, value: int) -> None:
|
|
358
|
+
self._reduce = value
|
|
359
|
+
|
|
360
|
+
def load(self) -> Image.core.PixelAccess | None:
|
|
361
|
+
if self.tile and self._reduce:
|
|
362
|
+
power = 1 << self._reduce
|
|
363
|
+
adjust = power >> 1
|
|
364
|
+
self._size = (
|
|
365
|
+
int((self.size[0] + adjust) / power),
|
|
366
|
+
int((self.size[1] + adjust) / power),
|
|
367
|
+
)
|
|
368
|
+
|
|
369
|
+
# Update the reduce and layers settings
|
|
370
|
+
t = self.tile[0]
|
|
371
|
+
assert isinstance(t[3], tuple)
|
|
372
|
+
t3 = (t[3][0], self._reduce, self.layers, t[3][3], t[3][4])
|
|
373
|
+
self.tile = [ImageFile._Tile(t[0], (0, 0) + self.size, t[2], t3)]
|
|
374
|
+
|
|
375
|
+
return ImageFile.ImageFile.load(self)
|
|
376
|
+
|
|
377
|
+
|
|
378
|
+
def _accept(prefix: bytes) -> bool:
|
|
379
|
+
return prefix.startswith(
|
|
380
|
+
(b"\xff\x4f\xff\x51", b"\x00\x00\x00\x0cjP \x0d\x0a\x87\x0a")
|
|
381
|
+
)
|
|
382
|
+
|
|
383
|
+
|
|
384
|
+
# ------------------------------------------------------------
|
|
385
|
+
# Save support
|
|
386
|
+
|
|
387
|
+
|
|
388
|
+
def _save(im: Image.Image, fp: IO[bytes], filename: str | bytes) -> None:
|
|
389
|
+
# Get the keyword arguments
|
|
390
|
+
info = im.encoderinfo
|
|
391
|
+
|
|
392
|
+
if isinstance(filename, str):
|
|
393
|
+
filename = filename.encode()
|
|
394
|
+
if filename.endswith(b".j2k") or info.get("no_jp2", False):
|
|
395
|
+
kind = "j2k"
|
|
396
|
+
else:
|
|
397
|
+
kind = "jp2"
|
|
398
|
+
|
|
399
|
+
offset = info.get("offset", None)
|
|
400
|
+
tile_offset = info.get("tile_offset", None)
|
|
401
|
+
tile_size = info.get("tile_size", None)
|
|
402
|
+
quality_mode = info.get("quality_mode", "rates")
|
|
403
|
+
quality_layers = info.get("quality_layers", None)
|
|
404
|
+
if quality_layers is not None and not (
|
|
405
|
+
isinstance(quality_layers, (list, tuple))
|
|
406
|
+
and all(
|
|
407
|
+
isinstance(quality_layer, (int, float)) for quality_layer in quality_layers
|
|
408
|
+
)
|
|
409
|
+
):
|
|
410
|
+
msg = "quality_layers must be a sequence of numbers"
|
|
411
|
+
raise ValueError(msg)
|
|
412
|
+
|
|
413
|
+
num_resolutions = info.get("num_resolutions", 0)
|
|
414
|
+
cblk_size = info.get("codeblock_size", None)
|
|
415
|
+
precinct_size = info.get("precinct_size", None)
|
|
416
|
+
irreversible = info.get("irreversible", False)
|
|
417
|
+
progression = info.get("progression", "LRCP")
|
|
418
|
+
cinema_mode = info.get("cinema_mode", "no")
|
|
419
|
+
mct = info.get("mct", 0)
|
|
420
|
+
signed = info.get("signed", False)
|
|
421
|
+
comment = info.get("comment")
|
|
422
|
+
if isinstance(comment, str):
|
|
423
|
+
comment = comment.encode()
|
|
424
|
+
plt = info.get("plt", False)
|
|
425
|
+
|
|
426
|
+
fd = -1
|
|
427
|
+
if hasattr(fp, "fileno"):
|
|
428
|
+
try:
|
|
429
|
+
fd = fp.fileno()
|
|
430
|
+
except Exception:
|
|
431
|
+
fd = -1
|
|
432
|
+
|
|
433
|
+
im.encoderconfig = (
|
|
434
|
+
offset,
|
|
435
|
+
tile_offset,
|
|
436
|
+
tile_size,
|
|
437
|
+
quality_mode,
|
|
438
|
+
quality_layers,
|
|
439
|
+
num_resolutions,
|
|
440
|
+
cblk_size,
|
|
441
|
+
precinct_size,
|
|
442
|
+
irreversible,
|
|
443
|
+
progression,
|
|
444
|
+
cinema_mode,
|
|
445
|
+
mct,
|
|
446
|
+
signed,
|
|
447
|
+
fd,
|
|
448
|
+
comment,
|
|
449
|
+
plt,
|
|
450
|
+
)
|
|
451
|
+
|
|
452
|
+
ImageFile._save(im, fp, [ImageFile._Tile("jpeg2k", (0, 0) + im.size, 0, kind)])
|
|
453
|
+
|
|
454
|
+
|
|
455
|
+
# ------------------------------------------------------------
|
|
456
|
+
# Registry stuff
|
|
457
|
+
|
|
458
|
+
|
|
459
|
+
Image.register_open(Jpeg2KImageFile.format, Jpeg2KImageFile, _accept)
|
|
460
|
+
Image.register_save(Jpeg2KImageFile.format, _save)
|
|
461
|
+
|
|
462
|
+
Image.register_extensions(
|
|
463
|
+
Jpeg2KImageFile.format, [".jp2", ".j2k", ".jpc", ".jpf", ".jpx", ".j2c"]
|
|
464
|
+
)
|
|
465
|
+
|
|
466
|
+
Image.register_mime(Jpeg2KImageFile.format, "image/jp2")
|