JpegImagePlugin.py 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826
  1. #
  2. # The Python Imaging Library.
  3. # $Id$
  4. #
  5. # JPEG (JFIF) file handling
  6. #
  7. # See "Digital Compression and Coding of Continuous-Tone Still Images,
  8. # Part 1, Requirements and Guidelines" (CCITT T.81 / ISO 10918-1)
  9. #
  10. # History:
  11. # 1995-09-09 fl Created
  12. # 1995-09-13 fl Added full parser
  13. # 1996-03-25 fl Added hack to use the IJG command line utilities
  14. # 1996-05-05 fl Workaround Photoshop 2.5 CMYK polarity bug
  15. # 1996-05-28 fl Added draft support, JFIF version (0.1)
  16. # 1996-12-30 fl Added encoder options, added progression property (0.2)
  17. # 1997-08-27 fl Save mode 1 images as BW (0.3)
  18. # 1998-07-12 fl Added YCbCr to draft and save methods (0.4)
  19. # 1998-10-19 fl Don't hang on files using 16-bit DQT's (0.4.1)
  20. # 2001-04-16 fl Extract DPI settings from JFIF files (0.4.2)
  21. # 2002-07-01 fl Skip pad bytes before markers; identify Exif files (0.4.3)
  22. # 2003-04-25 fl Added experimental EXIF decoder (0.5)
  23. # 2003-06-06 fl Added experimental EXIF GPSinfo decoder
  24. # 2003-09-13 fl Extract COM markers
  25. # 2009-09-06 fl Added icc_profile support (from Florian Hoech)
  26. # 2009-03-06 fl Changed CMYK handling; always use Adobe polarity (0.6)
  27. # 2009-03-08 fl Added subsampling support (from Justin Huff).
  28. #
  29. # Copyright (c) 1997-2003 by Secret Labs AB.
  30. # Copyright (c) 1995-1996 by Fredrik Lundh.
  31. #
  32. # See the README file for information on usage and redistribution.
  33. #
  34. import array
  35. import io
  36. import math
  37. import os
  38. import struct
  39. import subprocess
  40. import sys
  41. import tempfile
  42. import warnings
  43. from . import Image, ImageFile, TiffImagePlugin
  44. from ._binary import i16be as i16
  45. from ._binary import i32be as i32
  46. from ._binary import o8
  47. from .JpegPresets import presets
  48. #
  49. # Parser
  50. def Skip(self, marker):
  51. n = i16(self.fp.read(2)) - 2
  52. ImageFile._safe_read(self.fp, n)
  53. def APP(self, marker):
  54. #
  55. # Application marker. Store these in the APP dictionary.
  56. # Also look for well-known application markers.
  57. n = i16(self.fp.read(2)) - 2
  58. s = ImageFile._safe_read(self.fp, n)
  59. app = "APP%d" % (marker & 15)
  60. self.app[app] = s # compatibility
  61. self.applist.append((app, s))
  62. if marker == 0xFFE0 and s[:4] == b"JFIF":
  63. # extract JFIF information
  64. self.info["jfif"] = version = i16(s, 5) # version
  65. self.info["jfif_version"] = divmod(version, 256)
  66. # extract JFIF properties
  67. try:
  68. jfif_unit = s[7]
  69. jfif_density = i16(s, 8), i16(s, 10)
  70. except Exception:
  71. pass
  72. else:
  73. if jfif_unit == 1:
  74. self.info["dpi"] = jfif_density
  75. self.info["jfif_unit"] = jfif_unit
  76. self.info["jfif_density"] = jfif_density
  77. elif marker == 0xFFE1 and s[:5] == b"Exif\0":
  78. if "exif" not in self.info:
  79. # extract EXIF information (incomplete)
  80. self.info["exif"] = s # FIXME: value will change
  81. elif marker == 0xFFE2 and s[:5] == b"FPXR\0":
  82. # extract FlashPix information (incomplete)
  83. self.info["flashpix"] = s # FIXME: value will change
  84. elif marker == 0xFFE2 and s[:12] == b"ICC_PROFILE\0":
  85. # Since an ICC profile can be larger than the maximum size of
  86. # a JPEG marker (64K), we need provisions to split it into
  87. # multiple markers. The format defined by the ICC specifies
  88. # one or more APP2 markers containing the following data:
  89. # Identifying string ASCII "ICC_PROFILE\0" (12 bytes)
  90. # Marker sequence number 1, 2, etc (1 byte)
  91. # Number of markers Total of APP2's used (1 byte)
  92. # Profile data (remainder of APP2 data)
  93. # Decoders should use the marker sequence numbers to
  94. # reassemble the profile, rather than assuming that the APP2
  95. # markers appear in the correct sequence.
  96. self.icclist.append(s)
  97. elif marker == 0xFFED and s[:14] == b"Photoshop 3.0\x00":
  98. # parse the image resource block
  99. offset = 14
  100. photoshop = self.info.setdefault("photoshop", {})
  101. while s[offset : offset + 4] == b"8BIM":
  102. try:
  103. offset += 4
  104. # resource code
  105. code = i16(s, offset)
  106. offset += 2
  107. # resource name (usually empty)
  108. name_len = s[offset]
  109. # name = s[offset+1:offset+1+name_len]
  110. offset += 1 + name_len
  111. offset += offset & 1 # align
  112. # resource data block
  113. size = i32(s, offset)
  114. offset += 4
  115. data = s[offset : offset + size]
  116. if code == 0x03ED: # ResolutionInfo
  117. data = {
  118. "XResolution": i32(data, 0) / 65536,
  119. "DisplayedUnitsX": i16(data, 4),
  120. "YResolution": i32(data, 8) / 65536,
  121. "DisplayedUnitsY": i16(data, 12),
  122. }
  123. photoshop[code] = data
  124. offset += size
  125. offset += offset & 1 # align
  126. except struct.error:
  127. break # insufficient data
  128. elif marker == 0xFFEE and s[:5] == b"Adobe":
  129. self.info["adobe"] = i16(s, 5)
  130. # extract Adobe custom properties
  131. try:
  132. adobe_transform = s[11]
  133. except IndexError:
  134. pass
  135. else:
  136. self.info["adobe_transform"] = adobe_transform
  137. elif marker == 0xFFE2 and s[:4] == b"MPF\0":
  138. # extract MPO information
  139. self.info["mp"] = s[4:]
  140. # offset is current location minus buffer size
  141. # plus constant header size
  142. self.info["mpoffset"] = self.fp.tell() - n + 4
  143. # If DPI isn't in JPEG header, fetch from EXIF
  144. if "dpi" not in self.info and "exif" in self.info:
  145. try:
  146. exif = self.getexif()
  147. resolution_unit = exif[0x0128]
  148. x_resolution = exif[0x011A]
  149. try:
  150. dpi = float(x_resolution[0]) / x_resolution[1]
  151. except TypeError:
  152. dpi = x_resolution
  153. if math.isnan(dpi):
  154. raise ValueError
  155. if resolution_unit == 3: # cm
  156. # 1 dpcm = 2.54 dpi
  157. dpi *= 2.54
  158. self.info["dpi"] = dpi, dpi
  159. except (KeyError, SyntaxError, ValueError, ZeroDivisionError):
  160. # SyntaxError for invalid/unreadable EXIF
  161. # KeyError for dpi not included
  162. # ZeroDivisionError for invalid dpi rational value
  163. # ValueError for dpi being an invalid float
  164. self.info["dpi"] = 72, 72
  165. def COM(self, marker):
  166. #
  167. # Comment marker. Store these in the APP dictionary.
  168. n = i16(self.fp.read(2)) - 2
  169. s = ImageFile._safe_read(self.fp, n)
  170. self.info["comment"] = s
  171. self.app["COM"] = s # compatibility
  172. self.applist.append(("COM", s))
  173. def SOF(self, marker):
  174. #
  175. # Start of frame marker. Defines the size and mode of the
  176. # image. JPEG is colour blind, so we use some simple
  177. # heuristics to map the number of layers to an appropriate
  178. # mode. Note that this could be made a bit brighter, by
  179. # looking for JFIF and Adobe APP markers.
  180. n = i16(self.fp.read(2)) - 2
  181. s = ImageFile._safe_read(self.fp, n)
  182. self._size = i16(s, 3), i16(s, 1)
  183. self.bits = s[0]
  184. if self.bits != 8:
  185. raise SyntaxError(f"cannot handle {self.bits}-bit layers")
  186. self.layers = s[5]
  187. if self.layers == 1:
  188. self.mode = "L"
  189. elif self.layers == 3:
  190. self.mode = "RGB"
  191. elif self.layers == 4:
  192. self.mode = "CMYK"
  193. else:
  194. raise SyntaxError(f"cannot handle {self.layers}-layer images")
  195. if marker in [0xFFC2, 0xFFC6, 0xFFCA, 0xFFCE]:
  196. self.info["progressive"] = self.info["progression"] = 1
  197. if self.icclist:
  198. # fixup icc profile
  199. self.icclist.sort() # sort by sequence number
  200. if self.icclist[0][13] == len(self.icclist):
  201. profile = []
  202. for p in self.icclist:
  203. profile.append(p[14:])
  204. icc_profile = b"".join(profile)
  205. else:
  206. icc_profile = None # wrong number of fragments
  207. self.info["icc_profile"] = icc_profile
  208. self.icclist = []
  209. for i in range(6, len(s), 3):
  210. t = s[i : i + 3]
  211. # 4-tuples: id, vsamp, hsamp, qtable
  212. self.layer.append((t[0], t[1] // 16, t[1] & 15, t[2]))
  213. def DQT(self, marker):
  214. #
  215. # Define quantization table. Note that there might be more
  216. # than one table in each marker.
  217. # FIXME: The quantization tables can be used to estimate the
  218. # compression quality.
  219. n = i16(self.fp.read(2)) - 2
  220. s = ImageFile._safe_read(self.fp, n)
  221. while len(s):
  222. v = s[0]
  223. precision = 1 if (v // 16 == 0) else 2 # in bytes
  224. qt_length = 1 + precision * 64
  225. if len(s) < qt_length:
  226. raise SyntaxError("bad quantization table marker")
  227. data = array.array("B" if precision == 1 else "H", s[1:qt_length])
  228. if sys.byteorder == "little" and precision > 1:
  229. data.byteswap() # the values are always big-endian
  230. self.quantization[v & 15] = [data[i] for i in zigzag_index]
  231. s = s[qt_length:]
  232. #
  233. # JPEG marker table
  234. MARKER = {
  235. 0xFFC0: ("SOF0", "Baseline DCT", SOF),
  236. 0xFFC1: ("SOF1", "Extended Sequential DCT", SOF),
  237. 0xFFC2: ("SOF2", "Progressive DCT", SOF),
  238. 0xFFC3: ("SOF3", "Spatial lossless", SOF),
  239. 0xFFC4: ("DHT", "Define Huffman table", Skip),
  240. 0xFFC5: ("SOF5", "Differential sequential DCT", SOF),
  241. 0xFFC6: ("SOF6", "Differential progressive DCT", SOF),
  242. 0xFFC7: ("SOF7", "Differential spatial", SOF),
  243. 0xFFC8: ("JPG", "Extension", None),
  244. 0xFFC9: ("SOF9", "Extended sequential DCT (AC)", SOF),
  245. 0xFFCA: ("SOF10", "Progressive DCT (AC)", SOF),
  246. 0xFFCB: ("SOF11", "Spatial lossless DCT (AC)", SOF),
  247. 0xFFCC: ("DAC", "Define arithmetic coding conditioning", Skip),
  248. 0xFFCD: ("SOF13", "Differential sequential DCT (AC)", SOF),
  249. 0xFFCE: ("SOF14", "Differential progressive DCT (AC)", SOF),
  250. 0xFFCF: ("SOF15", "Differential spatial (AC)", SOF),
  251. 0xFFD0: ("RST0", "Restart 0", None),
  252. 0xFFD1: ("RST1", "Restart 1", None),
  253. 0xFFD2: ("RST2", "Restart 2", None),
  254. 0xFFD3: ("RST3", "Restart 3", None),
  255. 0xFFD4: ("RST4", "Restart 4", None),
  256. 0xFFD5: ("RST5", "Restart 5", None),
  257. 0xFFD6: ("RST6", "Restart 6", None),
  258. 0xFFD7: ("RST7", "Restart 7", None),
  259. 0xFFD8: ("SOI", "Start of image", None),
  260. 0xFFD9: ("EOI", "End of image", None),
  261. 0xFFDA: ("SOS", "Start of scan", Skip),
  262. 0xFFDB: ("DQT", "Define quantization table", DQT),
  263. 0xFFDC: ("DNL", "Define number of lines", Skip),
  264. 0xFFDD: ("DRI", "Define restart interval", Skip),
  265. 0xFFDE: ("DHP", "Define hierarchical progression", SOF),
  266. 0xFFDF: ("EXP", "Expand reference component", Skip),
  267. 0xFFE0: ("APP0", "Application segment 0", APP),
  268. 0xFFE1: ("APP1", "Application segment 1", APP),
  269. 0xFFE2: ("APP2", "Application segment 2", APP),
  270. 0xFFE3: ("APP3", "Application segment 3", APP),
  271. 0xFFE4: ("APP4", "Application segment 4", APP),
  272. 0xFFE5: ("APP5", "Application segment 5", APP),
  273. 0xFFE6: ("APP6", "Application segment 6", APP),
  274. 0xFFE7: ("APP7", "Application segment 7", APP),
  275. 0xFFE8: ("APP8", "Application segment 8", APP),
  276. 0xFFE9: ("APP9", "Application segment 9", APP),
  277. 0xFFEA: ("APP10", "Application segment 10", APP),
  278. 0xFFEB: ("APP11", "Application segment 11", APP),
  279. 0xFFEC: ("APP12", "Application segment 12", APP),
  280. 0xFFED: ("APP13", "Application segment 13", APP),
  281. 0xFFEE: ("APP14", "Application segment 14", APP),
  282. 0xFFEF: ("APP15", "Application segment 15", APP),
  283. 0xFFF0: ("JPG0", "Extension 0", None),
  284. 0xFFF1: ("JPG1", "Extension 1", None),
  285. 0xFFF2: ("JPG2", "Extension 2", None),
  286. 0xFFF3: ("JPG3", "Extension 3", None),
  287. 0xFFF4: ("JPG4", "Extension 4", None),
  288. 0xFFF5: ("JPG5", "Extension 5", None),
  289. 0xFFF6: ("JPG6", "Extension 6", None),
  290. 0xFFF7: ("JPG7", "Extension 7", None),
  291. 0xFFF8: ("JPG8", "Extension 8", None),
  292. 0xFFF9: ("JPG9", "Extension 9", None),
  293. 0xFFFA: ("JPG10", "Extension 10", None),
  294. 0xFFFB: ("JPG11", "Extension 11", None),
  295. 0xFFFC: ("JPG12", "Extension 12", None),
  296. 0xFFFD: ("JPG13", "Extension 13", None),
  297. 0xFFFE: ("COM", "Comment", COM),
  298. }
  299. def _accept(prefix):
  300. # Magic number was taken from https://en.wikipedia.org/wiki/JPEG
  301. return prefix[0:3] == b"\xFF\xD8\xFF"
  302. ##
  303. # Image plugin for JPEG and JFIF images.
  304. class JpegImageFile(ImageFile.ImageFile):
  305. format = "JPEG"
  306. format_description = "JPEG (ISO 10918)"
  307. def _open(self):
  308. s = self.fp.read(3)
  309. if not _accept(s):
  310. raise SyntaxError("not a JPEG file")
  311. s = b"\xFF"
  312. # Create attributes
  313. self.bits = self.layers = 0
  314. # JPEG specifics (internal)
  315. self.layer = []
  316. self.huffman_dc = {}
  317. self.huffman_ac = {}
  318. self.quantization = {}
  319. self.app = {} # compatibility
  320. self.applist = []
  321. self.icclist = []
  322. while True:
  323. i = s[0]
  324. if i == 0xFF:
  325. s = s + self.fp.read(1)
  326. i = i16(s)
  327. else:
  328. # Skip non-0xFF junk
  329. s = self.fp.read(1)
  330. continue
  331. if i in MARKER:
  332. name, description, handler = MARKER[i]
  333. if handler is not None:
  334. handler(self, i)
  335. if i == 0xFFDA: # start of scan
  336. rawmode = self.mode
  337. if self.mode == "CMYK":
  338. rawmode = "CMYK;I" # assume adobe conventions
  339. self.tile = [("jpeg", (0, 0) + self.size, 0, (rawmode, ""))]
  340. # self.__offset = self.fp.tell()
  341. break
  342. s = self.fp.read(1)
  343. elif i == 0 or i == 0xFFFF:
  344. # padded marker or junk; move on
  345. s = b"\xff"
  346. elif i == 0xFF00: # Skip extraneous data (escaped 0xFF)
  347. s = self.fp.read(1)
  348. else:
  349. raise SyntaxError("no marker found")
  350. def load_read(self, read_bytes):
  351. """
  352. internal: read more image data
  353. For premature EOF and LOAD_TRUNCATED_IMAGES adds EOI marker
  354. so libjpeg can finish decoding
  355. """
  356. s = self.fp.read(read_bytes)
  357. if not s and ImageFile.LOAD_TRUNCATED_IMAGES:
  358. # Premature EOF.
  359. # Pretend file is finished adding EOI marker
  360. return b"\xFF\xD9"
  361. return s
  362. def draft(self, mode, size):
  363. if len(self.tile) != 1:
  364. return
  365. # Protect from second call
  366. if self.decoderconfig:
  367. return
  368. d, e, o, a = self.tile[0]
  369. scale = 1
  370. original_size = self.size
  371. if a[0] == "RGB" and mode in ["L", "YCbCr"]:
  372. self.mode = mode
  373. a = mode, ""
  374. if size:
  375. scale = min(self.size[0] // size[0], self.size[1] // size[1])
  376. for s in [8, 4, 2, 1]:
  377. if scale >= s:
  378. break
  379. e = (
  380. e[0],
  381. e[1],
  382. (e[2] - e[0] + s - 1) // s + e[0],
  383. (e[3] - e[1] + s - 1) // s + e[1],
  384. )
  385. self._size = ((self.size[0] + s - 1) // s, (self.size[1] + s - 1) // s)
  386. scale = s
  387. self.tile = [(d, e, o, a)]
  388. self.decoderconfig = (scale, 0)
  389. box = (0, 0, original_size[0] / scale, original_size[1] / scale)
  390. return (self.mode, box)
  391. def load_djpeg(self):
  392. # ALTERNATIVE: handle JPEGs via the IJG command line utilities
  393. f, path = tempfile.mkstemp()
  394. os.close(f)
  395. if os.path.exists(self.filename):
  396. subprocess.check_call(["djpeg", "-outfile", path, self.filename])
  397. else:
  398. raise ValueError("Invalid Filename")
  399. try:
  400. with Image.open(path) as _im:
  401. _im.load()
  402. self.im = _im.im
  403. finally:
  404. try:
  405. os.unlink(path)
  406. except OSError:
  407. pass
  408. self.mode = self.im.mode
  409. self._size = self.im.size
  410. self.tile = []
  411. def _getexif(self):
  412. return _getexif(self)
  413. def _getmp(self):
  414. return _getmp(self)
  415. def getxmp(self):
  416. """
  417. Returns a dictionary containing the XMP tags.
  418. Requires defusedxml to be installed.
  419. :returns: XMP tags in a dictionary.
  420. """
  421. for segment, content in self.applist:
  422. if segment == "APP1":
  423. marker, xmp_tags = content.rsplit(b"\x00", 1)
  424. if marker == b"http://ns.adobe.com/xap/1.0/":
  425. return self._getxmp(xmp_tags)
  426. return {}
  427. def _getexif(self):
  428. if "exif" not in self.info:
  429. return None
  430. return self.getexif()._get_merged_dict()
  431. def _getmp(self):
  432. # Extract MP information. This method was inspired by the "highly
  433. # experimental" _getexif version that's been in use for years now,
  434. # itself based on the ImageFileDirectory class in the TIFF plugin.
  435. # The MP record essentially consists of a TIFF file embedded in a JPEG
  436. # application marker.
  437. try:
  438. data = self.info["mp"]
  439. except KeyError:
  440. return None
  441. file_contents = io.BytesIO(data)
  442. head = file_contents.read(8)
  443. endianness = ">" if head[:4] == b"\x4d\x4d\x00\x2a" else "<"
  444. # process dictionary
  445. try:
  446. info = TiffImagePlugin.ImageFileDirectory_v2(head)
  447. file_contents.seek(info.next)
  448. info.load(file_contents)
  449. mp = dict(info)
  450. except Exception as e:
  451. raise SyntaxError("malformed MP Index (unreadable directory)") from e
  452. # it's an error not to have a number of images
  453. try:
  454. quant = mp[0xB001]
  455. except KeyError as e:
  456. raise SyntaxError("malformed MP Index (no number of images)") from e
  457. # get MP entries
  458. mpentries = []
  459. try:
  460. rawmpentries = mp[0xB002]
  461. for entrynum in range(0, quant):
  462. unpackedentry = struct.unpack_from(
  463. f"{endianness}LLLHH", rawmpentries, entrynum * 16
  464. )
  465. labels = ("Attribute", "Size", "DataOffset", "EntryNo1", "EntryNo2")
  466. mpentry = dict(zip(labels, unpackedentry))
  467. mpentryattr = {
  468. "DependentParentImageFlag": bool(mpentry["Attribute"] & (1 << 31)),
  469. "DependentChildImageFlag": bool(mpentry["Attribute"] & (1 << 30)),
  470. "RepresentativeImageFlag": bool(mpentry["Attribute"] & (1 << 29)),
  471. "Reserved": (mpentry["Attribute"] & (3 << 27)) >> 27,
  472. "ImageDataFormat": (mpentry["Attribute"] & (7 << 24)) >> 24,
  473. "MPType": mpentry["Attribute"] & 0x00FFFFFF,
  474. }
  475. if mpentryattr["ImageDataFormat"] == 0:
  476. mpentryattr["ImageDataFormat"] = "JPEG"
  477. else:
  478. raise SyntaxError("unsupported picture format in MPO")
  479. mptypemap = {
  480. 0x000000: "Undefined",
  481. 0x010001: "Large Thumbnail (VGA Equivalent)",
  482. 0x010002: "Large Thumbnail (Full HD Equivalent)",
  483. 0x020001: "Multi-Frame Image (Panorama)",
  484. 0x020002: "Multi-Frame Image: (Disparity)",
  485. 0x020003: "Multi-Frame Image: (Multi-Angle)",
  486. 0x030000: "Baseline MP Primary Image",
  487. }
  488. mpentryattr["MPType"] = mptypemap.get(mpentryattr["MPType"], "Unknown")
  489. mpentry["Attribute"] = mpentryattr
  490. mpentries.append(mpentry)
  491. mp[0xB002] = mpentries
  492. except KeyError as e:
  493. raise SyntaxError("malformed MP Index (bad MP Entry)") from e
  494. # Next we should try and parse the individual image unique ID list;
  495. # we don't because I've never seen this actually used in a real MPO
  496. # file and so can't test it.
  497. return mp
  498. # --------------------------------------------------------------------
  499. # stuff to save JPEG files
  500. RAWMODE = {
  501. "1": "L",
  502. "L": "L",
  503. "RGB": "RGB",
  504. "RGBX": "RGB",
  505. "CMYK": "CMYK;I", # assume adobe conventions
  506. "YCbCr": "YCbCr",
  507. }
  508. # fmt: off
  509. zigzag_index = (
  510. 0, 1, 5, 6, 14, 15, 27, 28,
  511. 2, 4, 7, 13, 16, 26, 29, 42,
  512. 3, 8, 12, 17, 25, 30, 41, 43,
  513. 9, 11, 18, 24, 31, 40, 44, 53,
  514. 10, 19, 23, 32, 39, 45, 52, 54,
  515. 20, 22, 33, 38, 46, 51, 55, 60,
  516. 21, 34, 37, 47, 50, 56, 59, 61,
  517. 35, 36, 48, 49, 57, 58, 62, 63,
  518. )
  519. samplings = {
  520. (1, 1, 1, 1, 1, 1): 0,
  521. (2, 1, 1, 1, 1, 1): 1,
  522. (2, 2, 1, 1, 1, 1): 2,
  523. }
  524. # fmt: on
  525. def convert_dict_qtables(qtables):
  526. warnings.warn(
  527. "convert_dict_qtables is deprecated and will be removed in Pillow 10"
  528. "(2023-01-02). Conversion is no longer needed.",
  529. DeprecationWarning,
  530. )
  531. return qtables
  532. def get_sampling(im):
  533. # There's no subsampling when images have only 1 layer
  534. # (grayscale images) or when they are CMYK (4 layers),
  535. # so set subsampling to the default value.
  536. #
  537. # NOTE: currently Pillow can't encode JPEG to YCCK format.
  538. # If YCCK support is added in the future, subsampling code will have
  539. # to be updated (here and in JpegEncode.c) to deal with 4 layers.
  540. if not hasattr(im, "layers") or im.layers in (1, 4):
  541. return -1
  542. sampling = im.layer[0][1:3] + im.layer[1][1:3] + im.layer[2][1:3]
  543. return samplings.get(sampling, -1)
  544. def _save(im, fp, filename):
  545. try:
  546. rawmode = RAWMODE[im.mode]
  547. except KeyError as e:
  548. raise OSError(f"cannot write mode {im.mode} as JPEG") from e
  549. info = im.encoderinfo
  550. dpi = [round(x) for x in info.get("dpi", (0, 0))]
  551. quality = info.get("quality", -1)
  552. subsampling = info.get("subsampling", -1)
  553. qtables = info.get("qtables")
  554. if quality == "keep":
  555. quality = -1
  556. subsampling = "keep"
  557. qtables = "keep"
  558. elif quality in presets:
  559. preset = presets[quality]
  560. quality = -1
  561. subsampling = preset.get("subsampling", -1)
  562. qtables = preset.get("quantization")
  563. elif not isinstance(quality, int):
  564. raise ValueError("Invalid quality setting")
  565. else:
  566. if subsampling in presets:
  567. subsampling = presets[subsampling].get("subsampling", -1)
  568. if isinstance(qtables, str) and qtables in presets:
  569. qtables = presets[qtables].get("quantization")
  570. if subsampling == "4:4:4":
  571. subsampling = 0
  572. elif subsampling == "4:2:2":
  573. subsampling = 1
  574. elif subsampling == "4:2:0":
  575. subsampling = 2
  576. elif subsampling == "4:1:1":
  577. # For compatibility. Before Pillow 4.3, 4:1:1 actually meant 4:2:0.
  578. # Set 4:2:0 if someone is still using that value.
  579. subsampling = 2
  580. elif subsampling == "keep":
  581. if im.format != "JPEG":
  582. raise ValueError("Cannot use 'keep' when original image is not a JPEG")
  583. subsampling = get_sampling(im)
  584. def validate_qtables(qtables):
  585. if qtables is None:
  586. return qtables
  587. if isinstance(qtables, str):
  588. try:
  589. lines = [
  590. int(num)
  591. for line in qtables.splitlines()
  592. for num in line.split("#", 1)[0].split()
  593. ]
  594. except ValueError as e:
  595. raise ValueError("Invalid quantization table") from e
  596. else:
  597. qtables = [lines[s : s + 64] for s in range(0, len(lines), 64)]
  598. if isinstance(qtables, (tuple, list, dict)):
  599. if isinstance(qtables, dict):
  600. qtables = [
  601. qtables[key] for key in range(len(qtables)) if key in qtables
  602. ]
  603. elif isinstance(qtables, tuple):
  604. qtables = list(qtables)
  605. if not (0 < len(qtables) < 5):
  606. raise ValueError("None or too many quantization tables")
  607. for idx, table in enumerate(qtables):
  608. try:
  609. if len(table) != 64:
  610. raise TypeError
  611. table = array.array("H", table)
  612. except TypeError as e:
  613. raise ValueError("Invalid quantization table") from e
  614. else:
  615. qtables[idx] = list(table)
  616. return qtables
  617. if qtables == "keep":
  618. if im.format != "JPEG":
  619. raise ValueError("Cannot use 'keep' when original image is not a JPEG")
  620. qtables = getattr(im, "quantization", None)
  621. qtables = validate_qtables(qtables)
  622. extra = b""
  623. icc_profile = info.get("icc_profile")
  624. if icc_profile:
  625. ICC_OVERHEAD_LEN = 14
  626. MAX_BYTES_IN_MARKER = 65533
  627. MAX_DATA_BYTES_IN_MARKER = MAX_BYTES_IN_MARKER - ICC_OVERHEAD_LEN
  628. markers = []
  629. while icc_profile:
  630. markers.append(icc_profile[:MAX_DATA_BYTES_IN_MARKER])
  631. icc_profile = icc_profile[MAX_DATA_BYTES_IN_MARKER:]
  632. i = 1
  633. for marker in markers:
  634. size = struct.pack(">H", 2 + ICC_OVERHEAD_LEN + len(marker))
  635. extra += (
  636. b"\xFF\xE2"
  637. + size
  638. + b"ICC_PROFILE\0"
  639. + o8(i)
  640. + o8(len(markers))
  641. + marker
  642. )
  643. i += 1
  644. # "progressive" is the official name, but older documentation
  645. # says "progression"
  646. # FIXME: issue a warning if the wrong form is used (post-1.1.7)
  647. progressive = info.get("progressive", False) or info.get("progression", False)
  648. optimize = info.get("optimize", False)
  649. exif = info.get("exif", b"")
  650. if isinstance(exif, Image.Exif):
  651. exif = exif.tobytes()
  652. # get keyword arguments
  653. im.encoderconfig = (
  654. quality,
  655. progressive,
  656. info.get("smooth", 0),
  657. optimize,
  658. info.get("streamtype", 0),
  659. dpi[0],
  660. dpi[1],
  661. subsampling,
  662. qtables,
  663. extra,
  664. exif,
  665. )
  666. # if we optimize, libjpeg needs a buffer big enough to hold the whole image
  667. # in a shot. Guessing on the size, at im.size bytes. (raw pixel size is
  668. # channels*size, this is a value that's been used in a django patch.
  669. # https://github.com/matthewwithanm/django-imagekit/issues/50
  670. bufsize = 0
  671. if optimize or progressive:
  672. # CMYK can be bigger
  673. if im.mode == "CMYK":
  674. bufsize = 4 * im.size[0] * im.size[1]
  675. # keep sets quality to -1, but the actual value may be high.
  676. elif quality >= 95 or quality == -1:
  677. bufsize = 2 * im.size[0] * im.size[1]
  678. else:
  679. bufsize = im.size[0] * im.size[1]
  680. # The EXIF info needs to be written as one block, + APP1, + one spare byte.
  681. # Ensure that our buffer is big enough. Same with the icc_profile block.
  682. bufsize = max(ImageFile.MAXBLOCK, bufsize, len(exif) + 5, len(extra) + 1)
  683. ImageFile._save(im, fp, [("jpeg", (0, 0) + im.size, 0, rawmode)], bufsize)
  684. def _save_cjpeg(im, fp, filename):
  685. # ALTERNATIVE: handle JPEGs via the IJG command line utilities.
  686. tempfile = im._dump()
  687. subprocess.check_call(["cjpeg", "-outfile", filename, tempfile])
  688. try:
  689. os.unlink(tempfile)
  690. except OSError:
  691. pass
  692. ##
  693. # Factory for making JPEG and MPO instances
  694. def jpeg_factory(fp=None, filename=None):
  695. im = JpegImageFile(fp, filename)
  696. try:
  697. mpheader = im._getmp()
  698. if mpheader[45057] > 1:
  699. # It's actually an MPO
  700. from .MpoImagePlugin import MpoImageFile
  701. # Don't reload everything, just convert it.
  702. im = MpoImageFile.adopt(im, mpheader)
  703. except (TypeError, IndexError):
  704. # It is really a JPEG
  705. pass
  706. except SyntaxError:
  707. warnings.warn(
  708. "Image appears to be a malformed MPO file, it will be "
  709. "interpreted as a base JPEG file"
  710. )
  711. return im
  712. # ---------------------------------------------------------------------
  713. # Registry stuff
  714. Image.register_open(JpegImageFile.format, jpeg_factory, _accept)
  715. Image.register_save(JpegImageFile.format, _save)
  716. Image.register_extensions(JpegImageFile.format, [".jfif", ".jpe", ".jpg", ".jpeg"])
  717. Image.register_mime(JpegImageFile.format, "image/jpeg")