{
    "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            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 _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_v2(head)\n    info.load(file)\n    exif = 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_v2(head)\n        info.load(file)\n        exif.update(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] = dict([(k, v[0]) if len(v) == 1 else (k, v) for k, v in info.items()])\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    info = im.encoderinfo\n\n    dpi = 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            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 type(value) != type({}):\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    info = im.encoderinfo\n\n    dpi = 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",
            "header": {
                "file": {
                    "path": "PIL/JpegImagePlugin.py"
                },
                "misc_line": null
            },
            "index_line": {
                "old_commit_hash": "61a6af633",
                "new_commit_hash": "beda4f7a0",
                "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": 394,
                            "length": 6
                        },
                        "new_range": {
                            "start": 394,
                            "length": 19
                        },
                        "section": "class JpegImageFile(ImageFile.ImageFile):"
                    },
                    "line_group": {
                        "all_lines": [
                            {
                                "content": "        return _getmp(self)",
                                "type": "context"
                            },
                            {
                                "content": "",
                                "type": "context"
                            },
                            {
                                "content": "",
                                "type": "context"
                            },
                            {
                                "content": "def _fixup_dict(src_dict):",
                                "type": "added"
                            },
                            {
                                "content": "    # Helper function for _getexif()",
                                "type": "added"
                            },
                            {
                                "content": "    # returns a dict with any single item tuples/lists as individual values",
                                "type": "added"
                            },
                            {
                                "content": "    def _fixup(value):",
                                "type": "added"
                            },
                            {
                                "content": "        try:",
                                "type": "added"
                            },
                            {
                                "content": "            if len(value) == 1 and type(value) != type({}):",
                                "type": "added"
                            },
                            {
                                "content": "                return value[0]",
                                "type": "added"
                            },
                            {
                                "content": "        except: pass",
                                "type": "added"
                            },
                            {
                                "content": "        return value",
                                "type": "added"
                            },
                            {
                                "content": "",
                                "type": "added"
                            },
                            {
                                "content": "    return dict([(k, _fixup(v)) for k, v in src_dict.items()])",
                                "type": "added"
                            },
                            {
                                "content": "",
                                "type": "added"
                            },
                            {
                                "content": "",
                                "type": "added"
                            },
                            {
                                "content": "def _getexif(self):",
                                "type": "context"
                            },
                            {
                                "content": "    # Extract EXIF information.  This method is highly experimental,",
                                "type": "context"
                            },
                            {
                                "content": "    # and is likely to be replaced with something better in a future",
                                "type": "context"
                            }
                        ]
                    },
                    "modified_entities": [],
                    "added_entities": [
                        {
                            "file_name": "PIL/JpegImagePlugin.py",
                            "type": "function",
                            "name": "_fixup_dict",
                            "content": "def _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 type(value) != type({}):\n                return value[0]\n        except: pass\n        return value\n\n    return dict([(k, _fixup(v)) for k, v in src_dict.items()])",
                            "ast_type_str": "FunctionDef",
                            "start_lineno": 397,
                            "end_lineno": 407,
                            "parent": null
                        }
                    ],
                    "deleted_entities": []
                },
                {
                    "descriptor": {
                        "old_range": {
                            "start": 408,
                            "length": 9
                        },
                        "new_range": {
                            "start": 421,
                            "length": 9
                        },
                        "section": "def _getexif(self):"
                    },
                    "line_group": {
                        "all_lines": [
                            {
                                "content": "    file = io.BytesIO(data[6:])",
                                "type": "context"
                            },
                            {
                                "content": "    head = file.read(8)",
                                "type": "context"
                            },
                            {
                                "content": "    # process dictionary",
                                "type": "context"
                            },
                            {
                                "content": "    info = TiffImagePlugin.ImageFileDirectory_v2(head)",
                                "type": "deleted"
                            },
                            {
                                "content": "    info = TiffImagePlugin.ImageFileDirectory_v1(head)",
                                "type": "added"
                            },
                            {
                                "content": "    info.load(file)",
                                "type": "context"
                            },
                            {
                                "content": "    exif = dict(info)",
                                "type": "deleted"
                            },
                            {
                                "content": "    exif = dict(_fixup_dict(info))",
                                "type": "added"
                            },
                            {
                                "content": "    # get exif extension",
                                "type": "context"
                            },
                            {
                                "content": "    try:",
                                "type": "context"
                            },
                            {
                                "content": "        # exif field 0x8769 is an offset pointer to the location",
                                "type": "context"
                            }
                        ]
                    },
                    "modified_entities": [
                        {
                            "file_name": "PIL/JpegImagePlugin.py",
                            "type": "function",
                            "name": "_getexif",
                            "content": "def _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_v2(head)\n    info.load(file)\n    exif = 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_v2(head)\n        info.load(file)\n        exif.update(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] = dict([(k, v[0]) if len(v) == 1 else (k, v) for k, v in info.items()])\n    return exif",
                            "ast_type_str": "FunctionDef",
                            "start_lineno": 397,
                            "end_lineno": 438,
                            "parent": null
                        }
                    ],
                    "added_entities": [],
                    "deleted_entities": []
                },
                {
                    "descriptor": {
                        "old_range": {
                            "start": 420,
                            "length": 9
                        },
                        "new_range": {
                            "start": 433,
                            "length": 9
                        },
                        "section": "def _getexif(self):"
                    },
                    "line_group": {
                        "all_lines": [
                            {
                                "content": "    except (KeyError, TypeError):",
                                "type": "context"
                            },
                            {
                                "content": "        pass",
                                "type": "context"
                            },
                            {
                                "content": "    else:",
                                "type": "context"
                            },
                            {
                                "content": "        info = TiffImagePlugin.ImageFileDirectory_v2(head)",
                                "type": "deleted"
                            },
                            {
                                "content": "        info = TiffImagePlugin.ImageFileDirectory_v1(head)",
                                "type": "added"
                            },
                            {
                                "content": "        info.load(file)",
                                "type": "context"
                            },
                            {
                                "content": "        exif.update(info)",
                                "type": "deleted"
                            },
                            {
                                "content": "        exif.update(_fixup_dict(info))",
                                "type": "added"
                            },
                            {
                                "content": "    # get gpsinfo extension",
                                "type": "context"
                            },
                            {
                                "content": "    try:",
                                "type": "context"
                            },
                            {
                                "content": "        # exif field 0x8825 is an offset pointer to the location",
                                "type": "context"
                            }
                        ]
                    },
                    "modified_entities": [
                        {
                            "file_name": "PIL/JpegImagePlugin.py",
                            "type": "function",
                            "name": "_getexif",
                            "content": "def _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_v2(head)\n    info.load(file)\n    exif = 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_v2(head)\n        info.load(file)\n        exif.update(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] = dict([(k, v[0]) if len(v) == 1 else (k, v) for k, v in info.items()])\n    return exif",
                            "ast_type_str": "FunctionDef",
                            "start_lineno": 397,
                            "end_lineno": 438,
                            "parent": null
                        }
                    ],
                    "added_entities": [],
                    "deleted_entities": []
                },
                {
                    "descriptor": {
                        "old_range": {
                            "start": 434,
                            "length": 7
                        },
                        "new_range": {
                            "start": 447,
                            "length": 8
                        },
                        "section": "def _getexif(self):"
                    },
                    "line_group": {
                        "all_lines": [
                            {
                                "content": "    else:",
                                "type": "context"
                            },
                            {
                                "content": "        info = TiffImagePlugin.ImageFileDirectory_v1(head)",
                                "type": "context"
                            },
                            {
                                "content": "        info.load(file)",
                                "type": "context"
                            },
                            {
                                "content": "        exif[0x8825] = dict([(k, v[0]) if len(v) == 1 else (k, v) for k, v in info.items()])",
                                "type": "deleted"
                            },
                            {
                                "content": "        exif[0x8825] = _fixup_dict(info)",
                                "type": "added"
                            },
                            {
                                "content": "    ",
                                "type": "added"
                            },
                            {
                                "content": "    return exif",
                                "type": "context"
                            },
                            {
                                "content": "",
                                "type": "context"
                            },
                            {
                                "content": "",
                                "type": "context"
                            }
                        ]
                    },
                    "modified_entities": [
                        {
                            "file_name": "PIL/JpegImagePlugin.py",
                            "type": "function",
                            "name": "_getexif",
                            "content": "def _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_v2(head)\n    info.load(file)\n    exif = 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_v2(head)\n        info.load(file)\n        exif.update(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] = dict([(k, v[0]) if len(v) == 1 else (k, v) for k, v in info.items()])\n    return exif",
                            "ast_type_str": "FunctionDef",
                            "start_lineno": 397,
                            "end_lineno": 438,
                            "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_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_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        # 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        # 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\nif __name__ == '__main__':\n    unittest.main()\n\n# End of file\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\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_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        # 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        # 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\nif __name__ == '__main__':\n    unittest.main()\n\n# End of file\n",
            "header": {
                "file": {
                    "path": "Tests/test_file_jpeg.py"
                },
                "misc_line": null
            },
            "index_line": {
                "old_commit_hash": "6f144bfea",
                "new_commit_hash": "c54d08713",
                "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": 188,
                            "length": 6
                        },
                        "new_range": {
                            "start": 188,
                            "length": 38
                        },
                        "section": "class TestFileJpeg(PillowTestCase):"
                    },
                    "line_group": {
                        "all_lines": [
                            {
                                "content": "        # Assert",
                                "type": "context"
                            },
                            {
                                "content": "        self.assertEqual(exif[gps_index], expected_exif_gps)",
                                "type": "context"
                            },
                            {
                                "content": "",
                                "type": "context"
                            },
                            {
                                "content": "    def test_exif_rollback(self):",
                                "type": "added"
                            },
                            {
                                "content": "        # rolling back exif support in 3.1 to pre-3.0 formatting.",
                                "type": "added"
                            },
                            {
                                "content": "        # expected from 2.9, with b/u qualifiers switched for 3.2 compatibility",
                                "type": "added"
                            },
                            {
                                "content": "        # this test passes on 2.9 and 3.1, but not 3.0 ",
                                "type": "added"
                            },
                            {
                                "content": "        expected_exif = {34867: 4294967295,",
                                "type": "added"
                            },
                            {
                                "content": "                         258: (24, 24, 24), ",
                                "type": "added"
                            },
                            {
                                "content": "                         36867: '2099:09:29 10:10:10', ",
                                "type": "added"
                            },
                            {
                                "content": "                         34853: {0: b'\\x00\\x00\\x00\\x01', ",
                                "type": "added"
                            },
                            {
                                "content": "                                 2: (4294967295, 1), ",
                                "type": "added"
                            },
                            {
                                "content": "                                 5: b'\\x01', ",
                                "type": "added"
                            },
                            {
                                "content": "                                 30: 65535, ",
                                "type": "added"
                            },
                            {
                                "content": "                                 29: '1999:99:99 99:99:99'}, ",
                                "type": "added"
                            },
                            {
                                "content": "                         296: 65535, ",
                                "type": "added"
                            },
                            {
                                "content": "                         34665: 185, ",
                                "type": "added"
                            },
                            {
                                "content": "                         41994: 65535, ",
                                "type": "added"
                            },
                            {
                                "content": "                         514: 4294967295, ",
                                "type": "added"
                            },
                            {
                                "content": "                         271: 'Make',",
                                "type": "added"
                            },
                            {
                                "content": "                         272: 'XXX-XXX', ",
                                "type": "added"
                            },
                            {
                                "content": "                         305: 'PIL', ",
                                "type": "added"
                            },
                            {
                                "content": "                         42034: ((1, 1), (1, 1), (1, 1), (1, 1)), ",
                                "type": "added"
                            },
                            {
                                "content": "                         42035: 'LensMake', ",
                                "type": "added"
                            },
                            {
                                "content": "                         34856: b'\\xaa\\xaa\\xaa\\xaa\\xaa\\xaa', ",
                                "type": "added"
                            },
                            {
                                "content": "                         282: (4294967295, 1), ",
                                "type": "added"
                            },
                            {
                                "content": "                         33434: (4294967295, 1)}",
                                "type": "added"
                            },
                            {
                                "content": "",
                                "type": "added"
                            },
                            {
                                "content": "        im = Image.open('Tests/images/exif_gps.jpg')",
                                "type": "added"
                            },
                            {
                                "content": "        exif = im._getexif()",
                                "type": "added"
                            },
                            {
                                "content": "",
                                "type": "added"
                            },
                            {
                                "content": "        for tag, value in expected_exif.items():",
                                "type": "added"
                            },
                            {
                                "content": "            self.assertEqual(value, exif[tag])",
                                "type": "added"
                            },
                            {
                                "content": "            ",
                                "type": "added"
                            },
                            {
                                "content": "",
                                "type": "added"
                            },
                            {
                                "content": "    def test_exif_gps_typeerror(self):",
                                "type": "context"
                            },
                            {
                                "content": "        im = Image.open('Tests/images/exif_gps_typeerror.jpg')",
                                "type": "context"
                            },
                            {
                                "content": "",
                                "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\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_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        # 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        # 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\")",
                            "ast_type_str": "ClassDef",
                            "start_lineno": 17,
                            "end_lineno": 426,
                            "parent": null
                        }
                    ],
                    "added_entities": [
                        {
                            "file_name": "Tests/test_file_jpeg.py",
                            "type": "method",
                            "name": "TestFileJpeg.test_exif_rollback",
                            "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\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_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        # 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        # 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\")",
                            "ast_type_str": "ClassDef",
                            "start_lineno": 191,
                            "end_lineno": 220,
                            "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\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_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        # 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        # 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\")",
                                "ast_type_str": "ClassDef",
                                "start_lineno": 17,
                                "end_lineno": 426,
                                "parent": null
                            }
                        }
                    ],
                    "deleted_entities": []
                }
            ]
        }
    ],
    "old_commit_hash": "41eba2c8cf00cfc9329923d7f955b412c8d09c2b^",
    "new_commit_hash": "41eba2c8cf00cfc9329923d7f955b412c8d09c2b",
    "commit_message": "Merge pull request #1627 from wiredfool/exif_fixup\n\nRolling back exif support to pre-3.0 format",
    "commit_date": "2016-01-01T07:46:43-08:00",
    "metadata": {}
}
{
    "repo_name": "pillow",
    "new_commit_hash": "41eba2c8cf00cfc9329923d7f955b412c8d09c2b",
    "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\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_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        # 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        # 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\nif __name__ == '__main__':\n    unittest.main()\n\n# End of file\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 51ms\nInstalled 12 packages in 113ms\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.37s\nPrepared 1 package in 15.03s\nInstalled 1 package in 2ms\n + pillow==3.1.0.dev0 (from file:///home/gcpuser/buckets/local_repoeval_bucket/repos/pillow_41eba2c8cf00cfc9329923d7f955b412c8d09c2b)\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_41eba2c8cf00cfc9329923d7f955b412c8d09c2b\n..............................\ncollected 30 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_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_smooth\nPASSED test_1::TestFileJpeg::test_subsampling\n=================== 28 passed, 2 skipped in 0.89s ===================\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_41eba2c8cf00cfc9329923d7f955b412c8d09c2b\n...........=== orphaned temp file: /tmp/temp36ehbz0o.jpg\n...=== orphaned temp file: /tmp/tempt632e982.jpg\n.....=== orphaned temp file: /tmp/tempejayaapg.jpg\n...=== orphaned temp file: /tmp/tempfzc2u1cv.jpg\n.=== orphaned temp file: /tmp/temp0_r8bs14.jpg\n...=== orphaned temp file: /tmp/temp7pzm63zv.jpg\n=== orphaned temp file: /tmp/temprb2umiba.jpg\n=== orphaned temp file: /tmp/tempa47yjh4a.jpg\n....\ncollected 30 items\n\n=================================== FAILURES ===================================\n__________ TestFileJpeg.test_exif_rollback __________\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_41eba2c8cf00cfc9329923d7f955b412c8d09c2b/r2e_tests/test_1.py\", line 220, in test_exif_rollback\n    self.assertEqual(value, exif[tag])\nAssertionError: 4294967295 != (4294967295,)\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\nFAILED test_1::TestFileJpeg::test_exif_rollback\nPASSED test_1::TestFileJpeg::test_exif_typeerror\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_smooth\nPASSED test_1::TestFileJpeg::test_subsampling\n=================== 1 failed, 27 passed, 2 skipped in 1.14s ===================\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:** EXIF Data Parsed as Tuples Instead of Scalar Values Causing Assertion Errors

**Description:**
When extracting EXIF data from JPEG images, certain EXIF fields are being returned as single-item tuples instead of scalar values. This inconsistency leads to assertion failures in tests that expect these fields to be scalar.

**Example Code:**
```python
from PIL import Image

im = Image.open('Tests/images/exif_gps.jpg')
exif = im._getexif()

# Expected exif[34867] to be a scalar value
print(exif[34867])  # Outputs: (4294967295,)
```

**Expected Behavior:**
EXIF fields that are intended to be scalar values should be returned as single values. For instance, `exif[34867]` should return `4294967295` instead of `(4294967295,)`.

**Actual Behavior:**
EXIF fields are returned as single-item tuples, such as `exif[34867]` returning `(4294967295,)`. This causes tests to fail due to type mismatches.

[/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"
            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 _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_v2(head)
    info.load(file)
    exif = 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_v2(head)
        info.load(file)
        exif.update(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] = dict([(k, v[0]) if len(v) == 1 else (k, v) for k, v in info.items()])
    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)

    info = im.encoderinfo

    dpi = 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:** EXIF Data Parsed as Tuples Instead of Scalar Values Causing Assertion Errors

**Description:**
When extracting EXIF data from JPEG images, certain EXIF fields are being returned as single-item tuples instead of scalar values. This inconsistency leads to assertion failures in tests that expect these fields to be scalar.

**Example Code:**
```python
from PIL import Image

im = Image.open('Tests/images/exif_gps.jpg')
exif = im._getexif()

# Expected exif[34867] to be a scalar value
print(exif[34867])  # Outputs: (4294967295,)
```

**Expected Behavior:**
EXIF fields that are intended to be scalar values should be returned as single values. For instance, `exif[34867]` should return `4294967295` instead of `(4294967295,)`.

**Actual Behavior:**
EXIF fields are returned as single-item tuples, such as `exif[34867]` returning `(4294967295,)`. This causes tests to fail due to type mismatches.

[/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_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_smooth": "PASSED",
    "TestFileJpeg.test_subsampling": "PASSED"
}
diff --git a/PIL/JpegImagePlugin.py b/PIL/JpegImagePlugin.py
index 61a6af633..beda4f7a0 100644
--- a/PIL/JpegImagePlugin.py
+++ b/PIL/JpegImagePlugin.py
@@ -394,6 +394,19 @@ class JpegImageFile(ImageFile.ImageFile):
         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 type(value) != type({}):
+                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
@@ -408,9 +421,9 @@ def _getexif(self):
     file = io.BytesIO(data[6:])
     head = file.read(8)
     # process dictionary
-    info = TiffImagePlugin.ImageFileDirectory_v2(head)
+    info = TiffImagePlugin.ImageFileDirectory_v1(head)
     info.load(file)
-    exif = dict(info)
+    exif = dict(_fixup_dict(info))
     # get exif extension
     try:
         # exif field 0x8769 is an offset pointer to the location
@@ -420,9 +433,9 @@ def _getexif(self):
     except (KeyError, TypeError):
         pass
     else:
-        info = TiffImagePlugin.ImageFileDirectory_v2(head)
+        info = TiffImagePlugin.ImageFileDirectory_v1(head)
         info.load(file)
-        exif.update(info)
+        exif.update(_fixup_dict(info))
     # get gpsinfo extension
     try:
         # exif field 0x8825 is an offset pointer to the location
@@ -434,7 +447,8 @@ def _getexif(self):
     else:
         info = TiffImagePlugin.ImageFileDirectory_v1(head)
         info.load(file)
-        exif[0x8825] = dict([(k, v[0]) if len(v) == 1 else (k, v) for k, v in info.items()])
+        exif[0x8825] = _fixup_dict(info)
+    
     return exif
 
 

{"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            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 _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_v2(head)\n    info.load(file)\n    exif = 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_v2(head)\n        info.load(file)\n        exif.update(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] = dict([(k, v[0]) if len(v) == 1 else (k, v) for k, v in info.items()])\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    info = im.encoderinfo\n\n    dpi = 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:** EXIF Data Parsed as Tuples Instead of Scalar Values Causing Assertion Errors

**Description:**
When extracting EXIF data from JPEG images, certain EXIF fields are being returned as single-item tuples instead of scalar values. This inconsistency leads to assertion failures in tests that expect these fields to be scalar.

**Example Code:**
```python
from PIL import Image

im = Image.open('Tests/images/exif_gps.jpg')
exif = im._getexif()

# Expected exif[34867] to be a scalar value
print(exif[34867])  # Outputs: (4294967295,)
```

**Expected Behavior:**
EXIF fields that are intended to be scalar values should be returned as single values. For instance, `exif[34867]` should return `4294967295` instead of `(4294967295,)`.

**Actual Behavior:**
EXIF fields are returned as single-item tuples, such as `exif[34867]` returning `(4294967295,)`. This causes tests to fail due to type mismatches.

[/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"
            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 _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_v2(head)
    info.load(file)
    exif = 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_v2(head)
        info.load(file)
        exif.update(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] = dict([(k, v[0]) if len(v) == 1 else (k, v) for k, v in info.items()])
    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)

    info = im.encoderinfo

    dpi = 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