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