@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.
Files changed (217) hide show
  1. package/LICENSE +30 -0
  2. package/README.md +4 -0
  3. package/lib/python3.14/site-packages/PIL/AvifImagePlugin.py +295 -0
  4. package/lib/python3.14/site-packages/PIL/BdfFontFile.py +124 -0
  5. package/lib/python3.14/site-packages/PIL/BlpImagePlugin.py +498 -0
  6. package/lib/python3.14/site-packages/PIL/BmpImagePlugin.py +514 -0
  7. package/lib/python3.14/site-packages/PIL/BufrStubImagePlugin.py +72 -0
  8. package/lib/python3.14/site-packages/PIL/ContainerIO.py +173 -0
  9. package/lib/python3.14/site-packages/PIL/CurImagePlugin.py +103 -0
  10. package/lib/python3.14/site-packages/PIL/DcxImagePlugin.py +84 -0
  11. package/lib/python3.14/site-packages/PIL/DdsImagePlugin.py +632 -0
  12. package/lib/python3.14/site-packages/PIL/EpsImagePlugin.py +484 -0
  13. package/lib/python3.14/site-packages/PIL/ExifTags.py +384 -0
  14. package/lib/python3.14/site-packages/PIL/FitsImagePlugin.py +153 -0
  15. package/lib/python3.14/site-packages/PIL/FliImagePlugin.py +184 -0
  16. package/lib/python3.14/site-packages/PIL/FontFile.py +160 -0
  17. package/lib/python3.14/site-packages/PIL/FpxImagePlugin.py +262 -0
  18. package/lib/python3.14/site-packages/PIL/FtexImagePlugin.py +115 -0
  19. package/lib/python3.14/site-packages/PIL/GbrImagePlugin.py +103 -0
  20. package/lib/python3.14/site-packages/PIL/GdImageFile.py +104 -0
  21. package/lib/python3.14/site-packages/PIL/GifImagePlugin.py +1223 -0
  22. package/lib/python3.14/site-packages/PIL/GimpGradientFile.py +154 -0
  23. package/lib/python3.14/site-packages/PIL/GimpPaletteFile.py +75 -0
  24. package/lib/python3.14/site-packages/PIL/GribStubImagePlugin.py +72 -0
  25. package/lib/python3.14/site-packages/PIL/Hdf5StubImagePlugin.py +72 -0
  26. package/lib/python3.14/site-packages/PIL/IcnsImagePlugin.py +401 -0
  27. package/lib/python3.14/site-packages/PIL/IcoImagePlugin.py +396 -0
  28. package/lib/python3.14/site-packages/PIL/ImImagePlugin.py +390 -0
  29. package/lib/python3.14/site-packages/PIL/Image.py +4385 -0
  30. package/lib/python3.14/site-packages/PIL/ImageChops.py +311 -0
  31. package/lib/python3.14/site-packages/PIL/ImageCms.py +1078 -0
  32. package/lib/python3.14/site-packages/PIL/ImageColor.py +320 -0
  33. package/lib/python3.14/site-packages/PIL/ImageDraw.py +1002 -0
  34. package/lib/python3.14/site-packages/PIL/ImageDraw2.py +244 -0
  35. package/lib/python3.14/site-packages/PIL/ImageEnhance.py +113 -0
  36. package/lib/python3.14/site-packages/PIL/ImageFile.py +938 -0
  37. package/lib/python3.14/site-packages/PIL/ImageFilter.py +617 -0
  38. package/lib/python3.14/site-packages/PIL/ImageFont.py +1330 -0
  39. package/lib/python3.14/site-packages/PIL/ImageGrab.py +234 -0
  40. package/lib/python3.14/site-packages/PIL/ImageMath.py +314 -0
  41. package/lib/python3.14/site-packages/PIL/ImageMode.py +85 -0
  42. package/lib/python3.14/site-packages/PIL/ImageMorph.py +317 -0
  43. package/lib/python3.14/site-packages/PIL/ImageOps.py +749 -0
  44. package/lib/python3.14/site-packages/PIL/ImagePalette.py +290 -0
  45. package/lib/python3.14/site-packages/PIL/ImagePath.py +20 -0
  46. package/lib/python3.14/site-packages/PIL/ImageQt.py +219 -0
  47. package/lib/python3.14/site-packages/PIL/ImageSequence.py +88 -0
  48. package/lib/python3.14/site-packages/PIL/ImageShow.py +367 -0
  49. package/lib/python3.14/site-packages/PIL/ImageStat.py +167 -0
  50. package/lib/python3.14/site-packages/PIL/ImageText.py +509 -0
  51. package/lib/python3.14/site-packages/PIL/ImageTk.py +266 -0
  52. package/lib/python3.14/site-packages/PIL/ImageTransform.py +136 -0
  53. package/lib/python3.14/site-packages/PIL/ImageWin.py +247 -0
  54. package/lib/python3.14/site-packages/PIL/ImtImagePlugin.py +103 -0
  55. package/lib/python3.14/site-packages/PIL/IptcImagePlugin.py +226 -0
  56. package/lib/python3.14/site-packages/PIL/Jpeg2KImagePlugin.py +466 -0
  57. package/lib/python3.14/site-packages/PIL/JpegImagePlugin.py +889 -0
  58. package/lib/python3.14/site-packages/PIL/JpegPresets.py +242 -0
  59. package/lib/python3.14/site-packages/PIL/McIdasImagePlugin.py +78 -0
  60. package/lib/python3.14/site-packages/PIL/MicImagePlugin.py +103 -0
  61. package/lib/python3.14/site-packages/PIL/MpegImagePlugin.py +84 -0
  62. package/lib/python3.14/site-packages/PIL/MpoImagePlugin.py +203 -0
  63. package/lib/python3.14/site-packages/PIL/MspImagePlugin.py +200 -0
  64. package/lib/python3.14/site-packages/PIL/PSDraw.py +238 -0
  65. package/lib/python3.14/site-packages/PIL/PaletteFile.py +54 -0
  66. package/lib/python3.14/site-packages/PIL/PalmImagePlugin.py +217 -0
  67. package/lib/python3.14/site-packages/PIL/PcdImagePlugin.py +68 -0
  68. package/lib/python3.14/site-packages/PIL/PcfFontFile.py +256 -0
  69. package/lib/python3.14/site-packages/PIL/PcxImagePlugin.py +232 -0
  70. package/lib/python3.14/site-packages/PIL/PdfImagePlugin.py +315 -0
  71. package/lib/python3.14/site-packages/PIL/PdfParser.py +1092 -0
  72. package/lib/python3.14/site-packages/PIL/PixarImagePlugin.py +72 -0
  73. package/lib/python3.14/site-packages/PIL/PngImagePlugin.py +1578 -0
  74. package/lib/python3.14/site-packages/PIL/PpmImagePlugin.py +375 -0
  75. package/lib/python3.14/site-packages/PIL/PsdImagePlugin.py +339 -0
  76. package/lib/python3.14/site-packages/PIL/QoiImagePlugin.py +235 -0
  77. package/lib/python3.14/site-packages/PIL/SgiImagePlugin.py +231 -0
  78. package/lib/python3.14/site-packages/PIL/SpiderImagePlugin.py +332 -0
  79. package/lib/python3.14/site-packages/PIL/SunImagePlugin.py +145 -0
  80. package/lib/python3.14/site-packages/PIL/TarIO.py +61 -0
  81. package/lib/python3.14/site-packages/PIL/TgaImagePlugin.py +284 -0
  82. package/lib/python3.14/site-packages/PIL/TiffImagePlugin.py +2354 -0
  83. package/lib/python3.14/site-packages/PIL/TiffTags.py +566 -0
  84. package/lib/python3.14/site-packages/PIL/WalImageFile.py +129 -0
  85. package/lib/python3.14/site-packages/PIL/WebPImagePlugin.py +322 -0
  86. package/lib/python3.14/site-packages/PIL/WmfImagePlugin.py +183 -0
  87. package/lib/python3.14/site-packages/PIL/XVThumbImagePlugin.py +83 -0
  88. package/lib/python3.14/site-packages/PIL/XbmImagePlugin.py +98 -0
  89. package/lib/python3.14/site-packages/PIL/XpmImagePlugin.py +157 -0
  90. package/lib/python3.14/site-packages/PIL/__init__.py +87 -0
  91. package/lib/python3.14/site-packages/PIL/__main__.py +7 -0
  92. package/lib/python3.14/site-packages/PIL/__pycache__/AvifImagePlugin.cpython-314.pyc +0 -0
  93. package/lib/python3.14/site-packages/PIL/__pycache__/BdfFontFile.cpython-314.pyc +0 -0
  94. package/lib/python3.14/site-packages/PIL/__pycache__/BlpImagePlugin.cpython-314.pyc +0 -0
  95. package/lib/python3.14/site-packages/PIL/__pycache__/BmpImagePlugin.cpython-314.pyc +0 -0
  96. package/lib/python3.14/site-packages/PIL/__pycache__/BufrStubImagePlugin.cpython-314.pyc +0 -0
  97. package/lib/python3.14/site-packages/PIL/__pycache__/ContainerIO.cpython-314.pyc +0 -0
  98. package/lib/python3.14/site-packages/PIL/__pycache__/CurImagePlugin.cpython-314.pyc +0 -0
  99. package/lib/python3.14/site-packages/PIL/__pycache__/DcxImagePlugin.cpython-314.pyc +0 -0
  100. package/lib/python3.14/site-packages/PIL/__pycache__/DdsImagePlugin.cpython-314.pyc +0 -0
  101. package/lib/python3.14/site-packages/PIL/__pycache__/EpsImagePlugin.cpython-314.pyc +0 -0
  102. package/lib/python3.14/site-packages/PIL/__pycache__/ExifTags.cpython-314.pyc +0 -0
  103. package/lib/python3.14/site-packages/PIL/__pycache__/FitsImagePlugin.cpython-314.pyc +0 -0
  104. package/lib/python3.14/site-packages/PIL/__pycache__/FliImagePlugin.cpython-314.pyc +0 -0
  105. package/lib/python3.14/site-packages/PIL/__pycache__/FontFile.cpython-314.pyc +0 -0
  106. package/lib/python3.14/site-packages/PIL/__pycache__/FpxImagePlugin.cpython-314.pyc +0 -0
  107. package/lib/python3.14/site-packages/PIL/__pycache__/FtexImagePlugin.cpython-314.pyc +0 -0
  108. package/lib/python3.14/site-packages/PIL/__pycache__/GbrImagePlugin.cpython-314.pyc +0 -0
  109. package/lib/python3.14/site-packages/PIL/__pycache__/GdImageFile.cpython-314.pyc +0 -0
  110. package/lib/python3.14/site-packages/PIL/__pycache__/GifImagePlugin.cpython-314.pyc +0 -0
  111. package/lib/python3.14/site-packages/PIL/__pycache__/GimpGradientFile.cpython-314.pyc +0 -0
  112. package/lib/python3.14/site-packages/PIL/__pycache__/GimpPaletteFile.cpython-314.pyc +0 -0
  113. package/lib/python3.14/site-packages/PIL/__pycache__/GribStubImagePlugin.cpython-314.pyc +0 -0
  114. package/lib/python3.14/site-packages/PIL/__pycache__/Hdf5StubImagePlugin.cpython-314.pyc +0 -0
  115. package/lib/python3.14/site-packages/PIL/__pycache__/IcnsImagePlugin.cpython-314.pyc +0 -0
  116. package/lib/python3.14/site-packages/PIL/__pycache__/IcoImagePlugin.cpython-314.pyc +0 -0
  117. package/lib/python3.14/site-packages/PIL/__pycache__/ImImagePlugin.cpython-314.pyc +0 -0
  118. package/lib/python3.14/site-packages/PIL/__pycache__/Image.cpython-314.pyc +0 -0
  119. package/lib/python3.14/site-packages/PIL/__pycache__/ImageChops.cpython-314.pyc +0 -0
  120. package/lib/python3.14/site-packages/PIL/__pycache__/ImageCms.cpython-314.pyc +0 -0
  121. package/lib/python3.14/site-packages/PIL/__pycache__/ImageColor.cpython-314.pyc +0 -0
  122. package/lib/python3.14/site-packages/PIL/__pycache__/ImageDraw.cpython-314.pyc +0 -0
  123. package/lib/python3.14/site-packages/PIL/__pycache__/ImageDraw2.cpython-314.pyc +0 -0
  124. package/lib/python3.14/site-packages/PIL/__pycache__/ImageEnhance.cpython-314.pyc +0 -0
  125. package/lib/python3.14/site-packages/PIL/__pycache__/ImageFile.cpython-314.pyc +0 -0
  126. package/lib/python3.14/site-packages/PIL/__pycache__/ImageFilter.cpython-314.pyc +0 -0
  127. package/lib/python3.14/site-packages/PIL/__pycache__/ImageFont.cpython-314.pyc +0 -0
  128. package/lib/python3.14/site-packages/PIL/__pycache__/ImageGrab.cpython-314.pyc +0 -0
  129. package/lib/python3.14/site-packages/PIL/__pycache__/ImageMath.cpython-314.pyc +0 -0
  130. package/lib/python3.14/site-packages/PIL/__pycache__/ImageMode.cpython-314.pyc +0 -0
  131. package/lib/python3.14/site-packages/PIL/__pycache__/ImageMorph.cpython-314.pyc +0 -0
  132. package/lib/python3.14/site-packages/PIL/__pycache__/ImageOps.cpython-314.pyc +0 -0
  133. package/lib/python3.14/site-packages/PIL/__pycache__/ImagePalette.cpython-314.pyc +0 -0
  134. package/lib/python3.14/site-packages/PIL/__pycache__/ImagePath.cpython-314.pyc +0 -0
  135. package/lib/python3.14/site-packages/PIL/__pycache__/ImageQt.cpython-314.pyc +0 -0
  136. package/lib/python3.14/site-packages/PIL/__pycache__/ImageSequence.cpython-314.pyc +0 -0
  137. package/lib/python3.14/site-packages/PIL/__pycache__/ImageShow.cpython-314.pyc +0 -0
  138. package/lib/python3.14/site-packages/PIL/__pycache__/ImageStat.cpython-314.pyc +0 -0
  139. package/lib/python3.14/site-packages/PIL/__pycache__/ImageText.cpython-314.pyc +0 -0
  140. package/lib/python3.14/site-packages/PIL/__pycache__/ImageTk.cpython-314.pyc +0 -0
  141. package/lib/python3.14/site-packages/PIL/__pycache__/ImageTransform.cpython-314.pyc +0 -0
  142. package/lib/python3.14/site-packages/PIL/__pycache__/ImageWin.cpython-314.pyc +0 -0
  143. package/lib/python3.14/site-packages/PIL/__pycache__/ImtImagePlugin.cpython-314.pyc +0 -0
  144. package/lib/python3.14/site-packages/PIL/__pycache__/IptcImagePlugin.cpython-314.pyc +0 -0
  145. package/lib/python3.14/site-packages/PIL/__pycache__/Jpeg2KImagePlugin.cpython-314.pyc +0 -0
  146. package/lib/python3.14/site-packages/PIL/__pycache__/JpegImagePlugin.cpython-314.pyc +0 -0
  147. package/lib/python3.14/site-packages/PIL/__pycache__/JpegPresets.cpython-314.pyc +0 -0
  148. package/lib/python3.14/site-packages/PIL/__pycache__/McIdasImagePlugin.cpython-314.pyc +0 -0
  149. package/lib/python3.14/site-packages/PIL/__pycache__/MicImagePlugin.cpython-314.pyc +0 -0
  150. package/lib/python3.14/site-packages/PIL/__pycache__/MpegImagePlugin.cpython-314.pyc +0 -0
  151. package/lib/python3.14/site-packages/PIL/__pycache__/MpoImagePlugin.cpython-314.pyc +0 -0
  152. package/lib/python3.14/site-packages/PIL/__pycache__/MspImagePlugin.cpython-314.pyc +0 -0
  153. package/lib/python3.14/site-packages/PIL/__pycache__/PSDraw.cpython-314.pyc +0 -0
  154. package/lib/python3.14/site-packages/PIL/__pycache__/PaletteFile.cpython-314.pyc +0 -0
  155. package/lib/python3.14/site-packages/PIL/__pycache__/PalmImagePlugin.cpython-314.pyc +0 -0
  156. package/lib/python3.14/site-packages/PIL/__pycache__/PcdImagePlugin.cpython-314.pyc +0 -0
  157. package/lib/python3.14/site-packages/PIL/__pycache__/PcfFontFile.cpython-314.pyc +0 -0
  158. package/lib/python3.14/site-packages/PIL/__pycache__/PcxImagePlugin.cpython-314.pyc +0 -0
  159. package/lib/python3.14/site-packages/PIL/__pycache__/PdfImagePlugin.cpython-314.pyc +0 -0
  160. package/lib/python3.14/site-packages/PIL/__pycache__/PdfParser.cpython-314.pyc +0 -0
  161. package/lib/python3.14/site-packages/PIL/__pycache__/PixarImagePlugin.cpython-314.pyc +0 -0
  162. package/lib/python3.14/site-packages/PIL/__pycache__/PngImagePlugin.cpython-314.pyc +0 -0
  163. package/lib/python3.14/site-packages/PIL/__pycache__/PpmImagePlugin.cpython-314.pyc +0 -0
  164. package/lib/python3.14/site-packages/PIL/__pycache__/PsdImagePlugin.cpython-314.pyc +0 -0
  165. package/lib/python3.14/site-packages/PIL/__pycache__/QoiImagePlugin.cpython-314.pyc +0 -0
  166. package/lib/python3.14/site-packages/PIL/__pycache__/SgiImagePlugin.cpython-314.pyc +0 -0
  167. package/lib/python3.14/site-packages/PIL/__pycache__/SpiderImagePlugin.cpython-314.pyc +0 -0
  168. package/lib/python3.14/site-packages/PIL/__pycache__/SunImagePlugin.cpython-314.pyc +0 -0
  169. package/lib/python3.14/site-packages/PIL/__pycache__/TarIO.cpython-314.pyc +0 -0
  170. package/lib/python3.14/site-packages/PIL/__pycache__/TgaImagePlugin.cpython-314.pyc +0 -0
  171. package/lib/python3.14/site-packages/PIL/__pycache__/TiffImagePlugin.cpython-314.pyc +0 -0
  172. package/lib/python3.14/site-packages/PIL/__pycache__/TiffTags.cpython-314.pyc +0 -0
  173. package/lib/python3.14/site-packages/PIL/__pycache__/WalImageFile.cpython-314.pyc +0 -0
  174. package/lib/python3.14/site-packages/PIL/__pycache__/WebPImagePlugin.cpython-314.pyc +0 -0
  175. package/lib/python3.14/site-packages/PIL/__pycache__/WmfImagePlugin.cpython-314.pyc +0 -0
  176. package/lib/python3.14/site-packages/PIL/__pycache__/XVThumbImagePlugin.cpython-314.pyc +0 -0
  177. package/lib/python3.14/site-packages/PIL/__pycache__/XbmImagePlugin.cpython-314.pyc +0 -0
  178. package/lib/python3.14/site-packages/PIL/__pycache__/XpmImagePlugin.cpython-314.pyc +0 -0
  179. package/lib/python3.14/site-packages/PIL/__pycache__/__init__.cpython-314.pyc +0 -0
  180. package/lib/python3.14/site-packages/PIL/__pycache__/__main__.cpython-314.pyc +0 -0
  181. package/lib/python3.14/site-packages/PIL/__pycache__/_binary.cpython-314.pyc +0 -0
  182. package/lib/python3.14/site-packages/PIL/__pycache__/_deprecate.cpython-314.pyc +0 -0
  183. package/lib/python3.14/site-packages/PIL/__pycache__/_tkinter_finder.cpython-314.pyc +0 -0
  184. package/lib/python3.14/site-packages/PIL/__pycache__/_typing.cpython-314.pyc +0 -0
  185. package/lib/python3.14/site-packages/PIL/__pycache__/_util.cpython-314.pyc +0 -0
  186. package/lib/python3.14/site-packages/PIL/__pycache__/_version.cpython-314.pyc +0 -0
  187. package/lib/python3.14/site-packages/PIL/__pycache__/features.cpython-314.pyc +0 -0
  188. package/lib/python3.14/site-packages/PIL/__pycache__/report.cpython-314.pyc +0 -0
  189. package/lib/python3.14/site-packages/PIL/_avif.pyi +3 -0
  190. package/lib/python3.14/site-packages/PIL/_binary.py +113 -0
  191. package/lib/python3.14/site-packages/PIL/_deprecate.py +72 -0
  192. package/lib/python3.14/site-packages/PIL/_imaging.cpython-314-wasm32-wasix.so +0 -0
  193. package/lib/python3.14/site-packages/PIL/_imaging.pyi +31 -0
  194. package/lib/python3.14/site-packages/PIL/_imagingcms.pyi +143 -0
  195. package/lib/python3.14/site-packages/PIL/_imagingft.cpython-314-wasm32-wasix.so +0 -0
  196. package/lib/python3.14/site-packages/PIL/_imagingft.pyi +70 -0
  197. package/lib/python3.14/site-packages/PIL/_imagingmath.cpython-314-wasm32-wasix.so +0 -0
  198. package/lib/python3.14/site-packages/PIL/_imagingmath.pyi +3 -0
  199. package/lib/python3.14/site-packages/PIL/_imagingmorph.cpython-314-wasm32-wasix.so +0 -0
  200. package/lib/python3.14/site-packages/PIL/_imagingmorph.pyi +3 -0
  201. package/lib/python3.14/site-packages/PIL/_imagingtk.cpython-314-wasm32-wasix.so +0 -0
  202. package/lib/python3.14/site-packages/PIL/_imagingtk.pyi +3 -0
  203. package/lib/python3.14/site-packages/PIL/_tkinter_finder.py +20 -0
  204. package/lib/python3.14/site-packages/PIL/_typing.py +45 -0
  205. package/lib/python3.14/site-packages/PIL/_util.py +29 -0
  206. package/lib/python3.14/site-packages/PIL/_version.py +4 -0
  207. package/lib/python3.14/site-packages/PIL/_webp.pyi +3 -0
  208. package/lib/python3.14/site-packages/PIL/features.py +343 -0
  209. package/lib/python3.14/site-packages/PIL/py.typed +0 -0
  210. package/lib/python3.14/site-packages/PIL/report.py +5 -0
  211. package/lib/python3.14/site-packages/pillow-12.3.0.dist-info/METADATA +180 -0
  212. package/lib/python3.14/site-packages/pillow-12.3.0.dist-info/RECORD +117 -0
  213. package/lib/python3.14/site-packages/pillow-12.3.0.dist-info/WHEEL +5 -0
  214. package/lib/python3.14/site-packages/pillow-12.3.0.dist-info/licenses/LICENSE +30 -0
  215. package/lib/python3.14/site-packages/pillow-12.3.0.dist-info/top_level.txt +1 -0
  216. package/lib/python3.14/site-packages/pillow-12.3.0.dist-info/zip-safe +1 -0
  217. 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")