{
    "file_diffs": [
        {
            "old_file_content": "#\n# The Python Imaging Library.\n# $Id$\n#\n# JPEG (JFIF) file handling\n#\n# See \"Digital Compression and Coding of Continuous-Tone Still Images,\n# Part 1, Requirements and Guidelines\" (CCITT T.81 / ISO 10918-1)\n#\n# History:\n# 1995-09-09 fl   Created\n# 1995-09-13 fl   Added full parser\n# 1996-03-25 fl   Added hack to use the IJG command line utilities\n# 1996-05-05 fl   Workaround Photoshop 2.5 CMYK polarity bug\n# 1996-05-28 fl   Added draft support, JFIF version (0.1)\n# 1996-12-30 fl   Added encoder options, added progression property (0.2)\n# 1997-08-27 fl   Save mode 1 images as BW (0.3)\n# 1998-07-12 fl   Added YCbCr to draft and save methods (0.4)\n# 1998-10-19 fl   Don't hang on files using 16-bit DQT's (0.4.1)\n# 2001-04-16 fl   Extract DPI settings from JFIF files (0.4.2)\n# 2002-07-01 fl   Skip pad bytes before markers; identify Exif files (0.4.3)\n# 2003-04-25 fl   Added experimental EXIF decoder (0.5)\n# 2003-06-06 fl   Added experimental EXIF GPSinfo decoder\n# 2003-09-13 fl   Extract COM markers\n# 2009-09-06 fl   Added icc_profile support (from Florian Hoech)\n# 2009-03-06 fl   Changed CMYK handling; always use Adobe polarity (0.6)\n# 2009-03-08 fl   Added subsampling support (from Justin Huff).\n#\n# Copyright (c) 1997-2003 by Secret Labs AB.\n# Copyright (c) 1995-1996 by Fredrik Lundh.\n#\n# See the README file for information on usage and redistribution.\n#\n\nimport array\nimport struct\nimport io\nimport warnings\nfrom struct import unpack_from\nfrom PIL import Image, ImageFile, TiffImagePlugin, _binary\nfrom PIL.JpegPresets import presets\nfrom PIL._util import isStringType\n\ni8 = _binary.i8\no8 = _binary.o8\ni16 = _binary.i16be\ni32 = _binary.i32be\n\n__version__ = \"0.6\"\n\n\n#\n# Parser\n\ndef Skip(self, marker):\n    n = i16(self.fp.read(2))-2\n    ImageFile._safe_read(self.fp, n)\n\n\ndef APP(self, marker):\n    #\n    # Application marker.  Store these in the APP dictionary.\n    # Also look for well-known application markers.\n\n    n = i16(self.fp.read(2))-2\n    s = ImageFile._safe_read(self.fp, n)\n\n    app = \"APP%d\" % (marker & 15)\n\n    self.app[app] = s  # compatibility\n    self.applist.append((app, s))\n\n    if marker == 0xFFE0 and s[:4] == b\"JFIF\":\n        # extract JFIF information\n        self.info[\"jfif\"] = version = i16(s, 5)  # version\n        self.info[\"jfif_version\"] = divmod(version, 256)\n        # extract JFIF properties\n        try:\n            jfif_unit = i8(s[7])\n            jfif_density = i16(s, 8), i16(s, 10)\n        except:\n            pass\n        else:\n            if jfif_unit == 1:\n                self.info[\"dpi\"] = jfif_density\n            self.info[\"jfif_unit\"] = jfif_unit\n            self.info[\"jfif_density\"] = jfif_density\n    elif marker == 0xFFE1 and s[:5] == b\"Exif\\0\":\n        # extract Exif information (incomplete)\n        self.info[\"exif\"] = s  # FIXME: value will change\n    elif marker == 0xFFE2 and s[:5] == b\"FPXR\\0\":\n        # extract FlashPix information (incomplete)\n        self.info[\"flashpix\"] = s  # FIXME: value will change\n    elif marker == 0xFFE2 and s[:12] == b\"ICC_PROFILE\\0\":\n        # Since an ICC profile can be larger than the maximum size of\n        # a JPEG marker (64K), we need provisions to split it into\n        # multiple markers. The format defined by the ICC specifies\n        # one or more APP2 markers containing the following data:\n        #   Identifying string      ASCII \"ICC_PROFILE\\0\"  (12 bytes)\n        #   Marker sequence number  1, 2, etc (1 byte)\n        #   Number of markers       Total of APP2's used (1 byte)\n        #   Profile data            (remainder of APP2 data)\n        # Decoders should use the marker sequence numbers to\n        # reassemble the profile, rather than assuming that the APP2\n        # markers appear in the correct sequence.\n        self.icclist.append(s)\n    elif marker == 0xFFEE and s[:5] == b\"Adobe\":\n        self.info[\"adobe\"] = i16(s, 5)\n        # extract Adobe custom properties\n        try:\n            adobe_transform = i8(s[1])\n        except:\n            pass\n        else:\n            self.info[\"adobe_transform\"] = adobe_transform\n    elif marker == 0xFFE2 and s[:4] == b\"MPF\\0\":\n        # extract MPO information\n        self.info[\"mp\"] = s[4:]\n        # offset is current location minus buffer size\n        # plus constant header size\n        self.info[\"mpoffset\"] = self.fp.tell() - n + 4\n\n\ndef COM(self, marker):\n    #\n    # Comment marker.  Store these in the APP dictionary.\n    n = i16(self.fp.read(2))-2\n    s = ImageFile._safe_read(self.fp, n)\n\n    self.app[\"COM\"] = s  # compatibility\n    self.applist.append((\"COM\", s))\n\n\ndef SOF(self, marker):\n    #\n    # Start of frame marker.  Defines the size and mode of the\n    # image.  JPEG is colour blind, so we use some simple\n    # heuristics to map the number of layers to an appropriate\n    # mode.  Note that this could be made a bit brighter, by\n    # looking for JFIF and Adobe APP markers.\n\n    n = i16(self.fp.read(2))-2\n    s = ImageFile._safe_read(self.fp, n)\n    self.size = i16(s[3:]), i16(s[1:])\n\n    self.bits = i8(s[0])\n    if self.bits != 8:\n        raise SyntaxError(\"cannot handle %d-bit layers\" % self.bits)\n\n    self.layers = i8(s[5])\n    if self.layers == 1:\n        self.mode = \"L\"\n    elif self.layers == 3:\n        self.mode = \"RGB\"\n    elif self.layers == 4:\n        self.mode = \"CMYK\"\n    else:\n        raise SyntaxError(\"cannot handle %d-layer images\" % self.layers)\n\n    if marker in [0xFFC2, 0xFFC6, 0xFFCA, 0xFFCE]:\n        self.info[\"progressive\"] = self.info[\"progression\"] = 1\n\n    if self.icclist:\n        # fixup icc profile\n        self.icclist.sort()  # sort by sequence number\n        if i8(self.icclist[0][13]) == len(self.icclist):\n            profile = []\n            for p in self.icclist:\n                profile.append(p[14:])\n            icc_profile = b\"\".join(profile)\n        else:\n            icc_profile = None  # wrong number of fragments\n        self.info[\"icc_profile\"] = icc_profile\n        self.icclist = None\n\n    for i in range(6, len(s), 3):\n        t = s[i:i+3]\n        # 4-tuples: id, vsamp, hsamp, qtable\n        self.layer.append((t[0], i8(t[1])//16, i8(t[1]) & 15, i8(t[2])))\n\n\ndef DQT(self, marker):\n    #\n    # Define quantization table.  Support baseline 8-bit tables\n    # only.  Note that there might be more than one table in\n    # each marker.\n\n    # FIXME: The quantization tables can be used to estimate the\n    # compression quality.\n\n    n = i16(self.fp.read(2))-2\n    s = ImageFile._safe_read(self.fp, n)\n    while len(s):\n        if len(s) < 65:\n            raise SyntaxError(\"bad quantization table marker\")\n        v = i8(s[0])\n        if v//16 == 0:\n            self.quantization[v & 15] = array.array(\"B\", s[1:65])\n            s = s[65:]\n        else:\n            return  # FIXME: add code to read 16-bit tables!\n            # raise SyntaxError, \"bad quantization table element size\"\n\n\n#\n# JPEG marker table\n\nMARKER = {\n    0xFFC0: (\"SOF0\", \"Baseline DCT\", SOF),\n    0xFFC1: (\"SOF1\", \"Extended Sequential DCT\", SOF),\n    0xFFC2: (\"SOF2\", \"Progressive DCT\", SOF),\n    0xFFC3: (\"SOF3\", \"Spatial lossless\", SOF),\n    0xFFC4: (\"DHT\", \"Define Huffman table\", Skip),\n    0xFFC5: (\"SOF5\", \"Differential sequential DCT\", SOF),\n    0xFFC6: (\"SOF6\", \"Differential progressive DCT\", SOF),\n    0xFFC7: (\"SOF7\", \"Differential spatial\", SOF),\n    0xFFC8: (\"JPG\", \"Extension\", None),\n    0xFFC9: (\"SOF9\", \"Extended sequential DCT (AC)\", SOF),\n    0xFFCA: (\"SOF10\", \"Progressive DCT (AC)\", SOF),\n    0xFFCB: (\"SOF11\", \"Spatial lossless DCT (AC)\", SOF),\n    0xFFCC: (\"DAC\", \"Define arithmetic coding conditioning\", Skip),\n    0xFFCD: (\"SOF13\", \"Differential sequential DCT (AC)\", SOF),\n    0xFFCE: (\"SOF14\", \"Differential progressive DCT (AC)\", SOF),\n    0xFFCF: (\"SOF15\", \"Differential spatial (AC)\", SOF),\n    0xFFD0: (\"RST0\", \"Restart 0\", None),\n    0xFFD1: (\"RST1\", \"Restart 1\", None),\n    0xFFD2: (\"RST2\", \"Restart 2\", None),\n    0xFFD3: (\"RST3\", \"Restart 3\", None),\n    0xFFD4: (\"RST4\", \"Restart 4\", None),\n    0xFFD5: (\"RST5\", \"Restart 5\", None),\n    0xFFD6: (\"RST6\", \"Restart 6\", None),\n    0xFFD7: (\"RST7\", \"Restart 7\", None),\n    0xFFD8: (\"SOI\", \"Start of image\", None),\n    0xFFD9: (\"EOI\", \"End of image\", None),\n    0xFFDA: (\"SOS\", \"Start of scan\", Skip),\n    0xFFDB: (\"DQT\", \"Define quantization table\", DQT),\n    0xFFDC: (\"DNL\", \"Define number of lines\", Skip),\n    0xFFDD: (\"DRI\", \"Define restart interval\", Skip),\n    0xFFDE: (\"DHP\", \"Define hierarchical progression\", SOF),\n    0xFFDF: (\"EXP\", \"Expand reference component\", Skip),\n    0xFFE0: (\"APP0\", \"Application segment 0\", APP),\n    0xFFE1: (\"APP1\", \"Application segment 1\", APP),\n    0xFFE2: (\"APP2\", \"Application segment 2\", APP),\n    0xFFE3: (\"APP3\", \"Application segment 3\", APP),\n    0xFFE4: (\"APP4\", \"Application segment 4\", APP),\n    0xFFE5: (\"APP5\", \"Application segment 5\", APP),\n    0xFFE6: (\"APP6\", \"Application segment 6\", APP),\n    0xFFE7: (\"APP7\", \"Application segment 7\", APP),\n    0xFFE8: (\"APP8\", \"Application segment 8\", APP),\n    0xFFE9: (\"APP9\", \"Application segment 9\", APP),\n    0xFFEA: (\"APP10\", \"Application segment 10\", APP),\n    0xFFEB: (\"APP11\", \"Application segment 11\", APP),\n    0xFFEC: (\"APP12\", \"Application segment 12\", APP),\n    0xFFED: (\"APP13\", \"Application segment 13\", APP),\n    0xFFEE: (\"APP14\", \"Application segment 14\", APP),\n    0xFFEF: (\"APP15\", \"Application segment 15\", APP),\n    0xFFF0: (\"JPG0\", \"Extension 0\", None),\n    0xFFF1: (\"JPG1\", \"Extension 1\", None),\n    0xFFF2: (\"JPG2\", \"Extension 2\", None),\n    0xFFF3: (\"JPG3\", \"Extension 3\", None),\n    0xFFF4: (\"JPG4\", \"Extension 4\", None),\n    0xFFF5: (\"JPG5\", \"Extension 5\", None),\n    0xFFF6: (\"JPG6\", \"Extension 6\", None),\n    0xFFF7: (\"JPG7\", \"Extension 7\", None),\n    0xFFF8: (\"JPG8\", \"Extension 8\", None),\n    0xFFF9: (\"JPG9\", \"Extension 9\", None),\n    0xFFFA: (\"JPG10\", \"Extension 10\", None),\n    0xFFFB: (\"JPG11\", \"Extension 11\", None),\n    0xFFFC: (\"JPG12\", \"Extension 12\", None),\n    0xFFFD: (\"JPG13\", \"Extension 13\", None),\n    0xFFFE: (\"COM\", \"Comment\", COM)\n}\n\n\ndef _accept(prefix):\n    return prefix[0:1] == b\"\\377\"\n\n\n##\n# Image plugin for JPEG and JFIF images.\n\nclass JpegImageFile(ImageFile.ImageFile):\n\n    format = \"JPEG\"\n    format_description = \"JPEG (ISO 10918)\"\n\n    def _open(self):\n\n        s = self.fp.read(1)\n\n        if i8(s) != 255:\n            raise SyntaxError(\"not a JPEG file\")\n\n        # Create attributes\n        self.bits = self.layers = 0\n\n        # JPEG specifics (internal)\n        self.layer = []\n        self.huffman_dc = {}\n        self.huffman_ac = {}\n        self.quantization = {}\n        self.app = {}  # compatibility\n        self.applist = []\n        self.icclist = []\n\n        while True:\n\n            i = i8(s)\n            if i == 0xFF:\n                s = s + self.fp.read(1)\n                i = i16(s)\n            else:\n                # Skip non-0xFF junk\n                s = self.fp.read(1)\n                continue\n\n            if i in MARKER:\n                name, description, handler = MARKER[i]\n                # print hex(i), name, description\n                if handler is not None:\n                    handler(self, i)\n                if i == 0xFFDA:  # start of scan\n                    rawmode = self.mode\n                    if self.mode == \"CMYK\":\n                        rawmode = \"CMYK;I\"  # assume adobe conventions\n                    self.tile = [(\"jpeg\", (0, 0) + self.size, 0,\n                                 (rawmode, \"\"))]\n                    # self.__offset = self.fp.tell()\n                    break\n                s = self.fp.read(1)\n            elif i == 0 or i == 0xFFFF:\n                # padded marker or junk; move on\n                s = b\"\\xff\"\n            elif i == 0xFF00:  # Skip extraneous data (escaped 0xFF)\n                s = self.fp.read(1)\n            else:\n                raise SyntaxError(\"no marker found\")\n\n    def draft(self, mode, size):\n\n        if len(self.tile) != 1:\n            return\n\n        d, e, o, a = self.tile[0]\n        scale = 0\n\n        if a[0] == \"RGB\" and mode in [\"L\", \"YCbCr\"]:\n            self.mode = mode\n            a = mode, \"\"\n\n        if size:\n            scale = max(self.size[0] // size[0], self.size[1] // size[1])\n            for s in [8, 4, 2, 1]:\n                if scale >= s:\n                    break\n            e = e[0], e[1], (e[2]-e[0]+s-1)//s+e[0], (e[3]-e[1]+s-1)//s+e[1]\n            self.size = ((self.size[0]+s-1)//s, (self.size[1]+s-1)//s)\n            scale = s\n\n        self.tile = [(d, e, o, a)]\n        self.decoderconfig = (scale, 0)\n\n        return self\n\n    def load_djpeg(self):\n\n        # ALTERNATIVE: handle JPEGs via the IJG command line utilities\n\n        import subprocess\n        import tempfile\n        import os\n        f, path = tempfile.mkstemp()\n        os.close(f)\n        if os.path.exists(self.filename):\n            subprocess.check_call([\"djpeg\", \"-outfile\", path, self.filename])\n        else:\n            raise ValueError(\"Invalid Filename\")\n\n        try:\n            self.im = Image.core.open_ppm(path)\n        finally:\n            try:\n                os.unlink(path)\n            except OSError:\n                pass\n\n        self.mode = self.im.mode\n        self.size = self.im.size\n\n        self.tile = []\n\n    def _getexif(self):\n        return _getexif(self)\n\n    def _getmp(self):\n        return _getmp(self)\n\n\ndef _fixup_dict(src_dict):\n    # Helper function for _getexif()\n    # returns a dict with any single item tuples/lists as individual values\n    def _fixup(value):\n        try:\n            if len(value) == 1 and not isinstance(value, dict):\n                return value[0]\n        except: pass\n        return value\n\n    return dict([(k, _fixup(v)) for k, v in src_dict.items()])\n\n\ndef _getexif(self):\n    # Extract EXIF information.  This method is highly experimental,\n    # and is likely to be replaced with something better in a future\n    # version.\n\n    # The EXIF record consists of a TIFF file embedded in a JPEG\n    # application marker (!).\n    try:\n        data = self.info[\"exif\"]\n    except KeyError:\n        return None\n    file = io.BytesIO(data[6:])\n    head = file.read(8)\n    # process dictionary\n    info = TiffImagePlugin.ImageFileDirectory_v1(head)\n    info.load(file)\n    exif = dict(_fixup_dict(info))\n    # get exif extension\n    try:\n        # exif field 0x8769 is an offset pointer to the location\n        # of the nested embedded exif ifd.\n        # It should be a long, but may be corrupted.\n        file.seek(exif[0x8769])\n    except (KeyError, TypeError):\n        pass\n    else:\n        info = TiffImagePlugin.ImageFileDirectory_v1(head)\n        info.load(file)\n        exif.update(_fixup_dict(info))\n    # get gpsinfo extension\n    try:\n        # exif field 0x8825 is an offset pointer to the location\n        # of the nested embedded gps exif ifd.\n        # It should be a long, but may be corrupted.\n        file.seek(exif[0x8825])\n    except (KeyError, TypeError):\n        pass\n    else:\n        info = TiffImagePlugin.ImageFileDirectory_v1(head)\n        info.load(file)\n        exif[0x8825] = _fixup_dict(info)\n\n    return exif\n\n\ndef _getmp(self):\n    # Extract MP information.  This method was inspired by the \"highly\n    # experimental\" _getexif version that's been in use for years now,\n    # itself based on the ImageFileDirectory class in the TIFF plug-in.\n\n    # The MP record essentially consists of a TIFF file embedded in a JPEG\n    # application marker.\n    try:\n        data = self.info[\"mp\"]\n    except KeyError:\n        return None\n    file_contents = io.BytesIO(data)\n    head = file_contents.read(8)\n    endianness = '>' if head[:4] == b'\\x4d\\x4d\\x00\\x2a' else '<'\n    # process dictionary\n    try:\n        info = TiffImagePlugin.ImageFileDirectory_v2(head)\n        info.load(file_contents)\n        mp = dict(info)\n    except:\n        raise SyntaxError(\"malformed MP Index (unreadable directory)\")\n    # it's an error not to have a number of images\n    try:\n        quant = mp[0xB001]\n    except KeyError:\n        raise SyntaxError(\"malformed MP Index (no number of images)\")\n    # get MP entries\n    mpentries = []\n    try:\n        rawmpentries = mp[0xB002]\n        for entrynum in range(0, quant):\n            unpackedentry = unpack_from(\n                '{0}LLLHH'.format(endianness), rawmpentries, entrynum * 16)\n            labels = ('Attribute', 'Size', 'DataOffset', 'EntryNo1',\n                      'EntryNo2')\n            mpentry = dict(zip(labels, unpackedentry))\n            mpentryattr = {\n                'DependentParentImageFlag': bool(mpentry['Attribute'] &\n                                                 (1 << 31)),\n                'DependentChildImageFlag': bool(mpentry['Attribute'] &\n                                                (1 << 30)),\n                'RepresentativeImageFlag': bool(mpentry['Attribute'] &\n                                                (1 << 29)),\n                'Reserved': (mpentry['Attribute'] & (3 << 27)) >> 27,\n                'ImageDataFormat': (mpentry['Attribute'] & (7 << 24)) >> 24,\n                'MPType': mpentry['Attribute'] & 0x00FFFFFF\n            }\n            if mpentryattr['ImageDataFormat'] == 0:\n                mpentryattr['ImageDataFormat'] = 'JPEG'\n            else:\n                raise SyntaxError(\"unsupported picture format in MPO\")\n            mptypemap = {\n                0x000000: 'Undefined',\n                0x010001: 'Large Thumbnail (VGA Equivalent)',\n                0x010002: 'Large Thumbnail (Full HD Equivalent)',\n                0x020001: 'Multi-Frame Image (Panorama)',\n                0x020002: 'Multi-Frame Image: (Disparity)',\n                0x020003: 'Multi-Frame Image: (Multi-Angle)',\n                0x030000: 'Baseline MP Primary Image'\n            }\n            mpentryattr['MPType'] = mptypemap.get(mpentryattr['MPType'],\n                                                  'Unknown')\n            mpentry['Attribute'] = mpentryattr\n            mpentries.append(mpentry)\n        mp[0xB002] = mpentries\n    except KeyError:\n        raise SyntaxError(\"malformed MP Index (bad MP Entry)\")\n    # Next we should try and parse the individual image unique ID list;\n    # we don't because I've never seen this actually used in a real MPO\n    # file and so can't test it.\n    return mp\n\n\n# --------------------------------------------------------------------\n# stuff to save JPEG files\n\nRAWMODE = {\n    \"1\": \"L\",\n    \"L\": \"L\",\n    \"RGB\": \"RGB\",\n    \"RGBA\": \"RGB\",\n    \"RGBX\": \"RGB\",\n    \"CMYK\": \"CMYK;I\",  # assume adobe conventions\n    \"YCbCr\": \"YCbCr\",\n}\n\nzigzag_index = (0,  1,  5,  6, 14, 15, 27, 28,\n                2,  4,  7, 13, 16, 26, 29, 42,\n                3,  8, 12, 17, 25, 30, 41, 43,\n                9, 11, 18, 24, 31, 40, 44, 53,\n               10, 19, 23, 32, 39, 45, 52, 54,\n               20, 22, 33, 38, 46, 51, 55, 60,\n               21, 34, 37, 47, 50, 56, 59, 61,\n               35, 36, 48, 49, 57, 58, 62, 63)\n\nsamplings = {(1, 1, 1, 1, 1, 1): 0,\n             (2, 1, 1, 1, 1, 1): 1,\n             (2, 2, 1, 1, 1, 1): 2,\n             }\n\n\ndef convert_dict_qtables(qtables):\n    qtables = [qtables[key] for key in range(len(qtables)) if key in qtables]\n    for idx, table in enumerate(qtables):\n        qtables[idx] = [table[i] for i in zigzag_index]\n    return qtables\n\n\ndef get_sampling(im):\n    # There's no subsampling when image have only 1 layer\n    # (grayscale images) or when they are CMYK (4 layers),\n    # so set subsampling to default value.\n    #\n    # NOTE: currently Pillow can't encode JPEG to YCCK format.\n    # If YCCK support is added in the future, subsampling code will have\n    # to be updated (here and in JpegEncode.c) to deal with 4 layers.\n    if not hasattr(im, 'layers') or im.layers in (1, 4):\n        return -1\n    sampling = im.layer[0][1:3] + im.layer[1][1:3] + im.layer[2][1:3]\n    return samplings.get(sampling, -1)\n\n\ndef _save(im, fp, filename):\n\n    try:\n        rawmode = RAWMODE[im.mode]\n    except KeyError:\n        raise IOError(\"cannot write mode %s as JPEG\" % im.mode)\n\n    if im.mode == 'RGBA':\n        warnings.warn(\n            'You are saving RGBA image as JPEG. The alpha channel will be '\n            'discarded. This conversion is deprecated and will be disabled '\n            'in Pillow 3.7. Please, convert the image to RGB explicitly.',\n            DeprecationWarning\n        )\n\n    info = im.encoderinfo\n\n    dpi = [int(round(x)) for x in info.get(\"dpi\", (0, 0))]\n\n    quality = info.get(\"quality\", 0)\n    subsampling = info.get(\"subsampling\", -1)\n    qtables = info.get(\"qtables\")\n\n    if quality == \"keep\":\n        quality = 0\n        subsampling = \"keep\"\n        qtables = \"keep\"\n    elif quality in presets:\n        preset = presets[quality]\n        quality = 0\n        subsampling = preset.get('subsampling', -1)\n        qtables = preset.get('quantization')\n    elif not isinstance(quality, int):\n        raise ValueError(\"Invalid quality setting\")\n    else:\n        if subsampling in presets:\n            subsampling = presets[subsampling].get('subsampling', -1)\n        if isStringType(qtables) and qtables in presets:\n            qtables = presets[qtables].get('quantization')\n\n    if subsampling == \"4:4:4\":\n        subsampling = 0\n    elif subsampling == \"4:2:2\":\n        subsampling = 1\n    elif subsampling == \"4:1:1\":\n        subsampling = 2\n    elif subsampling == \"keep\":\n        if im.format != \"JPEG\":\n            raise ValueError(\n                \"Cannot use 'keep' when original image is not a JPEG\")\n        subsampling = get_sampling(im)\n\n    def validate_qtables(qtables):\n        if qtables is None:\n            return qtables\n        if isStringType(qtables):\n            try:\n                lines = [int(num) for line in qtables.splitlines()\n                         for num in line.split('#', 1)[0].split()]\n            except ValueError:\n                raise ValueError(\"Invalid quantization table\")\n            else:\n                qtables = [lines[s:s+64] for s in range(0, len(lines), 64)]\n        if isinstance(qtables, (tuple, list, dict)):\n            if isinstance(qtables, dict):\n                qtables = convert_dict_qtables(qtables)\n            elif isinstance(qtables, tuple):\n                qtables = list(qtables)\n            if not (0 < len(qtables) < 5):\n                raise ValueError(\"None or too many quantization tables\")\n            for idx, table in enumerate(qtables):\n                try:\n                    if len(table) != 64:\n                        raise\n                    table = array.array('B', table)\n                except TypeError:\n                    raise ValueError(\"Invalid quantization table\")\n                else:\n                    qtables[idx] = list(table)\n            return qtables\n\n    if qtables == \"keep\":\n        if im.format != \"JPEG\":\n            raise ValueError(\n                \"Cannot use 'keep' when original image is not a JPEG\")\n        qtables = getattr(im, \"quantization\", None)\n    qtables = validate_qtables(qtables)\n\n    extra = b\"\"\n\n    icc_profile = info.get(\"icc_profile\")\n    if icc_profile:\n        ICC_OVERHEAD_LEN = 14\n        MAX_BYTES_IN_MARKER = 65533\n        MAX_DATA_BYTES_IN_MARKER = MAX_BYTES_IN_MARKER - ICC_OVERHEAD_LEN\n        markers = []\n        while icc_profile:\n            markers.append(icc_profile[:MAX_DATA_BYTES_IN_MARKER])\n            icc_profile = icc_profile[MAX_DATA_BYTES_IN_MARKER:]\n        i = 1\n        for marker in markers:\n            size = struct.pack(\">H\", 2 + ICC_OVERHEAD_LEN + len(marker))\n            extra += (b\"\\xFF\\xE2\" + size + b\"ICC_PROFILE\\0\" + o8(i) +\n                      o8(len(markers)) + marker)\n            i += 1\n\n    # get keyword arguments\n    im.encoderconfig = (\n        quality,\n        # \"progressive\" is the official name, but older documentation\n        # says \"progression\"\n        # FIXME: issue a warning if the wrong form is used (post-1.1.7)\n        \"progressive\" in info or \"progression\" in info,\n        info.get(\"smooth\", 0),\n        \"optimize\" in info,\n        info.get(\"streamtype\", 0),\n        dpi[0], dpi[1],\n        subsampling,\n        qtables,\n        extra,\n        info.get(\"exif\", b\"\")\n        )\n\n    # if we optimize, libjpeg needs a buffer big enough to hold the whole image\n    # in a shot. Guessing on the size, at im.size bytes. (raw pizel size is\n    # channels*size, this is a value that's been used in a django patch.\n    # https://github.com/matthewwithanm/django-imagekit/issues/50\n    bufsize = 0\n    if \"optimize\" in info or \"progressive\" in info or \"progression\" in info:\n        # keep sets quality to 0, but the actual value may be high.\n        if quality >= 95 or quality == 0:\n            bufsize = 2 * im.size[0] * im.size[1]\n        else:\n            bufsize = im.size[0] * im.size[1]\n\n    # The exif info needs to be written as one block, + APP1, + one spare byte.\n    # Ensure that our buffer is big enough\n    bufsize = max(ImageFile.MAXBLOCK, bufsize, len(info.get(\"exif\", b\"\")) + 5)\n\n    ImageFile._save(im, fp, [(\"jpeg\", (0, 0)+im.size, 0, rawmode)], bufsize)\n\n\ndef _save_cjpeg(im, fp, filename):\n    # ALTERNATIVE: handle JPEGs via the IJG command line utilities.\n    import os\n    import subprocess\n    tempfile = im._dump()\n    subprocess.check_call([\"cjpeg\", \"-outfile\", filename, tempfile])\n    try:\n        os.unlink(tempfile)\n    except OSError:\n        pass\n\n\n##\n# Factory for making JPEG and MPO instances\ndef jpeg_factory(fp=None, filename=None):\n    im = JpegImageFile(fp, filename)\n    try:\n        mpheader = im._getmp()\n        if mpheader[45057] > 1:\n            # It's actually an MPO\n            from .MpoImagePlugin import MpoImageFile\n            im = MpoImageFile(fp, filename)\n    except (TypeError, IndexError):\n        # It is really a JPEG\n        pass\n    except SyntaxError:\n        warnings.warn(\"Image appears to be a malformed MPO file, it will be \"\n                      \"interpreted as a base JPEG file\")\n    return im\n\n\n# -------------------------------------------------------------------q-\n# Registry stuff\n\nImage.register_open(JpegImageFile.format, jpeg_factory, _accept)\nImage.register_save(JpegImageFile.format, _save)\n\nImage.register_extension(JpegImageFile.format, \".jfif\")\nImage.register_extension(JpegImageFile.format, \".jpe\")\nImage.register_extension(JpegImageFile.format, \".jpg\")\nImage.register_extension(JpegImageFile.format, \".jpeg\")\n\nImage.register_mime(JpegImageFile.format, \"image/jpeg\")\n",
            "new_file_content": "#\n# The Python Imaging Library.\n# $Id$\n#\n# JPEG (JFIF) file handling\n#\n# See \"Digital Compression and Coding of Continuous-Tone Still Images,\n# Part 1, Requirements and Guidelines\" (CCITT T.81 / ISO 10918-1)\n#\n# History:\n# 1995-09-09 fl   Created\n# 1995-09-13 fl   Added full parser\n# 1996-03-25 fl   Added hack to use the IJG command line utilities\n# 1996-05-05 fl   Workaround Photoshop 2.5 CMYK polarity bug\n# 1996-05-28 fl   Added draft support, JFIF version (0.1)\n# 1996-12-30 fl   Added encoder options, added progression property (0.2)\n# 1997-08-27 fl   Save mode 1 images as BW (0.3)\n# 1998-07-12 fl   Added YCbCr to draft and save methods (0.4)\n# 1998-10-19 fl   Don't hang on files using 16-bit DQT's (0.4.1)\n# 2001-04-16 fl   Extract DPI settings from JFIF files (0.4.2)\n# 2002-07-01 fl   Skip pad bytes before markers; identify Exif files (0.4.3)\n# 2003-04-25 fl   Added experimental EXIF decoder (0.5)\n# 2003-06-06 fl   Added experimental EXIF GPSinfo decoder\n# 2003-09-13 fl   Extract COM markers\n# 2009-09-06 fl   Added icc_profile support (from Florian Hoech)\n# 2009-03-06 fl   Changed CMYK handling; always use Adobe polarity (0.6)\n# 2009-03-08 fl   Added subsampling support (from Justin Huff).\n#\n# Copyright (c) 1997-2003 by Secret Labs AB.\n# Copyright (c) 1995-1996 by Fredrik Lundh.\n#\n# See the README file for information on usage and redistribution.\n#\n\nimport array\nimport struct\nimport io\nimport warnings\nfrom struct import unpack_from\nfrom PIL import Image, ImageFile, TiffImagePlugin, _binary\nfrom PIL.JpegPresets import presets\nfrom PIL._util import isStringType\n\ni8 = _binary.i8\no8 = _binary.o8\ni16 = _binary.i16be\ni32 = _binary.i32be\n\n__version__ = \"0.6\"\n\n\n#\n# Parser\n\ndef Skip(self, marker):\n    n = i16(self.fp.read(2))-2\n    ImageFile._safe_read(self.fp, n)\n\n\ndef APP(self, marker):\n    #\n    # Application marker.  Store these in the APP dictionary.\n    # Also look for well-known application markers.\n\n    n = i16(self.fp.read(2))-2\n    s = ImageFile._safe_read(self.fp, n)\n\n    app = \"APP%d\" % (marker & 15)\n\n    self.app[app] = s  # compatibility\n    self.applist.append((app, s))\n\n    if marker == 0xFFE0 and s[:4] == b\"JFIF\":\n        # extract JFIF information\n        self.info[\"jfif\"] = version = i16(s, 5)  # version\n        self.info[\"jfif_version\"] = divmod(version, 256)\n        # extract JFIF properties\n        try:\n            jfif_unit = i8(s[7])\n            jfif_density = i16(s, 8), i16(s, 10)\n        except:\n            pass\n        else:\n            if jfif_unit == 1:\n                self.info[\"dpi\"] = jfif_density\n            self.info[\"jfif_unit\"] = jfif_unit\n            self.info[\"jfif_density\"] = jfif_density\n    elif marker == 0xFFE1 and s[:5] == b\"Exif\\0\":\n        # extract Exif information (incomplete)\n        self.info[\"exif\"] = s  # FIXME: value will change\n    elif marker == 0xFFE2 and s[:5] == b\"FPXR\\0\":\n        # extract FlashPix information (incomplete)\n        self.info[\"flashpix\"] = s  # FIXME: value will change\n    elif marker == 0xFFE2 and s[:12] == b\"ICC_PROFILE\\0\":\n        # Since an ICC profile can be larger than the maximum size of\n        # a JPEG marker (64K), we need provisions to split it into\n        # multiple markers. The format defined by the ICC specifies\n        # one or more APP2 markers containing the following data:\n        #   Identifying string      ASCII \"ICC_PROFILE\\0\"  (12 bytes)\n        #   Marker sequence number  1, 2, etc (1 byte)\n        #   Number of markers       Total of APP2's used (1 byte)\n        #   Profile data            (remainder of APP2 data)\n        # Decoders should use the marker sequence numbers to\n        # reassemble the profile, rather than assuming that the APP2\n        # markers appear in the correct sequence.\n        self.icclist.append(s)\n    elif marker == 0xFFEE and s[:5] == b\"Adobe\":\n        self.info[\"adobe\"] = i16(s, 5)\n        # extract Adobe custom properties\n        try:\n            adobe_transform = i8(s[1])\n        except:\n            pass\n        else:\n            self.info[\"adobe_transform\"] = adobe_transform\n    elif marker == 0xFFE2 and s[:4] == b\"MPF\\0\":\n        # extract MPO information\n        self.info[\"mp\"] = s[4:]\n        # offset is current location minus buffer size\n        # plus constant header size\n        self.info[\"mpoffset\"] = self.fp.tell() - n + 4\n\n\ndef COM(self, marker):\n    #\n    # Comment marker.  Store these in the APP dictionary.\n    n = i16(self.fp.read(2))-2\n    s = ImageFile._safe_read(self.fp, n)\n\n    self.app[\"COM\"] = s  # compatibility\n    self.applist.append((\"COM\", s))\n\n\ndef SOF(self, marker):\n    #\n    # Start of frame marker.  Defines the size and mode of the\n    # image.  JPEG is colour blind, so we use some simple\n    # heuristics to map the number of layers to an appropriate\n    # mode.  Note that this could be made a bit brighter, by\n    # looking for JFIF and Adobe APP markers.\n\n    n = i16(self.fp.read(2))-2\n    s = ImageFile._safe_read(self.fp, n)\n    self.size = i16(s[3:]), i16(s[1:])\n\n    self.bits = i8(s[0])\n    if self.bits != 8:\n        raise SyntaxError(\"cannot handle %d-bit layers\" % self.bits)\n\n    self.layers = i8(s[5])\n    if self.layers == 1:\n        self.mode = \"L\"\n    elif self.layers == 3:\n        self.mode = \"RGB\"\n    elif self.layers == 4:\n        self.mode = \"CMYK\"\n    else:\n        raise SyntaxError(\"cannot handle %d-layer images\" % self.layers)\n\n    if marker in [0xFFC2, 0xFFC6, 0xFFCA, 0xFFCE]:\n        self.info[\"progressive\"] = self.info[\"progression\"] = 1\n\n    if self.icclist:\n        # fixup icc profile\n        self.icclist.sort()  # sort by sequence number\n        if i8(self.icclist[0][13]) == len(self.icclist):\n            profile = []\n            for p in self.icclist:\n                profile.append(p[14:])\n            icc_profile = b\"\".join(profile)\n        else:\n            icc_profile = None  # wrong number of fragments\n        self.info[\"icc_profile\"] = icc_profile\n        self.icclist = None\n\n    for i in range(6, len(s), 3):\n        t = s[i:i+3]\n        # 4-tuples: id, vsamp, hsamp, qtable\n        self.layer.append((t[0], i8(t[1])//16, i8(t[1]) & 15, i8(t[2])))\n\n\ndef DQT(self, marker):\n    #\n    # Define quantization table.  Support baseline 8-bit tables\n    # only.  Note that there might be more than one table in\n    # each marker.\n\n    # FIXME: The quantization tables can be used to estimate the\n    # compression quality.\n\n    n = i16(self.fp.read(2))-2\n    s = ImageFile._safe_read(self.fp, n)\n    while len(s):\n        if len(s) < 65:\n            raise SyntaxError(\"bad quantization table marker\")\n        v = i8(s[0])\n        if v//16 == 0:\n            self.quantization[v & 15] = array.array(\"B\", s[1:65])\n            s = s[65:]\n        else:\n            return  # FIXME: add code to read 16-bit tables!\n            # raise SyntaxError, \"bad quantization table element size\"\n\n\n#\n# JPEG marker table\n\nMARKER = {\n    0xFFC0: (\"SOF0\", \"Baseline DCT\", SOF),\n    0xFFC1: (\"SOF1\", \"Extended Sequential DCT\", SOF),\n    0xFFC2: (\"SOF2\", \"Progressive DCT\", SOF),\n    0xFFC3: (\"SOF3\", \"Spatial lossless\", SOF),\n    0xFFC4: (\"DHT\", \"Define Huffman table\", Skip),\n    0xFFC5: (\"SOF5\", \"Differential sequential DCT\", SOF),\n    0xFFC6: (\"SOF6\", \"Differential progressive DCT\", SOF),\n    0xFFC7: (\"SOF7\", \"Differential spatial\", SOF),\n    0xFFC8: (\"JPG\", \"Extension\", None),\n    0xFFC9: (\"SOF9\", \"Extended sequential DCT (AC)\", SOF),\n    0xFFCA: (\"SOF10\", \"Progressive DCT (AC)\", SOF),\n    0xFFCB: (\"SOF11\", \"Spatial lossless DCT (AC)\", SOF),\n    0xFFCC: (\"DAC\", \"Define arithmetic coding conditioning\", Skip),\n    0xFFCD: (\"SOF13\", \"Differential sequential DCT (AC)\", SOF),\n    0xFFCE: (\"SOF14\", \"Differential progressive DCT (AC)\", SOF),\n    0xFFCF: (\"SOF15\", \"Differential spatial (AC)\", SOF),\n    0xFFD0: (\"RST0\", \"Restart 0\", None),\n    0xFFD1: (\"RST1\", \"Restart 1\", None),\n    0xFFD2: (\"RST2\", \"Restart 2\", None),\n    0xFFD3: (\"RST3\", \"Restart 3\", None),\n    0xFFD4: (\"RST4\", \"Restart 4\", None),\n    0xFFD5: (\"RST5\", \"Restart 5\", None),\n    0xFFD6: (\"RST6\", \"Restart 6\", None),\n    0xFFD7: (\"RST7\", \"Restart 7\", None),\n    0xFFD8: (\"SOI\", \"Start of image\", None),\n    0xFFD9: (\"EOI\", \"End of image\", None),\n    0xFFDA: (\"SOS\", \"Start of scan\", Skip),\n    0xFFDB: (\"DQT\", \"Define quantization table\", DQT),\n    0xFFDC: (\"DNL\", \"Define number of lines\", Skip),\n    0xFFDD: (\"DRI\", \"Define restart interval\", Skip),\n    0xFFDE: (\"DHP\", \"Define hierarchical progression\", SOF),\n    0xFFDF: (\"EXP\", \"Expand reference component\", Skip),\n    0xFFE0: (\"APP0\", \"Application segment 0\", APP),\n    0xFFE1: (\"APP1\", \"Application segment 1\", APP),\n    0xFFE2: (\"APP2\", \"Application segment 2\", APP),\n    0xFFE3: (\"APP3\", \"Application segment 3\", APP),\n    0xFFE4: (\"APP4\", \"Application segment 4\", APP),\n    0xFFE5: (\"APP5\", \"Application segment 5\", APP),\n    0xFFE6: (\"APP6\", \"Application segment 6\", APP),\n    0xFFE7: (\"APP7\", \"Application segment 7\", APP),\n    0xFFE8: (\"APP8\", \"Application segment 8\", APP),\n    0xFFE9: (\"APP9\", \"Application segment 9\", APP),\n    0xFFEA: (\"APP10\", \"Application segment 10\", APP),\n    0xFFEB: (\"APP11\", \"Application segment 11\", APP),\n    0xFFEC: (\"APP12\", \"Application segment 12\", APP),\n    0xFFED: (\"APP13\", \"Application segment 13\", APP),\n    0xFFEE: (\"APP14\", \"Application segment 14\", APP),\n    0xFFEF: (\"APP15\", \"Application segment 15\", APP),\n    0xFFF0: (\"JPG0\", \"Extension 0\", None),\n    0xFFF1: (\"JPG1\", \"Extension 1\", None),\n    0xFFF2: (\"JPG2\", \"Extension 2\", None),\n    0xFFF3: (\"JPG3\", \"Extension 3\", None),\n    0xFFF4: (\"JPG4\", \"Extension 4\", None),\n    0xFFF5: (\"JPG5\", \"Extension 5\", None),\n    0xFFF6: (\"JPG6\", \"Extension 6\", None),\n    0xFFF7: (\"JPG7\", \"Extension 7\", None),\n    0xFFF8: (\"JPG8\", \"Extension 8\", None),\n    0xFFF9: (\"JPG9\", \"Extension 9\", None),\n    0xFFFA: (\"JPG10\", \"Extension 10\", None),\n    0xFFFB: (\"JPG11\", \"Extension 11\", None),\n    0xFFFC: (\"JPG12\", \"Extension 12\", None),\n    0xFFFD: (\"JPG13\", \"Extension 13\", None),\n    0xFFFE: (\"COM\", \"Comment\", COM)\n}\n\n\ndef _accept(prefix):\n    return prefix[0:1] == b\"\\377\"\n\n\n##\n# Image plugin for JPEG and JFIF images.\n\nclass JpegImageFile(ImageFile.ImageFile):\n\n    format = \"JPEG\"\n    format_description = \"JPEG (ISO 10918)\"\n\n    def _open(self):\n\n        s = self.fp.read(1)\n\n        if i8(s) != 255:\n            raise SyntaxError(\"not a JPEG file\")\n\n        # Create attributes\n        self.bits = self.layers = 0\n\n        # JPEG specifics (internal)\n        self.layer = []\n        self.huffman_dc = {}\n        self.huffman_ac = {}\n        self.quantization = {}\n        self.app = {}  # compatibility\n        self.applist = []\n        self.icclist = []\n\n        while True:\n\n            i = i8(s)\n            if i == 0xFF:\n                s = s + self.fp.read(1)\n                i = i16(s)\n            else:\n                # Skip non-0xFF junk\n                s = self.fp.read(1)\n                continue\n\n            if i in MARKER:\n                name, description, handler = MARKER[i]\n                # print hex(i), name, description\n                if handler is not None:\n                    handler(self, i)\n                if i == 0xFFDA:  # start of scan\n                    rawmode = self.mode\n                    if self.mode == \"CMYK\":\n                        rawmode = \"CMYK;I\"  # assume adobe conventions\n                    self.tile = [(\"jpeg\", (0, 0) + self.size, 0,\n                                 (rawmode, \"\"))]\n                    # self.__offset = self.fp.tell()\n                    break\n                s = self.fp.read(1)\n            elif i == 0 or i == 0xFFFF:\n                # padded marker or junk; move on\n                s = b\"\\xff\"\n            elif i == 0xFF00:  # Skip extraneous data (escaped 0xFF)\n                s = self.fp.read(1)\n            else:\n                raise SyntaxError(\"no marker found\")\n\n    def draft(self, mode, size):\n\n        if len(self.tile) != 1:\n            return\n\n        d, e, o, a = self.tile[0]\n        scale = 0\n\n        if a[0] == \"RGB\" and mode in [\"L\", \"YCbCr\"]:\n            self.mode = mode\n            a = mode, \"\"\n\n        if size:\n            scale = max(self.size[0] // size[0], self.size[1] // size[1])\n            for s in [8, 4, 2, 1]:\n                if scale >= s:\n                    break\n            e = e[0], e[1], (e[2]-e[0]+s-1)//s+e[0], (e[3]-e[1]+s-1)//s+e[1]\n            self.size = ((self.size[0]+s-1)//s, (self.size[1]+s-1)//s)\n            scale = s\n\n        self.tile = [(d, e, o, a)]\n        self.decoderconfig = (scale, 0)\n\n        return self\n\n    def load_djpeg(self):\n\n        # ALTERNATIVE: handle JPEGs via the IJG command line utilities\n\n        import subprocess\n        import tempfile\n        import os\n        f, path = tempfile.mkstemp()\n        os.close(f)\n        if os.path.exists(self.filename):\n            subprocess.check_call([\"djpeg\", \"-outfile\", path, self.filename])\n        else:\n            raise ValueError(\"Invalid Filename\")\n\n        try:\n            self.im = Image.core.open_ppm(path)\n        finally:\n            try:\n                os.unlink(path)\n            except OSError:\n                pass\n\n        self.mode = self.im.mode\n        self.size = self.im.size\n\n        self.tile = []\n\n    def _getexif(self):\n        return _getexif(self)\n\n    def _getmp(self):\n        return _getmp(self)\n\n\ndef _fixup_dict(src_dict):\n    # Helper function for _getexif()\n    # returns a dict with any single item tuples/lists as individual values\n    def _fixup(value):\n        try:\n            if len(value) == 1 and not isinstance(value, dict):\n                return value[0]\n        except: pass\n        return value\n\n    return dict([(k, _fixup(v)) for k, v in src_dict.items()])\n\n\ndef _getexif(self):\n    # Extract EXIF information.  This method is highly experimental,\n    # and is likely to be replaced with something better in a future\n    # version.\n\n    # The EXIF record consists of a TIFF file embedded in a JPEG\n    # application marker (!).\n    try:\n        data = self.info[\"exif\"]\n    except KeyError:\n        return None\n    file = io.BytesIO(data[6:])\n    head = file.read(8)\n    # process dictionary\n    info = TiffImagePlugin.ImageFileDirectory_v1(head)\n    info.load(file)\n    exif = dict(_fixup_dict(info))\n    # get exif extension\n    try:\n        # exif field 0x8769 is an offset pointer to the location\n        # of the nested embedded exif ifd.\n        # It should be a long, but may be corrupted.\n        file.seek(exif[0x8769])\n    except (KeyError, TypeError):\n        pass\n    else:\n        info = TiffImagePlugin.ImageFileDirectory_v1(head)\n        info.load(file)\n        exif.update(_fixup_dict(info))\n    # get gpsinfo extension\n    try:\n        # exif field 0x8825 is an offset pointer to the location\n        # of the nested embedded gps exif ifd.\n        # It should be a long, but may be corrupted.\n        file.seek(exif[0x8825])\n    except (KeyError, TypeError):\n        pass\n    else:\n        info = TiffImagePlugin.ImageFileDirectory_v1(head)\n        info.load(file)\n        exif[0x8825] = _fixup_dict(info)\n\n    return exif\n\n\ndef _getmp(self):\n    # Extract MP information.  This method was inspired by the \"highly\n    # experimental\" _getexif version that's been in use for years now,\n    # itself based on the ImageFileDirectory class in the TIFF plug-in.\n\n    # The MP record essentially consists of a TIFF file embedded in a JPEG\n    # application marker.\n    try:\n        data = self.info[\"mp\"]\n    except KeyError:\n        return None\n    file_contents = io.BytesIO(data)\n    head = file_contents.read(8)\n    endianness = '>' if head[:4] == b'\\x4d\\x4d\\x00\\x2a' else '<'\n    # process dictionary\n    try:\n        info = TiffImagePlugin.ImageFileDirectory_v2(head)\n        info.load(file_contents)\n        mp = dict(info)\n    except:\n        raise SyntaxError(\"malformed MP Index (unreadable directory)\")\n    # it's an error not to have a number of images\n    try:\n        quant = mp[0xB001]\n    except KeyError:\n        raise SyntaxError(\"malformed MP Index (no number of images)\")\n    # get MP entries\n    mpentries = []\n    try:\n        rawmpentries = mp[0xB002]\n        for entrynum in range(0, quant):\n            unpackedentry = unpack_from(\n                '{0}LLLHH'.format(endianness), rawmpentries, entrynum * 16)\n            labels = ('Attribute', 'Size', 'DataOffset', 'EntryNo1',\n                      'EntryNo2')\n            mpentry = dict(zip(labels, unpackedentry))\n            mpentryattr = {\n                'DependentParentImageFlag': bool(mpentry['Attribute'] &\n                                                 (1 << 31)),\n                'DependentChildImageFlag': bool(mpentry['Attribute'] &\n                                                (1 << 30)),\n                'RepresentativeImageFlag': bool(mpentry['Attribute'] &\n                                                (1 << 29)),\n                'Reserved': (mpentry['Attribute'] & (3 << 27)) >> 27,\n                'ImageDataFormat': (mpentry['Attribute'] & (7 << 24)) >> 24,\n                'MPType': mpentry['Attribute'] & 0x00FFFFFF\n            }\n            if mpentryattr['ImageDataFormat'] == 0:\n                mpentryattr['ImageDataFormat'] = 'JPEG'\n            else:\n                raise SyntaxError(\"unsupported picture format in MPO\")\n            mptypemap = {\n                0x000000: 'Undefined',\n                0x010001: 'Large Thumbnail (VGA Equivalent)',\n                0x010002: 'Large Thumbnail (Full HD Equivalent)',\n                0x020001: 'Multi-Frame Image (Panorama)',\n                0x020002: 'Multi-Frame Image: (Disparity)',\n                0x020003: 'Multi-Frame Image: (Multi-Angle)',\n                0x030000: 'Baseline MP Primary Image'\n            }\n            mpentryattr['MPType'] = mptypemap.get(mpentryattr['MPType'],\n                                                  'Unknown')\n            mpentry['Attribute'] = mpentryattr\n            mpentries.append(mpentry)\n        mp[0xB002] = mpentries\n    except KeyError:\n        raise SyntaxError(\"malformed MP Index (bad MP Entry)\")\n    # Next we should try and parse the individual image unique ID list;\n    # we don't because I've never seen this actually used in a real MPO\n    # file and so can't test it.\n    return mp\n\n\n# --------------------------------------------------------------------\n# stuff to save JPEG files\n\nRAWMODE = {\n    \"1\": \"L\",\n    \"L\": \"L\",\n    \"RGB\": \"RGB\",\n    \"RGBA\": \"RGB\",\n    \"RGBX\": \"RGB\",\n    \"CMYK\": \"CMYK;I\",  # assume adobe conventions\n    \"YCbCr\": \"YCbCr\",\n}\n\nzigzag_index = (0,  1,  5,  6, 14, 15, 27, 28,\n                2,  4,  7, 13, 16, 26, 29, 42,\n                3,  8, 12, 17, 25, 30, 41, 43,\n                9, 11, 18, 24, 31, 40, 44, 53,\n               10, 19, 23, 32, 39, 45, 52, 54,\n               20, 22, 33, 38, 46, 51, 55, 60,\n               21, 34, 37, 47, 50, 56, 59, 61,\n               35, 36, 48, 49, 57, 58, 62, 63)\n\nsamplings = {(1, 1, 1, 1, 1, 1): 0,\n             (2, 1, 1, 1, 1, 1): 1,\n             (2, 2, 1, 1, 1, 1): 2,\n             }\n\n\ndef convert_dict_qtables(qtables):\n    qtables = [qtables[key] for key in range(len(qtables)) if key in qtables]\n    for idx, table in enumerate(qtables):\n        qtables[idx] = [table[i] for i in zigzag_index]\n    return qtables\n\n\ndef get_sampling(im):\n    # There's no subsampling when image have only 1 layer\n    # (grayscale images) or when they are CMYK (4 layers),\n    # so set subsampling to default value.\n    #\n    # NOTE: currently Pillow can't encode JPEG to YCCK format.\n    # If YCCK support is added in the future, subsampling code will have\n    # to be updated (here and in JpegEncode.c) to deal with 4 layers.\n    if not hasattr(im, 'layers') or im.layers in (1, 4):\n        return -1\n    sampling = im.layer[0][1:3] + im.layer[1][1:3] + im.layer[2][1:3]\n    return samplings.get(sampling, -1)\n\n\ndef _save(im, fp, filename):\n\n    try:\n        rawmode = RAWMODE[im.mode]\n    except KeyError:\n        raise IOError(\"cannot write mode %s as JPEG\" % im.mode)\n\n    if im.mode == 'RGBA':\n        warnings.warn(\n            'You are saving RGBA image as JPEG. The alpha channel will be '\n            'discarded. This conversion is deprecated and will be disabled '\n            'in Pillow 3.7. Please, convert the image to RGB explicitly.',\n            DeprecationWarning\n        )\n\n    info = im.encoderinfo\n\n    dpi = [int(round(x)) for x in info.get(\"dpi\", (0, 0))]\n\n    quality = info.get(\"quality\", 0)\n    subsampling = info.get(\"subsampling\", -1)\n    qtables = info.get(\"qtables\")\n\n    if quality == \"keep\":\n        quality = 0\n        subsampling = \"keep\"\n        qtables = \"keep\"\n    elif quality in presets:\n        preset = presets[quality]\n        quality = 0\n        subsampling = preset.get('subsampling', -1)\n        qtables = preset.get('quantization')\n    elif not isinstance(quality, int):\n        raise ValueError(\"Invalid quality setting\")\n    else:\n        if subsampling in presets:\n            subsampling = presets[subsampling].get('subsampling', -1)\n        if isStringType(qtables) and qtables in presets:\n            qtables = presets[qtables].get('quantization')\n\n    if subsampling == \"4:4:4\":\n        subsampling = 0\n    elif subsampling == \"4:2:2\":\n        subsampling = 1\n    elif subsampling == \"4:1:1\":\n        subsampling = 2\n    elif subsampling == \"keep\":\n        if im.format != \"JPEG\":\n            raise ValueError(\n                \"Cannot use 'keep' when original image is not a JPEG\")\n        subsampling = get_sampling(im)\n\n    def validate_qtables(qtables):\n        if qtables is None:\n            return qtables\n        if isStringType(qtables):\n            try:\n                lines = [int(num) for line in qtables.splitlines()\n                         for num in line.split('#', 1)[0].split()]\n            except ValueError:\n                raise ValueError(\"Invalid quantization table\")\n            else:\n                qtables = [lines[s:s+64] for s in range(0, len(lines), 64)]\n        if isinstance(qtables, (tuple, list, dict)):\n            if isinstance(qtables, dict):\n                qtables = convert_dict_qtables(qtables)\n            elif isinstance(qtables, tuple):\n                qtables = list(qtables)\n            if not (0 < len(qtables) < 5):\n                raise ValueError(\"None or too many quantization tables\")\n            for idx, table in enumerate(qtables):\n                try:\n                    if len(table) != 64:\n                        raise\n                    table = array.array('B', table)\n                except TypeError:\n                    raise ValueError(\"Invalid quantization table\")\n                else:\n                    qtables[idx] = list(table)\n            return qtables\n\n    if qtables == \"keep\":\n        if im.format != \"JPEG\":\n            raise ValueError(\n                \"Cannot use 'keep' when original image is not a JPEG\")\n        qtables = getattr(im, \"quantization\", None)\n    qtables = validate_qtables(qtables)\n\n    extra = b\"\"\n\n    icc_profile = info.get(\"icc_profile\")\n    if icc_profile:\n        ICC_OVERHEAD_LEN = 14\n        MAX_BYTES_IN_MARKER = 65533\n        MAX_DATA_BYTES_IN_MARKER = MAX_BYTES_IN_MARKER - ICC_OVERHEAD_LEN\n        markers = []\n        while icc_profile:\n            markers.append(icc_profile[:MAX_DATA_BYTES_IN_MARKER])\n            icc_profile = icc_profile[MAX_DATA_BYTES_IN_MARKER:]\n        i = 1\n        for marker in markers:\n            size = struct.pack(\">H\", 2 + ICC_OVERHEAD_LEN + len(marker))\n            extra += (b\"\\xFF\\xE2\" + size + b\"ICC_PROFILE\\0\" + o8(i) +\n                      o8(len(markers)) + marker)\n            i += 1\n\n    # \"progressive\" is the official name, but older documentation\n    # says \"progression\"\n    # FIXME: issue a warning if the wrong form is used (post-1.1.7)\n    progressive = info.get(\"progressive\", False) or\\\n                  info.get(\"progression\", False)\n\n    # get keyword arguments\n    im.encoderconfig = (\n        quality,\n        progressive,\n        info.get(\"smooth\", 0),\n        \"optimize\" in info,\n        info.get(\"streamtype\", 0),\n        dpi[0], dpi[1],\n        subsampling,\n        qtables,\n        extra,\n        info.get(\"exif\", b\"\")\n        )\n\n    # if we optimize, libjpeg needs a buffer big enough to hold the whole image\n    # in a shot. Guessing on the size, at im.size bytes. (raw pizel size is\n    # channels*size, this is a value that's been used in a django patch.\n    # https://github.com/matthewwithanm/django-imagekit/issues/50\n    bufsize = 0\n    if \"optimize\" in info or progressive:\n        # keep sets quality to 0, but the actual value may be high.\n        if quality >= 95 or quality == 0:\n            bufsize = 2 * im.size[0] * im.size[1]\n        else:\n            bufsize = im.size[0] * im.size[1]\n\n    # The exif info needs to be written as one block, + APP1, + one spare byte.\n    # Ensure that our buffer is big enough\n    bufsize = max(ImageFile.MAXBLOCK, bufsize, len(info.get(\"exif\", b\"\")) + 5)\n\n    ImageFile._save(im, fp, [(\"jpeg\", (0, 0)+im.size, 0, rawmode)], bufsize)\n\n\ndef _save_cjpeg(im, fp, filename):\n    # ALTERNATIVE: handle JPEGs via the IJG command line utilities.\n    import os\n    import subprocess\n    tempfile = im._dump()\n    subprocess.check_call([\"cjpeg\", \"-outfile\", filename, tempfile])\n    try:\n        os.unlink(tempfile)\n    except OSError:\n        pass\n\n\n##\n# Factory for making JPEG and MPO instances\ndef jpeg_factory(fp=None, filename=None):\n    im = JpegImageFile(fp, filename)\n    try:\n        mpheader = im._getmp()\n        if mpheader[45057] > 1:\n            # It's actually an MPO\n            from .MpoImagePlugin import MpoImageFile\n            im = MpoImageFile(fp, filename)\n    except (TypeError, IndexError):\n        # It is really a JPEG\n        pass\n    except SyntaxError:\n        warnings.warn(\"Image appears to be a malformed MPO file, it will be \"\n                      \"interpreted as a base JPEG file\")\n    return im\n\n\n# -------------------------------------------------------------------q-\n# Registry stuff\n\nImage.register_open(JpegImageFile.format, jpeg_factory, _accept)\nImage.register_save(JpegImageFile.format, _save)\n\nImage.register_extension(JpegImageFile.format, \".jfif\")\nImage.register_extension(JpegImageFile.format, \".jpe\")\nImage.register_extension(JpegImageFile.format, \".jpg\")\nImage.register_extension(JpegImageFile.format, \".jpeg\")\n\nImage.register_mime(JpegImageFile.format, \"image/jpeg\")\n",
            "header": {
                "file": {
                    "path": "PIL/JpegImagePlugin.py"
                },
                "misc_line": null
            },
            "index_line": {
                "old_commit_hash": "815ee02b6",
                "new_commit_hash": "63fd0dcbf",
                "mode": "100644"
            },
            "is_binary_file": false,
            "binary_line": null,
            "minus_file": {
                "path": "a/PIL/JpegImagePlugin.py"
            },
            "plus_file": {
                "path": "b/PIL/JpegImagePlugin.py"
            },
            "hunks": [
                {
                    "descriptor": {
                        "old_range": {
                            "start": 682,
                            "length": 13
                        },
                        "new_range": {
                            "start": 682,
                            "length": 16
                        },
                        "section": "def _save(im, fp, filename):"
                    },
                    "line_group": {
                        "all_lines": [
                            {
                                "content": "                      o8(len(markers)) + marker)",
                                "type": "context"
                            },
                            {
                                "content": "            i += 1",
                                "type": "context"
                            },
                            {
                                "content": "",
                                "type": "context"
                            },
                            {
                                "content": "    # \"progressive\" is the official name, but older documentation",
                                "type": "added"
                            },
                            {
                                "content": "    # says \"progression\"",
                                "type": "added"
                            },
                            {
                                "content": "    # FIXME: issue a warning if the wrong form is used (post-1.1.7)",
                                "type": "added"
                            },
                            {
                                "content": "    progressive = info.get(\"progressive\", False) or\\",
                                "type": "added"
                            },
                            {
                                "content": "                  info.get(\"progression\", False)",
                                "type": "added"
                            },
                            {
                                "content": "",
                                "type": "added"
                            },
                            {
                                "content": "    # get keyword arguments",
                                "type": "context"
                            },
                            {
                                "content": "    im.encoderconfig = (",
                                "type": "context"
                            },
                            {
                                "content": "        quality,",
                                "type": "context"
                            },
                            {
                                "content": "        # \"progressive\" is the official name, but older documentation",
                                "type": "deleted"
                            },
                            {
                                "content": "        # says \"progression\"",
                                "type": "deleted"
                            },
                            {
                                "content": "        # FIXME: issue a warning if the wrong form is used (post-1.1.7)",
                                "type": "deleted"
                            },
                            {
                                "content": "        \"progressive\" in info or \"progression\" in info,",
                                "type": "deleted"
                            },
                            {
                                "content": "        progressive,",
                                "type": "added"
                            },
                            {
                                "content": "        info.get(\"smooth\", 0),",
                                "type": "context"
                            },
                            {
                                "content": "        \"optimize\" in info,",
                                "type": "context"
                            },
                            {
                                "content": "        info.get(\"streamtype\", 0),",
                                "type": "context"
                            }
                        ]
                    },
                    "modified_entities": [
                        {
                            "file_name": "PIL/JpegImagePlugin.py",
                            "type": "function",
                            "name": "_save",
                            "content": "def _save(im, fp, filename):\n\n    try:\n        rawmode = RAWMODE[im.mode]\n    except KeyError:\n        raise IOError(\"cannot write mode %s as JPEG\" % im.mode)\n\n    if im.mode == 'RGBA':\n        warnings.warn(\n            'You are saving RGBA image as JPEG. The alpha channel will be '\n            'discarded. This conversion is deprecated and will be disabled '\n            'in Pillow 3.7. Please, convert the image to RGB explicitly.',\n            DeprecationWarning\n        )\n\n    info = im.encoderinfo\n\n    dpi = [int(round(x)) for x in info.get(\"dpi\", (0, 0))]\n\n    quality = info.get(\"quality\", 0)\n    subsampling = info.get(\"subsampling\", -1)\n    qtables = info.get(\"qtables\")\n\n    if quality == \"keep\":\n        quality = 0\n        subsampling = \"keep\"\n        qtables = \"keep\"\n    elif quality in presets:\n        preset = presets[quality]\n        quality = 0\n        subsampling = preset.get('subsampling', -1)\n        qtables = preset.get('quantization')\n    elif not isinstance(quality, int):\n        raise ValueError(\"Invalid quality setting\")\n    else:\n        if subsampling in presets:\n            subsampling = presets[subsampling].get('subsampling', -1)\n        if isStringType(qtables) and qtables in presets:\n            qtables = presets[qtables].get('quantization')\n\n    if subsampling == \"4:4:4\":\n        subsampling = 0\n    elif subsampling == \"4:2:2\":\n        subsampling = 1\n    elif subsampling == \"4:1:1\":\n        subsampling = 2\n    elif subsampling == \"keep\":\n        if im.format != \"JPEG\":\n            raise ValueError(\n                \"Cannot use 'keep' when original image is not a JPEG\")\n        subsampling = get_sampling(im)\n\n    def validate_qtables(qtables):\n        if qtables is None:\n            return qtables\n        if isStringType(qtables):\n            try:\n                lines = [int(num) for line in qtables.splitlines()\n                         for num in line.split('#', 1)[0].split()]\n            except ValueError:\n                raise ValueError(\"Invalid quantization table\")\n            else:\n                qtables = [lines[s:s+64] for s in range(0, len(lines), 64)]\n        if isinstance(qtables, (tuple, list, dict)):\n            if isinstance(qtables, dict):\n                qtables = convert_dict_qtables(qtables)\n            elif isinstance(qtables, tuple):\n                qtables = list(qtables)\n            if not (0 < len(qtables) < 5):\n                raise ValueError(\"None or too many quantization tables\")\n            for idx, table in enumerate(qtables):\n                try:\n                    if len(table) != 64:\n                        raise\n                    table = array.array('B', table)\n                except TypeError:\n                    raise ValueError(\"Invalid quantization table\")\n                else:\n                    qtables[idx] = list(table)\n            return qtables\n\n    if qtables == \"keep\":\n        if im.format != \"JPEG\":\n            raise ValueError(\n                \"Cannot use 'keep' when original image is not a JPEG\")\n        qtables = getattr(im, \"quantization\", None)\n    qtables = validate_qtables(qtables)\n\n    extra = b\"\"\n\n    icc_profile = info.get(\"icc_profile\")\n    if icc_profile:\n        ICC_OVERHEAD_LEN = 14\n        MAX_BYTES_IN_MARKER = 65533\n        MAX_DATA_BYTES_IN_MARKER = MAX_BYTES_IN_MARKER - ICC_OVERHEAD_LEN\n        markers = []\n        while icc_profile:\n            markers.append(icc_profile[:MAX_DATA_BYTES_IN_MARKER])\n            icc_profile = icc_profile[MAX_DATA_BYTES_IN_MARKER:]\n        i = 1\n        for marker in markers:\n            size = struct.pack(\">H\", 2 + ICC_OVERHEAD_LEN + len(marker))\n            extra += (b\"\\xFF\\xE2\" + size + b\"ICC_PROFILE\\0\" + o8(i) +\n                      o8(len(markers)) + marker)\n            i += 1\n\n    # get keyword arguments\n    im.encoderconfig = (\n        quality,\n        # \"progressive\" is the official name, but older documentation\n        # says \"progression\"\n        # FIXME: issue a warning if the wrong form is used (post-1.1.7)\n        \"progressive\" in info or \"progression\" in info,\n        info.get(\"smooth\", 0),\n        \"optimize\" in info,\n        info.get(\"streamtype\", 0),\n        dpi[0], dpi[1],\n        subsampling,\n        qtables,\n        extra,\n        info.get(\"exif\", b\"\")\n        )\n\n    # if we optimize, libjpeg needs a buffer big enough to hold the whole image\n    # in a shot. Guessing on the size, at im.size bytes. (raw pizel size is\n    # channels*size, this is a value that's been used in a django patch.\n    # https://github.com/matthewwithanm/django-imagekit/issues/50\n    bufsize = 0\n    if \"optimize\" in info or \"progressive\" in info or \"progression\" in info:\n        # keep sets quality to 0, but the actual value may be high.\n        if quality >= 95 or quality == 0:\n            bufsize = 2 * im.size[0] * im.size[1]\n        else:\n            bufsize = im.size[0] * im.size[1]\n\n    # The exif info needs to be written as one block, + APP1, + one spare byte.\n    # Ensure that our buffer is big enough\n    bufsize = max(ImageFile.MAXBLOCK, bufsize, len(info.get(\"exif\", b\"\")) + 5)\n\n    ImageFile._save(im, fp, [(\"jpeg\", (0, 0)+im.size, 0, rawmode)], bufsize)",
                            "ast_type_str": "FunctionDef",
                            "start_lineno": 579,
                            "end_lineno": 718,
                            "parent": null
                        }
                    ],
                    "added_entities": [],
                    "deleted_entities": []
                },
                {
                    "descriptor": {
                        "old_range": {
                            "start": 704,
                            "length": 7
                        },
                        "new_range": {
                            "start": 707,
                            "length": 7
                        },
                        "section": "def _save(im, fp, filename):"
                    },
                    "line_group": {
                        "all_lines": [
                            {
                                "content": "    # channels*size, this is a value that's been used in a django patch.",
                                "type": "context"
                            },
                            {
                                "content": "    # https://github.com/matthewwithanm/django-imagekit/issues/50",
                                "type": "context"
                            },
                            {
                                "content": "    bufsize = 0",
                                "type": "context"
                            },
                            {
                                "content": "    if \"optimize\" in info or \"progressive\" in info or \"progression\" in info:",
                                "type": "deleted"
                            },
                            {
                                "content": "    if \"optimize\" in info or progressive:",
                                "type": "added"
                            },
                            {
                                "content": "        # keep sets quality to 0, but the actual value may be high.",
                                "type": "context"
                            },
                            {
                                "content": "        if quality >= 95 or quality == 0:",
                                "type": "context"
                            },
                            {
                                "content": "            bufsize = 2 * im.size[0] * im.size[1]",
                                "type": "context"
                            }
                        ]
                    },
                    "modified_entities": [
                        {
                            "file_name": "PIL/JpegImagePlugin.py",
                            "type": "function",
                            "name": "_save",
                            "content": "def _save(im, fp, filename):\n\n    try:\n        rawmode = RAWMODE[im.mode]\n    except KeyError:\n        raise IOError(\"cannot write mode %s as JPEG\" % im.mode)\n\n    if im.mode == 'RGBA':\n        warnings.warn(\n            'You are saving RGBA image as JPEG. The alpha channel will be '\n            'discarded. This conversion is deprecated and will be disabled '\n            'in Pillow 3.7. Please, convert the image to RGB explicitly.',\n            DeprecationWarning\n        )\n\n    info = im.encoderinfo\n\n    dpi = [int(round(x)) for x in info.get(\"dpi\", (0, 0))]\n\n    quality = info.get(\"quality\", 0)\n    subsampling = info.get(\"subsampling\", -1)\n    qtables = info.get(\"qtables\")\n\n    if quality == \"keep\":\n        quality = 0\n        subsampling = \"keep\"\n        qtables = \"keep\"\n    elif quality in presets:\n        preset = presets[quality]\n        quality = 0\n        subsampling = preset.get('subsampling', -1)\n        qtables = preset.get('quantization')\n    elif not isinstance(quality, int):\n        raise ValueError(\"Invalid quality setting\")\n    else:\n        if subsampling in presets:\n            subsampling = presets[subsampling].get('subsampling', -1)\n        if isStringType(qtables) and qtables in presets:\n            qtables = presets[qtables].get('quantization')\n\n    if subsampling == \"4:4:4\":\n        subsampling = 0\n    elif subsampling == \"4:2:2\":\n        subsampling = 1\n    elif subsampling == \"4:1:1\":\n        subsampling = 2\n    elif subsampling == \"keep\":\n        if im.format != \"JPEG\":\n            raise ValueError(\n                \"Cannot use 'keep' when original image is not a JPEG\")\n        subsampling = get_sampling(im)\n\n    def validate_qtables(qtables):\n        if qtables is None:\n            return qtables\n        if isStringType(qtables):\n            try:\n                lines = [int(num) for line in qtables.splitlines()\n                         for num in line.split('#', 1)[0].split()]\n            except ValueError:\n                raise ValueError(\"Invalid quantization table\")\n            else:\n                qtables = [lines[s:s+64] for s in range(0, len(lines), 64)]\n        if isinstance(qtables, (tuple, list, dict)):\n            if isinstance(qtables, dict):\n                qtables = convert_dict_qtables(qtables)\n            elif isinstance(qtables, tuple):\n                qtables = list(qtables)\n            if not (0 < len(qtables) < 5):\n                raise ValueError(\"None or too many quantization tables\")\n            for idx, table in enumerate(qtables):\n                try:\n                    if len(table) != 64:\n                        raise\n                    table = array.array('B', table)\n                except TypeError:\n                    raise ValueError(\"Invalid quantization table\")\n                else:\n                    qtables[idx] = list(table)\n            return qtables\n\n    if qtables == \"keep\":\n        if im.format != \"JPEG\":\n            raise ValueError(\n                \"Cannot use 'keep' when original image is not a JPEG\")\n        qtables = getattr(im, \"quantization\", None)\n    qtables = validate_qtables(qtables)\n\n    extra = b\"\"\n\n    icc_profile = info.get(\"icc_profile\")\n    if icc_profile:\n        ICC_OVERHEAD_LEN = 14\n        MAX_BYTES_IN_MARKER = 65533\n        MAX_DATA_BYTES_IN_MARKER = MAX_BYTES_IN_MARKER - ICC_OVERHEAD_LEN\n        markers = []\n        while icc_profile:\n            markers.append(icc_profile[:MAX_DATA_BYTES_IN_MARKER])\n            icc_profile = icc_profile[MAX_DATA_BYTES_IN_MARKER:]\n        i = 1\n        for marker in markers:\n            size = struct.pack(\">H\", 2 + ICC_OVERHEAD_LEN + len(marker))\n            extra += (b\"\\xFF\\xE2\" + size + b\"ICC_PROFILE\\0\" + o8(i) +\n                      o8(len(markers)) + marker)\n            i += 1\n\n    # get keyword arguments\n    im.encoderconfig = (\n        quality,\n        # \"progressive\" is the official name, but older documentation\n        # says \"progression\"\n        # FIXME: issue a warning if the wrong form is used (post-1.1.7)\n        \"progressive\" in info or \"progression\" in info,\n        info.get(\"smooth\", 0),\n        \"optimize\" in info,\n        info.get(\"streamtype\", 0),\n        dpi[0], dpi[1],\n        subsampling,\n        qtables,\n        extra,\n        info.get(\"exif\", b\"\")\n        )\n\n    # if we optimize, libjpeg needs a buffer big enough to hold the whole image\n    # in a shot. Guessing on the size, at im.size bytes. (raw pizel size is\n    # channels*size, this is a value that's been used in a django patch.\n    # https://github.com/matthewwithanm/django-imagekit/issues/50\n    bufsize = 0\n    if \"optimize\" in info or \"progressive\" in info or \"progression\" in info:\n        # keep sets quality to 0, but the actual value may be high.\n        if quality >= 95 or quality == 0:\n            bufsize = 2 * im.size[0] * im.size[1]\n        else:\n            bufsize = im.size[0] * im.size[1]\n\n    # The exif info needs to be written as one block, + APP1, + one spare byte.\n    # Ensure that our buffer is big enough\n    bufsize = max(ImageFile.MAXBLOCK, bufsize, len(info.get(\"exif\", b\"\")) + 5)\n\n    ImageFile._save(im, fp, [(\"jpeg\", (0, 0)+im.size, 0, rawmode)], bufsize)",
                            "ast_type_str": "FunctionDef",
                            "start_lineno": 579,
                            "end_lineno": 718,
                            "parent": null
                        }
                    ],
                    "added_entities": [],
                    "deleted_entities": []
                }
            ]
        },
        {
            "old_file_content": "from helper import unittest, PillowTestCase, hopper, py3\nfrom helper import djpeg_available, cjpeg_available\n\nimport random\nfrom io import BytesIO\nimport os\n\nfrom PIL import Image\nfrom PIL import ImageFile\nfrom PIL import JpegImagePlugin\n\ncodecs = dir(Image.core)\n\nTEST_FILE = \"Tests/images/hopper.jpg\"\n\n\nclass TestFileJpeg(PillowTestCase):\n\n    def setUp(self):\n        if \"jpeg_encoder\" not in codecs or \"jpeg_decoder\" not in codecs:\n            self.skipTest(\"jpeg support not available\")\n\n    def roundtrip(self, im, **options):\n        out = BytesIO()\n        im.save(out, \"JPEG\", **options)\n        test_bytes = out.tell()\n        out.seek(0)\n        im = Image.open(out)\n        im.bytes = test_bytes  # for testing only\n        return im\n\n    def test_sanity(self):\n\n        # internal version number\n        self.assertRegexpMatches(Image.core.jpeglib_version, \"\\d+\\.\\d+$\")\n\n        im = Image.open(TEST_FILE)\n        im.load()\n        self.assertEqual(im.mode, \"RGB\")\n        self.assertEqual(im.size, (128, 128))\n        self.assertEqual(im.format, \"JPEG\")\n\n    def test_app(self):\n        # Test APP/COM reader (@PIL135)\n        im = Image.open(TEST_FILE)\n        self.assertEqual(\n            im.applist[0],\n            (\"APP0\", b\"JFIF\\x00\\x01\\x01\\x01\\x00`\\x00`\\x00\\x00\"))\n        self.assertEqual(im.applist[1], (\n            \"COM\", b\"File written by Adobe Photoshop\\xa8 4.0\\x00\"))\n        self.assertEqual(len(im.applist), 2)\n\n    def test_cmyk(self):\n        # Test CMYK handling.  Thanks to Tim and Charlie for test data,\n        # Michael for getting me to look one more time.\n        f = \"Tests/images/pil_sample_cmyk.jpg\"\n        im = Image.open(f)\n        # the source image has red pixels in the upper left corner.\n        c, m, y, k = [x / 255.0 for x in im.getpixel((0, 0))]\n        self.assertEqual(c, 0.0)\n        self.assertGreater(m, 0.8)\n        self.assertGreater(y, 0.8)\n        self.assertEqual(k, 0.0)\n        # the opposite corner is black\n        c, m, y, k = [x / 255.0 for x in im.getpixel((\n            im.size[0]-1, im.size[1]-1))]\n        self.assertGreater(k, 0.9)\n        # roundtrip, and check again\n        im = self.roundtrip(im)\n        c, m, y, k = [x / 255.0 for x in im.getpixel((0, 0))]\n        self.assertEqual(c, 0.0)\n        self.assertGreater(m, 0.8)\n        self.assertGreater(y, 0.8)\n        self.assertEqual(k, 0.0)\n        c, m, y, k = [x / 255.0 for x in im.getpixel((\n            im.size[0]-1, im.size[1]-1))]\n        self.assertGreater(k, 0.9)\n\n    def test_dpi(self):\n        def test(xdpi, ydpi=None):\n            im = Image.open(TEST_FILE)\n            im = self.roundtrip(im, dpi=(xdpi, ydpi or xdpi))\n            return im.info.get(\"dpi\")\n        self.assertEqual(test(72), (72, 72))\n        self.assertEqual(test(300), (300, 300))\n        self.assertEqual(test(100, 200), (100, 200))\n        self.assertEqual(test(0), None)  # square pixels\n\n    def test_icc(self):\n        # Test ICC support\n        im1 = Image.open(\"Tests/images/rgb.jpg\")\n        icc_profile = im1.info[\"icc_profile\"]\n        self.assertEqual(len(icc_profile), 3144)\n        # Roundtrip via physical file.\n        f = self.tempfile(\"temp.jpg\")\n        im1.save(f, icc_profile=icc_profile)\n        im2 = Image.open(f)\n        self.assertEqual(im2.info.get(\"icc_profile\"), icc_profile)\n        # Roundtrip via memory buffer.\n        im1 = self.roundtrip(hopper())\n        im2 = self.roundtrip(hopper(), icc_profile=icc_profile)\n        self.assert_image_equal(im1, im2)\n        self.assertFalse(im1.info.get(\"icc_profile\"))\n        self.assertTrue(im2.info.get(\"icc_profile\"))\n\n    def test_icc_big(self):\n        # Make sure that the \"extra\" support handles large blocks\n        def test(n):\n            # The ICC APP marker can store 65519 bytes per marker, so\n            # using a 4-byte test code should allow us to detect out of\n            # order issues.\n            icc_profile = (b\"Test\"*int(n/4+1))[:n]\n            assert len(icc_profile) == n  # sanity\n            im1 = self.roundtrip(hopper(), icc_profile=icc_profile)\n            self.assertEqual(im1.info.get(\"icc_profile\"), icc_profile or None)\n        test(0)\n        test(1)\n        test(3)\n        test(4)\n        test(5)\n        test(65533-14)  # full JPEG marker block\n        test(65533-14+1)  # full block plus one byte\n        test(ImageFile.MAXBLOCK)  # full buffer block\n        test(ImageFile.MAXBLOCK+1)  # full buffer block plus one byte\n        test(ImageFile.MAXBLOCK*4+3)  # large block\n\n    def test_optimize(self):\n        im1 = self.roundtrip(hopper())\n        im2 = self.roundtrip(hopper(), optimize=1)\n        self.assert_image_equal(im1, im2)\n        self.assertGreaterEqual(im1.bytes, im2.bytes)\n\n    def test_optimize_large_buffer(self):\n        # https://github.com/python-pillow/Pillow/issues/148\n        f = self.tempfile('temp.jpg')\n        # this requires ~ 1.5x Image.MAXBLOCK\n        im = Image.new(\"RGB\", (4096, 4096), 0xff3333)\n        im.save(f, format=\"JPEG\", optimize=True)\n\n    def test_progressive(self):\n        im1 = self.roundtrip(hopper())\n        im2 = self.roundtrip(hopper(), progressive=True)\n        self.assert_image_equal(im1, im2)\n        self.assertGreaterEqual(im1.bytes, im2.bytes)\n\n    def test_progressive_large_buffer(self):\n        f = self.tempfile('temp.jpg')\n        # this requires ~ 1.5x Image.MAXBLOCK\n        im = Image.new(\"RGB\", (4096, 4096), 0xff3333)\n        im.save(f, format=\"JPEG\", progressive=True)\n\n    def test_progressive_large_buffer_highest_quality(self):\n        f = self.tempfile('temp.jpg')\n        if py3:\n            a = bytes(random.randint(0, 255) for _ in range(256 * 256 * 3))\n        else:\n            a = b''.join(chr(random.randint(0, 255)) for _ in range(\n                256 * 256 * 3))\n        im = Image.frombuffer(\"RGB\", (256, 256), a, \"raw\", \"RGB\", 0, 1)\n        # this requires more bytes than pixels in the image\n        im.save(f, format=\"JPEG\", progressive=True, quality=100)\n\n    def test_large_exif(self):\n        # https://github.com/python-pillow/Pillow/issues/148\n        f = self.tempfile('temp.jpg')\n        im = hopper()\n        im.save(f, 'JPEG', quality=90, exif=b\"1\"*65532)\n\n    def test_exif_typeerror(self):\n        im = Image.open('Tests/images/exif_typeerror.jpg')\n        # Should not raise a TypeError\n        im._getexif()\n\n    def test_exif_gps(self):\n        # Arrange\n        im = Image.open('Tests/images/exif_gps.jpg')\n        gps_index = 34853\n        expected_exif_gps = {\n            0: b'\\x00\\x00\\x00\\x01',\n            2: (4294967295, 1),\n            5: b'\\x01',\n            30: 65535,\n            29: '1999:99:99 99:99:99'}\n\n        # Act\n        exif = im._getexif()\n\n        # Assert\n        self.assertEqual(exif[gps_index], expected_exif_gps)\n\n    def test_exif_rollback(self):\n        # rolling back exif support in 3.1 to pre-3.0 formatting.\n        # expected from 2.9, with b/u qualifiers switched for 3.2 compatibility\n        # this test passes on 2.9 and 3.1, but not 3.0\n        expected_exif = {34867: 4294967295,\n                         258: (24, 24, 24),\n                         36867: '2099:09:29 10:10:10',\n                         34853: {0: b'\\x00\\x00\\x00\\x01',\n                                 2: (4294967295, 1),\n                                 5: b'\\x01',\n                                 30: 65535,\n                                 29: '1999:99:99 99:99:99'},\n                         296: 65535,\n                         34665: 185,\n                         41994: 65535,\n                         514: 4294967295,\n                         271: 'Make',\n                         272: 'XXX-XXX',\n                         305: 'PIL',\n                         42034: ((1, 1), (1, 1), (1, 1), (1, 1)),\n                         42035: 'LensMake',\n                         34856: b'\\xaa\\xaa\\xaa\\xaa\\xaa\\xaa',\n                         282: (4294967295, 1),\n                         33434: (4294967295, 1)}\n\n        im = Image.open('Tests/images/exif_gps.jpg')\n        exif = im._getexif()\n\n        for tag, value in expected_exif.items():\n            self.assertEqual(value, exif[tag])\n\n    def test_exif_gps_typeerror(self):\n        im = Image.open('Tests/images/exif_gps_typeerror.jpg')\n\n        # Should not raise a TypeError\n        im._getexif()\n\n    def test_progressive_compat(self):\n        im1 = self.roundtrip(hopper())\n        im2 = self.roundtrip(hopper(), progressive=1)\n        im3 = self.roundtrip(hopper(), progression=1)  # compatibility\n        self.assert_image_equal(im1, im2)\n        self.assert_image_equal(im1, im3)\n        self.assertFalse(im1.info.get(\"progressive\"))\n        self.assertFalse(im1.info.get(\"progression\"))\n        self.assertTrue(im2.info.get(\"progressive\"))\n        self.assertTrue(im2.info.get(\"progression\"))\n        self.assertTrue(im3.info.get(\"progressive\"))\n        self.assertTrue(im3.info.get(\"progression\"))\n\n    def test_quality(self):\n        im1 = self.roundtrip(hopper())\n        im2 = self.roundtrip(hopper(), quality=50)\n        self.assert_image(im1, im2.mode, im2.size)\n        self.assertGreaterEqual(im1.bytes, im2.bytes)\n\n    def test_smooth(self):\n        im1 = self.roundtrip(hopper())\n        im2 = self.roundtrip(hopper(), smooth=100)\n        self.assert_image(im1, im2.mode, im2.size)\n\n    def test_subsampling(self):\n        def getsampling(im):\n            layer = im.layer\n            return layer[0][1:3] + layer[1][1:3] + layer[2][1:3]\n        # experimental API\n        im = self.roundtrip(hopper(), subsampling=-1)  # default\n        self.assertEqual(getsampling(im), (2, 2, 1, 1, 1, 1))\n        im = self.roundtrip(hopper(), subsampling=0)  # 4:4:4\n        self.assertEqual(getsampling(im), (1, 1, 1, 1, 1, 1))\n        im = self.roundtrip(hopper(), subsampling=1)  # 4:2:2\n        self.assertEqual(getsampling(im), (2, 1, 1, 1, 1, 1))\n        im = self.roundtrip(hopper(), subsampling=2)  # 4:1:1\n        self.assertEqual(getsampling(im), (2, 2, 1, 1, 1, 1))\n        im = self.roundtrip(hopper(), subsampling=3)  # default (undefined)\n        self.assertEqual(getsampling(im), (2, 2, 1, 1, 1, 1))\n\n        im = self.roundtrip(hopper(), subsampling=\"4:4:4\")\n        self.assertEqual(getsampling(im), (1, 1, 1, 1, 1, 1))\n        im = self.roundtrip(hopper(), subsampling=\"4:2:2\")\n        self.assertEqual(getsampling(im), (2, 1, 1, 1, 1, 1))\n        im = self.roundtrip(hopper(), subsampling=\"4:1:1\")\n        self.assertEqual(getsampling(im), (2, 2, 1, 1, 1, 1))\n\n        self.assertRaises(\n            TypeError, lambda: self.roundtrip(hopper(), subsampling=\"1:1:1\"))\n\n    def test_exif(self):\n        im = Image.open(\"Tests/images/pil_sample_rgb.jpg\")\n        info = im._getexif()\n        self.assertEqual(info[305], 'Adobe Photoshop CS Macintosh')\n\n    def test_mp(self):\n        im = Image.open(\"Tests/images/pil_sample_rgb.jpg\")\n        self.assertIsNone(im._getmp())\n\n    def test_quality_keep(self):\n        # RGB\n        im = Image.open(\"Tests/images/hopper.jpg\")\n        f = self.tempfile('temp.jpg')\n        im.save(f, quality='keep')\n        # Grayscale\n        im = Image.open(\"Tests/images/hopper_gray.jpg\")\n        f = self.tempfile('temp.jpg')\n        im.save(f, quality='keep')\n        # CMYK\n        im = Image.open(\"Tests/images/pil_sample_cmyk.jpg\")\n        f = self.tempfile('temp.jpg')\n        im.save(f, quality='keep')\n\n    def test_junk_jpeg_header(self):\n        # https://github.com/python-pillow/Pillow/issues/630\n        filename = \"Tests/images/junk_jpeg_header.jpg\"\n        Image.open(filename)\n\n    def test_ff00_jpeg_header(self):\n        filename = \"Tests/images/jpeg_ff00_header.jpg\"\n        Image.open(filename)\n\n    def _n_qtables_helper(self, n, test_file):\n        im = Image.open(test_file)\n        f = self.tempfile('temp.jpg')\n        im.save(f, qtables=[[n]*64]*n)\n        im = Image.open(f)\n        self.assertEqual(len(im.quantization), n)\n        reloaded = self.roundtrip(im, qtables=\"keep\")\n        self.assertEqual(im.quantization, reloaded.quantization)\n\n    def test_qtables(self):\n        im = Image.open(\"Tests/images/hopper.jpg\")\n        qtables = im.quantization\n        reloaded = self.roundtrip(im, qtables=qtables, subsampling=0)\n        self.assertEqual(im.quantization, reloaded.quantization)\n        self.assert_image_similar(im, self.roundtrip(im, qtables='web_low'),\n                                  30)\n        self.assert_image_similar(im, self.roundtrip(im, qtables='web_high'),\n                                  30)\n        self.assert_image_similar(im, self.roundtrip(im, qtables='keep'), 30)\n\n        # valid bounds for baseline qtable\n        bounds_qtable = [int(s) for s in (\"255 1 \" * 32).split(None)]\n        self.roundtrip(im, qtables=[bounds_qtable])\n\n        # values from wizard.txt in jpeg9-a src package.\n        standard_l_qtable = [int(s) for s in \"\"\"\n            16  11  10  16  24  40  51  61\n            12  12  14  19  26  58  60  55\n            14  13  16  24  40  57  69  56\n            14  17  22  29  51  87  80  62\n            18  22  37  56  68 109 103  77\n            24  35  55  64  81 104 113  92\n            49  64  78  87 103 121 120 101\n            72  92  95  98 112 100 103  99\n            \"\"\".split(None)]\n\n        standard_chrominance_qtable = [int(s) for s in \"\"\"\n            17  18  24  47  99  99  99  99\n            18  21  26  66  99  99  99  99\n            24  26  56  99  99  99  99  99\n            47  66  99  99  99  99  99  99\n            99  99  99  99  99  99  99  99\n            99  99  99  99  99  99  99  99\n            99  99  99  99  99  99  99  99\n            99  99  99  99  99  99  99  99\n            \"\"\".split(None)]\n        # list of qtable lists\n        self.assert_image_similar(\n            im, self.roundtrip(\n                im, qtables=[standard_l_qtable, standard_chrominance_qtable]),\n            30)\n\n        # tuple of qtable lists\n        self.assert_image_similar(\n            im, self.roundtrip(\n                im, qtables=(standard_l_qtable, standard_chrominance_qtable)),\n            30)\n\n        # dict of qtable lists\n        self.assert_image_similar(im,\n                                  self.roundtrip(im, qtables={\n                                      0: standard_l_qtable,\n                                      1: standard_chrominance_qtable\n                                  }), 30)\n\n        self._n_qtables_helper(1, \"Tests/images/hopper_gray.jpg\")\n        self._n_qtables_helper(1, \"Tests/images/pil_sample_rgb.jpg\")\n        self._n_qtables_helper(2, \"Tests/images/pil_sample_rgb.jpg\")\n        self._n_qtables_helper(3, \"Tests/images/pil_sample_rgb.jpg\")\n        self._n_qtables_helper(1, \"Tests/images/pil_sample_cmyk.jpg\")\n        self._n_qtables_helper(2, \"Tests/images/pil_sample_cmyk.jpg\")\n        self._n_qtables_helper(3, \"Tests/images/pil_sample_cmyk.jpg\")\n        self._n_qtables_helper(4, \"Tests/images/pil_sample_cmyk.jpg\")\n\n        # not a sequence\n        self.assertRaises(Exception, lambda: self.roundtrip(im, qtables='a'))\n        # sequence wrong length\n        self.assertRaises(Exception, lambda: self.roundtrip(im, qtables=[]))\n        # sequence wrong length\n        self.assertRaises(Exception,\n                          lambda: self.roundtrip(im, qtables=[1, 2, 3, 4, 5]))\n\n        # qtable entry not a sequence\n        self.assertRaises(Exception, lambda: self.roundtrip(im, qtables=[1]))\n        # qtable entry has wrong number of items\n        self.assertRaises(Exception,\n                          lambda: self.roundtrip(im, qtables=[[1, 2, 3, 4]]))\n\n    @unittest.skipUnless(djpeg_available(), \"djpeg not available\")\n    def test_load_djpeg(self):\n        img = Image.open(TEST_FILE)\n        img.load_djpeg()\n        self.assert_image_similar(img, Image.open(TEST_FILE), 0)\n\n    @unittest.skipUnless(cjpeg_available(), \"cjpeg not available\")\n    def test_save_cjpeg(self):\n        img = Image.open(TEST_FILE)\n\n        tempfile = self.tempfile(\"temp.jpg\")\n        JpegImagePlugin._save_cjpeg(img, 0, tempfile)\n        # Default save quality is 75%, so a tiny bit of difference is alright\n        self.assert_image_similar(img, Image.open(tempfile), 17)\n\n    def test_no_duplicate_0x1001_tag(self):\n        # Arrange\n        from PIL import ExifTags\n        tag_ids = dict(zip(ExifTags.TAGS.values(), ExifTags.TAGS.keys()))\n\n        # Assert\n        self.assertEqual(tag_ids['RelatedImageWidth'], 0x1001)\n        self.assertEqual(tag_ids['RelatedImageLength'], 0x1002)\n\n    def test_MAXBLOCK_scaling(self):\n        def gen_random_image(size):\n            \"\"\" Generates a very hard to compress file\n            :param size: tuple\n            \"\"\"\n            return Image.frombytes('RGB',\n                                   size, os.urandom(size[0]*size[1] * 3))\n\n        im = gen_random_image((512, 512))\n        f = self.tempfile(\"temp.jpeg\")\n        im.save(f, quality=100, optimize=True)\n\n        reloaded = Image.open(f)\n\n        # none of these should crash\n        reloaded.save(f, quality='keep')\n        reloaded.save(f, quality='keep', progressive=True)\n        reloaded.save(f, quality='keep', optimize=True)\n\n    def test_bad_mpo_header(self):\n        \"\"\" Treat unknown MPO as JPEG \"\"\"\n        # Arrange\n\n        # Act\n        # Shouldn't raise error\n        fn = \"Tests/images/sugarshack_bad_mpo_header.jpg\"\n        im = self.assert_warning(UserWarning, lambda: Image.open(fn))\n\n        # Assert\n        self.assertEqual(im.format, \"JPEG\")\n\n    def test_save_correct_modes(self):\n        out = BytesIO()\n        for mode in ['1', 'L', 'RGB', 'RGBX', 'CMYK', 'YCbCr']:\n            img = Image.new(mode, (20, 20))\n            img.save(out, \"JPEG\")\n\n    def test_save_wrong_modes(self):\n        out = BytesIO()\n        for mode in ['LA', 'La', 'RGBa', 'P']:\n            img = Image.new(mode, (20, 20))\n            self.assertRaises(IOError, img.save, out, \"JPEG\")\n\n    def test_save_modes_with_warnings(self):\n        # ref https://github.com/python-pillow/Pillow/issues/2005\n        out = BytesIO()\n        for mode in ['RGBA']:\n            img = Image.new(mode, (20, 20))\n            self.assert_warning(DeprecationWarning, img.save, out, \"JPEG\")\n\n    def test_save_tiff_with_dpi(self):\n        # Arrange\n        outfile = self.tempfile(\"temp.tif\")\n        im = Image.open(\"Tests/images/hopper.tif\")\n\n        # Act\n        im.save(outfile, 'JPEG', dpi=im.info['dpi'])\n\n        # Assert\n        reloaded = Image.open(outfile)\n        reloaded.load()\n        self.assertEqual(im.info['dpi'], reloaded.info['dpi'])\n\n\nif __name__ == '__main__':\n    unittest.main()\n",
            "new_file_content": "from helper import unittest, PillowTestCase, hopper, py3\nfrom helper import djpeg_available, cjpeg_available\n\nimport random\nfrom io import BytesIO\nimport os\n\nfrom PIL import Image\nfrom PIL import ImageFile\nfrom PIL import JpegImagePlugin\n\ncodecs = dir(Image.core)\n\nTEST_FILE = \"Tests/images/hopper.jpg\"\n\n\nclass TestFileJpeg(PillowTestCase):\n\n    def setUp(self):\n        if \"jpeg_encoder\" not in codecs or \"jpeg_decoder\" not in codecs:\n            self.skipTest(\"jpeg support not available\")\n\n    def roundtrip(self, im, **options):\n        out = BytesIO()\n        im.save(out, \"JPEG\", **options)\n        test_bytes = out.tell()\n        out.seek(0)\n        im = Image.open(out)\n        im.bytes = test_bytes  # for testing only\n        return im\n\n    def test_sanity(self):\n\n        # internal version number\n        self.assertRegexpMatches(Image.core.jpeglib_version, \"\\d+\\.\\d+$\")\n\n        im = Image.open(TEST_FILE)\n        im.load()\n        self.assertEqual(im.mode, \"RGB\")\n        self.assertEqual(im.size, (128, 128))\n        self.assertEqual(im.format, \"JPEG\")\n\n    def test_app(self):\n        # Test APP/COM reader (@PIL135)\n        im = Image.open(TEST_FILE)\n        self.assertEqual(\n            im.applist[0],\n            (\"APP0\", b\"JFIF\\x00\\x01\\x01\\x01\\x00`\\x00`\\x00\\x00\"))\n        self.assertEqual(im.applist[1], (\n            \"COM\", b\"File written by Adobe Photoshop\\xa8 4.0\\x00\"))\n        self.assertEqual(len(im.applist), 2)\n\n    def test_cmyk(self):\n        # Test CMYK handling.  Thanks to Tim and Charlie for test data,\n        # Michael for getting me to look one more time.\n        f = \"Tests/images/pil_sample_cmyk.jpg\"\n        im = Image.open(f)\n        # the source image has red pixels in the upper left corner.\n        c, m, y, k = [x / 255.0 for x in im.getpixel((0, 0))]\n        self.assertEqual(c, 0.0)\n        self.assertGreater(m, 0.8)\n        self.assertGreater(y, 0.8)\n        self.assertEqual(k, 0.0)\n        # the opposite corner is black\n        c, m, y, k = [x / 255.0 for x in im.getpixel((\n            im.size[0]-1, im.size[1]-1))]\n        self.assertGreater(k, 0.9)\n        # roundtrip, and check again\n        im = self.roundtrip(im)\n        c, m, y, k = [x / 255.0 for x in im.getpixel((0, 0))]\n        self.assertEqual(c, 0.0)\n        self.assertGreater(m, 0.8)\n        self.assertGreater(y, 0.8)\n        self.assertEqual(k, 0.0)\n        c, m, y, k = [x / 255.0 for x in im.getpixel((\n            im.size[0]-1, im.size[1]-1))]\n        self.assertGreater(k, 0.9)\n\n    def test_dpi(self):\n        def test(xdpi, ydpi=None):\n            im = Image.open(TEST_FILE)\n            im = self.roundtrip(im, dpi=(xdpi, ydpi or xdpi))\n            return im.info.get(\"dpi\")\n        self.assertEqual(test(72), (72, 72))\n        self.assertEqual(test(300), (300, 300))\n        self.assertEqual(test(100, 200), (100, 200))\n        self.assertEqual(test(0), None)  # square pixels\n\n    def test_icc(self):\n        # Test ICC support\n        im1 = Image.open(\"Tests/images/rgb.jpg\")\n        icc_profile = im1.info[\"icc_profile\"]\n        self.assertEqual(len(icc_profile), 3144)\n        # Roundtrip via physical file.\n        f = self.tempfile(\"temp.jpg\")\n        im1.save(f, icc_profile=icc_profile)\n        im2 = Image.open(f)\n        self.assertEqual(im2.info.get(\"icc_profile\"), icc_profile)\n        # Roundtrip via memory buffer.\n        im1 = self.roundtrip(hopper())\n        im2 = self.roundtrip(hopper(), icc_profile=icc_profile)\n        self.assert_image_equal(im1, im2)\n        self.assertFalse(im1.info.get(\"icc_profile\"))\n        self.assertTrue(im2.info.get(\"icc_profile\"))\n\n    def test_icc_big(self):\n        # Make sure that the \"extra\" support handles large blocks\n        def test(n):\n            # The ICC APP marker can store 65519 bytes per marker, so\n            # using a 4-byte test code should allow us to detect out of\n            # order issues.\n            icc_profile = (b\"Test\"*int(n/4+1))[:n]\n            assert len(icc_profile) == n  # sanity\n            im1 = self.roundtrip(hopper(), icc_profile=icc_profile)\n            self.assertEqual(im1.info.get(\"icc_profile\"), icc_profile or None)\n        test(0)\n        test(1)\n        test(3)\n        test(4)\n        test(5)\n        test(65533-14)  # full JPEG marker block\n        test(65533-14+1)  # full block plus one byte\n        test(ImageFile.MAXBLOCK)  # full buffer block\n        test(ImageFile.MAXBLOCK+1)  # full buffer block plus one byte\n        test(ImageFile.MAXBLOCK*4+3)  # large block\n\n    def test_optimize(self):\n        im1 = self.roundtrip(hopper())\n        im2 = self.roundtrip(hopper(), optimize=1)\n        self.assert_image_equal(im1, im2)\n        self.assertGreaterEqual(im1.bytes, im2.bytes)\n\n    def test_optimize_large_buffer(self):\n        # https://github.com/python-pillow/Pillow/issues/148\n        f = self.tempfile('temp.jpg')\n        # this requires ~ 1.5x Image.MAXBLOCK\n        im = Image.new(\"RGB\", (4096, 4096), 0xff3333)\n        im.save(f, format=\"JPEG\", optimize=True)\n\n    def test_progressive(self):\n        im1 = self.roundtrip(hopper())\n        im2 = self.roundtrip(hopper(), progressive=True)\n        self.assert_image_equal(im1, im2)\n        self.assertGreaterEqual(im1.bytes, im2.bytes)\n\n    def test_progressive_large_buffer(self):\n        f = self.tempfile('temp.jpg')\n        # this requires ~ 1.5x Image.MAXBLOCK\n        im = Image.new(\"RGB\", (4096, 4096), 0xff3333)\n        im.save(f, format=\"JPEG\", progressive=True)\n\n    def test_progressive_large_buffer_highest_quality(self):\n        f = self.tempfile('temp.jpg')\n        if py3:\n            a = bytes(random.randint(0, 255) for _ in range(256 * 256 * 3))\n        else:\n            a = b''.join(chr(random.randint(0, 255)) for _ in range(\n                256 * 256 * 3))\n        im = Image.frombuffer(\"RGB\", (256, 256), a, \"raw\", \"RGB\", 0, 1)\n        # this requires more bytes than pixels in the image\n        im.save(f, format=\"JPEG\", progressive=True, quality=100)\n\n    def test_large_exif(self):\n        # https://github.com/python-pillow/Pillow/issues/148\n        f = self.tempfile('temp.jpg')\n        im = hopper()\n        im.save(f, 'JPEG', quality=90, exif=b\"1\"*65532)\n\n    def test_exif_typeerror(self):\n        im = Image.open('Tests/images/exif_typeerror.jpg')\n        # Should not raise a TypeError\n        im._getexif()\n\n    def test_exif_gps(self):\n        # Arrange\n        im = Image.open('Tests/images/exif_gps.jpg')\n        gps_index = 34853\n        expected_exif_gps = {\n            0: b'\\x00\\x00\\x00\\x01',\n            2: (4294967295, 1),\n            5: b'\\x01',\n            30: 65535,\n            29: '1999:99:99 99:99:99'}\n\n        # Act\n        exif = im._getexif()\n\n        # Assert\n        self.assertEqual(exif[gps_index], expected_exif_gps)\n\n    def test_exif_rollback(self):\n        # rolling back exif support in 3.1 to pre-3.0 formatting.\n        # expected from 2.9, with b/u qualifiers switched for 3.2 compatibility\n        # this test passes on 2.9 and 3.1, but not 3.0\n        expected_exif = {34867: 4294967295,\n                         258: (24, 24, 24),\n                         36867: '2099:09:29 10:10:10',\n                         34853: {0: b'\\x00\\x00\\x00\\x01',\n                                 2: (4294967295, 1),\n                                 5: b'\\x01',\n                                 30: 65535,\n                                 29: '1999:99:99 99:99:99'},\n                         296: 65535,\n                         34665: 185,\n                         41994: 65535,\n                         514: 4294967295,\n                         271: 'Make',\n                         272: 'XXX-XXX',\n                         305: 'PIL',\n                         42034: ((1, 1), (1, 1), (1, 1), (1, 1)),\n                         42035: 'LensMake',\n                         34856: b'\\xaa\\xaa\\xaa\\xaa\\xaa\\xaa',\n                         282: (4294967295, 1),\n                         33434: (4294967295, 1)}\n\n        im = Image.open('Tests/images/exif_gps.jpg')\n        exif = im._getexif()\n\n        for tag, value in expected_exif.items():\n            self.assertEqual(value, exif[tag])\n\n    def test_exif_gps_typeerror(self):\n        im = Image.open('Tests/images/exif_gps_typeerror.jpg')\n\n        # Should not raise a TypeError\n        im._getexif()\n\n    def test_progressive_compat(self):\n        im1 = self.roundtrip(hopper())\n        self.assertFalse(im1.info.get(\"progressive\"))\n        self.assertFalse(im1.info.get(\"progression\"))\n\n        im2 = self.roundtrip(hopper(), progressive=0)\n        im3 = self.roundtrip(hopper(), progression=0)  # compatibility\n        self.assertFalse(im2.info.get(\"progressive\"))\n        self.assertFalse(im2.info.get(\"progression\"))\n        self.assertFalse(im3.info.get(\"progressive\"))\n        self.assertFalse(im3.info.get(\"progression\"))\n\n        im2 = self.roundtrip(hopper(), progressive=1)\n        im3 = self.roundtrip(hopper(), progression=1)  # compatibility\n        self.assert_image_equal(im1, im2)\n        self.assert_image_equal(im1, im3)\n        self.assertTrue(im2.info.get(\"progressive\"))\n        self.assertTrue(im2.info.get(\"progression\"))\n        self.assertTrue(im3.info.get(\"progressive\"))\n        self.assertTrue(im3.info.get(\"progression\"))\n\n    def test_quality(self):\n        im1 = self.roundtrip(hopper())\n        im2 = self.roundtrip(hopper(), quality=50)\n        self.assert_image(im1, im2.mode, im2.size)\n        self.assertGreaterEqual(im1.bytes, im2.bytes)\n\n    def test_smooth(self):\n        im1 = self.roundtrip(hopper())\n        im2 = self.roundtrip(hopper(), smooth=100)\n        self.assert_image(im1, im2.mode, im2.size)\n\n    def test_subsampling(self):\n        def getsampling(im):\n            layer = im.layer\n            return layer[0][1:3] + layer[1][1:3] + layer[2][1:3]\n        # experimental API\n        im = self.roundtrip(hopper(), subsampling=-1)  # default\n        self.assertEqual(getsampling(im), (2, 2, 1, 1, 1, 1))\n        im = self.roundtrip(hopper(), subsampling=0)  # 4:4:4\n        self.assertEqual(getsampling(im), (1, 1, 1, 1, 1, 1))\n        im = self.roundtrip(hopper(), subsampling=1)  # 4:2:2\n        self.assertEqual(getsampling(im), (2, 1, 1, 1, 1, 1))\n        im = self.roundtrip(hopper(), subsampling=2)  # 4:1:1\n        self.assertEqual(getsampling(im), (2, 2, 1, 1, 1, 1))\n        im = self.roundtrip(hopper(), subsampling=3)  # default (undefined)\n        self.assertEqual(getsampling(im), (2, 2, 1, 1, 1, 1))\n\n        im = self.roundtrip(hopper(), subsampling=\"4:4:4\")\n        self.assertEqual(getsampling(im), (1, 1, 1, 1, 1, 1))\n        im = self.roundtrip(hopper(), subsampling=\"4:2:2\")\n        self.assertEqual(getsampling(im), (2, 1, 1, 1, 1, 1))\n        im = self.roundtrip(hopper(), subsampling=\"4:1:1\")\n        self.assertEqual(getsampling(im), (2, 2, 1, 1, 1, 1))\n\n        self.assertRaises(\n            TypeError, lambda: self.roundtrip(hopper(), subsampling=\"1:1:1\"))\n\n    def test_exif(self):\n        im = Image.open(\"Tests/images/pil_sample_rgb.jpg\")\n        info = im._getexif()\n        self.assertEqual(info[305], 'Adobe Photoshop CS Macintosh')\n\n    def test_mp(self):\n        im = Image.open(\"Tests/images/pil_sample_rgb.jpg\")\n        self.assertIsNone(im._getmp())\n\n    def test_quality_keep(self):\n        # RGB\n        im = Image.open(\"Tests/images/hopper.jpg\")\n        f = self.tempfile('temp.jpg')\n        im.save(f, quality='keep')\n        # Grayscale\n        im = Image.open(\"Tests/images/hopper_gray.jpg\")\n        f = self.tempfile('temp.jpg')\n        im.save(f, quality='keep')\n        # CMYK\n        im = Image.open(\"Tests/images/pil_sample_cmyk.jpg\")\n        f = self.tempfile('temp.jpg')\n        im.save(f, quality='keep')\n\n    def test_junk_jpeg_header(self):\n        # https://github.com/python-pillow/Pillow/issues/630\n        filename = \"Tests/images/junk_jpeg_header.jpg\"\n        Image.open(filename)\n\n    def test_ff00_jpeg_header(self):\n        filename = \"Tests/images/jpeg_ff00_header.jpg\"\n        Image.open(filename)\n\n    def _n_qtables_helper(self, n, test_file):\n        im = Image.open(test_file)\n        f = self.tempfile('temp.jpg')\n        im.save(f, qtables=[[n]*64]*n)\n        im = Image.open(f)\n        self.assertEqual(len(im.quantization), n)\n        reloaded = self.roundtrip(im, qtables=\"keep\")\n        self.assertEqual(im.quantization, reloaded.quantization)\n\n    def test_qtables(self):\n        im = Image.open(\"Tests/images/hopper.jpg\")\n        qtables = im.quantization\n        reloaded = self.roundtrip(im, qtables=qtables, subsampling=0)\n        self.assertEqual(im.quantization, reloaded.quantization)\n        self.assert_image_similar(im, self.roundtrip(im, qtables='web_low'),\n                                  30)\n        self.assert_image_similar(im, self.roundtrip(im, qtables='web_high'),\n                                  30)\n        self.assert_image_similar(im, self.roundtrip(im, qtables='keep'), 30)\n\n        # valid bounds for baseline qtable\n        bounds_qtable = [int(s) for s in (\"255 1 \" * 32).split(None)]\n        self.roundtrip(im, qtables=[bounds_qtable])\n\n        # values from wizard.txt in jpeg9-a src package.\n        standard_l_qtable = [int(s) for s in \"\"\"\n            16  11  10  16  24  40  51  61\n            12  12  14  19  26  58  60  55\n            14  13  16  24  40  57  69  56\n            14  17  22  29  51  87  80  62\n            18  22  37  56  68 109 103  77\n            24  35  55  64  81 104 113  92\n            49  64  78  87 103 121 120 101\n            72  92  95  98 112 100 103  99\n            \"\"\".split(None)]\n\n        standard_chrominance_qtable = [int(s) for s in \"\"\"\n            17  18  24  47  99  99  99  99\n            18  21  26  66  99  99  99  99\n            24  26  56  99  99  99  99  99\n            47  66  99  99  99  99  99  99\n            99  99  99  99  99  99  99  99\n            99  99  99  99  99  99  99  99\n            99  99  99  99  99  99  99  99\n            99  99  99  99  99  99  99  99\n            \"\"\".split(None)]\n        # list of qtable lists\n        self.assert_image_similar(\n            im, self.roundtrip(\n                im, qtables=[standard_l_qtable, standard_chrominance_qtable]),\n            30)\n\n        # tuple of qtable lists\n        self.assert_image_similar(\n            im, self.roundtrip(\n                im, qtables=(standard_l_qtable, standard_chrominance_qtable)),\n            30)\n\n        # dict of qtable lists\n        self.assert_image_similar(im,\n                                  self.roundtrip(im, qtables={\n                                      0: standard_l_qtable,\n                                      1: standard_chrominance_qtable\n                                  }), 30)\n\n        self._n_qtables_helper(1, \"Tests/images/hopper_gray.jpg\")\n        self._n_qtables_helper(1, \"Tests/images/pil_sample_rgb.jpg\")\n        self._n_qtables_helper(2, \"Tests/images/pil_sample_rgb.jpg\")\n        self._n_qtables_helper(3, \"Tests/images/pil_sample_rgb.jpg\")\n        self._n_qtables_helper(1, \"Tests/images/pil_sample_cmyk.jpg\")\n        self._n_qtables_helper(2, \"Tests/images/pil_sample_cmyk.jpg\")\n        self._n_qtables_helper(3, \"Tests/images/pil_sample_cmyk.jpg\")\n        self._n_qtables_helper(4, \"Tests/images/pil_sample_cmyk.jpg\")\n\n        # not a sequence\n        self.assertRaises(Exception, lambda: self.roundtrip(im, qtables='a'))\n        # sequence wrong length\n        self.assertRaises(Exception, lambda: self.roundtrip(im, qtables=[]))\n        # sequence wrong length\n        self.assertRaises(Exception,\n                          lambda: self.roundtrip(im, qtables=[1, 2, 3, 4, 5]))\n\n        # qtable entry not a sequence\n        self.assertRaises(Exception, lambda: self.roundtrip(im, qtables=[1]))\n        # qtable entry has wrong number of items\n        self.assertRaises(Exception,\n                          lambda: self.roundtrip(im, qtables=[[1, 2, 3, 4]]))\n\n    @unittest.skipUnless(djpeg_available(), \"djpeg not available\")\n    def test_load_djpeg(self):\n        img = Image.open(TEST_FILE)\n        img.load_djpeg()\n        self.assert_image_similar(img, Image.open(TEST_FILE), 0)\n\n    @unittest.skipUnless(cjpeg_available(), \"cjpeg not available\")\n    def test_save_cjpeg(self):\n        img = Image.open(TEST_FILE)\n\n        tempfile = self.tempfile(\"temp.jpg\")\n        JpegImagePlugin._save_cjpeg(img, 0, tempfile)\n        # Default save quality is 75%, so a tiny bit of difference is alright\n        self.assert_image_similar(img, Image.open(tempfile), 17)\n\n    def test_no_duplicate_0x1001_tag(self):\n        # Arrange\n        from PIL import ExifTags\n        tag_ids = dict(zip(ExifTags.TAGS.values(), ExifTags.TAGS.keys()))\n\n        # Assert\n        self.assertEqual(tag_ids['RelatedImageWidth'], 0x1001)\n        self.assertEqual(tag_ids['RelatedImageLength'], 0x1002)\n\n    def test_MAXBLOCK_scaling(self):\n        def gen_random_image(size):\n            \"\"\" Generates a very hard to compress file\n            :param size: tuple\n            \"\"\"\n            return Image.frombytes('RGB',\n                                   size, os.urandom(size[0]*size[1] * 3))\n\n        im = gen_random_image((512, 512))\n        f = self.tempfile(\"temp.jpeg\")\n        im.save(f, quality=100, optimize=True)\n\n        reloaded = Image.open(f)\n\n        # none of these should crash\n        reloaded.save(f, quality='keep')\n        reloaded.save(f, quality='keep', progressive=True)\n        reloaded.save(f, quality='keep', optimize=True)\n\n    def test_bad_mpo_header(self):\n        \"\"\" Treat unknown MPO as JPEG \"\"\"\n        # Arrange\n\n        # Act\n        # Shouldn't raise error\n        fn = \"Tests/images/sugarshack_bad_mpo_header.jpg\"\n        im = self.assert_warning(UserWarning, lambda: Image.open(fn))\n\n        # Assert\n        self.assertEqual(im.format, \"JPEG\")\n\n    def test_save_correct_modes(self):\n        out = BytesIO()\n        for mode in ['1', 'L', 'RGB', 'RGBX', 'CMYK', 'YCbCr']:\n            img = Image.new(mode, (20, 20))\n            img.save(out, \"JPEG\")\n\n    def test_save_wrong_modes(self):\n        out = BytesIO()\n        for mode in ['LA', 'La', 'RGBa', 'P']:\n            img = Image.new(mode, (20, 20))\n            self.assertRaises(IOError, img.save, out, \"JPEG\")\n\n    def test_save_modes_with_warnings(self):\n        # ref https://github.com/python-pillow/Pillow/issues/2005\n        out = BytesIO()\n        for mode in ['RGBA']:\n            img = Image.new(mode, (20, 20))\n            self.assert_warning(DeprecationWarning, img.save, out, \"JPEG\")\n\n    def test_save_tiff_with_dpi(self):\n        # Arrange\n        outfile = self.tempfile(\"temp.tif\")\n        im = Image.open(\"Tests/images/hopper.tif\")\n\n        # Act\n        im.save(outfile, 'JPEG', dpi=im.info['dpi'])\n\n        # Assert\n        reloaded = Image.open(outfile)\n        reloaded.load()\n        self.assertEqual(im.info['dpi'], reloaded.info['dpi'])\n\n\nif __name__ == '__main__':\n    unittest.main()\n",
            "header": {
                "file": {
                    "path": "Tests/test_file_jpeg.py"
                },
                "misc_line": null
            },
            "index_line": {
                "old_commit_hash": "5e66cc765",
                "new_commit_hash": "0ae82fac2",
                "mode": "100644"
            },
            "is_binary_file": false,
            "binary_line": null,
            "minus_file": {
                "path": "a/Tests/test_file_jpeg.py"
            },
            "plus_file": {
                "path": "b/Tests/test_file_jpeg.py"
            },
            "hunks": [
                {
                    "descriptor": {
                        "old_range": {
                            "start": 227,
                            "length": 12
                        },
                        "new_range": {
                            "start": 227,
                            "length": 20
                        },
                        "section": "class TestFileJpeg(PillowTestCase):"
                    },
                    "line_group": {
                        "all_lines": [
                            {
                                "content": "",
                                "type": "context"
                            },
                            {
                                "content": "    def test_progressive_compat(self):",
                                "type": "context"
                            },
                            {
                                "content": "        im1 = self.roundtrip(hopper())",
                                "type": "context"
                            },
                            {
                                "content": "        self.assertFalse(im1.info.get(\"progressive\"))",
                                "type": "added"
                            },
                            {
                                "content": "        self.assertFalse(im1.info.get(\"progression\"))",
                                "type": "added"
                            },
                            {
                                "content": "",
                                "type": "added"
                            },
                            {
                                "content": "        im2 = self.roundtrip(hopper(), progressive=0)",
                                "type": "added"
                            },
                            {
                                "content": "        im3 = self.roundtrip(hopper(), progression=0)  # compatibility",
                                "type": "added"
                            },
                            {
                                "content": "        self.assertFalse(im2.info.get(\"progressive\"))",
                                "type": "added"
                            },
                            {
                                "content": "        self.assertFalse(im2.info.get(\"progression\"))",
                                "type": "added"
                            },
                            {
                                "content": "        self.assertFalse(im3.info.get(\"progressive\"))",
                                "type": "added"
                            },
                            {
                                "content": "        self.assertFalse(im3.info.get(\"progression\"))",
                                "type": "added"
                            },
                            {
                                "content": "",
                                "type": "added"
                            },
                            {
                                "content": "        im2 = self.roundtrip(hopper(), progressive=1)",
                                "type": "context"
                            },
                            {
                                "content": "        im3 = self.roundtrip(hopper(), progression=1)  # compatibility",
                                "type": "context"
                            },
                            {
                                "content": "        self.assert_image_equal(im1, im2)",
                                "type": "context"
                            },
                            {
                                "content": "        self.assert_image_equal(im1, im3)",
                                "type": "context"
                            },
                            {
                                "content": "        self.assertFalse(im1.info.get(\"progressive\"))",
                                "type": "deleted"
                            },
                            {
                                "content": "        self.assertFalse(im1.info.get(\"progression\"))",
                                "type": "deleted"
                            },
                            {
                                "content": "        self.assertTrue(im2.info.get(\"progressive\"))",
                                "type": "context"
                            },
                            {
                                "content": "        self.assertTrue(im2.info.get(\"progression\"))",
                                "type": "context"
                            },
                            {
                                "content": "        self.assertTrue(im3.info.get(\"progressive\"))",
                                "type": "context"
                            },
                            {
                                "content": "",
                                "type": "context"
                            }
                        ]
                    },
                    "modified_entities": [
                        {
                            "file_name": "Tests/test_file_jpeg.py",
                            "type": "class",
                            "name": "TestFileJpeg",
                            "content": "class TestFileJpeg(PillowTestCase):\n\n    def setUp(self):\n        if \"jpeg_encoder\" not in codecs or \"jpeg_decoder\" not in codecs:\n            self.skipTest(\"jpeg support not available\")\n\n    def roundtrip(self, im, **options):\n        out = BytesIO()\n        im.save(out, \"JPEG\", **options)\n        test_bytes = out.tell()\n        out.seek(0)\n        im = Image.open(out)\n        im.bytes = test_bytes  # for testing only\n        return im\n\n    def test_sanity(self):\n\n        # internal version number\n        self.assertRegexpMatches(Image.core.jpeglib_version, \"\\d+\\.\\d+$\")\n\n        im = Image.open(TEST_FILE)\n        im.load()\n        self.assertEqual(im.mode, \"RGB\")\n        self.assertEqual(im.size, (128, 128))\n        self.assertEqual(im.format, \"JPEG\")\n\n    def test_app(self):\n        # Test APP/COM reader (@PIL135)\n        im = Image.open(TEST_FILE)\n        self.assertEqual(\n            im.applist[0],\n            (\"APP0\", b\"JFIF\\x00\\x01\\x01\\x01\\x00`\\x00`\\x00\\x00\"))\n        self.assertEqual(im.applist[1], (\n            \"COM\", b\"File written by Adobe Photoshop\\xa8 4.0\\x00\"))\n        self.assertEqual(len(im.applist), 2)\n\n    def test_cmyk(self):\n        # Test CMYK handling.  Thanks to Tim and Charlie for test data,\n        # Michael for getting me to look one more time.\n        f = \"Tests/images/pil_sample_cmyk.jpg\"\n        im = Image.open(f)\n        # the source image has red pixels in the upper left corner.\n        c, m, y, k = [x / 255.0 for x in im.getpixel((0, 0))]\n        self.assertEqual(c, 0.0)\n        self.assertGreater(m, 0.8)\n        self.assertGreater(y, 0.8)\n        self.assertEqual(k, 0.0)\n        # the opposite corner is black\n        c, m, y, k = [x / 255.0 for x in im.getpixel((\n            im.size[0]-1, im.size[1]-1))]\n        self.assertGreater(k, 0.9)\n        # roundtrip, and check again\n        im = self.roundtrip(im)\n        c, m, y, k = [x / 255.0 for x in im.getpixel((0, 0))]\n        self.assertEqual(c, 0.0)\n        self.assertGreater(m, 0.8)\n        self.assertGreater(y, 0.8)\n        self.assertEqual(k, 0.0)\n        c, m, y, k = [x / 255.0 for x in im.getpixel((\n            im.size[0]-1, im.size[1]-1))]\n        self.assertGreater(k, 0.9)\n\n    def test_dpi(self):\n        def test(xdpi, ydpi=None):\n            im = Image.open(TEST_FILE)\n            im = self.roundtrip(im, dpi=(xdpi, ydpi or xdpi))\n            return im.info.get(\"dpi\")\n        self.assertEqual(test(72), (72, 72))\n        self.assertEqual(test(300), (300, 300))\n        self.assertEqual(test(100, 200), (100, 200))\n        self.assertEqual(test(0), None)  # square pixels\n\n    def test_icc(self):\n        # Test ICC support\n        im1 = Image.open(\"Tests/images/rgb.jpg\")\n        icc_profile = im1.info[\"icc_profile\"]\n        self.assertEqual(len(icc_profile), 3144)\n        # Roundtrip via physical file.\n        f = self.tempfile(\"temp.jpg\")\n        im1.save(f, icc_profile=icc_profile)\n        im2 = Image.open(f)\n        self.assertEqual(im2.info.get(\"icc_profile\"), icc_profile)\n        # Roundtrip via memory buffer.\n        im1 = self.roundtrip(hopper())\n        im2 = self.roundtrip(hopper(), icc_profile=icc_profile)\n        self.assert_image_equal(im1, im2)\n        self.assertFalse(im1.info.get(\"icc_profile\"))\n        self.assertTrue(im2.info.get(\"icc_profile\"))\n\n    def test_icc_big(self):\n        # Make sure that the \"extra\" support handles large blocks\n        def test(n):\n            # The ICC APP marker can store 65519 bytes per marker, so\n            # using a 4-byte test code should allow us to detect out of\n            # order issues.\n            icc_profile = (b\"Test\"*int(n/4+1))[:n]\n            assert len(icc_profile) == n  # sanity\n            im1 = self.roundtrip(hopper(), icc_profile=icc_profile)\n            self.assertEqual(im1.info.get(\"icc_profile\"), icc_profile or None)\n        test(0)\n        test(1)\n        test(3)\n        test(4)\n        test(5)\n        test(65533-14)  # full JPEG marker block\n        test(65533-14+1)  # full block plus one byte\n        test(ImageFile.MAXBLOCK)  # full buffer block\n        test(ImageFile.MAXBLOCK+1)  # full buffer block plus one byte\n        test(ImageFile.MAXBLOCK*4+3)  # large block\n\n    def test_optimize(self):\n        im1 = self.roundtrip(hopper())\n        im2 = self.roundtrip(hopper(), optimize=1)\n        self.assert_image_equal(im1, im2)\n        self.assertGreaterEqual(im1.bytes, im2.bytes)\n\n    def test_optimize_large_buffer(self):\n        # https://github.com/python-pillow/Pillow/issues/148\n        f = self.tempfile('temp.jpg')\n        # this requires ~ 1.5x Image.MAXBLOCK\n        im = Image.new(\"RGB\", (4096, 4096), 0xff3333)\n        im.save(f, format=\"JPEG\", optimize=True)\n\n    def test_progressive(self):\n        im1 = self.roundtrip(hopper())\n        im2 = self.roundtrip(hopper(), progressive=True)\n        self.assert_image_equal(im1, im2)\n        self.assertGreaterEqual(im1.bytes, im2.bytes)\n\n    def test_progressive_large_buffer(self):\n        f = self.tempfile('temp.jpg')\n        # this requires ~ 1.5x Image.MAXBLOCK\n        im = Image.new(\"RGB\", (4096, 4096), 0xff3333)\n        im.save(f, format=\"JPEG\", progressive=True)\n\n    def test_progressive_large_buffer_highest_quality(self):\n        f = self.tempfile('temp.jpg')\n        if py3:\n            a = bytes(random.randint(0, 255) for _ in range(256 * 256 * 3))\n        else:\n            a = b''.join(chr(random.randint(0, 255)) for _ in range(\n                256 * 256 * 3))\n        im = Image.frombuffer(\"RGB\", (256, 256), a, \"raw\", \"RGB\", 0, 1)\n        # this requires more bytes than pixels in the image\n        im.save(f, format=\"JPEG\", progressive=True, quality=100)\n\n    def test_large_exif(self):\n        # https://github.com/python-pillow/Pillow/issues/148\n        f = self.tempfile('temp.jpg')\n        im = hopper()\n        im.save(f, 'JPEG', quality=90, exif=b\"1\"*65532)\n\n    def test_exif_typeerror(self):\n        im = Image.open('Tests/images/exif_typeerror.jpg')\n        # Should not raise a TypeError\n        im._getexif()\n\n    def test_exif_gps(self):\n        # Arrange\n        im = Image.open('Tests/images/exif_gps.jpg')\n        gps_index = 34853\n        expected_exif_gps = {\n            0: b'\\x00\\x00\\x00\\x01',\n            2: (4294967295, 1),\n            5: b'\\x01',\n            30: 65535,\n            29: '1999:99:99 99:99:99'}\n\n        # Act\n        exif = im._getexif()\n\n        # Assert\n        self.assertEqual(exif[gps_index], expected_exif_gps)\n\n    def test_exif_rollback(self):\n        # rolling back exif support in 3.1 to pre-3.0 formatting.\n        # expected from 2.9, with b/u qualifiers switched for 3.2 compatibility\n        # this test passes on 2.9 and 3.1, but not 3.0\n        expected_exif = {34867: 4294967295,\n                         258: (24, 24, 24),\n                         36867: '2099:09:29 10:10:10',\n                         34853: {0: b'\\x00\\x00\\x00\\x01',\n                                 2: (4294967295, 1),\n                                 5: b'\\x01',\n                                 30: 65535,\n                                 29: '1999:99:99 99:99:99'},\n                         296: 65535,\n                         34665: 185,\n                         41994: 65535,\n                         514: 4294967295,\n                         271: 'Make',\n                         272: 'XXX-XXX',\n                         305: 'PIL',\n                         42034: ((1, 1), (1, 1), (1, 1), (1, 1)),\n                         42035: 'LensMake',\n                         34856: b'\\xaa\\xaa\\xaa\\xaa\\xaa\\xaa',\n                         282: (4294967295, 1),\n                         33434: (4294967295, 1)}\n\n        im = Image.open('Tests/images/exif_gps.jpg')\n        exif = im._getexif()\n\n        for tag, value in expected_exif.items():\n            self.assertEqual(value, exif[tag])\n\n    def test_exif_gps_typeerror(self):\n        im = Image.open('Tests/images/exif_gps_typeerror.jpg')\n\n        # Should not raise a TypeError\n        im._getexif()\n\n    def test_progressive_compat(self):\n        im1 = self.roundtrip(hopper())\n        im2 = self.roundtrip(hopper(), progressive=1)\n        im3 = self.roundtrip(hopper(), progression=1)  # compatibility\n        self.assert_image_equal(im1, im2)\n        self.assert_image_equal(im1, im3)\n        self.assertFalse(im1.info.get(\"progressive\"))\n        self.assertFalse(im1.info.get(\"progression\"))\n        self.assertTrue(im2.info.get(\"progressive\"))\n        self.assertTrue(im2.info.get(\"progression\"))\n        self.assertTrue(im3.info.get(\"progressive\"))\n        self.assertTrue(im3.info.get(\"progression\"))\n\n    def test_quality(self):\n        im1 = self.roundtrip(hopper())\n        im2 = self.roundtrip(hopper(), quality=50)\n        self.assert_image(im1, im2.mode, im2.size)\n        self.assertGreaterEqual(im1.bytes, im2.bytes)\n\n    def test_smooth(self):\n        im1 = self.roundtrip(hopper())\n        im2 = self.roundtrip(hopper(), smooth=100)\n        self.assert_image(im1, im2.mode, im2.size)\n\n    def test_subsampling(self):\n        def getsampling(im):\n            layer = im.layer\n            return layer[0][1:3] + layer[1][1:3] + layer[2][1:3]\n        # experimental API\n        im = self.roundtrip(hopper(), subsampling=-1)  # default\n        self.assertEqual(getsampling(im), (2, 2, 1, 1, 1, 1))\n        im = self.roundtrip(hopper(), subsampling=0)  # 4:4:4\n        self.assertEqual(getsampling(im), (1, 1, 1, 1, 1, 1))\n        im = self.roundtrip(hopper(), subsampling=1)  # 4:2:2\n        self.assertEqual(getsampling(im), (2, 1, 1, 1, 1, 1))\n        im = self.roundtrip(hopper(), subsampling=2)  # 4:1:1\n        self.assertEqual(getsampling(im), (2, 2, 1, 1, 1, 1))\n        im = self.roundtrip(hopper(), subsampling=3)  # default (undefined)\n        self.assertEqual(getsampling(im), (2, 2, 1, 1, 1, 1))\n\n        im = self.roundtrip(hopper(), subsampling=\"4:4:4\")\n        self.assertEqual(getsampling(im), (1, 1, 1, 1, 1, 1))\n        im = self.roundtrip(hopper(), subsampling=\"4:2:2\")\n        self.assertEqual(getsampling(im), (2, 1, 1, 1, 1, 1))\n        im = self.roundtrip(hopper(), subsampling=\"4:1:1\")\n        self.assertEqual(getsampling(im), (2, 2, 1, 1, 1, 1))\n\n        self.assertRaises(\n            TypeError, lambda: self.roundtrip(hopper(), subsampling=\"1:1:1\"))\n\n    def test_exif(self):\n        im = Image.open(\"Tests/images/pil_sample_rgb.jpg\")\n        info = im._getexif()\n        self.assertEqual(info[305], 'Adobe Photoshop CS Macintosh')\n\n    def test_mp(self):\n        im = Image.open(\"Tests/images/pil_sample_rgb.jpg\")\n        self.assertIsNone(im._getmp())\n\n    def test_quality_keep(self):\n        # RGB\n        im = Image.open(\"Tests/images/hopper.jpg\")\n        f = self.tempfile('temp.jpg')\n        im.save(f, quality='keep')\n        # Grayscale\n        im = Image.open(\"Tests/images/hopper_gray.jpg\")\n        f = self.tempfile('temp.jpg')\n        im.save(f, quality='keep')\n        # CMYK\n        im = Image.open(\"Tests/images/pil_sample_cmyk.jpg\")\n        f = self.tempfile('temp.jpg')\n        im.save(f, quality='keep')\n\n    def test_junk_jpeg_header(self):\n        # https://github.com/python-pillow/Pillow/issues/630\n        filename = \"Tests/images/junk_jpeg_header.jpg\"\n        Image.open(filename)\n\n    def test_ff00_jpeg_header(self):\n        filename = \"Tests/images/jpeg_ff00_header.jpg\"\n        Image.open(filename)\n\n    def _n_qtables_helper(self, n, test_file):\n        im = Image.open(test_file)\n        f = self.tempfile('temp.jpg')\n        im.save(f, qtables=[[n]*64]*n)\n        im = Image.open(f)\n        self.assertEqual(len(im.quantization), n)\n        reloaded = self.roundtrip(im, qtables=\"keep\")\n        self.assertEqual(im.quantization, reloaded.quantization)\n\n    def test_qtables(self):\n        im = Image.open(\"Tests/images/hopper.jpg\")\n        qtables = im.quantization\n        reloaded = self.roundtrip(im, qtables=qtables, subsampling=0)\n        self.assertEqual(im.quantization, reloaded.quantization)\n        self.assert_image_similar(im, self.roundtrip(im, qtables='web_low'),\n                                  30)\n        self.assert_image_similar(im, self.roundtrip(im, qtables='web_high'),\n                                  30)\n        self.assert_image_similar(im, self.roundtrip(im, qtables='keep'), 30)\n\n        # valid bounds for baseline qtable\n        bounds_qtable = [int(s) for s in (\"255 1 \" * 32).split(None)]\n        self.roundtrip(im, qtables=[bounds_qtable])\n\n        # values from wizard.txt in jpeg9-a src package.\n        standard_l_qtable = [int(s) for s in \"\"\"\n            16  11  10  16  24  40  51  61\n            12  12  14  19  26  58  60  55\n            14  13  16  24  40  57  69  56\n            14  17  22  29  51  87  80  62\n            18  22  37  56  68 109 103  77\n            24  35  55  64  81 104 113  92\n            49  64  78  87 103 121 120 101\n            72  92  95  98 112 100 103  99\n            \"\"\".split(None)]\n\n        standard_chrominance_qtable = [int(s) for s in \"\"\"\n            17  18  24  47  99  99  99  99\n            18  21  26  66  99  99  99  99\n            24  26  56  99  99  99  99  99\n            47  66  99  99  99  99  99  99\n            99  99  99  99  99  99  99  99\n            99  99  99  99  99  99  99  99\n            99  99  99  99  99  99  99  99\n            99  99  99  99  99  99  99  99\n            \"\"\".split(None)]\n        # list of qtable lists\n        self.assert_image_similar(\n            im, self.roundtrip(\n                im, qtables=[standard_l_qtable, standard_chrominance_qtable]),\n            30)\n\n        # tuple of qtable lists\n        self.assert_image_similar(\n            im, self.roundtrip(\n                im, qtables=(standard_l_qtable, standard_chrominance_qtable)),\n            30)\n\n        # dict of qtable lists\n        self.assert_image_similar(im,\n                                  self.roundtrip(im, qtables={\n                                      0: standard_l_qtable,\n                                      1: standard_chrominance_qtable\n                                  }), 30)\n\n        self._n_qtables_helper(1, \"Tests/images/hopper_gray.jpg\")\n        self._n_qtables_helper(1, \"Tests/images/pil_sample_rgb.jpg\")\n        self._n_qtables_helper(2, \"Tests/images/pil_sample_rgb.jpg\")\n        self._n_qtables_helper(3, \"Tests/images/pil_sample_rgb.jpg\")\n        self._n_qtables_helper(1, \"Tests/images/pil_sample_cmyk.jpg\")\n        self._n_qtables_helper(2, \"Tests/images/pil_sample_cmyk.jpg\")\n        self._n_qtables_helper(3, \"Tests/images/pil_sample_cmyk.jpg\")\n        self._n_qtables_helper(4, \"Tests/images/pil_sample_cmyk.jpg\")\n\n        # not a sequence\n        self.assertRaises(Exception, lambda: self.roundtrip(im, qtables='a'))\n        # sequence wrong length\n        self.assertRaises(Exception, lambda: self.roundtrip(im, qtables=[]))\n        # sequence wrong length\n        self.assertRaises(Exception,\n                          lambda: self.roundtrip(im, qtables=[1, 2, 3, 4, 5]))\n\n        # qtable entry not a sequence\n        self.assertRaises(Exception, lambda: self.roundtrip(im, qtables=[1]))\n        # qtable entry has wrong number of items\n        self.assertRaises(Exception,\n                          lambda: self.roundtrip(im, qtables=[[1, 2, 3, 4]]))\n\n    @unittest.skipUnless(djpeg_available(), \"djpeg not available\")\n    def test_load_djpeg(self):\n        img = Image.open(TEST_FILE)\n        img.load_djpeg()\n        self.assert_image_similar(img, Image.open(TEST_FILE), 0)\n\n    @unittest.skipUnless(cjpeg_available(), \"cjpeg not available\")\n    def test_save_cjpeg(self):\n        img = Image.open(TEST_FILE)\n\n        tempfile = self.tempfile(\"temp.jpg\")\n        JpegImagePlugin._save_cjpeg(img, 0, tempfile)\n        # Default save quality is 75%, so a tiny bit of difference is alright\n        self.assert_image_similar(img, Image.open(tempfile), 17)\n\n    def test_no_duplicate_0x1001_tag(self):\n        # Arrange\n        from PIL import ExifTags\n        tag_ids = dict(zip(ExifTags.TAGS.values(), ExifTags.TAGS.keys()))\n\n        # Assert\n        self.assertEqual(tag_ids['RelatedImageWidth'], 0x1001)\n        self.assertEqual(tag_ids['RelatedImageLength'], 0x1002)\n\n    def test_MAXBLOCK_scaling(self):\n        def gen_random_image(size):\n            \"\"\" Generates a very hard to compress file\n            :param size: tuple\n            \"\"\"\n            return Image.frombytes('RGB',\n                                   size, os.urandom(size[0]*size[1] * 3))\n\n        im = gen_random_image((512, 512))\n        f = self.tempfile(\"temp.jpeg\")\n        im.save(f, quality=100, optimize=True)\n\n        reloaded = Image.open(f)\n\n        # none of these should crash\n        reloaded.save(f, quality='keep')\n        reloaded.save(f, quality='keep', progressive=True)\n        reloaded.save(f, quality='keep', optimize=True)\n\n    def test_bad_mpo_header(self):\n        \"\"\" Treat unknown MPO as JPEG \"\"\"\n        # Arrange\n\n        # Act\n        # Shouldn't raise error\n        fn = \"Tests/images/sugarshack_bad_mpo_header.jpg\"\n        im = self.assert_warning(UserWarning, lambda: Image.open(fn))\n\n        # Assert\n        self.assertEqual(im.format, \"JPEG\")\n\n    def test_save_correct_modes(self):\n        out = BytesIO()\n        for mode in ['1', 'L', 'RGB', 'RGBX', 'CMYK', 'YCbCr']:\n            img = Image.new(mode, (20, 20))\n            img.save(out, \"JPEG\")\n\n    def test_save_wrong_modes(self):\n        out = BytesIO()\n        for mode in ['LA', 'La', 'RGBa', 'P']:\n            img = Image.new(mode, (20, 20))\n            self.assertRaises(IOError, img.save, out, \"JPEG\")\n\n    def test_save_modes_with_warnings(self):\n        # ref https://github.com/python-pillow/Pillow/issues/2005\n        out = BytesIO()\n        for mode in ['RGBA']:\n            img = Image.new(mode, (20, 20))\n            self.assert_warning(DeprecationWarning, img.save, out, \"JPEG\")\n\n    def test_save_tiff_with_dpi(self):\n        # Arrange\n        outfile = self.tempfile(\"temp.tif\")\n        im = Image.open(\"Tests/images/hopper.tif\")\n\n        # Act\n        im.save(outfile, 'JPEG', dpi=im.info['dpi'])\n\n        # Assert\n        reloaded = Image.open(outfile)\n        reloaded.load()\n        self.assertEqual(im.info['dpi'], reloaded.info['dpi'])",
                            "ast_type_str": "ClassDef",
                            "start_lineno": 17,
                            "end_lineno": 483,
                            "parent": null
                        },
                        {
                            "file_name": "Tests/test_file_jpeg.py",
                            "type": "method",
                            "name": "TestFileJpeg.test_progressive_compat",
                            "content": "class TestFileJpeg(PillowTestCase):\n\n    def setUp(self):\n        if \"jpeg_encoder\" not in codecs or \"jpeg_decoder\" not in codecs:\n            self.skipTest(\"jpeg support not available\")\n\n    def roundtrip(self, im, **options):\n        out = BytesIO()\n        im.save(out, \"JPEG\", **options)\n        test_bytes = out.tell()\n        out.seek(0)\n        im = Image.open(out)\n        im.bytes = test_bytes  # for testing only\n        return im\n\n    def test_sanity(self):\n\n        # internal version number\n        self.assertRegexpMatches(Image.core.jpeglib_version, \"\\d+\\.\\d+$\")\n\n        im = Image.open(TEST_FILE)\n        im.load()\n        self.assertEqual(im.mode, \"RGB\")\n        self.assertEqual(im.size, (128, 128))\n        self.assertEqual(im.format, \"JPEG\")\n\n    def test_app(self):\n        # Test APP/COM reader (@PIL135)\n        im = Image.open(TEST_FILE)\n        self.assertEqual(\n            im.applist[0],\n            (\"APP0\", b\"JFIF\\x00\\x01\\x01\\x01\\x00`\\x00`\\x00\\x00\"))\n        self.assertEqual(im.applist[1], (\n            \"COM\", b\"File written by Adobe Photoshop\\xa8 4.0\\x00\"))\n        self.assertEqual(len(im.applist), 2)\n\n    def test_cmyk(self):\n        # Test CMYK handling.  Thanks to Tim and Charlie for test data,\n        # Michael for getting me to look one more time.\n        f = \"Tests/images/pil_sample_cmyk.jpg\"\n        im = Image.open(f)\n        # the source image has red pixels in the upper left corner.\n        c, m, y, k = [x / 255.0 for x in im.getpixel((0, 0))]\n        self.assertEqual(c, 0.0)\n        self.assertGreater(m, 0.8)\n        self.assertGreater(y, 0.8)\n        self.assertEqual(k, 0.0)\n        # the opposite corner is black\n        c, m, y, k = [x / 255.0 for x in im.getpixel((\n            im.size[0]-1, im.size[1]-1))]\n        self.assertGreater(k, 0.9)\n        # roundtrip, and check again\n        im = self.roundtrip(im)\n        c, m, y, k = [x / 255.0 for x in im.getpixel((0, 0))]\n        self.assertEqual(c, 0.0)\n        self.assertGreater(m, 0.8)\n        self.assertGreater(y, 0.8)\n        self.assertEqual(k, 0.0)\n        c, m, y, k = [x / 255.0 for x in im.getpixel((\n            im.size[0]-1, im.size[1]-1))]\n        self.assertGreater(k, 0.9)\n\n    def test_dpi(self):\n        def test(xdpi, ydpi=None):\n            im = Image.open(TEST_FILE)\n            im = self.roundtrip(im, dpi=(xdpi, ydpi or xdpi))\n            return im.info.get(\"dpi\")\n        self.assertEqual(test(72), (72, 72))\n        self.assertEqual(test(300), (300, 300))\n        self.assertEqual(test(100, 200), (100, 200))\n        self.assertEqual(test(0), None)  # square pixels\n\n    def test_icc(self):\n        # Test ICC support\n        im1 = Image.open(\"Tests/images/rgb.jpg\")\n        icc_profile = im1.info[\"icc_profile\"]\n        self.assertEqual(len(icc_profile), 3144)\n        # Roundtrip via physical file.\n        f = self.tempfile(\"temp.jpg\")\n        im1.save(f, icc_profile=icc_profile)\n        im2 = Image.open(f)\n        self.assertEqual(im2.info.get(\"icc_profile\"), icc_profile)\n        # Roundtrip via memory buffer.\n        im1 = self.roundtrip(hopper())\n        im2 = self.roundtrip(hopper(), icc_profile=icc_profile)\n        self.assert_image_equal(im1, im2)\n        self.assertFalse(im1.info.get(\"icc_profile\"))\n        self.assertTrue(im2.info.get(\"icc_profile\"))\n\n    def test_icc_big(self):\n        # Make sure that the \"extra\" support handles large blocks\n        def test(n):\n            # The ICC APP marker can store 65519 bytes per marker, so\n            # using a 4-byte test code should allow us to detect out of\n            # order issues.\n            icc_profile = (b\"Test\"*int(n/4+1))[:n]\n            assert len(icc_profile) == n  # sanity\n            im1 = self.roundtrip(hopper(), icc_profile=icc_profile)\n            self.assertEqual(im1.info.get(\"icc_profile\"), icc_profile or None)\n        test(0)\n        test(1)\n        test(3)\n        test(4)\n        test(5)\n        test(65533-14)  # full JPEG marker block\n        test(65533-14+1)  # full block plus one byte\n        test(ImageFile.MAXBLOCK)  # full buffer block\n        test(ImageFile.MAXBLOCK+1)  # full buffer block plus one byte\n        test(ImageFile.MAXBLOCK*4+3)  # large block\n\n    def test_optimize(self):\n        im1 = self.roundtrip(hopper())\n        im2 = self.roundtrip(hopper(), optimize=1)\n        self.assert_image_equal(im1, im2)\n        self.assertGreaterEqual(im1.bytes, im2.bytes)\n\n    def test_optimize_large_buffer(self):\n        # https://github.com/python-pillow/Pillow/issues/148\n        f = self.tempfile('temp.jpg')\n        # this requires ~ 1.5x Image.MAXBLOCK\n        im = Image.new(\"RGB\", (4096, 4096), 0xff3333)\n        im.save(f, format=\"JPEG\", optimize=True)\n\n    def test_progressive(self):\n        im1 = self.roundtrip(hopper())\n        im2 = self.roundtrip(hopper(), progressive=True)\n        self.assert_image_equal(im1, im2)\n        self.assertGreaterEqual(im1.bytes, im2.bytes)\n\n    def test_progressive_large_buffer(self):\n        f = self.tempfile('temp.jpg')\n        # this requires ~ 1.5x Image.MAXBLOCK\n        im = Image.new(\"RGB\", (4096, 4096), 0xff3333)\n        im.save(f, format=\"JPEG\", progressive=True)\n\n    def test_progressive_large_buffer_highest_quality(self):\n        f = self.tempfile('temp.jpg')\n        if py3:\n            a = bytes(random.randint(0, 255) for _ in range(256 * 256 * 3))\n        else:\n            a = b''.join(chr(random.randint(0, 255)) for _ in range(\n                256 * 256 * 3))\n        im = Image.frombuffer(\"RGB\", (256, 256), a, \"raw\", \"RGB\", 0, 1)\n        # this requires more bytes than pixels in the image\n        im.save(f, format=\"JPEG\", progressive=True, quality=100)\n\n    def test_large_exif(self):\n        # https://github.com/python-pillow/Pillow/issues/148\n        f = self.tempfile('temp.jpg')\n        im = hopper()\n        im.save(f, 'JPEG', quality=90, exif=b\"1\"*65532)\n\n    def test_exif_typeerror(self):\n        im = Image.open('Tests/images/exif_typeerror.jpg')\n        # Should not raise a TypeError\n        im._getexif()\n\n    def test_exif_gps(self):\n        # Arrange\n        im = Image.open('Tests/images/exif_gps.jpg')\n        gps_index = 34853\n        expected_exif_gps = {\n            0: b'\\x00\\x00\\x00\\x01',\n            2: (4294967295, 1),\n            5: b'\\x01',\n            30: 65535,\n            29: '1999:99:99 99:99:99'}\n\n        # Act\n        exif = im._getexif()\n\n        # Assert\n        self.assertEqual(exif[gps_index], expected_exif_gps)\n\n    def test_exif_rollback(self):\n        # rolling back exif support in 3.1 to pre-3.0 formatting.\n        # expected from 2.9, with b/u qualifiers switched for 3.2 compatibility\n        # this test passes on 2.9 and 3.1, but not 3.0\n        expected_exif = {34867: 4294967295,\n                         258: (24, 24, 24),\n                         36867: '2099:09:29 10:10:10',\n                         34853: {0: b'\\x00\\x00\\x00\\x01',\n                                 2: (4294967295, 1),\n                                 5: b'\\x01',\n                                 30: 65535,\n                                 29: '1999:99:99 99:99:99'},\n                         296: 65535,\n                         34665: 185,\n                         41994: 65535,\n                         514: 4294967295,\n                         271: 'Make',\n                         272: 'XXX-XXX',\n                         305: 'PIL',\n                         42034: ((1, 1), (1, 1), (1, 1), (1, 1)),\n                         42035: 'LensMake',\n                         34856: b'\\xaa\\xaa\\xaa\\xaa\\xaa\\xaa',\n                         282: (4294967295, 1),\n                         33434: (4294967295, 1)}\n\n        im = Image.open('Tests/images/exif_gps.jpg')\n        exif = im._getexif()\n\n        for tag, value in expected_exif.items():\n            self.assertEqual(value, exif[tag])\n\n    def test_exif_gps_typeerror(self):\n        im = Image.open('Tests/images/exif_gps_typeerror.jpg')\n\n        # Should not raise a TypeError\n        im._getexif()\n\n    def test_progressive_compat(self):\n        im1 = self.roundtrip(hopper())\n        im2 = self.roundtrip(hopper(), progressive=1)\n        im3 = self.roundtrip(hopper(), progression=1)  # compatibility\n        self.assert_image_equal(im1, im2)\n        self.assert_image_equal(im1, im3)\n        self.assertFalse(im1.info.get(\"progressive\"))\n        self.assertFalse(im1.info.get(\"progression\"))\n        self.assertTrue(im2.info.get(\"progressive\"))\n        self.assertTrue(im2.info.get(\"progression\"))\n        self.assertTrue(im3.info.get(\"progressive\"))\n        self.assertTrue(im3.info.get(\"progression\"))\n\n    def test_quality(self):\n        im1 = self.roundtrip(hopper())\n        im2 = self.roundtrip(hopper(), quality=50)\n        self.assert_image(im1, im2.mode, im2.size)\n        self.assertGreaterEqual(im1.bytes, im2.bytes)\n\n    def test_smooth(self):\n        im1 = self.roundtrip(hopper())\n        im2 = self.roundtrip(hopper(), smooth=100)\n        self.assert_image(im1, im2.mode, im2.size)\n\n    def test_subsampling(self):\n        def getsampling(im):\n            layer = im.layer\n            return layer[0][1:3] + layer[1][1:3] + layer[2][1:3]\n        # experimental API\n        im = self.roundtrip(hopper(), subsampling=-1)  # default\n        self.assertEqual(getsampling(im), (2, 2, 1, 1, 1, 1))\n        im = self.roundtrip(hopper(), subsampling=0)  # 4:4:4\n        self.assertEqual(getsampling(im), (1, 1, 1, 1, 1, 1))\n        im = self.roundtrip(hopper(), subsampling=1)  # 4:2:2\n        self.assertEqual(getsampling(im), (2, 1, 1, 1, 1, 1))\n        im = self.roundtrip(hopper(), subsampling=2)  # 4:1:1\n        self.assertEqual(getsampling(im), (2, 2, 1, 1, 1, 1))\n        im = self.roundtrip(hopper(), subsampling=3)  # default (undefined)\n        self.assertEqual(getsampling(im), (2, 2, 1, 1, 1, 1))\n\n        im = self.roundtrip(hopper(), subsampling=\"4:4:4\")\n        self.assertEqual(getsampling(im), (1, 1, 1, 1, 1, 1))\n        im = self.roundtrip(hopper(), subsampling=\"4:2:2\")\n        self.assertEqual(getsampling(im), (2, 1, 1, 1, 1, 1))\n        im = self.roundtrip(hopper(), subsampling=\"4:1:1\")\n        self.assertEqual(getsampling(im), (2, 2, 1, 1, 1, 1))\n\n        self.assertRaises(\n            TypeError, lambda: self.roundtrip(hopper(), subsampling=\"1:1:1\"))\n\n    def test_exif(self):\n        im = Image.open(\"Tests/images/pil_sample_rgb.jpg\")\n        info = im._getexif()\n        self.assertEqual(info[305], 'Adobe Photoshop CS Macintosh')\n\n    def test_mp(self):\n        im = Image.open(\"Tests/images/pil_sample_rgb.jpg\")\n        self.assertIsNone(im._getmp())\n\n    def test_quality_keep(self):\n        # RGB\n        im = Image.open(\"Tests/images/hopper.jpg\")\n        f = self.tempfile('temp.jpg')\n        im.save(f, quality='keep')\n        # Grayscale\n        im = Image.open(\"Tests/images/hopper_gray.jpg\")\n        f = self.tempfile('temp.jpg')\n        im.save(f, quality='keep')\n        # CMYK\n        im = Image.open(\"Tests/images/pil_sample_cmyk.jpg\")\n        f = self.tempfile('temp.jpg')\n        im.save(f, quality='keep')\n\n    def test_junk_jpeg_header(self):\n        # https://github.com/python-pillow/Pillow/issues/630\n        filename = \"Tests/images/junk_jpeg_header.jpg\"\n        Image.open(filename)\n\n    def test_ff00_jpeg_header(self):\n        filename = \"Tests/images/jpeg_ff00_header.jpg\"\n        Image.open(filename)\n\n    def _n_qtables_helper(self, n, test_file):\n        im = Image.open(test_file)\n        f = self.tempfile('temp.jpg')\n        im.save(f, qtables=[[n]*64]*n)\n        im = Image.open(f)\n        self.assertEqual(len(im.quantization), n)\n        reloaded = self.roundtrip(im, qtables=\"keep\")\n        self.assertEqual(im.quantization, reloaded.quantization)\n\n    def test_qtables(self):\n        im = Image.open(\"Tests/images/hopper.jpg\")\n        qtables = im.quantization\n        reloaded = self.roundtrip(im, qtables=qtables, subsampling=0)\n        self.assertEqual(im.quantization, reloaded.quantization)\n        self.assert_image_similar(im, self.roundtrip(im, qtables='web_low'),\n                                  30)\n        self.assert_image_similar(im, self.roundtrip(im, qtables='web_high'),\n                                  30)\n        self.assert_image_similar(im, self.roundtrip(im, qtables='keep'), 30)\n\n        # valid bounds for baseline qtable\n        bounds_qtable = [int(s) for s in (\"255 1 \" * 32).split(None)]\n        self.roundtrip(im, qtables=[bounds_qtable])\n\n        # values from wizard.txt in jpeg9-a src package.\n        standard_l_qtable = [int(s) for s in \"\"\"\n            16  11  10  16  24  40  51  61\n            12  12  14  19  26  58  60  55\n            14  13  16  24  40  57  69  56\n            14  17  22  29  51  87  80  62\n            18  22  37  56  68 109 103  77\n            24  35  55  64  81 104 113  92\n            49  64  78  87 103 121 120 101\n            72  92  95  98 112 100 103  99\n            \"\"\".split(None)]\n\n        standard_chrominance_qtable = [int(s) for s in \"\"\"\n            17  18  24  47  99  99  99  99\n            18  21  26  66  99  99  99  99\n            24  26  56  99  99  99  99  99\n            47  66  99  99  99  99  99  99\n            99  99  99  99  99  99  99  99\n            99  99  99  99  99  99  99  99\n            99  99  99  99  99  99  99  99\n            99  99  99  99  99  99  99  99\n            \"\"\".split(None)]\n        # list of qtable lists\n        self.assert_image_similar(\n            im, self.roundtrip(\n                im, qtables=[standard_l_qtable, standard_chrominance_qtable]),\n            30)\n\n        # tuple of qtable lists\n        self.assert_image_similar(\n            im, self.roundtrip(\n                im, qtables=(standard_l_qtable, standard_chrominance_qtable)),\n            30)\n\n        # dict of qtable lists\n        self.assert_image_similar(im,\n                                  self.roundtrip(im, qtables={\n                                      0: standard_l_qtable,\n                                      1: standard_chrominance_qtable\n                                  }), 30)\n\n        self._n_qtables_helper(1, \"Tests/images/hopper_gray.jpg\")\n        self._n_qtables_helper(1, \"Tests/images/pil_sample_rgb.jpg\")\n        self._n_qtables_helper(2, \"Tests/images/pil_sample_rgb.jpg\")\n        self._n_qtables_helper(3, \"Tests/images/pil_sample_rgb.jpg\")\n        self._n_qtables_helper(1, \"Tests/images/pil_sample_cmyk.jpg\")\n        self._n_qtables_helper(2, \"Tests/images/pil_sample_cmyk.jpg\")\n        self._n_qtables_helper(3, \"Tests/images/pil_sample_cmyk.jpg\")\n        self._n_qtables_helper(4, \"Tests/images/pil_sample_cmyk.jpg\")\n\n        # not a sequence\n        self.assertRaises(Exception, lambda: self.roundtrip(im, qtables='a'))\n        # sequence wrong length\n        self.assertRaises(Exception, lambda: self.roundtrip(im, qtables=[]))\n        # sequence wrong length\n        self.assertRaises(Exception,\n                          lambda: self.roundtrip(im, qtables=[1, 2, 3, 4, 5]))\n\n        # qtable entry not a sequence\n        self.assertRaises(Exception, lambda: self.roundtrip(im, qtables=[1]))\n        # qtable entry has wrong number of items\n        self.assertRaises(Exception,\n                          lambda: self.roundtrip(im, qtables=[[1, 2, 3, 4]]))\n\n    @unittest.skipUnless(djpeg_available(), \"djpeg not available\")\n    def test_load_djpeg(self):\n        img = Image.open(TEST_FILE)\n        img.load_djpeg()\n        self.assert_image_similar(img, Image.open(TEST_FILE), 0)\n\n    @unittest.skipUnless(cjpeg_available(), \"cjpeg not available\")\n    def test_save_cjpeg(self):\n        img = Image.open(TEST_FILE)\n\n        tempfile = self.tempfile(\"temp.jpg\")\n        JpegImagePlugin._save_cjpeg(img, 0, tempfile)\n        # Default save quality is 75%, so a tiny bit of difference is alright\n        self.assert_image_similar(img, Image.open(tempfile), 17)\n\n    def test_no_duplicate_0x1001_tag(self):\n        # Arrange\n        from PIL import ExifTags\n        tag_ids = dict(zip(ExifTags.TAGS.values(), ExifTags.TAGS.keys()))\n\n        # Assert\n        self.assertEqual(tag_ids['RelatedImageWidth'], 0x1001)\n        self.assertEqual(tag_ids['RelatedImageLength'], 0x1002)\n\n    def test_MAXBLOCK_scaling(self):\n        def gen_random_image(size):\n            \"\"\" Generates a very hard to compress file\n            :param size: tuple\n            \"\"\"\n            return Image.frombytes('RGB',\n                                   size, os.urandom(size[0]*size[1] * 3))\n\n        im = gen_random_image((512, 512))\n        f = self.tempfile(\"temp.jpeg\")\n        im.save(f, quality=100, optimize=True)\n\n        reloaded = Image.open(f)\n\n        # none of these should crash\n        reloaded.save(f, quality='keep')\n        reloaded.save(f, quality='keep', progressive=True)\n        reloaded.save(f, quality='keep', optimize=True)\n\n    def test_bad_mpo_header(self):\n        \"\"\" Treat unknown MPO as JPEG \"\"\"\n        # Arrange\n\n        # Act\n        # Shouldn't raise error\n        fn = \"Tests/images/sugarshack_bad_mpo_header.jpg\"\n        im = self.assert_warning(UserWarning, lambda: Image.open(fn))\n\n        # Assert\n        self.assertEqual(im.format, \"JPEG\")\n\n    def test_save_correct_modes(self):\n        out = BytesIO()\n        for mode in ['1', 'L', 'RGB', 'RGBX', 'CMYK', 'YCbCr']:\n            img = Image.new(mode, (20, 20))\n            img.save(out, \"JPEG\")\n\n    def test_save_wrong_modes(self):\n        out = BytesIO()\n        for mode in ['LA', 'La', 'RGBa', 'P']:\n            img = Image.new(mode, (20, 20))\n            self.assertRaises(IOError, img.save, out, \"JPEG\")\n\n    def test_save_modes_with_warnings(self):\n        # ref https://github.com/python-pillow/Pillow/issues/2005\n        out = BytesIO()\n        for mode in ['RGBA']:\n            img = Image.new(mode, (20, 20))\n            self.assert_warning(DeprecationWarning, img.save, out, \"JPEG\")\n\n    def test_save_tiff_with_dpi(self):\n        # Arrange\n        outfile = self.tempfile(\"temp.tif\")\n        im = Image.open(\"Tests/images/hopper.tif\")\n\n        # Act\n        im.save(outfile, 'JPEG', dpi=im.info['dpi'])\n\n        # Assert\n        reloaded = Image.open(outfile)\n        reloaded.load()\n        self.assertEqual(im.info['dpi'], reloaded.info['dpi'])",
                            "ast_type_str": "ClassDef",
                            "start_lineno": 228,
                            "end_lineno": 239,
                            "parent": {
                                "file_name": "Tests/test_file_jpeg.py",
                                "type": "class",
                                "name": "TestFileJpeg",
                                "content": "class TestFileJpeg(PillowTestCase):\n\n    def setUp(self):\n        if \"jpeg_encoder\" not in codecs or \"jpeg_decoder\" not in codecs:\n            self.skipTest(\"jpeg support not available\")\n\n    def roundtrip(self, im, **options):\n        out = BytesIO()\n        im.save(out, \"JPEG\", **options)\n        test_bytes = out.tell()\n        out.seek(0)\n        im = Image.open(out)\n        im.bytes = test_bytes  # for testing only\n        return im\n\n    def test_sanity(self):\n\n        # internal version number\n        self.assertRegexpMatches(Image.core.jpeglib_version, \"\\d+\\.\\d+$\")\n\n        im = Image.open(TEST_FILE)\n        im.load()\n        self.assertEqual(im.mode, \"RGB\")\n        self.assertEqual(im.size, (128, 128))\n        self.assertEqual(im.format, \"JPEG\")\n\n    def test_app(self):\n        # Test APP/COM reader (@PIL135)\n        im = Image.open(TEST_FILE)\n        self.assertEqual(\n            im.applist[0],\n            (\"APP0\", b\"JFIF\\x00\\x01\\x01\\x01\\x00`\\x00`\\x00\\x00\"))\n        self.assertEqual(im.applist[1], (\n            \"COM\", b\"File written by Adobe Photoshop\\xa8 4.0\\x00\"))\n        self.assertEqual(len(im.applist), 2)\n\n    def test_cmyk(self):\n        # Test CMYK handling.  Thanks to Tim and Charlie for test data,\n        # Michael for getting me to look one more time.\n        f = \"Tests/images/pil_sample_cmyk.jpg\"\n        im = Image.open(f)\n        # the source image has red pixels in the upper left corner.\n        c, m, y, k = [x / 255.0 for x in im.getpixel((0, 0))]\n        self.assertEqual(c, 0.0)\n        self.assertGreater(m, 0.8)\n        self.assertGreater(y, 0.8)\n        self.assertEqual(k, 0.0)\n        # the opposite corner is black\n        c, m, y, k = [x / 255.0 for x in im.getpixel((\n            im.size[0]-1, im.size[1]-1))]\n        self.assertGreater(k, 0.9)\n        # roundtrip, and check again\n        im = self.roundtrip(im)\n        c, m, y, k = [x / 255.0 for x in im.getpixel((0, 0))]\n        self.assertEqual(c, 0.0)\n        self.assertGreater(m, 0.8)\n        self.assertGreater(y, 0.8)\n        self.assertEqual(k, 0.0)\n        c, m, y, k = [x / 255.0 for x in im.getpixel((\n            im.size[0]-1, im.size[1]-1))]\n        self.assertGreater(k, 0.9)\n\n    def test_dpi(self):\n        def test(xdpi, ydpi=None):\n            im = Image.open(TEST_FILE)\n            im = self.roundtrip(im, dpi=(xdpi, ydpi or xdpi))\n            return im.info.get(\"dpi\")\n        self.assertEqual(test(72), (72, 72))\n        self.assertEqual(test(300), (300, 300))\n        self.assertEqual(test(100, 200), (100, 200))\n        self.assertEqual(test(0), None)  # square pixels\n\n    def test_icc(self):\n        # Test ICC support\n        im1 = Image.open(\"Tests/images/rgb.jpg\")\n        icc_profile = im1.info[\"icc_profile\"]\n        self.assertEqual(len(icc_profile), 3144)\n        # Roundtrip via physical file.\n        f = self.tempfile(\"temp.jpg\")\n        im1.save(f, icc_profile=icc_profile)\n        im2 = Image.open(f)\n        self.assertEqual(im2.info.get(\"icc_profile\"), icc_profile)\n        # Roundtrip via memory buffer.\n        im1 = self.roundtrip(hopper())\n        im2 = self.roundtrip(hopper(), icc_profile=icc_profile)\n        self.assert_image_equal(im1, im2)\n        self.assertFalse(im1.info.get(\"icc_profile\"))\n        self.assertTrue(im2.info.get(\"icc_profile\"))\n\n    def test_icc_big(self):\n        # Make sure that the \"extra\" support handles large blocks\n        def test(n):\n            # The ICC APP marker can store 65519 bytes per marker, so\n            # using a 4-byte test code should allow us to detect out of\n            # order issues.\n            icc_profile = (b\"Test\"*int(n/4+1))[:n]\n            assert len(icc_profile) == n  # sanity\n            im1 = self.roundtrip(hopper(), icc_profile=icc_profile)\n            self.assertEqual(im1.info.get(\"icc_profile\"), icc_profile or None)\n        test(0)\n        test(1)\n        test(3)\n        test(4)\n        test(5)\n        test(65533-14)  # full JPEG marker block\n        test(65533-14+1)  # full block plus one byte\n        test(ImageFile.MAXBLOCK)  # full buffer block\n        test(ImageFile.MAXBLOCK+1)  # full buffer block plus one byte\n        test(ImageFile.MAXBLOCK*4+3)  # large block\n\n    def test_optimize(self):\n        im1 = self.roundtrip(hopper())\n        im2 = self.roundtrip(hopper(), optimize=1)\n        self.assert_image_equal(im1, im2)\n        self.assertGreaterEqual(im1.bytes, im2.bytes)\n\n    def test_optimize_large_buffer(self):\n        # https://github.com/python-pillow/Pillow/issues/148\n        f = self.tempfile('temp.jpg')\n        # this requires ~ 1.5x Image.MAXBLOCK\n        im = Image.new(\"RGB\", (4096, 4096), 0xff3333)\n        im.save(f, format=\"JPEG\", optimize=True)\n\n    def test_progressive(self):\n        im1 = self.roundtrip(hopper())\n        im2 = self.roundtrip(hopper(), progressive=True)\n        self.assert_image_equal(im1, im2)\n        self.assertGreaterEqual(im1.bytes, im2.bytes)\n\n    def test_progressive_large_buffer(self):\n        f = self.tempfile('temp.jpg')\n        # this requires ~ 1.5x Image.MAXBLOCK\n        im = Image.new(\"RGB\", (4096, 4096), 0xff3333)\n        im.save(f, format=\"JPEG\", progressive=True)\n\n    def test_progressive_large_buffer_highest_quality(self):\n        f = self.tempfile('temp.jpg')\n        if py3:\n            a = bytes(random.randint(0, 255) for _ in range(256 * 256 * 3))\n        else:\n            a = b''.join(chr(random.randint(0, 255)) for _ in range(\n                256 * 256 * 3))\n        im = Image.frombuffer(\"RGB\", (256, 256), a, \"raw\", \"RGB\", 0, 1)\n        # this requires more bytes than pixels in the image\n        im.save(f, format=\"JPEG\", progressive=True, quality=100)\n\n    def test_large_exif(self):\n        # https://github.com/python-pillow/Pillow/issues/148\n        f = self.tempfile('temp.jpg')\n        im = hopper()\n        im.save(f, 'JPEG', quality=90, exif=b\"1\"*65532)\n\n    def test_exif_typeerror(self):\n        im = Image.open('Tests/images/exif_typeerror.jpg')\n        # Should not raise a TypeError\n        im._getexif()\n\n    def test_exif_gps(self):\n        # Arrange\n        im = Image.open('Tests/images/exif_gps.jpg')\n        gps_index = 34853\n        expected_exif_gps = {\n            0: b'\\x00\\x00\\x00\\x01',\n            2: (4294967295, 1),\n            5: b'\\x01',\n            30: 65535,\n            29: '1999:99:99 99:99:99'}\n\n        # Act\n        exif = im._getexif()\n\n        # Assert\n        self.assertEqual(exif[gps_index], expected_exif_gps)\n\n    def test_exif_rollback(self):\n        # rolling back exif support in 3.1 to pre-3.0 formatting.\n        # expected from 2.9, with b/u qualifiers switched for 3.2 compatibility\n        # this test passes on 2.9 and 3.1, but not 3.0\n        expected_exif = {34867: 4294967295,\n                         258: (24, 24, 24),\n                         36867: '2099:09:29 10:10:10',\n                         34853: {0: b'\\x00\\x00\\x00\\x01',\n                                 2: (4294967295, 1),\n                                 5: b'\\x01',\n                                 30: 65535,\n                                 29: '1999:99:99 99:99:99'},\n                         296: 65535,\n                         34665: 185,\n                         41994: 65535,\n                         514: 4294967295,\n                         271: 'Make',\n                         272: 'XXX-XXX',\n                         305: 'PIL',\n                         42034: ((1, 1), (1, 1), (1, 1), (1, 1)),\n                         42035: 'LensMake',\n                         34856: b'\\xaa\\xaa\\xaa\\xaa\\xaa\\xaa',\n                         282: (4294967295, 1),\n                         33434: (4294967295, 1)}\n\n        im = Image.open('Tests/images/exif_gps.jpg')\n        exif = im._getexif()\n\n        for tag, value in expected_exif.items():\n            self.assertEqual(value, exif[tag])\n\n    def test_exif_gps_typeerror(self):\n        im = Image.open('Tests/images/exif_gps_typeerror.jpg')\n\n        # Should not raise a TypeError\n        im._getexif()\n\n    def test_progressive_compat(self):\n        im1 = self.roundtrip(hopper())\n        im2 = self.roundtrip(hopper(), progressive=1)\n        im3 = self.roundtrip(hopper(), progression=1)  # compatibility\n        self.assert_image_equal(im1, im2)\n        self.assert_image_equal(im1, im3)\n        self.assertFalse(im1.info.get(\"progressive\"))\n        self.assertFalse(im1.info.get(\"progression\"))\n        self.assertTrue(im2.info.get(\"progressive\"))\n        self.assertTrue(im2.info.get(\"progression\"))\n        self.assertTrue(im3.info.get(\"progressive\"))\n        self.assertTrue(im3.info.get(\"progression\"))\n\n    def test_quality(self):\n        im1 = self.roundtrip(hopper())\n        im2 = self.roundtrip(hopper(), quality=50)\n        self.assert_image(im1, im2.mode, im2.size)\n        self.assertGreaterEqual(im1.bytes, im2.bytes)\n\n    def test_smooth(self):\n        im1 = self.roundtrip(hopper())\n        im2 = self.roundtrip(hopper(), smooth=100)\n        self.assert_image(im1, im2.mode, im2.size)\n\n    def test_subsampling(self):\n        def getsampling(im):\n            layer = im.layer\n            return layer[0][1:3] + layer[1][1:3] + layer[2][1:3]\n        # experimental API\n        im = self.roundtrip(hopper(), subsampling=-1)  # default\n        self.assertEqual(getsampling(im), (2, 2, 1, 1, 1, 1))\n        im = self.roundtrip(hopper(), subsampling=0)  # 4:4:4\n        self.assertEqual(getsampling(im), (1, 1, 1, 1, 1, 1))\n        im = self.roundtrip(hopper(), subsampling=1)  # 4:2:2\n        self.assertEqual(getsampling(im), (2, 1, 1, 1, 1, 1))\n        im = self.roundtrip(hopper(), subsampling=2)  # 4:1:1\n        self.assertEqual(getsampling(im), (2, 2, 1, 1, 1, 1))\n        im = self.roundtrip(hopper(), subsampling=3)  # default (undefined)\n        self.assertEqual(getsampling(im), (2, 2, 1, 1, 1, 1))\n\n        im = self.roundtrip(hopper(), subsampling=\"4:4:4\")\n        self.assertEqual(getsampling(im), (1, 1, 1, 1, 1, 1))\n        im = self.roundtrip(hopper(), subsampling=\"4:2:2\")\n        self.assertEqual(getsampling(im), (2, 1, 1, 1, 1, 1))\n        im = self.roundtrip(hopper(), subsampling=\"4:1:1\")\n        self.assertEqual(getsampling(im), (2, 2, 1, 1, 1, 1))\n\n        self.assertRaises(\n            TypeError, lambda: self.roundtrip(hopper(), subsampling=\"1:1:1\"))\n\n    def test_exif(self):\n        im = Image.open(\"Tests/images/pil_sample_rgb.jpg\")\n        info = im._getexif()\n        self.assertEqual(info[305], 'Adobe Photoshop CS Macintosh')\n\n    def test_mp(self):\n        im = Image.open(\"Tests/images/pil_sample_rgb.jpg\")\n        self.assertIsNone(im._getmp())\n\n    def test_quality_keep(self):\n        # RGB\n        im = Image.open(\"Tests/images/hopper.jpg\")\n        f = self.tempfile('temp.jpg')\n        im.save(f, quality='keep')\n        # Grayscale\n        im = Image.open(\"Tests/images/hopper_gray.jpg\")\n        f = self.tempfile('temp.jpg')\n        im.save(f, quality='keep')\n        # CMYK\n        im = Image.open(\"Tests/images/pil_sample_cmyk.jpg\")\n        f = self.tempfile('temp.jpg')\n        im.save(f, quality='keep')\n\n    def test_junk_jpeg_header(self):\n        # https://github.com/python-pillow/Pillow/issues/630\n        filename = \"Tests/images/junk_jpeg_header.jpg\"\n        Image.open(filename)\n\n    def test_ff00_jpeg_header(self):\n        filename = \"Tests/images/jpeg_ff00_header.jpg\"\n        Image.open(filename)\n\n    def _n_qtables_helper(self, n, test_file):\n        im = Image.open(test_file)\n        f = self.tempfile('temp.jpg')\n        im.save(f, qtables=[[n]*64]*n)\n        im = Image.open(f)\n        self.assertEqual(len(im.quantization), n)\n        reloaded = self.roundtrip(im, qtables=\"keep\")\n        self.assertEqual(im.quantization, reloaded.quantization)\n\n    def test_qtables(self):\n        im = Image.open(\"Tests/images/hopper.jpg\")\n        qtables = im.quantization\n        reloaded = self.roundtrip(im, qtables=qtables, subsampling=0)\n        self.assertEqual(im.quantization, reloaded.quantization)\n        self.assert_image_similar(im, self.roundtrip(im, qtables='web_low'),\n                                  30)\n        self.assert_image_similar(im, self.roundtrip(im, qtables='web_high'),\n                                  30)\n        self.assert_image_similar(im, self.roundtrip(im, qtables='keep'), 30)\n\n        # valid bounds for baseline qtable\n        bounds_qtable = [int(s) for s in (\"255 1 \" * 32).split(None)]\n        self.roundtrip(im, qtables=[bounds_qtable])\n\n        # values from wizard.txt in jpeg9-a src package.\n        standard_l_qtable = [int(s) for s in \"\"\"\n            16  11  10  16  24  40  51  61\n            12  12  14  19  26  58  60  55\n            14  13  16  24  40  57  69  56\n            14  17  22  29  51  87  80  62\n            18  22  37  56  68 109 103  77\n            24  35  55  64  81 104 113  92\n            49  64  78  87 103 121 120 101\n            72  92  95  98 112 100 103  99\n            \"\"\".split(None)]\n\n        standard_chrominance_qtable = [int(s) for s in \"\"\"\n            17  18  24  47  99  99  99  99\n            18  21  26  66  99  99  99  99\n            24  26  56  99  99  99  99  99\n            47  66  99  99  99  99  99  99\n            99  99  99  99  99  99  99  99\n            99  99  99  99  99  99  99  99\n            99  99  99  99  99  99  99  99\n            99  99  99  99  99  99  99  99\n            \"\"\".split(None)]\n        # list of qtable lists\n        self.assert_image_similar(\n            im, self.roundtrip(\n                im, qtables=[standard_l_qtable, standard_chrominance_qtable]),\n            30)\n\n        # tuple of qtable lists\n        self.assert_image_similar(\n            im, self.roundtrip(\n                im, qtables=(standard_l_qtable, standard_chrominance_qtable)),\n            30)\n\n        # dict of qtable lists\n        self.assert_image_similar(im,\n                                  self.roundtrip(im, qtables={\n                                      0: standard_l_qtable,\n                                      1: standard_chrominance_qtable\n                                  }), 30)\n\n        self._n_qtables_helper(1, \"Tests/images/hopper_gray.jpg\")\n        self._n_qtables_helper(1, \"Tests/images/pil_sample_rgb.jpg\")\n        self._n_qtables_helper(2, \"Tests/images/pil_sample_rgb.jpg\")\n        self._n_qtables_helper(3, \"Tests/images/pil_sample_rgb.jpg\")\n        self._n_qtables_helper(1, \"Tests/images/pil_sample_cmyk.jpg\")\n        self._n_qtables_helper(2, \"Tests/images/pil_sample_cmyk.jpg\")\n        self._n_qtables_helper(3, \"Tests/images/pil_sample_cmyk.jpg\")\n        self._n_qtables_helper(4, \"Tests/images/pil_sample_cmyk.jpg\")\n\n        # not a sequence\n        self.assertRaises(Exception, lambda: self.roundtrip(im, qtables='a'))\n        # sequence wrong length\n        self.assertRaises(Exception, lambda: self.roundtrip(im, qtables=[]))\n        # sequence wrong length\n        self.assertRaises(Exception,\n                          lambda: self.roundtrip(im, qtables=[1, 2, 3, 4, 5]))\n\n        # qtable entry not a sequence\n        self.assertRaises(Exception, lambda: self.roundtrip(im, qtables=[1]))\n        # qtable entry has wrong number of items\n        self.assertRaises(Exception,\n                          lambda: self.roundtrip(im, qtables=[[1, 2, 3, 4]]))\n\n    @unittest.skipUnless(djpeg_available(), \"djpeg not available\")\n    def test_load_djpeg(self):\n        img = Image.open(TEST_FILE)\n        img.load_djpeg()\n        self.assert_image_similar(img, Image.open(TEST_FILE), 0)\n\n    @unittest.skipUnless(cjpeg_available(), \"cjpeg not available\")\n    def test_save_cjpeg(self):\n        img = Image.open(TEST_FILE)\n\n        tempfile = self.tempfile(\"temp.jpg\")\n        JpegImagePlugin._save_cjpeg(img, 0, tempfile)\n        # Default save quality is 75%, so a tiny bit of difference is alright\n        self.assert_image_similar(img, Image.open(tempfile), 17)\n\n    def test_no_duplicate_0x1001_tag(self):\n        # Arrange\n        from PIL import ExifTags\n        tag_ids = dict(zip(ExifTags.TAGS.values(), ExifTags.TAGS.keys()))\n\n        # Assert\n        self.assertEqual(tag_ids['RelatedImageWidth'], 0x1001)\n        self.assertEqual(tag_ids['RelatedImageLength'], 0x1002)\n\n    def test_MAXBLOCK_scaling(self):\n        def gen_random_image(size):\n            \"\"\" Generates a very hard to compress file\n            :param size: tuple\n            \"\"\"\n            return Image.frombytes('RGB',\n                                   size, os.urandom(size[0]*size[1] * 3))\n\n        im = gen_random_image((512, 512))\n        f = self.tempfile(\"temp.jpeg\")\n        im.save(f, quality=100, optimize=True)\n\n        reloaded = Image.open(f)\n\n        # none of these should crash\n        reloaded.save(f, quality='keep')\n        reloaded.save(f, quality='keep', progressive=True)\n        reloaded.save(f, quality='keep', optimize=True)\n\n    def test_bad_mpo_header(self):\n        \"\"\" Treat unknown MPO as JPEG \"\"\"\n        # Arrange\n\n        # Act\n        # Shouldn't raise error\n        fn = \"Tests/images/sugarshack_bad_mpo_header.jpg\"\n        im = self.assert_warning(UserWarning, lambda: Image.open(fn))\n\n        # Assert\n        self.assertEqual(im.format, \"JPEG\")\n\n    def test_save_correct_modes(self):\n        out = BytesIO()\n        for mode in ['1', 'L', 'RGB', 'RGBX', 'CMYK', 'YCbCr']:\n            img = Image.new(mode, (20, 20))\n            img.save(out, \"JPEG\")\n\n    def test_save_wrong_modes(self):\n        out = BytesIO()\n        for mode in ['LA', 'La', 'RGBa', 'P']:\n            img = Image.new(mode, (20, 20))\n            self.assertRaises(IOError, img.save, out, \"JPEG\")\n\n    def test_save_modes_with_warnings(self):\n        # ref https://github.com/python-pillow/Pillow/issues/2005\n        out = BytesIO()\n        for mode in ['RGBA']:\n            img = Image.new(mode, (20, 20))\n            self.assert_warning(DeprecationWarning, img.save, out, \"JPEG\")\n\n    def test_save_tiff_with_dpi(self):\n        # Arrange\n        outfile = self.tempfile(\"temp.tif\")\n        im = Image.open(\"Tests/images/hopper.tif\")\n\n        # Act\n        im.save(outfile, 'JPEG', dpi=im.info['dpi'])\n\n        # Assert\n        reloaded = Image.open(outfile)\n        reloaded.load()\n        self.assertEqual(im.info['dpi'], reloaded.info['dpi'])",
                                "ast_type_str": "ClassDef",
                                "start_lineno": 17,
                                "end_lineno": 483,
                                "parent": null
                            }
                        }
                    ],
                    "added_entities": [],
                    "deleted_entities": []
                }
            ]
        }
    ],
    "old_commit_hash": "de6158f5f1d0b464d1f3817790d6a299dcf4b19c^",
    "new_commit_hash": "de6158f5f1d0b464d1f3817790d6a299dcf4b19c",
    "commit_message": "Read value when checking for JPEG progressive key",
    "commit_date": "2016-09-23T20:32:21+10:00",
    "metadata": {}
}
{
    "repo_name": "pillow",
    "new_commit_hash": "de6158f5f1d0b464d1f3817790d6a299dcf4b19c",
    "test_file_codes": [
        "from helper import unittest, PillowTestCase, hopper, py3\nfrom helper import djpeg_available, cjpeg_available\n\nimport random\nfrom io import BytesIO\nimport os\n\nfrom PIL import Image\nfrom PIL import ImageFile\nfrom PIL import JpegImagePlugin\n\ncodecs = dir(Image.core)\n\nTEST_FILE = \"Tests/images/hopper.jpg\"\n\n\nclass TestFileJpeg(PillowTestCase):\n\n    def setUp(self):\n        if \"jpeg_encoder\" not in codecs or \"jpeg_decoder\" not in codecs:\n            self.skipTest(\"jpeg support not available\")\n\n    def roundtrip(self, im, **options):\n        out = BytesIO()\n        im.save(out, \"JPEG\", **options)\n        test_bytes = out.tell()\n        out.seek(0)\n        im = Image.open(out)\n        im.bytes = test_bytes  # for testing only\n        return im\n\n    def test_sanity(self):\n\n        # internal version number\n        self.assertRegexpMatches(Image.core.jpeglib_version, \"\\d+\\.\\d+$\")\n\n        im = Image.open(TEST_FILE)\n        im.load()\n        self.assertEqual(im.mode, \"RGB\")\n        self.assertEqual(im.size, (128, 128))\n        self.assertEqual(im.format, \"JPEG\")\n\n    def test_app(self):\n        # Test APP/COM reader (@PIL135)\n        im = Image.open(TEST_FILE)\n        self.assertEqual(\n            im.applist[0],\n            (\"APP0\", b\"JFIF\\x00\\x01\\x01\\x01\\x00`\\x00`\\x00\\x00\"))\n        self.assertEqual(im.applist[1], (\n            \"COM\", b\"File written by Adobe Photoshop\\xa8 4.0\\x00\"))\n        self.assertEqual(len(im.applist), 2)\n\n    def test_cmyk(self):\n        # Test CMYK handling.  Thanks to Tim and Charlie for test data,\n        # Michael for getting me to look one more time.\n        f = \"Tests/images/pil_sample_cmyk.jpg\"\n        im = Image.open(f)\n        # the source image has red pixels in the upper left corner.\n        c, m, y, k = [x / 255.0 for x in im.getpixel((0, 0))]\n        self.assertEqual(c, 0.0)\n        self.assertGreater(m, 0.8)\n        self.assertGreater(y, 0.8)\n        self.assertEqual(k, 0.0)\n        # the opposite corner is black\n        c, m, y, k = [x / 255.0 for x in im.getpixel((\n            im.size[0]-1, im.size[1]-1))]\n        self.assertGreater(k, 0.9)\n        # roundtrip, and check again\n        im = self.roundtrip(im)\n        c, m, y, k = [x / 255.0 for x in im.getpixel((0, 0))]\n        self.assertEqual(c, 0.0)\n        self.assertGreater(m, 0.8)\n        self.assertGreater(y, 0.8)\n        self.assertEqual(k, 0.0)\n        c, m, y, k = [x / 255.0 for x in im.getpixel((\n            im.size[0]-1, im.size[1]-1))]\n        self.assertGreater(k, 0.9)\n\n    def test_dpi(self):\n        def test(xdpi, ydpi=None):\n            im = Image.open(TEST_FILE)\n            im = self.roundtrip(im, dpi=(xdpi, ydpi or xdpi))\n            return im.info.get(\"dpi\")\n        self.assertEqual(test(72), (72, 72))\n        self.assertEqual(test(300), (300, 300))\n        self.assertEqual(test(100, 200), (100, 200))\n        self.assertEqual(test(0), None)  # square pixels\n\n    def test_icc(self):\n        # Test ICC support\n        im1 = Image.open(\"Tests/images/rgb.jpg\")\n        icc_profile = im1.info[\"icc_profile\"]\n        self.assertEqual(len(icc_profile), 3144)\n        # Roundtrip via physical file.\n        f = self.tempfile(\"temp.jpg\")\n        im1.save(f, icc_profile=icc_profile)\n        im2 = Image.open(f)\n        self.assertEqual(im2.info.get(\"icc_profile\"), icc_profile)\n        # Roundtrip via memory buffer.\n        im1 = self.roundtrip(hopper())\n        im2 = self.roundtrip(hopper(), icc_profile=icc_profile)\n        self.assert_image_equal(im1, im2)\n        self.assertFalse(im1.info.get(\"icc_profile\"))\n        self.assertTrue(im2.info.get(\"icc_profile\"))\n\n    def test_icc_big(self):\n        # Make sure that the \"extra\" support handles large blocks\n        def test(n):\n            # The ICC APP marker can store 65519 bytes per marker, so\n            # using a 4-byte test code should allow us to detect out of\n            # order issues.\n            icc_profile = (b\"Test\"*int(n/4+1))[:n]\n            assert len(icc_profile) == n  # sanity\n            im1 = self.roundtrip(hopper(), icc_profile=icc_profile)\n            self.assertEqual(im1.info.get(\"icc_profile\"), icc_profile or None)\n        test(0)\n        test(1)\n        test(3)\n        test(4)\n        test(5)\n        test(65533-14)  # full JPEG marker block\n        test(65533-14+1)  # full block plus one byte\n        test(ImageFile.MAXBLOCK)  # full buffer block\n        test(ImageFile.MAXBLOCK+1)  # full buffer block plus one byte\n        test(ImageFile.MAXBLOCK*4+3)  # large block\n\n    def test_optimize(self):\n        im1 = self.roundtrip(hopper())\n        im2 = self.roundtrip(hopper(), optimize=1)\n        self.assert_image_equal(im1, im2)\n        self.assertGreaterEqual(im1.bytes, im2.bytes)\n\n    def test_optimize_large_buffer(self):\n        # https://github.com/python-pillow/Pillow/issues/148\n        f = self.tempfile('temp.jpg')\n        # this requires ~ 1.5x Image.MAXBLOCK\n        im = Image.new(\"RGB\", (4096, 4096), 0xff3333)\n        im.save(f, format=\"JPEG\", optimize=True)\n\n    def test_progressive(self):\n        im1 = self.roundtrip(hopper())\n        im2 = self.roundtrip(hopper(), progressive=True)\n        self.assert_image_equal(im1, im2)\n        self.assertGreaterEqual(im1.bytes, im2.bytes)\n\n    def test_progressive_large_buffer(self):\n        f = self.tempfile('temp.jpg')\n        # this requires ~ 1.5x Image.MAXBLOCK\n        im = Image.new(\"RGB\", (4096, 4096), 0xff3333)\n        im.save(f, format=\"JPEG\", progressive=True)\n\n    def test_progressive_large_buffer_highest_quality(self):\n        f = self.tempfile('temp.jpg')\n        if py3:\n            a = bytes(random.randint(0, 255) for _ in range(256 * 256 * 3))\n        else:\n            a = b''.join(chr(random.randint(0, 255)) for _ in range(\n                256 * 256 * 3))\n        im = Image.frombuffer(\"RGB\", (256, 256), a, \"raw\", \"RGB\", 0, 1)\n        # this requires more bytes than pixels in the image\n        im.save(f, format=\"JPEG\", progressive=True, quality=100)\n\n    def test_large_exif(self):\n        # https://github.com/python-pillow/Pillow/issues/148\n        f = self.tempfile('temp.jpg')\n        im = hopper()\n        im.save(f, 'JPEG', quality=90, exif=b\"1\"*65532)\n\n    def test_exif_typeerror(self):\n        im = Image.open('Tests/images/exif_typeerror.jpg')\n        # Should not raise a TypeError\n        im._getexif()\n\n    def test_exif_gps(self):\n        # Arrange\n        im = Image.open('Tests/images/exif_gps.jpg')\n        gps_index = 34853\n        expected_exif_gps = {\n            0: b'\\x00\\x00\\x00\\x01',\n            2: (4294967295, 1),\n            5: b'\\x01',\n            30: 65535,\n            29: '1999:99:99 99:99:99'}\n\n        # Act\n        exif = im._getexif()\n\n        # Assert\n        self.assertEqual(exif[gps_index], expected_exif_gps)\n\n    def test_exif_rollback(self):\n        # rolling back exif support in 3.1 to pre-3.0 formatting.\n        # expected from 2.9, with b/u qualifiers switched for 3.2 compatibility\n        # this test passes on 2.9 and 3.1, but not 3.0\n        expected_exif = {34867: 4294967295,\n                         258: (24, 24, 24),\n                         36867: '2099:09:29 10:10:10',\n                         34853: {0: b'\\x00\\x00\\x00\\x01',\n                                 2: (4294967295, 1),\n                                 5: b'\\x01',\n                                 30: 65535,\n                                 29: '1999:99:99 99:99:99'},\n                         296: 65535,\n                         34665: 185,\n                         41994: 65535,\n                         514: 4294967295,\n                         271: 'Make',\n                         272: 'XXX-XXX',\n                         305: 'PIL',\n                         42034: ((1, 1), (1, 1), (1, 1), (1, 1)),\n                         42035: 'LensMake',\n                         34856: b'\\xaa\\xaa\\xaa\\xaa\\xaa\\xaa',\n                         282: (4294967295, 1),\n                         33434: (4294967295, 1)}\n\n        im = Image.open('Tests/images/exif_gps.jpg')\n        exif = im._getexif()\n\n        for tag, value in expected_exif.items():\n            self.assertEqual(value, exif[tag])\n\n    def test_exif_gps_typeerror(self):\n        im = Image.open('Tests/images/exif_gps_typeerror.jpg')\n\n        # Should not raise a TypeError\n        im._getexif()\n\n    def test_progressive_compat(self):\n        im1 = self.roundtrip(hopper())\n        self.assertFalse(im1.info.get(\"progressive\"))\n        self.assertFalse(im1.info.get(\"progression\"))\n\n        im2 = self.roundtrip(hopper(), progressive=0)\n        im3 = self.roundtrip(hopper(), progression=0)  # compatibility\n        self.assertFalse(im2.info.get(\"progressive\"))\n        self.assertFalse(im2.info.get(\"progression\"))\n        self.assertFalse(im3.info.get(\"progressive\"))\n        self.assertFalse(im3.info.get(\"progression\"))\n\n        im2 = self.roundtrip(hopper(), progressive=1)\n        im3 = self.roundtrip(hopper(), progression=1)  # compatibility\n        self.assert_image_equal(im1, im2)\n        self.assert_image_equal(im1, im3)\n        self.assertTrue(im2.info.get(\"progressive\"))\n        self.assertTrue(im2.info.get(\"progression\"))\n        self.assertTrue(im3.info.get(\"progressive\"))\n        self.assertTrue(im3.info.get(\"progression\"))\n\n    def test_quality(self):\n        im1 = self.roundtrip(hopper())\n        im2 = self.roundtrip(hopper(), quality=50)\n        self.assert_image(im1, im2.mode, im2.size)\n        self.assertGreaterEqual(im1.bytes, im2.bytes)\n\n    def test_smooth(self):\n        im1 = self.roundtrip(hopper())\n        im2 = self.roundtrip(hopper(), smooth=100)\n        self.assert_image(im1, im2.mode, im2.size)\n\n    def test_subsampling(self):\n        def getsampling(im):\n            layer = im.layer\n            return layer[0][1:3] + layer[1][1:3] + layer[2][1:3]\n        # experimental API\n        im = self.roundtrip(hopper(), subsampling=-1)  # default\n        self.assertEqual(getsampling(im), (2, 2, 1, 1, 1, 1))\n        im = self.roundtrip(hopper(), subsampling=0)  # 4:4:4\n        self.assertEqual(getsampling(im), (1, 1, 1, 1, 1, 1))\n        im = self.roundtrip(hopper(), subsampling=1)  # 4:2:2\n        self.assertEqual(getsampling(im), (2, 1, 1, 1, 1, 1))\n        im = self.roundtrip(hopper(), subsampling=2)  # 4:1:1\n        self.assertEqual(getsampling(im), (2, 2, 1, 1, 1, 1))\n        im = self.roundtrip(hopper(), subsampling=3)  # default (undefined)\n        self.assertEqual(getsampling(im), (2, 2, 1, 1, 1, 1))\n\n        im = self.roundtrip(hopper(), subsampling=\"4:4:4\")\n        self.assertEqual(getsampling(im), (1, 1, 1, 1, 1, 1))\n        im = self.roundtrip(hopper(), subsampling=\"4:2:2\")\n        self.assertEqual(getsampling(im), (2, 1, 1, 1, 1, 1))\n        im = self.roundtrip(hopper(), subsampling=\"4:1:1\")\n        self.assertEqual(getsampling(im), (2, 2, 1, 1, 1, 1))\n\n        self.assertRaises(\n            TypeError, lambda: self.roundtrip(hopper(), subsampling=\"1:1:1\"))\n\n    def test_exif(self):\n        im = Image.open(\"Tests/images/pil_sample_rgb.jpg\")\n        info = im._getexif()\n        self.assertEqual(info[305], 'Adobe Photoshop CS Macintosh')\n\n    def test_mp(self):\n        im = Image.open(\"Tests/images/pil_sample_rgb.jpg\")\n        self.assertIsNone(im._getmp())\n\n    def test_quality_keep(self):\n        # RGB\n        im = Image.open(\"Tests/images/hopper.jpg\")\n        f = self.tempfile('temp.jpg')\n        im.save(f, quality='keep')\n        # Grayscale\n        im = Image.open(\"Tests/images/hopper_gray.jpg\")\n        f = self.tempfile('temp.jpg')\n        im.save(f, quality='keep')\n        # CMYK\n        im = Image.open(\"Tests/images/pil_sample_cmyk.jpg\")\n        f = self.tempfile('temp.jpg')\n        im.save(f, quality='keep')\n\n    def test_junk_jpeg_header(self):\n        # https://github.com/python-pillow/Pillow/issues/630\n        filename = \"Tests/images/junk_jpeg_header.jpg\"\n        Image.open(filename)\n\n    def test_ff00_jpeg_header(self):\n        filename = \"Tests/images/jpeg_ff00_header.jpg\"\n        Image.open(filename)\n\n    def _n_qtables_helper(self, n, test_file):\n        im = Image.open(test_file)\n        f = self.tempfile('temp.jpg')\n        im.save(f, qtables=[[n]*64]*n)\n        im = Image.open(f)\n        self.assertEqual(len(im.quantization), n)\n        reloaded = self.roundtrip(im, qtables=\"keep\")\n        self.assertEqual(im.quantization, reloaded.quantization)\n\n    def test_qtables(self):\n        im = Image.open(\"Tests/images/hopper.jpg\")\n        qtables = im.quantization\n        reloaded = self.roundtrip(im, qtables=qtables, subsampling=0)\n        self.assertEqual(im.quantization, reloaded.quantization)\n        self.assert_image_similar(im, self.roundtrip(im, qtables='web_low'),\n                                  30)\n        self.assert_image_similar(im, self.roundtrip(im, qtables='web_high'),\n                                  30)\n        self.assert_image_similar(im, self.roundtrip(im, qtables='keep'), 30)\n\n        # valid bounds for baseline qtable\n        bounds_qtable = [int(s) for s in (\"255 1 \" * 32).split(None)]\n        self.roundtrip(im, qtables=[bounds_qtable])\n\n        # values from wizard.txt in jpeg9-a src package.\n        standard_l_qtable = [int(s) for s in \"\"\"\n            16  11  10  16  24  40  51  61\n            12  12  14  19  26  58  60  55\n            14  13  16  24  40  57  69  56\n            14  17  22  29  51  87  80  62\n            18  22  37  56  68 109 103  77\n            24  35  55  64  81 104 113  92\n            49  64  78  87 103 121 120 101\n            72  92  95  98 112 100 103  99\n            \"\"\".split(None)]\n\n        standard_chrominance_qtable = [int(s) for s in \"\"\"\n            17  18  24  47  99  99  99  99\n            18  21  26  66  99  99  99  99\n            24  26  56  99  99  99  99  99\n            47  66  99  99  99  99  99  99\n            99  99  99  99  99  99  99  99\n            99  99  99  99  99  99  99  99\n            99  99  99  99  99  99  99  99\n            99  99  99  99  99  99  99  99\n            \"\"\".split(None)]\n        # list of qtable lists\n        self.assert_image_similar(\n            im, self.roundtrip(\n                im, qtables=[standard_l_qtable, standard_chrominance_qtable]),\n            30)\n\n        # tuple of qtable lists\n        self.assert_image_similar(\n            im, self.roundtrip(\n                im, qtables=(standard_l_qtable, standard_chrominance_qtable)),\n            30)\n\n        # dict of qtable lists\n        self.assert_image_similar(im,\n                                  self.roundtrip(im, qtables={\n                                      0: standard_l_qtable,\n                                      1: standard_chrominance_qtable\n                                  }), 30)\n\n        self._n_qtables_helper(1, \"Tests/images/hopper_gray.jpg\")\n        self._n_qtables_helper(1, \"Tests/images/pil_sample_rgb.jpg\")\n        self._n_qtables_helper(2, \"Tests/images/pil_sample_rgb.jpg\")\n        self._n_qtables_helper(3, \"Tests/images/pil_sample_rgb.jpg\")\n        self._n_qtables_helper(1, \"Tests/images/pil_sample_cmyk.jpg\")\n        self._n_qtables_helper(2, \"Tests/images/pil_sample_cmyk.jpg\")\n        self._n_qtables_helper(3, \"Tests/images/pil_sample_cmyk.jpg\")\n        self._n_qtables_helper(4, \"Tests/images/pil_sample_cmyk.jpg\")\n\n        # not a sequence\n        self.assertRaises(Exception, lambda: self.roundtrip(im, qtables='a'))\n        # sequence wrong length\n        self.assertRaises(Exception, lambda: self.roundtrip(im, qtables=[]))\n        # sequence wrong length\n        self.assertRaises(Exception,\n                          lambda: self.roundtrip(im, qtables=[1, 2, 3, 4, 5]))\n\n        # qtable entry not a sequence\n        self.assertRaises(Exception, lambda: self.roundtrip(im, qtables=[1]))\n        # qtable entry has wrong number of items\n        self.assertRaises(Exception,\n                          lambda: self.roundtrip(im, qtables=[[1, 2, 3, 4]]))\n\n    @unittest.skipUnless(djpeg_available(), \"djpeg not available\")\n    def test_load_djpeg(self):\n        img = Image.open(TEST_FILE)\n        img.load_djpeg()\n        self.assert_image_similar(img, Image.open(TEST_FILE), 0)\n\n    @unittest.skipUnless(cjpeg_available(), \"cjpeg not available\")\n    def test_save_cjpeg(self):\n        img = Image.open(TEST_FILE)\n\n        tempfile = self.tempfile(\"temp.jpg\")\n        JpegImagePlugin._save_cjpeg(img, 0, tempfile)\n        # Default save quality is 75%, so a tiny bit of difference is alright\n        self.assert_image_similar(img, Image.open(tempfile), 17)\n\n    def test_no_duplicate_0x1001_tag(self):\n        # Arrange\n        from PIL import ExifTags\n        tag_ids = dict(zip(ExifTags.TAGS.values(), ExifTags.TAGS.keys()))\n\n        # Assert\n        self.assertEqual(tag_ids['RelatedImageWidth'], 0x1001)\n        self.assertEqual(tag_ids['RelatedImageLength'], 0x1002)\n\n    def test_MAXBLOCK_scaling(self):\n        def gen_random_image(size):\n            \"\"\" Generates a very hard to compress file\n            :param size: tuple\n            \"\"\"\n            return Image.frombytes('RGB',\n                                   size, os.urandom(size[0]*size[1] * 3))\n\n        im = gen_random_image((512, 512))\n        f = self.tempfile(\"temp.jpeg\")\n        im.save(f, quality=100, optimize=True)\n\n        reloaded = Image.open(f)\n\n        # none of these should crash\n        reloaded.save(f, quality='keep')\n        reloaded.save(f, quality='keep', progressive=True)\n        reloaded.save(f, quality='keep', optimize=True)\n\n    def test_bad_mpo_header(self):\n        \"\"\" Treat unknown MPO as JPEG \"\"\"\n        # Arrange\n\n        # Act\n        # Shouldn't raise error\n        fn = \"Tests/images/sugarshack_bad_mpo_header.jpg\"\n        im = self.assert_warning(UserWarning, lambda: Image.open(fn))\n\n        # Assert\n        self.assertEqual(im.format, \"JPEG\")\n\n    def test_save_correct_modes(self):\n        out = BytesIO()\n        for mode in ['1', 'L', 'RGB', 'RGBX', 'CMYK', 'YCbCr']:\n            img = Image.new(mode, (20, 20))\n            img.save(out, \"JPEG\")\n\n    def test_save_wrong_modes(self):\n        out = BytesIO()\n        for mode in ['LA', 'La', 'RGBa', 'P']:\n            img = Image.new(mode, (20, 20))\n            self.assertRaises(IOError, img.save, out, \"JPEG\")\n\n    def test_save_modes_with_warnings(self):\n        # ref https://github.com/python-pillow/Pillow/issues/2005\n        out = BytesIO()\n        for mode in ['RGBA']:\n            img = Image.new(mode, (20, 20))\n            self.assert_warning(DeprecationWarning, img.save, out, \"JPEG\")\n\n    def test_save_tiff_with_dpi(self):\n        # Arrange\n        outfile = self.tempfile(\"temp.tif\")\n        im = Image.open(\"Tests/images/hopper.tif\")\n\n        # Act\n        im.save(outfile, 'JPEG', dpi=im.info['dpi'])\n\n        # Assert\n        reloaded = Image.open(outfile)\n        reloaded.load()\n        self.assertEqual(im.info['dpi'], reloaded.info['dpi'])\n\n\nif __name__ == '__main__':\n    unittest.main()\n",
        "#!/usr/bin/env python\nimport unittest\nimport sys\nimport time\nimport traceback\nimport platform\nimport os\n\n\nclass PytestLikeResult(unittest.TestResult):\n    def __init__(self, *args, **kwargs):\n        super().__init__(*args, **kwargs)\n        self.start_time = None\n        # Store (test, outcome, info) for each test:\n        #   outcome ∈ { \"passed\", \"failed\", \"error\", \"skipped\" }\n        #   info can hold the exception or skip reason, etc.\n        self.results = []\n\n    def startTestRun(self):\n        \"\"\"Called once before all tests.\"\"\"\n        self.start_time = time.time()\n        # Print a header similar to pytest\n        print(\"=\" * 29 + \" test session starts \" + \"=\" * 29)\n        # You can customize the platform/versions/paths as you like:\n        print(\n            f\"platform {platform.system().lower()} -- Python {platform.python_version()}\"\n        )\n        print(f\"rootdir: {os.path.abspath(os.curdir)}\")\n\n    def stopTestRun(self):\n        \"\"\"Called once after all tests are done.\"\"\"\n        elapsed = time.time() - self.start_time\n        print()  # blank line after final dot\n\n        # Summarize\n        ntests = len(self.results)\n        print(f\"collected {ntests} items\")\n\n        # Show details of failures/errors\n        failures = [r for r in self.results if r[1] == \"failed\"]\n        errors = [r for r in self.results if r[1] == \"error\"]\n\n        if failures:\n            print(\"\\n\" + \"=\" * 35 + \" FAILURES \" + \"=\" * 35)\n            for test, outcome, err_info in failures:\n                test = test.split(\"::\")\n                if len(test) == 3:\n                    test = f\"{test[1]}.{test[2]}\"\n                else:\n                    test = f\"{test[1]}\"\n                print(f\"__________ {test} __________\")\n                self._print_traceback(err_info)\n        if errors:\n            print(\"\\n\" + \"=\" * 35 + \" ERRORS \" + \"=\" * 37)\n            for test, outcome, err_info in errors:\n                test = test.split(\"::\")\n                if len(test) == 3:\n                    test = f\"{test[1]}.{test[2]}\"\n                else:\n                    test = f\"{test[1]}\"\n                print(f\"__________ {test} __________\")\n                self._print_traceback(err_info)\n\n        # Optional short summary lines for passes/fails/skips\n        print(\"\\n==================== short test summary info ====================\")\n        for test, outcome, info in self.results:\n            if outcome in (\"passed\", \"failed\", \"error\", \"skipped\"):\n                print(f\"{outcome.upper()} {test}\")\n\n        # Final one-line summary: “1 failed, 1 errors, 10 passed in 2.04s”\n        passed_count = sum(1 for r in self.results if r[1] == \"passed\")\n        failed_count = len(failures)\n        error_count = len(errors)\n        skipped_count = sum(1 for r in self.results if r[1] == \"skipped\")\n\n        summary_bits = []\n        if failed_count:\n            summary_bits.append(f\"{failed_count} failed\")\n        if error_count:\n            summary_bits.append(f\"{error_count} error{'s' if error_count != 1 else ''}\")\n        if passed_count:\n            summary_bits.append(f\"{passed_count} passed\")\n        if skipped_count:\n            summary_bits.append(f\"{skipped_count} skipped\")\n\n        summary_str = \", \".join(summary_bits) if summary_bits else \"no tests run\"\n        print(\n            f\"=================== {summary_str} in {elapsed:.2f}s ===================\"\n        )\n\n    def startTest(self, test):\n        \"\"\"Called right before each test method.\"\"\"\n        super().startTest(test)\n        # Print a dot or some indicator\n        print(\".\", end=\"\", flush=True)\n\n    def addSuccess(self, test):\n        super().addSuccess(test)\n        self.results.append((self._test_id(test), \"passed\", None))\n\n    def addFailure(self, test, err):\n        super().addFailure(test, err)\n        self.results.append((self._test_id(test), \"failed\", err))\n\n    def addError(self, test, err):\n        super().addError(test, err)\n        self.results.append((self._test_id(test), \"error\", err))\n\n    def addSkip(self, test, reason):\n        super().addSkip(test, reason)\n        self.results.append((self._test_id(test), \"skipped\", reason))\n\n    def _print_traceback(self, err_info):\n        \"\"\"Helper to print the traceback like pytest does.\"\"\"\n        if isinstance(err_info, tuple) and len(err_info) == 3:\n            exc_type, exc_value, tb = err_info\n            traceback.print_exception(exc_type, exc_value, tb, file=sys.stdout)\n        else:\n            print(str(err_info))\n\n    def _test_id(self, test):\n        try:\n            return f\"{test.__class__.__module__}::{test.__class__.__name__}::{test._testMethodName}\"\n        except AttributeError:\n            # Fallback for _ErrorHolder objects\n            return f\"{test.__class__.__module__}::{test.__class__.__name__}\"\n\n\nclass PytestLikeRunner(unittest.TextTestRunner):\n    \"\"\"A custom TextTestRunner that uses the PytestLikeResult.\"\"\"\n\n    resultclass = PytestLikeResult\n\n    def run(self, test):\n        result = self._makeResult()\n        # Overriding run to ensure we call startTestRun/stopTestRun\n        result.startTestRun()\n        test(result)\n        result.stopTestRun()\n        return result\n\n\ndef main():\n    loader = unittest.TestLoader()\n    suite = loader.discover(\"r2e_tests\")  # discover tests in r2e_tests/\n    runner = PytestLikeRunner(verbosity=0)\n    result = runner.run(suite)\n    # Exit with code 0 if all tests passed/skipped, 1 if there were fails/errors\n    sys.exit(not result.wasSuccessful())\n\n\nif __name__ == \"__main__\":\n    main()\n"
    ],
    "test_file_names": [
        "test_1.py",
        "unittest_custom_runner.py"
    ],
    "setup_res_code": 0,
    "setup_res_stdout": "Starting Pillow installation attempts...\nVerifying Pillow installation...\n✅ Pillow installation successful!\nSuccessfully installed Pillow\n",
    "setup_res_stderr": "Using CPython 3.9.21\nCreating virtual environment at: .venv\nActivate with: source .venv/bin/activate\nResolved 12 packages in 13ms\nInstalled 12 packages in 76ms\n + coverage==7.6.10\n + exceptiongroup==1.2.2\n + iniconfig==2.0.0\n + packaging==24.2\n + pluggy==1.5.0\n + pyqt5==5.15.11\n + pyqt5-qt5==5.15.16\n + pyqt5-sip==12.16.1\n + pytest==8.3.4\n + pytest-cov==6.0.0\n + setuptools==75.8.0\n + tomli==2.2.1\nResolved 1 package in 1.08s\nPrepared 1 package in 16.56s\nInstalled 1 package in 2ms\n + pillow==3.4.0.dev0 (from file:///home/gcpuser/buckets/local_repoeval_bucket/repos/pillow_de6158f5f1d0b464d1f3817790d6a299dcf4b19c)\n",
    "new_commit_res_code": 0,
    "new_commit_res_stdout": "============================= test session starts =============================\nplatform linux -- Python 3.9.21\nrootdir: /home/gcpuser/buckets/local_repoeval_bucket/repos/pillow_de6158f5f1d0b464d1f3817790d6a299dcf4b19c\n...................................\ncollected 35 items\n\n==================== short test summary info ====================\nPASSED test_1::TestFileJpeg::test_MAXBLOCK_scaling\nPASSED test_1::TestFileJpeg::test_app\nPASSED test_1::TestFileJpeg::test_bad_mpo_header\nPASSED test_1::TestFileJpeg::test_cmyk\nPASSED test_1::TestFileJpeg::test_dpi\nPASSED test_1::TestFileJpeg::test_exif\nPASSED test_1::TestFileJpeg::test_exif_gps\nPASSED test_1::TestFileJpeg::test_exif_gps_typeerror\nPASSED test_1::TestFileJpeg::test_exif_rollback\nPASSED test_1::TestFileJpeg::test_exif_typeerror\nPASSED test_1::TestFileJpeg::test_ff00_jpeg_header\nPASSED test_1::TestFileJpeg::test_icc\nPASSED test_1::TestFileJpeg::test_icc_big\nPASSED test_1::TestFileJpeg::test_junk_jpeg_header\nPASSED test_1::TestFileJpeg::test_large_exif\nSKIPPED test_1::TestFileJpeg::test_load_djpeg\nPASSED test_1::TestFileJpeg::test_mp\nPASSED test_1::TestFileJpeg::test_no_duplicate_0x1001_tag\nPASSED test_1::TestFileJpeg::test_optimize\nPASSED test_1::TestFileJpeg::test_optimize_large_buffer\nPASSED test_1::TestFileJpeg::test_progressive\nPASSED test_1::TestFileJpeg::test_progressive_compat\nPASSED test_1::TestFileJpeg::test_progressive_large_buffer\nPASSED test_1::TestFileJpeg::test_progressive_large_buffer_highest_quality\nPASSED test_1::TestFileJpeg::test_qtables\nPASSED test_1::TestFileJpeg::test_quality\nPASSED test_1::TestFileJpeg::test_quality_keep\nPASSED test_1::TestFileJpeg::test_sanity\nSKIPPED test_1::TestFileJpeg::test_save_cjpeg\nPASSED test_1::TestFileJpeg::test_save_correct_modes\nPASSED test_1::TestFileJpeg::test_save_modes_with_warnings\nPASSED test_1::TestFileJpeg::test_save_tiff_with_dpi\nPASSED test_1::TestFileJpeg::test_save_wrong_modes\nPASSED test_1::TestFileJpeg::test_smooth\nPASSED test_1::TestFileJpeg::test_subsampling\n=================== 33 passed, 2 skipped in 0.98s ===================\n",
    "new_commit_res_stderr": "",
    "old_commit_res_code": 1,
    "old_commit_res_stdout": "============================= test session starts =============================\nplatform linux -- Python 3.9.21\nrootdir: /home/gcpuser/buckets/local_repoeval_bucket/repos/pillow_de6158f5f1d0b464d1f3817790d6a299dcf4b19c\n.......................=== orphaned temp file: /tmp/tempk0qznp0g.jpg\n.=== orphaned temp file: /tmp/tempjbahrw_u.jpg\n.=== orphaned temp file: /tmp/tempd7dl5q4s.jpg\n=== orphaned temp file: /tmp/temp1ib7p1fw.jpg\n=== orphaned temp file: /tmp/tempvzbxnf4y.jpg\n=== orphaned temp file: /tmp/temp9aleuiui.jpg\n=== orphaned temp file: /tmp/tempcdbeor16.jpg\n=== orphaned temp file: /tmp/tempfivq93am.jpg\n=== orphaned temp file: /tmp/temp6jbwabnw.jpg\n=== orphaned temp file: /tmp/tempnirj61m2.jpg\n..=== orphaned temp file: /tmp/temp000hoox6.jpg\n=== orphaned temp file: /tmp/temphffxglhk.jpg\n=== orphaned temp file: /tmp/temp4_lu_fe4.jpg\n.....=== orphaned temp file: /tmp/tempfmdwfkj5.tif\n...\ncollected 35 items\n\n=================================== FAILURES ===================================\n__________ TestFileJpeg.test_progressive_compat __________\nTraceback (most recent call last):\n  File \"/home/gcpuser/.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/unittest/case.py\", line 59, in testPartExecutor\n    yield\n  File \"/home/gcpuser/.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/unittest/case.py\", line 592, in run\n    self._callTestMethod(testMethod)\n  File \"/home/gcpuser/.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/unittest/case.py\", line 550, in _callTestMethod\n    method()\n  File \"/home/gcpuser/buckets/local_repoeval_bucket/repos/pillow_de6158f5f1d0b464d1f3817790d6a299dcf4b19c/r2e_tests/test_1.py\", line 235, in test_progressive_compat\n    self.assertFalse(im2.info.get(\"progressive\"))\nAssertionError: 1 is not false\n\n==================== short test summary info ====================\nPASSED test_1::TestFileJpeg::test_MAXBLOCK_scaling\nPASSED test_1::TestFileJpeg::test_app\nPASSED test_1::TestFileJpeg::test_bad_mpo_header\nPASSED test_1::TestFileJpeg::test_cmyk\nPASSED test_1::TestFileJpeg::test_dpi\nPASSED test_1::TestFileJpeg::test_exif\nPASSED test_1::TestFileJpeg::test_exif_gps\nPASSED test_1::TestFileJpeg::test_exif_gps_typeerror\nPASSED test_1::TestFileJpeg::test_exif_rollback\nPASSED test_1::TestFileJpeg::test_exif_typeerror\nPASSED test_1::TestFileJpeg::test_ff00_jpeg_header\nPASSED test_1::TestFileJpeg::test_icc\nPASSED test_1::TestFileJpeg::test_icc_big\nPASSED test_1::TestFileJpeg::test_junk_jpeg_header\nPASSED test_1::TestFileJpeg::test_large_exif\nSKIPPED test_1::TestFileJpeg::test_load_djpeg\nPASSED test_1::TestFileJpeg::test_mp\nPASSED test_1::TestFileJpeg::test_no_duplicate_0x1001_tag\nPASSED test_1::TestFileJpeg::test_optimize\nPASSED test_1::TestFileJpeg::test_optimize_large_buffer\nPASSED test_1::TestFileJpeg::test_progressive\nFAILED test_1::TestFileJpeg::test_progressive_compat\nPASSED test_1::TestFileJpeg::test_progressive_large_buffer\nPASSED test_1::TestFileJpeg::test_progressive_large_buffer_highest_quality\nPASSED test_1::TestFileJpeg::test_qtables\nPASSED test_1::TestFileJpeg::test_quality\nPASSED test_1::TestFileJpeg::test_quality_keep\nPASSED test_1::TestFileJpeg::test_sanity\nSKIPPED test_1::TestFileJpeg::test_save_cjpeg\nPASSED test_1::TestFileJpeg::test_save_correct_modes\nPASSED test_1::TestFileJpeg::test_save_modes_with_warnings\nPASSED test_1::TestFileJpeg::test_save_tiff_with_dpi\nPASSED test_1::TestFileJpeg::test_save_wrong_modes\nPASSED test_1::TestFileJpeg::test_smooth\nPASSED test_1::TestFileJpeg::test_subsampling\n=================== 1 failed, 32 passed, 2 skipped in 0.78s ===================\n",
    "old_commit_res_stderr": ""
}
PIL/JpegImagePlugin.py Tests/test_file_jpeg.py
We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
[ISSUE]
**Title:** Incorrect Handling of 'progressive' Flag Causes Compatibility Test to Fail

**Description:**
When saving JPEG images with the `progressive` or `progression` option set to `0`, the metadata should indicate that the image is not progressive. However, the current implementation incorrectly sets the `progressive` flag to `1`, leading to unexpected behavior.

**Example Code:**
```python
# Attempt to save JPEG without progressive encoding
im1 = self.roundtrip(hopper())
im2 = self.roundtrip(hopper(), progressive=0)
im3 = self.roundtrip(hopper(), progression=0)

# Check the metadata
print(im2.info.get("progressive"))  # Expected: False
print(im3.info.get("progression"))  # Expected: False
```

**Expected Behavior:**
When the `progressive` or `progression` option is set to `0`, the resulting JPEG image's metadata should reflect that it is not progressive. Specifically, both `im2.info.get("progressive")` and `im3.info.get("progression")` should return `False`.

**Actual Behavior:**
Despite setting `progressive=0` and `progression=0`, the metadata incorrectly shows `im2.info.get("progressive")` as `1`, indicating that the image is progressive. This causes compatibility tests to fail as the image does not behave as expected.

[/ISSUE]
--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.

--- BEGIN FILE ---
```
### PIL/JpegImagePlugin.py
#
# The Python Imaging Library.
# $Id$
#
# JPEG (JFIF) file handling
#
# See "Digital Compression and Coding of Continuous-Tone Still Images,
# Part 1, Requirements and Guidelines" (CCITT T.81 / ISO 10918-1)
#
# History:
# 1995-09-09 fl   Created
# 1995-09-13 fl   Added full parser
# 1996-03-25 fl   Added hack to use the IJG command line utilities
# 1996-05-05 fl   Workaround Photoshop 2.5 CMYK polarity bug
# 1996-05-28 fl   Added draft support, JFIF version (0.1)
# 1996-12-30 fl   Added encoder options, added progression property (0.2)
# 1997-08-27 fl   Save mode 1 images as BW (0.3)
# 1998-07-12 fl   Added YCbCr to draft and save methods (0.4)
# 1998-10-19 fl   Don't hang on files using 16-bit DQT's (0.4.1)
# 2001-04-16 fl   Extract DPI settings from JFIF files (0.4.2)
# 2002-07-01 fl   Skip pad bytes before markers; identify Exif files (0.4.3)
# 2003-04-25 fl   Added experimental EXIF decoder (0.5)
# 2003-06-06 fl   Added experimental EXIF GPSinfo decoder
# 2003-09-13 fl   Extract COM markers
# 2009-09-06 fl   Added icc_profile support (from Florian Hoech)
# 2009-03-06 fl   Changed CMYK handling; always use Adobe polarity (0.6)
# 2009-03-08 fl   Added subsampling support (from Justin Huff).
#
# Copyright (c) 1997-2003 by Secret Labs AB.
# Copyright (c) 1995-1996 by Fredrik Lundh.
#
# See the README file for information on usage and redistribution.
#

import array
import struct
import io
import warnings
from struct import unpack_from
from PIL import Image, ImageFile, TiffImagePlugin, _binary
from PIL.JpegPresets import presets
from PIL._util import isStringType

i8 = _binary.i8
o8 = _binary.o8
i16 = _binary.i16be
i32 = _binary.i32be

__version__ = "0.6"


#
# Parser

def Skip(self, marker):
    n = i16(self.fp.read(2))-2
    ImageFile._safe_read(self.fp, n)


def APP(self, marker):
    #
    # Application marker.  Store these in the APP dictionary.
    # Also look for well-known application markers.

    n = i16(self.fp.read(2))-2
    s = ImageFile._safe_read(self.fp, n)

    app = "APP%d" % (marker & 15)

    self.app[app] = s  # compatibility
    self.applist.append((app, s))

    if marker == 0xFFE0 and s[:4] == b"JFIF":
        # extract JFIF information
        self.info["jfif"] = version = i16(s, 5)  # version
        self.info["jfif_version"] = divmod(version, 256)
        # extract JFIF properties
        try:
            jfif_unit = i8(s[7])
            jfif_density = i16(s, 8), i16(s, 10)
        except:
            pass
        else:
            if jfif_unit == 1:
                self.info["dpi"] = jfif_density
            self.info["jfif_unit"] = jfif_unit
            self.info["jfif_density"] = jfif_density
    elif marker == 0xFFE1 and s[:5] == b"Exif\0":
        # extract Exif information (incomplete)
        self.info["exif"] = s  # FIXME: value will change
    elif marker == 0xFFE2 and s[:5] == b"FPXR\0":
        # extract FlashPix information (incomplete)
        self.info["flashpix"] = s  # FIXME: value will change
    elif marker == 0xFFE2 and s[:12] == b"ICC_PROFILE\0":
        # Since an ICC profile can be larger than the maximum size of
        # a JPEG marker (64K), we need provisions to split it into
        # multiple markers. The format defined by the ICC specifies
        # one or more APP2 markers containing the following data:
        #   Identifying string      ASCII "ICC_PROFILE\0"  (12 bytes)
        #   Marker sequence number  1, 2, etc (1 byte)
        #   Number of markers       Total of APP2's used (1 byte)
        #   Profile data            (remainder of APP2 data)
        # Decoders should use the marker sequence numbers to
        # reassemble the profile, rather than assuming that the APP2
        # markers appear in the correct sequence.
        self.icclist.append(s)
    elif marker == 0xFFEE and s[:5] == b"Adobe":
        self.info["adobe"] = i16(s, 5)
        # extract Adobe custom properties
        try:
            adobe_transform = i8(s[1])
        except:
            pass
        else:
            self.info["adobe_transform"] = adobe_transform
    elif marker == 0xFFE2 and s[:4] == b"MPF\0":
        # extract MPO information
        self.info["mp"] = s[4:]
        # offset is current location minus buffer size
        # plus constant header size
        self.info["mpoffset"] = self.fp.tell() - n + 4


def COM(self, marker):
    #
    # Comment marker.  Store these in the APP dictionary.
    n = i16(self.fp.read(2))-2
    s = ImageFile._safe_read(self.fp, n)

    self.app["COM"] = s  # compatibility
    self.applist.append(("COM", s))


def SOF(self, marker):
    #
    # Start of frame marker.  Defines the size and mode of the
    # image.  JPEG is colour blind, so we use some simple
    # heuristics to map the number of layers to an appropriate
    # mode.  Note that this could be made a bit brighter, by
    # looking for JFIF and Adobe APP markers.

    n = i16(self.fp.read(2))-2
    s = ImageFile._safe_read(self.fp, n)
    self.size = i16(s[3:]), i16(s[1:])

    self.bits = i8(s[0])
    if self.bits != 8:
        raise SyntaxError("cannot handle %d-bit layers" % self.bits)

    self.layers = i8(s[5])
    if self.layers == 1:
        self.mode = "L"
    elif self.layers == 3:
        self.mode = "RGB"
    elif self.layers == 4:
        self.mode = "CMYK"
    else:
        raise SyntaxError("cannot handle %d-layer images" % self.layers)

    if marker in [0xFFC2, 0xFFC6, 0xFFCA, 0xFFCE]:
        self.info["progressive"] = self.info["progression"] = 1

    if self.icclist:
        # fixup icc profile
        self.icclist.sort()  # sort by sequence number
        if i8(self.icclist[0][13]) == len(self.icclist):
            profile = []
            for p in self.icclist:
                profile.append(p[14:])
            icc_profile = b"".join(profile)
        else:
            icc_profile = None  # wrong number of fragments
        self.info["icc_profile"] = icc_profile
        self.icclist = None

    for i in range(6, len(s), 3):
        t = s[i:i+3]
        # 4-tuples: id, vsamp, hsamp, qtable
        self.layer.append((t[0], i8(t[1])//16, i8(t[1]) & 15, i8(t[2])))


def DQT(self, marker):
    #
    # Define quantization table.  Support baseline 8-bit tables
    # only.  Note that there might be more than one table in
    # each marker.

    # FIXME: The quantization tables can be used to estimate the
    # compression quality.

    n = i16(self.fp.read(2))-2
    s = ImageFile._safe_read(self.fp, n)
    while len(s):
        if len(s) < 65:
            raise SyntaxError("bad quantization table marker")
        v = i8(s[0])
        if v//16 == 0:
            self.quantization[v & 15] = array.array("B", s[1:65])
            s = s[65:]
        else:
            return  # FIXME: add code to read 16-bit tables!
            # raise SyntaxError, "bad quantization table element size"


#
# JPEG marker table

MARKER = {
    0xFFC0: ("SOF0", "Baseline DCT", SOF),
    0xFFC1: ("SOF1", "Extended Sequential DCT", SOF),
    0xFFC2: ("SOF2", "Progressive DCT", SOF),
    0xFFC3: ("SOF3", "Spatial lossless", SOF),
    0xFFC4: ("DHT", "Define Huffman table", Skip),
    0xFFC5: ("SOF5", "Differential sequential DCT", SOF),
    0xFFC6: ("SOF6", "Differential progressive DCT", SOF),
    0xFFC7: ("SOF7", "Differential spatial", SOF),
    0xFFC8: ("JPG", "Extension", None),
    0xFFC9: ("SOF9", "Extended sequential DCT (AC)", SOF),
    0xFFCA: ("SOF10", "Progressive DCT (AC)", SOF),
    0xFFCB: ("SOF11", "Spatial lossless DCT (AC)", SOF),
    0xFFCC: ("DAC", "Define arithmetic coding conditioning", Skip),
    0xFFCD: ("SOF13", "Differential sequential DCT (AC)", SOF),
    0xFFCE: ("SOF14", "Differential progressive DCT (AC)", SOF),
    0xFFCF: ("SOF15", "Differential spatial (AC)", SOF),
    0xFFD0: ("RST0", "Restart 0", None),
    0xFFD1: ("RST1", "Restart 1", None),
    0xFFD2: ("RST2", "Restart 2", None),
    0xFFD3: ("RST3", "Restart 3", None),
    0xFFD4: ("RST4", "Restart 4", None),
    0xFFD5: ("RST5", "Restart 5", None),
    0xFFD6: ("RST6", "Restart 6", None),
    0xFFD7: ("RST7", "Restart 7", None),
    0xFFD8: ("SOI", "Start of image", None),
    0xFFD9: ("EOI", "End of image", None),
    0xFFDA: ("SOS", "Start of scan", Skip),
    0xFFDB: ("DQT", "Define quantization table", DQT),
    0xFFDC: ("DNL", "Define number of lines", Skip),
    0xFFDD: ("DRI", "Define restart interval", Skip),
    0xFFDE: ("DHP", "Define hierarchical progression", SOF),
    0xFFDF: ("EXP", "Expand reference component", Skip),
    0xFFE0: ("APP0", "Application segment 0", APP),
    0xFFE1: ("APP1", "Application segment 1", APP),
    0xFFE2: ("APP2", "Application segment 2", APP),
    0xFFE3: ("APP3", "Application segment 3", APP),
    0xFFE4: ("APP4", "Application segment 4", APP),
    0xFFE5: ("APP5", "Application segment 5", APP),
    0xFFE6: ("APP6", "Application segment 6", APP),
    0xFFE7: ("APP7", "Application segment 7", APP),
    0xFFE8: ("APP8", "Application segment 8", APP),
    0xFFE9: ("APP9", "Application segment 9", APP),
    0xFFEA: ("APP10", "Application segment 10", APP),
    0xFFEB: ("APP11", "Application segment 11", APP),
    0xFFEC: ("APP12", "Application segment 12", APP),
    0xFFED: ("APP13", "Application segment 13", APP),
    0xFFEE: ("APP14", "Application segment 14", APP),
    0xFFEF: ("APP15", "Application segment 15", APP),
    0xFFF0: ("JPG0", "Extension 0", None),
    0xFFF1: ("JPG1", "Extension 1", None),
    0xFFF2: ("JPG2", "Extension 2", None),
    0xFFF3: ("JPG3", "Extension 3", None),
    0xFFF4: ("JPG4", "Extension 4", None),
    0xFFF5: ("JPG5", "Extension 5", None),
    0xFFF6: ("JPG6", "Extension 6", None),
    0xFFF7: ("JPG7", "Extension 7", None),
    0xFFF8: ("JPG8", "Extension 8", None),
    0xFFF9: ("JPG9", "Extension 9", None),
    0xFFFA: ("JPG10", "Extension 10", None),
    0xFFFB: ("JPG11", "Extension 11", None),
    0xFFFC: ("JPG12", "Extension 12", None),
    0xFFFD: ("JPG13", "Extension 13", None),
    0xFFFE: ("COM", "Comment", COM)
}


def _accept(prefix):
    return prefix[0:1] == b"\377"


##
# Image plugin for JPEG and JFIF images.

class JpegImageFile(ImageFile.ImageFile):

    format = "JPEG"
    format_description = "JPEG (ISO 10918)"

    def _open(self):

        s = self.fp.read(1)

        if i8(s) != 255:
            raise SyntaxError("not a JPEG file")

        # Create attributes
        self.bits = self.layers = 0

        # JPEG specifics (internal)
        self.layer = []
        self.huffman_dc = {}
        self.huffman_ac = {}
        self.quantization = {}
        self.app = {}  # compatibility
        self.applist = []
        self.icclist = []

        while True:

            i = i8(s)
            if i == 0xFF:
                s = s + self.fp.read(1)
                i = i16(s)
            else:
                # Skip non-0xFF junk
                s = self.fp.read(1)
                continue

            if i in MARKER:
                name, description, handler = MARKER[i]
                # print hex(i), name, description
                if handler is not None:
                    handler(self, i)
                if i == 0xFFDA:  # start of scan
                    rawmode = self.mode
                    if self.mode == "CMYK":
                        rawmode = "CMYK;I"  # assume adobe conventions
                    self.tile = [("jpeg", (0, 0) + self.size, 0,
                                 (rawmode, ""))]
                    # self.__offset = self.fp.tell()
                    break
                s = self.fp.read(1)
            elif i == 0 or i == 0xFFFF:
                # padded marker or junk; move on
                s = b"\xff"
            elif i == 0xFF00:  # Skip extraneous data (escaped 0xFF)
                s = self.fp.read(1)
            else:
                raise SyntaxError("no marker found")

    def draft(self, mode, size):

        if len(self.tile) != 1:
            return

        d, e, o, a = self.tile[0]
        scale = 0

        if a[0] == "RGB" and mode in ["L", "YCbCr"]:
            self.mode = mode
            a = mode, ""

        if size:
            scale = max(self.size[0] // size[0], self.size[1] // size[1])
            for s in [8, 4, 2, 1]:
                if scale >= s:
                    break
            e = e[0], e[1], (e[2]-e[0]+s-1)//s+e[0], (e[3]-e[1]+s-1)//s+e[1]
            self.size = ((self.size[0]+s-1)//s, (self.size[1]+s-1)//s)
            scale = s

        self.tile = [(d, e, o, a)]
        self.decoderconfig = (scale, 0)

        return self

    def load_djpeg(self):

        # ALTERNATIVE: handle JPEGs via the IJG command line utilities

        import subprocess
        import tempfile
        import os
        f, path = tempfile.mkstemp()
        os.close(f)
        if os.path.exists(self.filename):
            subprocess.check_call(["djpeg", "-outfile", path, self.filename])
        else:
            raise ValueError("Invalid Filename")

        try:
            self.im = Image.core.open_ppm(path)
        finally:
            try:
                os.unlink(path)
            except OSError:
                pass

        self.mode = self.im.mode
        self.size = self.im.size

        self.tile = []

    def _getexif(self):
        return _getexif(self)

    def _getmp(self):
        return _getmp(self)


def _fixup_dict(src_dict):
    # Helper function for _getexif()
    # returns a dict with any single item tuples/lists as individual values
    def _fixup(value):
        try:
            if len(value) == 1 and not isinstance(value, dict):
                return value[0]
        except: pass
        return value

    return dict([(k, _fixup(v)) for k, v in src_dict.items()])


def _getexif(self):
    # Extract EXIF information.  This method is highly experimental,
    # and is likely to be replaced with something better in a future
    # version.

    # The EXIF record consists of a TIFF file embedded in a JPEG
    # application marker (!).
    try:
        data = self.info["exif"]
    except KeyError:
        return None
    file = io.BytesIO(data[6:])
    head = file.read(8)
    # process dictionary
    info = TiffImagePlugin.ImageFileDirectory_v1(head)
    info.load(file)
    exif = dict(_fixup_dict(info))
    # get exif extension
    try:
        # exif field 0x8769 is an offset pointer to the location
        # of the nested embedded exif ifd.
        # It should be a long, but may be corrupted.
        file.seek(exif[0x8769])
    except (KeyError, TypeError):
        pass
    else:
        info = TiffImagePlugin.ImageFileDirectory_v1(head)
        info.load(file)
        exif.update(_fixup_dict(info))
    # get gpsinfo extension
    try:
        # exif field 0x8825 is an offset pointer to the location
        # of the nested embedded gps exif ifd.
        # It should be a long, but may be corrupted.
        file.seek(exif[0x8825])
    except (KeyError, TypeError):
        pass
    else:
        info = TiffImagePlugin.ImageFileDirectory_v1(head)
        info.load(file)
        exif[0x8825] = _fixup_dict(info)

    return exif


def _getmp(self):
    # Extract MP information.  This method was inspired by the "highly
    # experimental" _getexif version that's been in use for years now,
    # itself based on the ImageFileDirectory class in the TIFF plug-in.

    # The MP record essentially consists of a TIFF file embedded in a JPEG
    # application marker.
    try:
        data = self.info["mp"]
    except KeyError:
        return None
    file_contents = io.BytesIO(data)
    head = file_contents.read(8)
    endianness = '>' if head[:4] == b'\x4d\x4d\x00\x2a' else '<'
    # process dictionary
    try:
        info = TiffImagePlugin.ImageFileDirectory_v2(head)
        info.load(file_contents)
        mp = dict(info)
    except:
        raise SyntaxError("malformed MP Index (unreadable directory)")
    # it's an error not to have a number of images
    try:
        quant = mp[0xB001]
    except KeyError:
        raise SyntaxError("malformed MP Index (no number of images)")
    # get MP entries
    mpentries = []
    try:
        rawmpentries = mp[0xB002]
        for entrynum in range(0, quant):
            unpackedentry = unpack_from(
                '{0}LLLHH'.format(endianness), rawmpentries, entrynum * 16)
            labels = ('Attribute', 'Size', 'DataOffset', 'EntryNo1',
                      'EntryNo2')
            mpentry = dict(zip(labels, unpackedentry))
            mpentryattr = {
                'DependentParentImageFlag': bool(mpentry['Attribute'] &
                                                 (1 << 31)),
                'DependentChildImageFlag': bool(mpentry['Attribute'] &
                                                (1 << 30)),
                'RepresentativeImageFlag': bool(mpentry['Attribute'] &
                                                (1 << 29)),
                'Reserved': (mpentry['Attribute'] & (3 << 27)) >> 27,
                'ImageDataFormat': (mpentry['Attribute'] & (7 << 24)) >> 24,
                'MPType': mpentry['Attribute'] & 0x00FFFFFF
            }
            if mpentryattr['ImageDataFormat'] == 0:
                mpentryattr['ImageDataFormat'] = 'JPEG'
            else:
                raise SyntaxError("unsupported picture format in MPO")
            mptypemap = {
                0x000000: 'Undefined',
                0x010001: 'Large Thumbnail (VGA Equivalent)',
                0x010002: 'Large Thumbnail (Full HD Equivalent)',
                0x020001: 'Multi-Frame Image (Panorama)',
                0x020002: 'Multi-Frame Image: (Disparity)',
                0x020003: 'Multi-Frame Image: (Multi-Angle)',
                0x030000: 'Baseline MP Primary Image'
            }
            mpentryattr['MPType'] = mptypemap.get(mpentryattr['MPType'],
                                                  'Unknown')
            mpentry['Attribute'] = mpentryattr
            mpentries.append(mpentry)
        mp[0xB002] = mpentries
    except KeyError:
        raise SyntaxError("malformed MP Index (bad MP Entry)")
    # Next we should try and parse the individual image unique ID list;
    # we don't because I've never seen this actually used in a real MPO
    # file and so can't test it.
    return mp


# --------------------------------------------------------------------
# stuff to save JPEG files

RAWMODE = {
    "1": "L",
    "L": "L",
    "RGB": "RGB",
    "RGBA": "RGB",
    "RGBX": "RGB",
    "CMYK": "CMYK;I",  # assume adobe conventions
    "YCbCr": "YCbCr",
}

zigzag_index = (0,  1,  5,  6, 14, 15, 27, 28,
                2,  4,  7, 13, 16, 26, 29, 42,
                3,  8, 12, 17, 25, 30, 41, 43,
                9, 11, 18, 24, 31, 40, 44, 53,
               10, 19, 23, 32, 39, 45, 52, 54,
               20, 22, 33, 38, 46, 51, 55, 60,
               21, 34, 37, 47, 50, 56, 59, 61,
               35, 36, 48, 49, 57, 58, 62, 63)

samplings = {(1, 1, 1, 1, 1, 1): 0,
             (2, 1, 1, 1, 1, 1): 1,
             (2, 2, 1, 1, 1, 1): 2,
             }


def convert_dict_qtables(qtables):
    qtables = [qtables[key] for key in range(len(qtables)) if key in qtables]
    for idx, table in enumerate(qtables):
        qtables[idx] = [table[i] for i in zigzag_index]
    return qtables


def get_sampling(im):
    # There's no subsampling when image have only 1 layer
    # (grayscale images) or when they are CMYK (4 layers),
    # so set subsampling to default value.
    #
    # NOTE: currently Pillow can't encode JPEG to YCCK format.
    # If YCCK support is added in the future, subsampling code will have
    # to be updated (here and in JpegEncode.c) to deal with 4 layers.
    if not hasattr(im, 'layers') or im.layers in (1, 4):
        return -1
    sampling = im.layer[0][1:3] + im.layer[1][1:3] + im.layer[2][1:3]
    return samplings.get(sampling, -1)


def _save(im, fp, filename):

    try:
        rawmode = RAWMODE[im.mode]
    except KeyError:
        raise IOError("cannot write mode %s as JPEG" % im.mode)

    if im.mode == 'RGBA':
        warnings.warn(
            'You are saving RGBA image as JPEG. The alpha channel will be '
            'discarded. This conversion is deprecated and will be disabled '
            'in Pillow 3.7. Please, convert the image to RGB explicitly.',
            DeprecationWarning
        )

    info = im.encoderinfo

    dpi = [int(round(x)) for x in info.get("dpi", (0, 0))]

    quality = info.get("quality", 0)
    subsampling = info.get("subsampling", -1)
    qtables = info.get("qtables")

    if quality == "keep":
        quality = 0
        subsampling = "keep"
        qtables = "keep"
    elif quality in presets:
        preset = presets[quality]
        quality = 0
        subsampling = preset.get('subsampling', -1)
        qtables = preset.get('quantization')
    elif not isinstance(quality, int):
        raise ValueError("Invalid quality setting")
    else:
        if subsampling in presets:
            subsampling = presets[subsampling].get('subsampling', -1)
        if isStringType(qtables) and qtables in presets:
            qtables = presets[qtables].get('quantization')

    if subsampling == "4:4:4":
        subsampling = 0
    elif subsampling == "4:2:2":
        subsampling = 1
    elif subsampling == "4:1:1":
        subsampling = 2
    elif subsampling == "keep":
        if im.format != "JPEG":
            raise ValueError(
                "Cannot use 'keep' when original image is not a JPEG")
        subsampling = get_sampling(im)

    def validate_qtables(qtables):
        if qtables is None:
            return qtables
        if isStringType(qtables):
            try:
                lines = [int(num) for line in qtables.splitlines()
                         for num in line.split('#', 1)[0].split()]
            except ValueError:
                raise ValueError("Invalid quantization table")
            else:
                qtables = [lines[s:s+64] for s in range(0, len(lines), 64)]
        if isinstance(qtables, (tuple, list, dict)):
            if isinstance(qtables, dict):
                qtables = convert_dict_qtables(qtables)
            elif isinstance(qtables, tuple):
                qtables = list(qtables)
            if not (0 < len(qtables) < 5):
                raise ValueError("None or too many quantization tables")
            for idx, table in enumerate(qtables):
                try:
                    if len(table) != 64:
                        raise
                    table = array.array('B', table)
                except TypeError:
                    raise ValueError("Invalid quantization table")
                else:
                    qtables[idx] = list(table)
            return qtables

    if qtables == "keep":
        if im.format != "JPEG":
            raise ValueError(
                "Cannot use 'keep' when original image is not a JPEG")
        qtables = getattr(im, "quantization", None)
    qtables = validate_qtables(qtables)

    extra = b""

    icc_profile = info.get("icc_profile")
    if icc_profile:
        ICC_OVERHEAD_LEN = 14
        MAX_BYTES_IN_MARKER = 65533
        MAX_DATA_BYTES_IN_MARKER = MAX_BYTES_IN_MARKER - ICC_OVERHEAD_LEN
        markers = []
        while icc_profile:
            markers.append(icc_profile[:MAX_DATA_BYTES_IN_MARKER])
            icc_profile = icc_profile[MAX_DATA_BYTES_IN_MARKER:]
        i = 1
        for marker in markers:
            size = struct.pack(">H", 2 + ICC_OVERHEAD_LEN + len(marker))
            extra += (b"\xFF\xE2" + size + b"ICC_PROFILE\0" + o8(i) +
                      o8(len(markers)) + marker)
            i += 1

    # get keyword arguments
    im.encoderconfig = (
        quality,
        # "progressive" is the official name, but older documentation
        # says "progression"
        # FIXME: issue a warning if the wrong form is used (post-1.1.7)
        "progressive" in info or "progression" in info,
        info.get("smooth", 0),
        "optimize" in info,
        info.get("streamtype", 0),
        dpi[0], dpi[1],
        subsampling,
        qtables,
        extra,
        info.get("exif", b"")
        )

    # if we optimize, libjpeg needs a buffer big enough to hold the whole image
    # in a shot. Guessing on the size, at im.size bytes. (raw pizel size is
    # channels*size, this is a value that's been used in a django patch.
    # https://github.com/matthewwithanm/django-imagekit/issues/50
    bufsize = 0
    if "optimize" in info or "progressive" in info or "progression" in info:
        # keep sets quality to 0, but the actual value may be high.
        if quality >= 95 or quality == 0:
            bufsize = 2 * im.size[0] * im.size[1]
        else:
            bufsize = im.size[0] * im.size[1]

    # The exif info needs to be written as one block, + APP1, + one spare byte.
    # Ensure that our buffer is big enough
    bufsize = max(ImageFile.MAXBLOCK, bufsize, len(info.get("exif", b"")) + 5)

    ImageFile._save(im, fp, [("jpeg", (0, 0)+im.size, 0, rawmode)], bufsize)


def _save_cjpeg(im, fp, filename):
    # ALTERNATIVE: handle JPEGs via the IJG command line utilities.
    import os
    import subprocess
    tempfile = im._dump()
    subprocess.check_call(["cjpeg", "-outfile", filename, tempfile])
    try:
        os.unlink(tempfile)
    except OSError:
        pass


##
# Factory for making JPEG and MPO instances
def jpeg_factory(fp=None, filename=None):
    im = JpegImageFile(fp, filename)
    try:
        mpheader = im._getmp()
        if mpheader[45057] > 1:
            # It's actually an MPO
            from .MpoImagePlugin import MpoImageFile
            im = MpoImageFile(fp, filename)
    except (TypeError, IndexError):
        # It is really a JPEG
        pass
    except SyntaxError:
        warnings.warn("Image appears to be a malformed MPO file, it will be "
                      "interpreted as a base JPEG file")
    return im


# -------------------------------------------------------------------q-
# Registry stuff

Image.register_open(JpegImageFile.format, jpeg_factory, _accept)
Image.register_save(JpegImageFile.format, _save)

Image.register_extension(JpegImageFile.format, ".jfif")
Image.register_extension(JpegImageFile.format, ".jpe")
Image.register_extension(JpegImageFile.format, ".jpg")
Image.register_extension(JpegImageFile.format, ".jpeg")

Image.register_mime(JpegImageFile.format, "image/jpeg")

```
--- END FILE ---

Please first localize the bug based on the issue statement, and then generate *SEARCH/REPLACE* edits to fix the issue.

Every *SEARCH/REPLACE* edit must use this format:
1. The file path
2. The start of search block: <<<<<<< SEARCH
3. A contiguous chunk of lines to search for in the existing source code
4. The dividing line: =======
5. The lines to replace into the source code
6. The end of the replace block: >>>>>>> REPLACE

Here is an example:

```python
### mathweb/flask/app.py
<<<<<<< SEARCH
from flask import Flask
=======
import math
from flask import Flask
>>>>>>> REPLACE
```
</solution>

Please note that the *SEARCH/REPLACE* edit REQUIRES PROPER INDENTATION. If you would like to add the line '        print(x)', you must fully write that out, with all those spaces before the code!
Wrap each *SEARCH/REPLACE* edit in a code block as shown in the example above. If you have multiple *SEARCH/REPLACE* edits, use a separate code block for each one.
[ISSUE]
**Title:** Incorrect Handling of 'progressive' Flag Causes Compatibility Test to Fail

**Description:**
When saving JPEG images with the `progressive` or `progression` option set to `0`, the metadata should indicate that the image is not progressive. However, the current implementation incorrectly sets the `progressive` flag to `1`, leading to unexpected behavior.

**Example Code:**
```python
# Attempt to save JPEG without progressive encoding
im1 = self.roundtrip(hopper())
im2 = self.roundtrip(hopper(), progressive=0)
im3 = self.roundtrip(hopper(), progression=0)

# Check the metadata
print(im2.info.get("progressive"))  # Expected: False
print(im3.info.get("progression"))  # Expected: False
```

**Expected Behavior:**
When the `progressive` or `progression` option is set to `0`, the resulting JPEG image's metadata should reflect that it is not progressive. Specifically, both `im2.info.get("progressive")` and `im3.info.get("progression")` should return `False`.

**Actual Behavior:**
Despite setting `progressive=0` and `progression=0`, the metadata incorrectly shows `im2.info.get("progressive")` as `1`, indicating that the image is progressive. This causes compatibility tests to fail as the image does not behave as expected.

[/ISSUE]
{
    "TestFileJpeg.test_MAXBLOCK_scaling": "PASSED",
    "TestFileJpeg.test_app": "PASSED",
    "TestFileJpeg.test_bad_mpo_header": "PASSED",
    "TestFileJpeg.test_cmyk": "PASSED",
    "TestFileJpeg.test_dpi": "PASSED",
    "TestFileJpeg.test_exif": "PASSED",
    "TestFileJpeg.test_exif_gps": "PASSED",
    "TestFileJpeg.test_exif_gps_typeerror": "PASSED",
    "TestFileJpeg.test_exif_rollback": "PASSED",
    "TestFileJpeg.test_exif_typeerror": "PASSED",
    "TestFileJpeg.test_ff00_jpeg_header": "PASSED",
    "TestFileJpeg.test_icc": "PASSED",
    "TestFileJpeg.test_icc_big": "PASSED",
    "TestFileJpeg.test_junk_jpeg_header": "PASSED",
    "TestFileJpeg.test_large_exif": "PASSED",
    "TestFileJpeg.test_mp": "PASSED",
    "TestFileJpeg.test_no_duplicate_0x1001_tag": "PASSED",
    "TestFileJpeg.test_optimize": "PASSED",
    "TestFileJpeg.test_optimize_large_buffer": "PASSED",
    "TestFileJpeg.test_progressive": "PASSED",
    "TestFileJpeg.test_progressive_compat": "PASSED",
    "TestFileJpeg.test_progressive_large_buffer": "PASSED",
    "TestFileJpeg.test_progressive_large_buffer_highest_quality": "PASSED",
    "TestFileJpeg.test_qtables": "PASSED",
    "TestFileJpeg.test_quality": "PASSED",
    "TestFileJpeg.test_quality_keep": "PASSED",
    "TestFileJpeg.test_sanity": "PASSED",
    "TestFileJpeg.test_save_correct_modes": "PASSED",
    "TestFileJpeg.test_save_modes_with_warnings": "PASSED",
    "TestFileJpeg.test_save_tiff_with_dpi": "PASSED",
    "TestFileJpeg.test_save_wrong_modes": "PASSED",
    "TestFileJpeg.test_smooth": "PASSED",
    "TestFileJpeg.test_subsampling": "PASSED"
}
diff --git a/PIL/JpegImagePlugin.py b/PIL/JpegImagePlugin.py
index 815ee02b6..63fd0dcbf 100644
--- a/PIL/JpegImagePlugin.py
+++ b/PIL/JpegImagePlugin.py
@@ -682,13 +682,16 @@ def _save(im, fp, filename):
                       o8(len(markers)) + marker)
             i += 1
 
+    # "progressive" is the official name, but older documentation
+    # says "progression"
+    # FIXME: issue a warning if the wrong form is used (post-1.1.7)
+    progressive = info.get("progressive", False) or\
+                  info.get("progression", False)
+
     # get keyword arguments
     im.encoderconfig = (
         quality,
-        # "progressive" is the official name, but older documentation
-        # says "progression"
-        # FIXME: issue a warning if the wrong form is used (post-1.1.7)
-        "progressive" in info or "progression" in info,
+        progressive,
         info.get("smooth", 0),
         "optimize" in info,
         info.get("streamtype", 0),
@@ -704,7 +707,7 @@ def _save(im, fp, filename):
     # channels*size, this is a value that's been used in a django patch.
     # https://github.com/matthewwithanm/django-imagekit/issues/50
     bufsize = 0
-    if "optimize" in info or "progressive" in info or "progression" in info:
+    if "optimize" in info or progressive:
         # keep sets quality to 0, but the actual value may be high.
         if quality >= 95 or quality == 0:
             bufsize = 2 * im.size[0] * im.size[1]

{"PIL/JpegImagePlugin.py": "#\n# The Python Imaging Library.\n# $Id$\n#\n# JPEG (JFIF) file handling\n#\n# See \"Digital Compression and Coding of Continuous-Tone Still Images,\n# Part 1, Requirements and Guidelines\" (CCITT T.81 / ISO 10918-1)\n#\n# History:\n# 1995-09-09 fl   Created\n# 1995-09-13 fl   Added full parser\n# 1996-03-25 fl   Added hack to use the IJG command line utilities\n# 1996-05-05 fl   Workaround Photoshop 2.5 CMYK polarity bug\n# 1996-05-28 fl   Added draft support, JFIF version (0.1)\n# 1996-12-30 fl   Added encoder options, added progression property (0.2)\n# 1997-08-27 fl   Save mode 1 images as BW (0.3)\n# 1998-07-12 fl   Added YCbCr to draft and save methods (0.4)\n# 1998-10-19 fl   Don't hang on files using 16-bit DQT's (0.4.1)\n# 2001-04-16 fl   Extract DPI settings from JFIF files (0.4.2)\n# 2002-07-01 fl   Skip pad bytes before markers; identify Exif files (0.4.3)\n# 2003-04-25 fl   Added experimental EXIF decoder (0.5)\n# 2003-06-06 fl   Added experimental EXIF GPSinfo decoder\n# 2003-09-13 fl   Extract COM markers\n# 2009-09-06 fl   Added icc_profile support (from Florian Hoech)\n# 2009-03-06 fl   Changed CMYK handling; always use Adobe polarity (0.6)\n# 2009-03-08 fl   Added subsampling support (from Justin Huff).\n#\n# Copyright (c) 1997-2003 by Secret Labs AB.\n# Copyright (c) 1995-1996 by Fredrik Lundh.\n#\n# See the README file for information on usage and redistribution.\n#\n\nimport array\nimport struct\nimport io\nimport warnings\nfrom struct import unpack_from\nfrom PIL import Image, ImageFile, TiffImagePlugin, _binary\nfrom PIL.JpegPresets import presets\nfrom PIL._util import isStringType\n\ni8 = _binary.i8\no8 = _binary.o8\ni16 = _binary.i16be\ni32 = _binary.i32be\n\n__version__ = \"0.6\"\n\n\n#\n# Parser\n\ndef Skip(self, marker):\n    n = i16(self.fp.read(2))-2\n    ImageFile._safe_read(self.fp, n)\n\n\ndef APP(self, marker):\n    #\n    # Application marker.  Store these in the APP dictionary.\n    # Also look for well-known application markers.\n\n    n = i16(self.fp.read(2))-2\n    s = ImageFile._safe_read(self.fp, n)\n\n    app = \"APP%d\" % (marker & 15)\n\n    self.app[app] = s  # compatibility\n    self.applist.append((app, s))\n\n    if marker == 0xFFE0 and s[:4] == b\"JFIF\":\n        # extract JFIF information\n        self.info[\"jfif\"] = version = i16(s, 5)  # version\n        self.info[\"jfif_version\"] = divmod(version, 256)\n        # extract JFIF properties\n        try:\n            jfif_unit = i8(s[7])\n            jfif_density = i16(s, 8), i16(s, 10)\n        except:\n            pass\n        else:\n            if jfif_unit == 1:\n                self.info[\"dpi\"] = jfif_density\n            self.info[\"jfif_unit\"] = jfif_unit\n            self.info[\"jfif_density\"] = jfif_density\n    elif marker == 0xFFE1 and s[:5] == b\"Exif\\0\":\n        # extract Exif information (incomplete)\n        self.info[\"exif\"] = s  # FIXME: value will change\n    elif marker == 0xFFE2 and s[:5] == b\"FPXR\\0\":\n        # extract FlashPix information (incomplete)\n        self.info[\"flashpix\"] = s  # FIXME: value will change\n    elif marker == 0xFFE2 and s[:12] == b\"ICC_PROFILE\\0\":\n        # Since an ICC profile can be larger than the maximum size of\n        # a JPEG marker (64K), we need provisions to split it into\n        # multiple markers. The format defined by the ICC specifies\n        # one or more APP2 markers containing the following data:\n        #   Identifying string      ASCII \"ICC_PROFILE\\0\"  (12 bytes)\n        #   Marker sequence number  1, 2, etc (1 byte)\n        #   Number of markers       Total of APP2's used (1 byte)\n        #   Profile data            (remainder of APP2 data)\n        # Decoders should use the marker sequence numbers to\n        # reassemble the profile, rather than assuming that the APP2\n        # markers appear in the correct sequence.\n        self.icclist.append(s)\n    elif marker == 0xFFEE and s[:5] == b\"Adobe\":\n        self.info[\"adobe\"] = i16(s, 5)\n        # extract Adobe custom properties\n        try:\n            adobe_transform = i8(s[1])\n        except:\n            pass\n        else:\n            self.info[\"adobe_transform\"] = adobe_transform\n    elif marker == 0xFFE2 and s[:4] == b\"MPF\\0\":\n        # extract MPO information\n        self.info[\"mp\"] = s[4:]\n        # offset is current location minus buffer size\n        # plus constant header size\n        self.info[\"mpoffset\"] = self.fp.tell() - n + 4\n\n\ndef COM(self, marker):\n    #\n    # Comment marker.  Store these in the APP dictionary.\n    n = i16(self.fp.read(2))-2\n    s = ImageFile._safe_read(self.fp, n)\n\n    self.app[\"COM\"] = s  # compatibility\n    self.applist.append((\"COM\", s))\n\n\ndef SOF(self, marker):\n    #\n    # Start of frame marker.  Defines the size and mode of the\n    # image.  JPEG is colour blind, so we use some simple\n    # heuristics to map the number of layers to an appropriate\n    # mode.  Note that this could be made a bit brighter, by\n    # looking for JFIF and Adobe APP markers.\n\n    n = i16(self.fp.read(2))-2\n    s = ImageFile._safe_read(self.fp, n)\n    self.size = i16(s[3:]), i16(s[1:])\n\n    self.bits = i8(s[0])\n    if self.bits != 8:\n        raise SyntaxError(\"cannot handle %d-bit layers\" % self.bits)\n\n    self.layers = i8(s[5])\n    if self.layers == 1:\n        self.mode = \"L\"\n    elif self.layers == 3:\n        self.mode = \"RGB\"\n    elif self.layers == 4:\n        self.mode = \"CMYK\"\n    else:\n        raise SyntaxError(\"cannot handle %d-layer images\" % self.layers)\n\n    if marker in [0xFFC2, 0xFFC6, 0xFFCA, 0xFFCE]:\n        self.info[\"progressive\"] = self.info[\"progression\"] = 1\n\n    if self.icclist:\n        # fixup icc profile\n        self.icclist.sort()  # sort by sequence number\n        if i8(self.icclist[0][13]) == len(self.icclist):\n            profile = []\n            for p in self.icclist:\n                profile.append(p[14:])\n            icc_profile = b\"\".join(profile)\n        else:\n            icc_profile = None  # wrong number of fragments\n        self.info[\"icc_profile\"] = icc_profile\n        self.icclist = None\n\n    for i in range(6, len(s), 3):\n        t = s[i:i+3]\n        # 4-tuples: id, vsamp, hsamp, qtable\n        self.layer.append((t[0], i8(t[1])//16, i8(t[1]) & 15, i8(t[2])))\n\n\ndef DQT(self, marker):\n    #\n    # Define quantization table.  Support baseline 8-bit tables\n    # only.  Note that there might be more than one table in\n    # each marker.\n\n    # FIXME: The quantization tables can be used to estimate the\n    # compression quality.\n\n    n = i16(self.fp.read(2))-2\n    s = ImageFile._safe_read(self.fp, n)\n    while len(s):\n        if len(s) < 65:\n            raise SyntaxError(\"bad quantization table marker\")\n        v = i8(s[0])\n        if v//16 == 0:\n            self.quantization[v & 15] = array.array(\"B\", s[1:65])\n            s = s[65:]\n        else:\n            return  # FIXME: add code to read 16-bit tables!\n            # raise SyntaxError, \"bad quantization table element size\"\n\n\n#\n# JPEG marker table\n\nMARKER = {\n    0xFFC0: (\"SOF0\", \"Baseline DCT\", SOF),\n    0xFFC1: (\"SOF1\", \"Extended Sequential DCT\", SOF),\n    0xFFC2: (\"SOF2\", \"Progressive DCT\", SOF),\n    0xFFC3: (\"SOF3\", \"Spatial lossless\", SOF),\n    0xFFC4: (\"DHT\", \"Define Huffman table\", Skip),\n    0xFFC5: (\"SOF5\", \"Differential sequential DCT\", SOF),\n    0xFFC6: (\"SOF6\", \"Differential progressive DCT\", SOF),\n    0xFFC7: (\"SOF7\", \"Differential spatial\", SOF),\n    0xFFC8: (\"JPG\", \"Extension\", None),\n    0xFFC9: (\"SOF9\", \"Extended sequential DCT (AC)\", SOF),\n    0xFFCA: (\"SOF10\", \"Progressive DCT (AC)\", SOF),\n    0xFFCB: (\"SOF11\", \"Spatial lossless DCT (AC)\", SOF),\n    0xFFCC: (\"DAC\", \"Define arithmetic coding conditioning\", Skip),\n    0xFFCD: (\"SOF13\", \"Differential sequential DCT (AC)\", SOF),\n    0xFFCE: (\"SOF14\", \"Differential progressive DCT (AC)\", SOF),\n    0xFFCF: (\"SOF15\", \"Differential spatial (AC)\", SOF),\n    0xFFD0: (\"RST0\", \"Restart 0\", None),\n    0xFFD1: (\"RST1\", \"Restart 1\", None),\n    0xFFD2: (\"RST2\", \"Restart 2\", None),\n    0xFFD3: (\"RST3\", \"Restart 3\", None),\n    0xFFD4: (\"RST4\", \"Restart 4\", None),\n    0xFFD5: (\"RST5\", \"Restart 5\", None),\n    0xFFD6: (\"RST6\", \"Restart 6\", None),\n    0xFFD7: (\"RST7\", \"Restart 7\", None),\n    0xFFD8: (\"SOI\", \"Start of image\", None),\n    0xFFD9: (\"EOI\", \"End of image\", None),\n    0xFFDA: (\"SOS\", \"Start of scan\", Skip),\n    0xFFDB: (\"DQT\", \"Define quantization table\", DQT),\n    0xFFDC: (\"DNL\", \"Define number of lines\", Skip),\n    0xFFDD: (\"DRI\", \"Define restart interval\", Skip),\n    0xFFDE: (\"DHP\", \"Define hierarchical progression\", SOF),\n    0xFFDF: (\"EXP\", \"Expand reference component\", Skip),\n    0xFFE0: (\"APP0\", \"Application segment 0\", APP),\n    0xFFE1: (\"APP1\", \"Application segment 1\", APP),\n    0xFFE2: (\"APP2\", \"Application segment 2\", APP),\n    0xFFE3: (\"APP3\", \"Application segment 3\", APP),\n    0xFFE4: (\"APP4\", \"Application segment 4\", APP),\n    0xFFE5: (\"APP5\", \"Application segment 5\", APP),\n    0xFFE6: (\"APP6\", \"Application segment 6\", APP),\n    0xFFE7: (\"APP7\", \"Application segment 7\", APP),\n    0xFFE8: (\"APP8\", \"Application segment 8\", APP),\n    0xFFE9: (\"APP9\", \"Application segment 9\", APP),\n    0xFFEA: (\"APP10\", \"Application segment 10\", APP),\n    0xFFEB: (\"APP11\", \"Application segment 11\", APP),\n    0xFFEC: (\"APP12\", \"Application segment 12\", APP),\n    0xFFED: (\"APP13\", \"Application segment 13\", APP),\n    0xFFEE: (\"APP14\", \"Application segment 14\", APP),\n    0xFFEF: (\"APP15\", \"Application segment 15\", APP),\n    0xFFF0: (\"JPG0\", \"Extension 0\", None),\n    0xFFF1: (\"JPG1\", \"Extension 1\", None),\n    0xFFF2: (\"JPG2\", \"Extension 2\", None),\n    0xFFF3: (\"JPG3\", \"Extension 3\", None),\n    0xFFF4: (\"JPG4\", \"Extension 4\", None),\n    0xFFF5: (\"JPG5\", \"Extension 5\", None),\n    0xFFF6: (\"JPG6\", \"Extension 6\", None),\n    0xFFF7: (\"JPG7\", \"Extension 7\", None),\n    0xFFF8: (\"JPG8\", \"Extension 8\", None),\n    0xFFF9: (\"JPG9\", \"Extension 9\", None),\n    0xFFFA: (\"JPG10\", \"Extension 10\", None),\n    0xFFFB: (\"JPG11\", \"Extension 11\", None),\n    0xFFFC: (\"JPG12\", \"Extension 12\", None),\n    0xFFFD: (\"JPG13\", \"Extension 13\", None),\n    0xFFFE: (\"COM\", \"Comment\", COM)\n}\n\n\ndef _accept(prefix):\n    return prefix[0:1] == b\"\\377\"\n\n\n##\n# Image plugin for JPEG and JFIF images.\n\nclass JpegImageFile(ImageFile.ImageFile):\n\n    format = \"JPEG\"\n    format_description = \"JPEG (ISO 10918)\"\n\n    def _open(self):\n\n        s = self.fp.read(1)\n\n        if i8(s) != 255:\n            raise SyntaxError(\"not a JPEG file\")\n\n        # Create attributes\n        self.bits = self.layers = 0\n\n        # JPEG specifics (internal)\n        self.layer = []\n        self.huffman_dc = {}\n        self.huffman_ac = {}\n        self.quantization = {}\n        self.app = {}  # compatibility\n        self.applist = []\n        self.icclist = []\n\n        while True:\n\n            i = i8(s)\n            if i == 0xFF:\n                s = s + self.fp.read(1)\n                i = i16(s)\n            else:\n                # Skip non-0xFF junk\n                s = self.fp.read(1)\n                continue\n\n            if i in MARKER:\n                name, description, handler = MARKER[i]\n                # print hex(i), name, description\n                if handler is not None:\n                    handler(self, i)\n                if i == 0xFFDA:  # start of scan\n                    rawmode = self.mode\n                    if self.mode == \"CMYK\":\n                        rawmode = \"CMYK;I\"  # assume adobe conventions\n                    self.tile = [(\"jpeg\", (0, 0) + self.size, 0,\n                                 (rawmode, \"\"))]\n                    # self.__offset = self.fp.tell()\n                    break\n                s = self.fp.read(1)\n            elif i == 0 or i == 0xFFFF:\n                # padded marker or junk; move on\n                s = b\"\\xff\"\n            elif i == 0xFF00:  # Skip extraneous data (escaped 0xFF)\n                s = self.fp.read(1)\n            else:\n                raise SyntaxError(\"no marker found\")\n\n    def draft(self, mode, size):\n\n        if len(self.tile) != 1:\n            return\n\n        d, e, o, a = self.tile[0]\n        scale = 0\n\n        if a[0] == \"RGB\" and mode in [\"L\", \"YCbCr\"]:\n            self.mode = mode\n            a = mode, \"\"\n\n        if size:\n            scale = max(self.size[0] // size[0], self.size[1] // size[1])\n            for s in [8, 4, 2, 1]:\n                if scale >= s:\n                    break\n            e = e[0], e[1], (e[2]-e[0]+s-1)//s+e[0], (e[3]-e[1]+s-1)//s+e[1]\n            self.size = ((self.size[0]+s-1)//s, (self.size[1]+s-1)//s)\n            scale = s\n\n        self.tile = [(d, e, o, a)]\n        self.decoderconfig = (scale, 0)\n\n        return self\n\n    def load_djpeg(self):\n\n        # ALTERNATIVE: handle JPEGs via the IJG command line utilities\n\n        import subprocess\n        import tempfile\n        import os\n        f, path = tempfile.mkstemp()\n        os.close(f)\n        if os.path.exists(self.filename):\n            subprocess.check_call([\"djpeg\", \"-outfile\", path, self.filename])\n        else:\n            raise ValueError(\"Invalid Filename\")\n\n        try:\n            self.im = Image.core.open_ppm(path)\n        finally:\n            try:\n                os.unlink(path)\n            except OSError:\n                pass\n\n        self.mode = self.im.mode\n        self.size = self.im.size\n\n        self.tile = []\n\n    def _getexif(self):\n        return _getexif(self)\n\n    def _getmp(self):\n        return _getmp(self)\n\n\ndef _fixup_dict(src_dict):\n    # Helper function for _getexif()\n    # returns a dict with any single item tuples/lists as individual values\n    def _fixup(value):\n        try:\n            if len(value) == 1 and not isinstance(value, dict):\n                return value[0]\n        except: pass\n        return value\n\n    return dict([(k, _fixup(v)) for k, v in src_dict.items()])\n\n\ndef _getexif(self):\n    # Extract EXIF information.  This method is highly experimental,\n    # and is likely to be replaced with something better in a future\n    # version.\n\n    # The EXIF record consists of a TIFF file embedded in a JPEG\n    # application marker (!).\n    try:\n        data = self.info[\"exif\"]\n    except KeyError:\n        return None\n    file = io.BytesIO(data[6:])\n    head = file.read(8)\n    # process dictionary\n    info = TiffImagePlugin.ImageFileDirectory_v1(head)\n    info.load(file)\n    exif = dict(_fixup_dict(info))\n    # get exif extension\n    try:\n        # exif field 0x8769 is an offset pointer to the location\n        # of the nested embedded exif ifd.\n        # It should be a long, but may be corrupted.\n        file.seek(exif[0x8769])\n    except (KeyError, TypeError):\n        pass\n    else:\n        info = TiffImagePlugin.ImageFileDirectory_v1(head)\n        info.load(file)\n        exif.update(_fixup_dict(info))\n    # get gpsinfo extension\n    try:\n        # exif field 0x8825 is an offset pointer to the location\n        # of the nested embedded gps exif ifd.\n        # It should be a long, but may be corrupted.\n        file.seek(exif[0x8825])\n    except (KeyError, TypeError):\n        pass\n    else:\n        info = TiffImagePlugin.ImageFileDirectory_v1(head)\n        info.load(file)\n        exif[0x8825] = _fixup_dict(info)\n\n    return exif\n\n\ndef _getmp(self):\n    # Extract MP information.  This method was inspired by the \"highly\n    # experimental\" _getexif version that's been in use for years now,\n    # itself based on the ImageFileDirectory class in the TIFF plug-in.\n\n    # The MP record essentially consists of a TIFF file embedded in a JPEG\n    # application marker.\n    try:\n        data = self.info[\"mp\"]\n    except KeyError:\n        return None\n    file_contents = io.BytesIO(data)\n    head = file_contents.read(8)\n    endianness = '>' if head[:4] == b'\\x4d\\x4d\\x00\\x2a' else '<'\n    # process dictionary\n    try:\n        info = TiffImagePlugin.ImageFileDirectory_v2(head)\n        info.load(file_contents)\n        mp = dict(info)\n    except:\n        raise SyntaxError(\"malformed MP Index (unreadable directory)\")\n    # it's an error not to have a number of images\n    try:\n        quant = mp[0xB001]\n    except KeyError:\n        raise SyntaxError(\"malformed MP Index (no number of images)\")\n    # get MP entries\n    mpentries = []\n    try:\n        rawmpentries = mp[0xB002]\n        for entrynum in range(0, quant):\n            unpackedentry = unpack_from(\n                '{0}LLLHH'.format(endianness), rawmpentries, entrynum * 16)\n            labels = ('Attribute', 'Size', 'DataOffset', 'EntryNo1',\n                      'EntryNo2')\n            mpentry = dict(zip(labels, unpackedentry))\n            mpentryattr = {\n                'DependentParentImageFlag': bool(mpentry['Attribute'] &\n                                                 (1 << 31)),\n                'DependentChildImageFlag': bool(mpentry['Attribute'] &\n                                                (1 << 30)),\n                'RepresentativeImageFlag': bool(mpentry['Attribute'] &\n                                                (1 << 29)),\n                'Reserved': (mpentry['Attribute'] & (3 << 27)) >> 27,\n                'ImageDataFormat': (mpentry['Attribute'] & (7 << 24)) >> 24,\n                'MPType': mpentry['Attribute'] & 0x00FFFFFF\n            }\n            if mpentryattr['ImageDataFormat'] == 0:\n                mpentryattr['ImageDataFormat'] = 'JPEG'\n            else:\n                raise SyntaxError(\"unsupported picture format in MPO\")\n            mptypemap = {\n                0x000000: 'Undefined',\n                0x010001: 'Large Thumbnail (VGA Equivalent)',\n                0x010002: 'Large Thumbnail (Full HD Equivalent)',\n                0x020001: 'Multi-Frame Image (Panorama)',\n                0x020002: 'Multi-Frame Image: (Disparity)',\n                0x020003: 'Multi-Frame Image: (Multi-Angle)',\n                0x030000: 'Baseline MP Primary Image'\n            }\n            mpentryattr['MPType'] = mptypemap.get(mpentryattr['MPType'],\n                                                  'Unknown')\n            mpentry['Attribute'] = mpentryattr\n            mpentries.append(mpentry)\n        mp[0xB002] = mpentries\n    except KeyError:\n        raise SyntaxError(\"malformed MP Index (bad MP Entry)\")\n    # Next we should try and parse the individual image unique ID list;\n    # we don't because I've never seen this actually used in a real MPO\n    # file and so can't test it.\n    return mp\n\n\n# --------------------------------------------------------------------\n# stuff to save JPEG files\n\nRAWMODE = {\n    \"1\": \"L\",\n    \"L\": \"L\",\n    \"RGB\": \"RGB\",\n    \"RGBA\": \"RGB\",\n    \"RGBX\": \"RGB\",\n    \"CMYK\": \"CMYK;I\",  # assume adobe conventions\n    \"YCbCr\": \"YCbCr\",\n}\n\nzigzag_index = (0,  1,  5,  6, 14, 15, 27, 28,\n                2,  4,  7, 13, 16, 26, 29, 42,\n                3,  8, 12, 17, 25, 30, 41, 43,\n                9, 11, 18, 24, 31, 40, 44, 53,\n               10, 19, 23, 32, 39, 45, 52, 54,\n               20, 22, 33, 38, 46, 51, 55, 60,\n               21, 34, 37, 47, 50, 56, 59, 61,\n               35, 36, 48, 49, 57, 58, 62, 63)\n\nsamplings = {(1, 1, 1, 1, 1, 1): 0,\n             (2, 1, 1, 1, 1, 1): 1,\n             (2, 2, 1, 1, 1, 1): 2,\n             }\n\n\ndef convert_dict_qtables(qtables):\n    qtables = [qtables[key] for key in range(len(qtables)) if key in qtables]\n    for idx, table in enumerate(qtables):\n        qtables[idx] = [table[i] for i in zigzag_index]\n    return qtables\n\n\ndef get_sampling(im):\n    # There's no subsampling when image have only 1 layer\n    # (grayscale images) or when they are CMYK (4 layers),\n    # so set subsampling to default value.\n    #\n    # NOTE: currently Pillow can't encode JPEG to YCCK format.\n    # If YCCK support is added in the future, subsampling code will have\n    # to be updated (here and in JpegEncode.c) to deal with 4 layers.\n    if not hasattr(im, 'layers') or im.layers in (1, 4):\n        return -1\n    sampling = im.layer[0][1:3] + im.layer[1][1:3] + im.layer[2][1:3]\n    return samplings.get(sampling, -1)\n\n\ndef _save(im, fp, filename):\n\n    try:\n        rawmode = RAWMODE[im.mode]\n    except KeyError:\n        raise IOError(\"cannot write mode %s as JPEG\" % im.mode)\n\n    if im.mode == 'RGBA':\n        warnings.warn(\n            'You are saving RGBA image as JPEG. The alpha channel will be '\n            'discarded. This conversion is deprecated and will be disabled '\n            'in Pillow 3.7. Please, convert the image to RGB explicitly.',\n            DeprecationWarning\n        )\n\n    info = im.encoderinfo\n\n    dpi = [int(round(x)) for x in info.get(\"dpi\", (0, 0))]\n\n    quality = info.get(\"quality\", 0)\n    subsampling = info.get(\"subsampling\", -1)\n    qtables = info.get(\"qtables\")\n\n    if quality == \"keep\":\n        quality = 0\n        subsampling = \"keep\"\n        qtables = \"keep\"\n    elif quality in presets:\n        preset = presets[quality]\n        quality = 0\n        subsampling = preset.get('subsampling', -1)\n        qtables = preset.get('quantization')\n    elif not isinstance(quality, int):\n        raise ValueError(\"Invalid quality setting\")\n    else:\n        if subsampling in presets:\n            subsampling = presets[subsampling].get('subsampling', -1)\n        if isStringType(qtables) and qtables in presets:\n            qtables = presets[qtables].get('quantization')\n\n    if subsampling == \"4:4:4\":\n        subsampling = 0\n    elif subsampling == \"4:2:2\":\n        subsampling = 1\n    elif subsampling == \"4:1:1\":\n        subsampling = 2\n    elif subsampling == \"keep\":\n        if im.format != \"JPEG\":\n            raise ValueError(\n                \"Cannot use 'keep' when original image is not a JPEG\")\n        subsampling = get_sampling(im)\n\n    def validate_qtables(qtables):\n        if qtables is None:\n            return qtables\n        if isStringType(qtables):\n            try:\n                lines = [int(num) for line in qtables.splitlines()\n                         for num in line.split('#', 1)[0].split()]\n            except ValueError:\n                raise ValueError(\"Invalid quantization table\")\n            else:\n                qtables = [lines[s:s+64] for s in range(0, len(lines), 64)]\n        if isinstance(qtables, (tuple, list, dict)):\n            if isinstance(qtables, dict):\n                qtables = convert_dict_qtables(qtables)\n            elif isinstance(qtables, tuple):\n                qtables = list(qtables)\n            if not (0 < len(qtables) < 5):\n                raise ValueError(\"None or too many quantization tables\")\n            for idx, table in enumerate(qtables):\n                try:\n                    if len(table) != 64:\n                        raise\n                    table = array.array('B', table)\n                except TypeError:\n                    raise ValueError(\"Invalid quantization table\")\n                else:\n                    qtables[idx] = list(table)\n            return qtables\n\n    if qtables == \"keep\":\n        if im.format != \"JPEG\":\n            raise ValueError(\n                \"Cannot use 'keep' when original image is not a JPEG\")\n        qtables = getattr(im, \"quantization\", None)\n    qtables = validate_qtables(qtables)\n\n    extra = b\"\"\n\n    icc_profile = info.get(\"icc_profile\")\n    if icc_profile:\n        ICC_OVERHEAD_LEN = 14\n        MAX_BYTES_IN_MARKER = 65533\n        MAX_DATA_BYTES_IN_MARKER = MAX_BYTES_IN_MARKER - ICC_OVERHEAD_LEN\n        markers = []\n        while icc_profile:\n            markers.append(icc_profile[:MAX_DATA_BYTES_IN_MARKER])\n            icc_profile = icc_profile[MAX_DATA_BYTES_IN_MARKER:]\n        i = 1\n        for marker in markers:\n            size = struct.pack(\">H\", 2 + ICC_OVERHEAD_LEN + len(marker))\n            extra += (b\"\\xFF\\xE2\" + size + b\"ICC_PROFILE\\0\" + o8(i) +\n                      o8(len(markers)) + marker)\n            i += 1\n\n    # get keyword arguments\n    im.encoderconfig = (\n        quality,\n        # \"progressive\" is the official name, but older documentation\n        # says \"progression\"\n        # FIXME: issue a warning if the wrong form is used (post-1.1.7)\n        \"progressive\" in info or \"progression\" in info,\n        info.get(\"smooth\", 0),\n        \"optimize\" in info,\n        info.get(\"streamtype\", 0),\n        dpi[0], dpi[1],\n        subsampling,\n        qtables,\n        extra,\n        info.get(\"exif\", b\"\")\n        )\n\n    # if we optimize, libjpeg needs a buffer big enough to hold the whole image\n    # in a shot. Guessing on the size, at im.size bytes. (raw pizel size is\n    # channels*size, this is a value that's been used in a django patch.\n    # https://github.com/matthewwithanm/django-imagekit/issues/50\n    bufsize = 0\n    if \"optimize\" in info or \"progressive\" in info or \"progression\" in info:\n        # keep sets quality to 0, but the actual value may be high.\n        if quality >= 95 or quality == 0:\n            bufsize = 2 * im.size[0] * im.size[1]\n        else:\n            bufsize = im.size[0] * im.size[1]\n\n    # The exif info needs to be written as one block, + APP1, + one spare byte.\n    # Ensure that our buffer is big enough\n    bufsize = max(ImageFile.MAXBLOCK, bufsize, len(info.get(\"exif\", b\"\")) + 5)\n\n    ImageFile._save(im, fp, [(\"jpeg\", (0, 0)+im.size, 0, rawmode)], bufsize)\n\n\ndef _save_cjpeg(im, fp, filename):\n    # ALTERNATIVE: handle JPEGs via the IJG command line utilities.\n    import os\n    import subprocess\n    tempfile = im._dump()\n    subprocess.check_call([\"cjpeg\", \"-outfile\", filename, tempfile])\n    try:\n        os.unlink(tempfile)\n    except OSError:\n        pass\n\n\n##\n# Factory for making JPEG and MPO instances\ndef jpeg_factory(fp=None, filename=None):\n    im = JpegImageFile(fp, filename)\n    try:\n        mpheader = im._getmp()\n        if mpheader[45057] > 1:\n            # It's actually an MPO\n            from .MpoImagePlugin import MpoImageFile\n            im = MpoImageFile(fp, filename)\n    except (TypeError, IndexError):\n        # It is really a JPEG\n        pass\n    except SyntaxError:\n        warnings.warn(\"Image appears to be a malformed MPO file, it will be \"\n                      \"interpreted as a base JPEG file\")\n    return im\n\n\n# -------------------------------------------------------------------q-\n# Registry stuff\n\nImage.register_open(JpegImageFile.format, jpeg_factory, _accept)\nImage.register_save(JpegImageFile.format, _save)\n\nImage.register_extension(JpegImageFile.format, \".jfif\")\nImage.register_extension(JpegImageFile.format, \".jpe\")\nImage.register_extension(JpegImageFile.format, \".jpg\")\nImage.register_extension(JpegImageFile.format, \".jpeg\")\n\nImage.register_mime(JpegImageFile.format, \"image/jpeg\")\n"}
A user will ask you to solve a task. You should first draft your thinking process (inner monologue). Then, generate the solution.

Your response format must follow the template below:
<think>
Your thoughts or/and draft, like working through an exercise on scratch paper. Be as casual and as long as you want until you are confident to generate a correct solution.
</think>
<solution>
Final solution presented to the user.
</solution>
system
We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
[ISSUE]
**Title:** Incorrect Handling of 'progressive' Flag Causes Compatibility Test to Fail

**Description:**
When saving JPEG images with the `progressive` or `progression` option set to `0`, the metadata should indicate that the image is not progressive. However, the current implementation incorrectly sets the `progressive` flag to `1`, leading to unexpected behavior.

**Example Code:**
```python
# Attempt to save JPEG without progressive encoding
im1 = self.roundtrip(hopper())
im2 = self.roundtrip(hopper(), progressive=0)
im3 = self.roundtrip(hopper(), progression=0)

# Check the metadata
print(im2.info.get("progressive"))  # Expected: False
print(im3.info.get("progression"))  # Expected: False
```

**Expected Behavior:**
When the `progressive` or `progression` option is set to `0`, the resulting JPEG image's metadata should reflect that it is not progressive. Specifically, both `im2.info.get("progressive")` and `im3.info.get("progression")` should return `False`.

**Actual Behavior:**
Despite setting `progressive=0` and `progression=0`, the metadata incorrectly shows `im2.info.get("progressive")` as `1`, indicating that the image is progressive. This causes compatibility tests to fail as the image does not behave as expected.

[/ISSUE]
--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.

--- BEGIN FILE ---
```
### PIL/JpegImagePlugin.py
#
# The Python Imaging Library.
# $Id$
#
# JPEG (JFIF) file handling
#
# See "Digital Compression and Coding of Continuous-Tone Still Images,
# Part 1, Requirements and Guidelines" (CCITT T.81 / ISO 10918-1)
#
# History:
# 1995-09-09 fl   Created
# 1995-09-13 fl   Added full parser
# 1996-03-25 fl   Added hack to use the IJG command line utilities
# 1996-05-05 fl   Workaround Photoshop 2.5 CMYK polarity bug
# 1996-05-28 fl   Added draft support, JFIF version (0.1)
# 1996-12-30 fl   Added encoder options, added progression property (0.2)
# 1997-08-27 fl   Save mode 1 images as BW (0.3)
# 1998-07-12 fl   Added YCbCr to draft and save methods (0.4)
# 1998-10-19 fl   Don't hang on files using 16-bit DQT's (0.4.1)
# 2001-04-16 fl   Extract DPI settings from JFIF files (0.4.2)
# 2002-07-01 fl   Skip pad bytes before markers; identify Exif files (0.4.3)
# 2003-04-25 fl   Added experimental EXIF decoder (0.5)
# 2003-06-06 fl   Added experimental EXIF GPSinfo decoder
# 2003-09-13 fl   Extract COM markers
# 2009-09-06 fl   Added icc_profile support (from Florian Hoech)
# 2009-03-06 fl   Changed CMYK handling; always use Adobe polarity (0.6)
# 2009-03-08 fl   Added subsampling support (from Justin Huff).
#
# Copyright (c) 1997-2003 by Secret Labs AB.
# Copyright (c) 1995-1996 by Fredrik Lundh.
#
# See the README file for information on usage and redistribution.
#

import array
import struct
import io
import warnings
from struct import unpack_from
from PIL import Image, ImageFile, TiffImagePlugin, _binary
from PIL.JpegPresets import presets
from PIL._util import isStringType

i8 = _binary.i8
o8 = _binary.o8
i16 = _binary.i16be
i32 = _binary.i32be

__version__ = "0.6"


#
# Parser

def Skip(self, marker):
    n = i16(self.fp.read(2))-2
    ImageFile._safe_read(self.fp, n)


def APP(self, marker):
    #
    # Application marker.  Store these in the APP dictionary.
    # Also look for well-known application markers.

    n = i16(self.fp.read(2))-2
    s = ImageFile._safe_read(self.fp, n)

    app = "APP%d" % (marker & 15)

    self.app[app] = s  # compatibility
    self.applist.append((app, s))

    if marker == 0xFFE0 and s[:4] == b"JFIF":
        # extract JFIF information
        self.info["jfif"] = version = i16(s, 5)  # version
        self.info["jfif_version"] = divmod(version, 256)
        # extract JFIF properties
        try:
            jfif_unit = i8(s[7])
            jfif_density = i16(s, 8), i16(s, 10)
        except:
            pass
        else:
            if jfif_unit == 1:
                self.info["dpi"] = jfif_density
            self.info["jfif_unit"] = jfif_unit
            self.info["jfif_density"] = jfif_density
    elif marker == 0xFFE1 and s[:5] == b"Exif\0":
        # extract Exif information (incomplete)
        self.info["exif"] = s  # FIXME: value will change
    elif marker == 0xFFE2 and s[:5] == b"FPXR\0":
        # extract FlashPix information (incomplete)
        self.info["flashpix"] = s  # FIXME: value will change
    elif marker == 0xFFE2 and s[:12] == b"ICC_PROFILE\0":
        # Since an ICC profile can be larger than the maximum size of
        # a JPEG marker (64K), we need provisions to split it into
        # multiple markers. The format defined by the ICC specifies
        # one or more APP2 markers containing the following data:
        #   Identifying string      ASCII "ICC_PROFILE\0"  (12 bytes)
        #   Marker sequence number  1, 2, etc (1 byte)
        #   Number of markers       Total of APP2's used (1 byte)
        #   Profile data            (remainder of APP2 data)
        # Decoders should use the marker sequence numbers to
        # reassemble the profile, rather than assuming that the APP2
        # markers appear in the correct sequence.
        self.icclist.append(s)
    elif marker == 0xFFEE and s[:5] == b"Adobe":
        self.info["adobe"] = i16(s, 5)
        # extract Adobe custom properties
        try:
            adobe_transform = i8(s[1])
        except:
            pass
        else:
            self.info["adobe_transform"] = adobe_transform
    elif marker == 0xFFE2 and s[:4] == b"MPF\0":
        # extract MPO information
        self.info["mp"] = s[4:]
        # offset is current location minus buffer size
        # plus constant header size
        self.info["mpoffset"] = self.fp.tell() - n + 4


def COM(self, marker):
    #
    # Comment marker.  Store these in the APP dictionary.
    n = i16(self.fp.read(2))-2
    s = ImageFile._safe_read(self.fp, n)

    self.app["COM"] = s  # compatibility
    self.applist.append(("COM", s))


def SOF(self, marker):
    #
    # Start of frame marker.  Defines the size and mode of the
    # image.  JPEG is colour blind, so we use some simple
    # heuristics to map the number of layers to an appropriate
    # mode.  Note that this could be made a bit brighter, by
    # looking for JFIF and Adobe APP markers.

    n = i16(self.fp.read(2))-2
    s = ImageFile._safe_read(self.fp, n)
    self.size = i16(s[3:]), i16(s[1:])

    self.bits = i8(s[0])
    if self.bits != 8:
        raise SyntaxError("cannot handle %d-bit layers" % self.bits)

    self.layers = i8(s[5])
    if self.layers == 1:
        self.mode = "L"
    elif self.layers == 3:
        self.mode = "RGB"
    elif self.layers == 4:
        self.mode = "CMYK"
    else:
        raise SyntaxError("cannot handle %d-layer images" % self.layers)

    if marker in [0xFFC2, 0xFFC6, 0xFFCA, 0xFFCE]:
        self.info["progressive"] = self.info["progression"] = 1

    if self.icclist:
        # fixup icc profile
        self.icclist.sort()  # sort by sequence number
        if i8(self.icclist[0][13]) == len(self.icclist):
            profile = []
            for p in self.icclist:
                profile.append(p[14:])
            icc_profile = b"".join(profile)
        else:
            icc_profile = None  # wrong number of fragments
        self.info["icc_profile"] = icc_profile
        self.icclist = None

    for i in range(6, len(s), 3):
        t = s[i:i+3]
        # 4-tuples: id, vsamp, hsamp, qtable
        self.layer.append((t[0], i8(t[1])//16, i8(t[1]) & 15, i8(t[2])))


def DQT(self, marker):
    #
    # Define quantization table.  Support baseline 8-bit tables
    # only.  Note that there might be more than one table in
    # each marker.

    # FIXME: The quantization tables can be used to estimate the
    # compression quality.

    n = i16(self.fp.read(2))-2
    s = ImageFile._safe_read(self.fp, n)
    while len(s):
        if len(s) < 65:
            raise SyntaxError("bad quantization table marker")
        v = i8(s[0])
        if v//16 == 0:
            self.quantization[v & 15] = array.array("B", s[1:65])
            s = s[65:]
        else:
            return  # FIXME: add code to read 16-bit tables!
            # raise SyntaxError, "bad quantization table element size"


#
# JPEG marker table

MARKER = {
    0xFFC0: ("SOF0", "Baseline DCT", SOF),
    0xFFC1: ("SOF1", "Extended Sequential DCT", SOF),
    0xFFC2: ("SOF2", "Progressive DCT", SOF),
    0xFFC3: ("SOF3", "Spatial lossless", SOF),
    0xFFC4: ("DHT", "Define Huffman table", Skip),
    0xFFC5: ("SOF5", "Differential sequential DCT", SOF),
    0xFFC6: ("SOF6", "Differential progressive DCT", SOF),
    0xFFC7: ("SOF7", "Differential spatial", SOF),
    0xFFC8: ("JPG", "Extension", None),
    0xFFC9: ("SOF9", "Extended sequential DCT (AC)", SOF),
    0xFFCA: ("SOF10", "Progressive DCT (AC)", SOF),
    0xFFCB: ("SOF11", "Spatial lossless DCT (AC)", SOF),
    0xFFCC: ("DAC", "Define arithmetic coding conditioning", Skip),
    0xFFCD: ("SOF13", "Differential sequential DCT (AC)", SOF),
    0xFFCE: ("SOF14", "Differential progressive DCT (AC)", SOF),
    0xFFCF: ("SOF15", "Differential spatial (AC)", SOF),
    0xFFD0: ("RST0", "Restart 0", None),
    0xFFD1: ("RST1", "Restart 1", None),
    0xFFD2: ("RST2", "Restart 2", None),
    0xFFD3: ("RST3", "Restart 3", None),
    0xFFD4: ("RST4", "Restart 4", None),
    0xFFD5: ("RST5", "Restart 5", None),
    0xFFD6: ("RST6", "Restart 6", None),
    0xFFD7: ("RST7", "Restart 7", None),
    0xFFD8: ("SOI", "Start of image", None),
    0xFFD9: ("EOI", "End of image", None),
    0xFFDA: ("SOS", "Start of scan", Skip),
    0xFFDB: ("DQT", "Define quantization table", DQT),
    0xFFDC: ("DNL", "Define number of lines", Skip),
    0xFFDD: ("DRI", "Define restart interval", Skip),
    0xFFDE: ("DHP", "Define hierarchical progression", SOF),
    0xFFDF: ("EXP", "Expand reference component", Skip),
    0xFFE0: ("APP0", "Application segment 0", APP),
    0xFFE1: ("APP1", "Application segment 1", APP),
    0xFFE2: ("APP2", "Application segment 2", APP),
    0xFFE3: ("APP3", "Application segment 3", APP),
    0xFFE4: ("APP4", "Application segment 4", APP),
    0xFFE5: ("APP5", "Application segment 5", APP),
    0xFFE6: ("APP6", "Application segment 6", APP),
    0xFFE7: ("APP7", "Application segment 7", APP),
    0xFFE8: ("APP8", "Application segment 8", APP),
    0xFFE9: ("APP9", "Application segment 9", APP),
    0xFFEA: ("APP10", "Application segment 10", APP),
    0xFFEB: ("APP11", "Application segment 11", APP),
    0xFFEC: ("APP12", "Application segment 12", APP),
    0xFFED: ("APP13", "Application segment 13", APP),
    0xFFEE: ("APP14", "Application segment 14", APP),
    0xFFEF: ("APP15", "Application segment 15", APP),
    0xFFF0: ("JPG0", "Extension 0", None),
    0xFFF1: ("JPG1", "Extension 1", None),
    0xFFF2: ("JPG2", "Extension 2", None),
    0xFFF3: ("JPG3", "Extension 3", None),
    0xFFF4: ("JPG4", "Extension 4", None),
    0xFFF5: ("JPG5", "Extension 5", None),
    0xFFF6: ("JPG6", "Extension 6", None),
    0xFFF7: ("JPG7", "Extension 7", None),
    0xFFF8: ("JPG8", "Extension 8", None),
    0xFFF9: ("JPG9", "Extension 9", None),
    0xFFFA: ("JPG10", "Extension 10", None),
    0xFFFB: ("JPG11", "Extension 11", None),
    0xFFFC: ("JPG12", "Extension 12", None),
    0xFFFD: ("JPG13", "Extension 13", None),
    0xFFFE: ("COM", "Comment", COM)
}


def _accept(prefix):
    return prefix[0:1] == b"\377"


##
# Image plugin for JPEG and JFIF images.

class JpegImageFile(ImageFile.ImageFile):

    format = "JPEG"
    format_description = "JPEG (ISO 10918)"

    def _open(self):

        s = self.fp.read(1)

        if i8(s) != 255:
            raise SyntaxError("not a JPEG file")

        # Create attributes
        self.bits = self.layers = 0

        # JPEG specifics (internal)
        self.layer = []
        self.huffman_dc = {}
        self.huffman_ac = {}
        self.quantization = {}
        self.app = {}  # compatibility
        self.applist = []
        self.icclist = []

        while True:

            i = i8(s)
            if i == 0xFF:
                s = s + self.fp.read(1)
                i = i16(s)
            else:
                # Skip non-0xFF junk
                s = self.fp.read(1)
                continue

            if i in MARKER:
                name, description, handler = MARKER[i]
                # print hex(i), name, description
                if handler is not None:
                    handler(self, i)
                if i == 0xFFDA:  # start of scan
                    rawmode = self.mode
                    if self.mode == "CMYK":
                        rawmode = "CMYK;I"  # assume adobe conventions
                    self.tile = [("jpeg", (0, 0) + self.size, 0,
                                 (rawmode, ""))]
                    # self.__offset = self.fp.tell()
                    break
                s = self.fp.read(1)
            elif i == 0 or i == 0xFFFF:
                # padded marker or junk; move on
                s = b"\xff"
            elif i == 0xFF00:  # Skip extraneous data (escaped 0xFF)
                s = self.fp.read(1)
            else:
                raise SyntaxError("no marker found")

    def draft(self, mode, size):

        if len(self.tile) != 1:
            return

        d, e, o, a = self.tile[0]
        scale = 0

        if a[0] == "RGB" and mode in ["L", "YCbCr"]:
            self.mode = mode
            a = mode, ""

        if size:
            scale = max(self.size[0] // size[0], self.size[1] // size[1])
            for s in [8, 4, 2, 1]:
                if scale >= s:
                    break
            e = e[0], e[1], (e[2]-e[0]+s-1)//s+e[0], (e[3]-e[1]+s-1)//s+e[1]
            self.size = ((self.size[0]+s-1)//s, (self.size[1]+s-1)//s)
            scale = s

        self.tile = [(d, e, o, a)]
        self.decoderconfig = (scale, 0)

        return self

    def load_djpeg(self):

        # ALTERNATIVE: handle JPEGs via the IJG command line utilities

        import subprocess
        import tempfile
        import os
        f, path = tempfile.mkstemp()
        os.close(f)
        if os.path.exists(self.filename):
            subprocess.check_call(["djpeg", "-outfile", path, self.filename])
        else:
            raise ValueError("Invalid Filename")

        try:
            self.im = Image.core.open_ppm(path)
        finally:
            try:
                os.unlink(path)
            except OSError:
                pass

        self.mode = self.im.mode
        self.size = self.im.size

        self.tile = []

    def _getexif(self):
        return _getexif(self)

    def _getmp(self):
        return _getmp(self)


def _fixup_dict(src_dict):
    # Helper function for _getexif()
    # returns a dict with any single item tuples/lists as individual values
    def _fixup(value):
        try:
            if len(value) == 1 and not isinstance(value, dict):
                return value[0]
        except: pass
        return value

    return dict([(k, _fixup(v)) for k, v in src_dict.items()])


def _getexif(self):
    # Extract EXIF information.  This method is highly experimental,
    # and is likely to be replaced with something better in a future
    # version.

    # The EXIF record consists of a TIFF file embedded in a JPEG
    # application marker (!).
    try:
        data = self.info["exif"]
    except KeyError:
        return None
    file = io.BytesIO(data[6:])
    head = file.read(8)
    # process dictionary
    info = TiffImagePlugin.ImageFileDirectory_v1(head)
    info.load(file)
    exif = dict(_fixup_dict(info))
    # get exif extension
    try:
        # exif field 0x8769 is an offset pointer to the location
        # of the nested embedded exif ifd.
        # It should be a long, but may be corrupted.
        file.seek(exif[0x8769])
    except (KeyError, TypeError):
        pass
    else:
        info = TiffImagePlugin.ImageFileDirectory_v1(head)
        info.load(file)
        exif.update(_fixup_dict(info))
    # get gpsinfo extension
    try:
        # exif field 0x8825 is an offset pointer to the location
        # of the nested embedded gps exif ifd.
        # It should be a long, but may be corrupted.
        file.seek(exif[0x8825])
    except (KeyError, TypeError):
        pass
    else:
        info = TiffImagePlugin.ImageFileDirectory_v1(head)
        info.load(file)
        exif[0x8825] = _fixup_dict(info)

    return exif


def _getmp(self):
    # Extract MP information.  This method was inspired by the "highly
    # experimental" _getexif version that's been in use for years now,
    # itself based on the ImageFileDirectory class in the TIFF plug-in.

    # The MP record essentially consists of a TIFF file embedded in a JPEG
    # application marker.
    try:
        data = self.info["mp"]
    except KeyError:
        return None
    file_contents = io.BytesIO(data)
    head = file_contents.read(8)
    endianness = '>' if head[:4] == b'\x4d\x4d\x00\x2a' else '<'
    # process dictionary
    try:
        info = TiffImagePlugin.ImageFileDirectory_v2(head)
        info.load(file_contents)
        mp = dict(info)
    except:
        raise SyntaxError("malformed MP Index (unreadable directory)")
    # it's an error not to have a number of images
    try:
        quant = mp[0xB001]
    except KeyError:
        raise SyntaxError("malformed MP Index (no number of images)")
    # get MP entries
    mpentries = []
    try:
        rawmpentries = mp[0xB002]
        for entrynum in range(0, quant):
            unpackedentry = unpack_from(
                '{0}LLLHH'.format(endianness), rawmpentries, entrynum * 16)
            labels = ('Attribute', 'Size', 'DataOffset', 'EntryNo1',
                      'EntryNo2')
            mpentry = dict(zip(labels, unpackedentry))
            mpentryattr = {
                'DependentParentImageFlag': bool(mpentry['Attribute'] &
                                                 (1 << 31)),
                'DependentChildImageFlag': bool(mpentry['Attribute'] &
                                                (1 << 30)),
                'RepresentativeImageFlag': bool(mpentry['Attribute'] &
                                                (1 << 29)),
                'Reserved': (mpentry['Attribute'] & (3 << 27)) >> 27,
                'ImageDataFormat': (mpentry['Attribute'] & (7 << 24)) >> 24,
                'MPType': mpentry['Attribute'] & 0x00FFFFFF
            }
            if mpentryattr['ImageDataFormat'] == 0:
                mpentryattr['ImageDataFormat'] = 'JPEG'
            else:
                raise SyntaxError("unsupported picture format in MPO")
            mptypemap = {
                0x000000: 'Undefined',
                0x010001: 'Large Thumbnail (VGA Equivalent)',
                0x010002: 'Large Thumbnail (Full HD Equivalent)',
                0x020001: 'Multi-Frame Image (Panorama)',
                0x020002: 'Multi-Frame Image: (Disparity)',
                0x020003: 'Multi-Frame Image: (Multi-Angle)',
                0x030000: 'Baseline MP Primary Image'
            }
            mpentryattr['MPType'] = mptypemap.get(mpentryattr['MPType'],
                                                  'Unknown')
            mpentry['Attribute'] = mpentryattr
            mpentries.append(mpentry)
        mp[0xB002] = mpentries
    except KeyError:
        raise SyntaxError("malformed MP Index (bad MP Entry)")
    # Next we should try and parse the individual image unique ID list;
    # we don't because I've never seen this actually used in a real MPO
    # file and so can't test it.
    return mp


# --------------------------------------------------------------------
# stuff to save JPEG files

RAWMODE = {
    "1": "L",
    "L": "L",
    "RGB": "RGB",
    "RGBA": "RGB",
    "RGBX": "RGB",
    "CMYK": "CMYK;I",  # assume adobe conventions
    "YCbCr": "YCbCr",
}

zigzag_index = (0,  1,  5,  6, 14, 15, 27, 28,
                2,  4,  7, 13, 16, 26, 29, 42,
                3,  8, 12, 17, 25, 30, 41, 43,
                9, 11, 18, 24, 31, 40, 44, 53,
               10, 19, 23, 32, 39, 45, 52, 54,
               20, 22, 33, 38, 46, 51, 55, 60,
               21, 34, 37, 47, 50, 56, 59, 61,
               35, 36, 48, 49, 57, 58, 62, 63)

samplings = {(1, 1, 1, 1, 1, 1): 0,
             (2, 1, 1, 1, 1, 1): 1,
             (2, 2, 1, 1, 1, 1): 2,
             }


def convert_dict_qtables(qtables):
    qtables = [qtables[key] for key in range(len(qtables)) if key in qtables]
    for idx, table in enumerate(qtables):
        qtables[idx] = [table[i] for i in zigzag_index]
    return qtables


def get_sampling(im):
    # There's no subsampling when image have only 1 layer
    # (grayscale images) or when they are CMYK (4 layers),
    # so set subsampling to default value.
    #
    # NOTE: currently Pillow can't encode JPEG to YCCK format.
    # If YCCK support is added in the future, subsampling code will have
    # to be updated (here and in JpegEncode.c) to deal with 4 layers.
    if not hasattr(im, 'layers') or im.layers in (1, 4):
        return -1
    sampling = im.layer[0][1:3] + im.layer[1][1:3] + im.layer[2][1:3]
    return samplings.get(sampling, -1)


def _save(im, fp, filename):

    try:
        rawmode = RAWMODE[im.mode]
    except KeyError:
        raise IOError("cannot write mode %s as JPEG" % im.mode)

    if im.mode == 'RGBA':
        warnings.warn(
            'You are saving RGBA image as JPEG. The alpha channel will be '
            'discarded. This conversion is deprecated and will be disabled '
            'in Pillow 3.7. Please, convert the image to RGB explicitly.',
            DeprecationWarning
        )

    info = im.encoderinfo

    dpi = [int(round(x)) for x in info.get("dpi", (0, 0))]

    quality = info.get("quality", 0)
    subsampling = info.get("subsampling", -1)
    qtables = info.get("qtables")

    if quality == "keep":
        quality = 0
        subsampling = "keep"
        qtables = "keep"
    elif quality in presets:
        preset = presets[quality]
        quality = 0
        subsampling = preset.get('subsampling', -1)
        qtables = preset.get('quantization')
    elif not isinstance(quality, int):
        raise ValueError("Invalid quality setting")
    else:
        if subsampling in presets:
            subsampling = presets[subsampling].get('subsampling', -1)
        if isStringType(qtables) and qtables in presets:
            qtables = presets[qtables].get('quantization')

    if subsampling == "4:4:4":
        subsampling = 0
    elif subsampling == "4:2:2":
        subsampling = 1
    elif subsampling == "4:1:1":
        subsampling = 2
    elif subsampling == "keep":
        if im.format != "JPEG":
            raise ValueError(
                "Cannot use 'keep' when original image is not a JPEG")
        subsampling = get_sampling(im)

    def validate_qtables(qtables):
        if qtables is None:
            return qtables
        if isStringType(qtables):
            try:
                lines = [int(num) for line in qtables.splitlines()
                         for num in line.split('#', 1)[0].split()]
            except ValueError:
                raise ValueError("Invalid quantization table")
            else:
                qtables = [lines[s:s+64] for s in range(0, len(lines), 64)]
        if isinstance(qtables, (tuple, list, dict)):
            if isinstance(qtables, dict):
                qtables = convert_dict_qtables(qtables)
            elif isinstance(qtables, tuple):
                qtables = list(qtables)
            if not (0 < len(qtables) < 5):
                raise ValueError("None or too many quantization tables")
            for idx, table in enumerate(qtables):
                try:
                    if len(table) != 64:
                        raise
                    table = array.array('B', table)
                except TypeError:
                    raise ValueError("Invalid quantization table")
                else:
                    qtables[idx] = list(table)
            return qtables

    if qtables == "keep":
        if im.format != "JPEG":
            raise ValueError(
                "Cannot use 'keep' when original image is not a JPEG")
        qtables = getattr(im, "quantization", None)
    qtables = validate_qtables(qtables)

    extra = b""

    icc_profile = info.get("icc_profile")
    if icc_profile:
        ICC_OVERHEAD_LEN = 14
        MAX_BYTES_IN_MARKER = 65533
        MAX_DATA_BYTES_IN_MARKER = MAX_BYTES_IN_MARKER - ICC_OVERHEAD_LEN
        markers = []
        while icc_profile:
            markers.append(icc_profile[:MAX_DATA_BYTES_IN_MARKER])
            icc_profile = icc_profile[MAX_DATA_BYTES_IN_MARKER:]
        i = 1
        for marker in markers:
            size = struct.pack(">H", 2 + ICC_OVERHEAD_LEN + len(marker))
            extra += (b"\xFF\xE2" + size + b"ICC_PROFILE\0" + o8(i) +
                      o8(len(markers)) + marker)
            i += 1

    # get keyword arguments
    im.encoderconfig = (
        quality,
        # "progressive" is the official name, but older documentation
        # says "progression"
        # FIXME: issue a warning if the wrong form is used (post-1.1.7)
        "progressive" in info or "progression" in info,
        info.get("smooth", 0),
        "optimize" in info,
        info.get("streamtype", 0),
        dpi[0], dpi[1],
        subsampling,
        qtables,
        extra,
        info.get("exif", b"")
        )

    # if we optimize, libjpeg needs a buffer big enough to hold the whole image
    # in a shot. Guessing on the size, at im.size bytes. (raw pizel size is
    # channels*size, this is a value that's been used in a django patch.
    # https://github.com/matthewwithanm/django-imagekit/issues/50
    bufsize = 0
    if "optimize" in info or "progressive" in info or "progression" in info:
        # keep sets quality to 0, but the actual value may be high.
        if quality >= 95 or quality == 0:
            bufsize = 2 * im.size[0] * im.size[1]
        else:
            bufsize = im.size[0] * im.size[1]

    # The exif info needs to be written as one block, + APP1, + one spare byte.
    # Ensure that our buffer is big enough
    bufsize = max(ImageFile.MAXBLOCK, bufsize, len(info.get("exif", b"")) + 5)

    ImageFile._save(im, fp, [("jpeg", (0, 0)+im.size, 0, rawmode)], bufsize)


def _save_cjpeg(im, fp, filename):
    # ALTERNATIVE: handle JPEGs via the IJG command line utilities.
    import os
    import subprocess
    tempfile = im._dump()
    subprocess.check_call(["cjpeg", "-outfile", filename, tempfile])
    try:
        os.unlink(tempfile)
    except OSError:
        pass


##
# Factory for making JPEG and MPO instances
def jpeg_factory(fp=None, filename=None):
    im = JpegImageFile(fp, filename)
    try:
        mpheader = im._getmp()
        if mpheader[45057] > 1:
            # It's actually an MPO
            from .MpoImagePlugin import MpoImageFile
            im = MpoImageFile(fp, filename)
    except (TypeError, IndexError):
        # It is really a JPEG
        pass
    except SyntaxError:
        warnings.warn("Image appears to be a malformed MPO file, it will be "
                      "interpreted as a base JPEG file")
    return im


# -------------------------------------------------------------------q-
# Registry stuff

Image.register_open(JpegImageFile.format, jpeg_factory, _accept)
Image.register_save(JpegImageFile.format, _save)

Image.register_extension(JpegImageFile.format, ".jfif")
Image.register_extension(JpegImageFile.format, ".jpe")
Image.register_extension(JpegImageFile.format, ".jpg")
Image.register_extension(JpegImageFile.format, ".jpeg")

Image.register_mime(JpegImageFile.format, "image/jpeg")

```
--- END FILE ---

Please first localize the bug based on the issue statement, and then generate *SEARCH/REPLACE* edits to fix the issue.

Every *SEARCH/REPLACE* edit must use this format:
1. The file path
2. The start of search block: <<<<<<< SEARCH
3. A contiguous chunk of lines to search for in the existing source code
4. The dividing line: =======
5. The lines to replace into the source code
6. The end of the replace block: >>>>>>> REPLACE

Here is an example:

```python
### mathweb/flask/app.py
<<<<<<< SEARCH
from flask import Flask
=======
import math
from flask import Flask
>>>>>>> REPLACE
```
</solution>

Please note that the *SEARCH/REPLACE* edit REQUIRES PROPER INDENTATION. If you would like to add the line '        print(x)', you must fully write that out, with all those spaces before the code!
Wrap each *SEARCH/REPLACE* edit in a code block as shown in the example above. If you have multiple *SEARCH/REPLACE* edits, use a separate code block for each one.
user