Example #1
0
 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 xrange(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
 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
Example #3
0
 def __init__(self, profile):
     # accepts a string (filename), a file-like object, or a low-level
     # profile object
     if isStringType(profile):
         self._set(core.profile_open(profile), profile)
     elif hasattr(profile, "read"):
         self._set(core.profile_frombytes(profile.read()))
     else:
         self._set(profile)  # assume it's already a profile
Example #4
0
    def test_is_path(self):
        # Arrange
        fp = "filename.ext"

        # Act
        it_is = _util.isStringType(fp)

        # Assert
        self.assertTrue(it_is)
Example #5
0
    def test_is_path(self):
        # Arrange
        fp = "filename.ext"

        # Act
        it_is = _util.isStringType(fp)

        # Assert
        self.assertTrue(it_is)
Example #6
0
    def test_is_string_type(self):
        # Arrange
        color = "red"

        # Act
        it_is = _util.isStringType(color)

        # Assert
        self.assertTrue(it_is)
Example #7
0
 def __init__(self, profile):
     # accepts a string (filename), a file-like object, or a low-level
     # profile object
     if isStringType(profile):
         self._set(core.profile_open(profile), profile)
     elif hasattr(profile, "read"):
         self._set(core.profile_frombytes(profile.read()))
     else:
         self._set(profile)  # assume it's already a profile
Example #8
0
    def test_is_not_string_type(self):
        # Arrange
        color = (255, 0, 0)

        # Act
        it_is_not = _util.isStringType(color)

        # Assert
        self.assertFalse(it_is_not)
Example #9
0
    def test_is_not_string_type(self):
        # Arrange
        color = (255, 0, 0)

        # Act
        it_is_not = _util.isStringType(color)

        # Assert
        self.assertFalse(it_is_not)
 def setink(self, ink):
     # compatibility
     if warnings:
         warnings.warn("'setink' is deprecated; use keyword arguments instead", DeprecationWarning, stacklevel=2)
     if isStringType(ink):
         ink = ImageColor.getcolor(ink, self.mode)
     if self.palette and not isinstance(ink, numbers.Number):
         ink = self.palette.getcolor(ink)
     self.ink = self.draw.draw_ink(ink, self.mode)
Example #11
0
    def test_is_string_type(self):
        # Arrange
        color = "red"

        # Act
        it_is = _util.isStringType(color)

        # Assert
        self.assertTrue(it_is)
Example #12
0
 def _getink(self, ink, fill=None):
     if ink is None and fill is None:
         if self.fill:
             fill = self.ink
         else:
             ink = self.ink
     else:
         if ink is not None:
             if isStringType(ink):
                 ink = ImageColor.getcolor(ink, self.mode)
             if self.palette and not isinstance(ink, numbers.Number):
                 ink = self.palette.getcolor(ink)
             ink = self.draw.draw_ink(ink, self.mode)
         if fill is not None:
             if isStringType(fill):
                 fill = ImageColor.getcolor(fill, self.mode)
             if self.palette and not isinstance(fill, numbers.Number):
                 fill = self.palette.getcolor(fill)
             fill = self.draw.draw_ink(fill, self.mode)
     return ink, fill
Example #13
0
 def _getink(self, ink, fill=None):
     if ink is None and fill is None:
         if self.fill:
             fill = self.ink
         else:
             ink = self.ink
     else:
         if ink is not None:
             if isStringType(ink):
                 ink = ImageColor.getcolor(ink, self.mode)
             if self.palette and not isinstance(ink, numbers.Number):
                 ink = self.palette.getcolor(ink)
             ink = self.draw.draw_ink(ink, self.mode)
         if fill is not None:
             if isStringType(fill):
                 fill = ImageColor.getcolor(fill, self.mode)
             if self.palette and not isinstance(fill, numbers.Number):
                 fill = self.palette.getcolor(fill)
             fill = self.draw.draw_ink(fill, self.mode)
     return ink, fill
Example #14
0
 def setink(self, ink):
     # compatibility
     if warnings:
         warnings.warn(
             "'setink' is deprecated; use keyword arguments instead",
             DeprecationWarning,
             stacklevel=2)
     if isStringType(ink):
         ink = ImageColor.getcolor(ink, self.mode)
     if self.palette and not isinstance(ink, numbers.Number):
         ink = self.palette.getcolor(ink)
     self.ink = self.draw.draw_ink(ink, self.mode)
Example #15
0
    def __init__(self, profile):
        """
        :param profile: Either a string representing a filename,
            a file like object containing a profile or a
            low-level profile object

        """

        if isStringType(profile):
            self._set(core.profile_open(profile), profile)
        elif hasattr(profile, "read"):
            self._set(core.profile_frombytes(profile.read()))
        else:
            self._set(profile)  # assume it's already a profile
Example #16
0
    def __init__(self, profile):
        """
        :param profile: Either a string representing a filename,
            a file like object containing a profile or a
            low-level profile object

        """

        if isStringType(profile):
            self._set(core.profile_open(profile), profile)
        elif hasattr(profile, "read"):
            self._set(core.profile_frombytes(profile.read()))
        else:
            self._set(profile)  # assume it's already a profile
Example #17
0
    def __init__(self, profile):
        """
        :param profile: Either a string representing a filename,
            a file like object containing a profile or a
            low-level profile object

        """

        if isStringType(profile):
            self._set(core.profile_open(profile), profile)
        elif hasattr(profile, "read"):
            self._set(core.profile_frombytes(profile.read()))
        elif isinstance(profile, _imagingcms.CmsProfile):
            self._set(profile)
        else:
            raise TypeError("Invalid type for Profile")
Example #18
0
    def __init__(self, profile):
        """
        :param profile: Either a string representing a filename,
            a file like object containing a profile or a
            low-level profile object

        """

        if isStringType(profile):
            self._set(core.profile_open(profile), profile)
        elif hasattr(profile, "read"):
            self._set(core.profile_frombytes(profile.read()))
        elif isinstance(profile, _imagingcms.CmsProfile):
            self._set(profile)
        else:
            raise TypeError("Invalid type for Profile")
Example #19
0
    def save(self, fp):

        o16 = self.o16
        o32 = self.o32

        fp.write(o16(len(self.tags)))

        # always write in ascending tag order
        tags = sorted(self.tags.items())

        directory = []
        append = directory.append

        offset = fp.tell() + len(self.tags) * 12 + 4

        stripoffsets = None

        # pass 1: convert tags to binary format
        for tag, value in tags:

            typ = None

            if tag in self.tagtype:
                typ = self.tagtype[tag]

            if Image.DEBUG:
                print("Tag %s, Type: %s, Value: %s" % (tag, typ, value))

            if typ == 1:
                # byte data
                if isinstance(value, tuple):
                    data = value = value[-1]
                else:
                    data = value
            elif typ == 7:
                # untyped data
                data = value = b"".join(value)
            elif isStringType(value[0]):
                # string data
                if isinstance(value, tuple):
                    value = value[-1]
                typ = 2
                # was b'\0'.join(str), which led to \x00a\x00b sorts
                # of strings which I don't see in in the wild tiffs
                # and doesn't match the tiff spec: 8-bit byte that
                # contains a 7-bit ASCII code; the last byte must be
                # NUL (binary zero). Also, I don't think this was well
                # excersized before.
                data = value = b"" + value.encode('ascii', 'replace') + b"\0"
            else:
                # integer data
                if tag == STRIPOFFSETS:
                    stripoffsets = len(directory)
                    typ = 4  # to avoid catch-22
                elif tag in (X_RESOLUTION, Y_RESOLUTION) or typ == 5:
                    # identify rational data fields
                    typ = 5
                    if isinstance(value[0], tuple):
                        # long name for flatten
                        value = tuple(itertools.chain.from_iterable(value))
                elif not typ:
                    typ = 3
                    for v in value:
                        if v >= 65536:
                            typ = 4
                if typ == 3:
                    data = b"".join(map(o16, value))
                else:
                    data = b"".join(map(o32, value))

            if Image.DEBUG:
                from PIL import TiffTags
                tagname = TiffTags.TAGS.get(tag, "unknown")
                typname = TiffTags.TYPES.get(typ, "unknown")
                print("save: %s (%d)" % (tagname, tag), end=' ')
                print("- type: %s (%d)" % (typname, typ), end=' ')
                if tag in (COLORMAP, IPTC_NAA_CHUNK, PHOTOSHOP_CHUNK,
                           ICCPROFILE, XMP):
                    size = len(data)
                    print("- value: <table: %d bytes>" % size)
                else:
                    print("- value:", value)

            # figure out if data fits into the directory
            if len(data) == 4:
                append((tag, typ, len(value), data, b""))
            elif len(data) < 4:
                append((tag, typ, len(value), data + (4 - len(data)) * b"\0",
                        b""))
            else:
                count = len(value)
                if typ == 5:
                    count = count // 2  # adjust for rational data field

                append((tag, typ, count, o32(offset), data))
                offset += len(data)
                if offset & 1:
                    offset += 1  # word padding

        # update strip offset data to point beyond auxiliary data
        if stripoffsets is not None:
            tag, typ, count, value, data = directory[stripoffsets]
            assert not data, "multistrip support not yet implemented"
            value = o32(self.i32(value) + offset)
            directory[stripoffsets] = tag, typ, count, value, data

        # pass 2: write directory to file
        for tag, typ, count, value, data in directory:
            if Image.DEBUG > 1:
                print(tag, typ, count, repr(value), repr(data))
            fp.write(o16(tag) + o16(typ) + o32(count) + value)

        # -- overwrite here for multi-page --
        fp.write(b"\0\0\0\0")  # end of directory

        # pass 3: write auxiliary data to file
        for tag, typ, count, value, data in directory:
            fp.write(data)
            if len(data) & 1:
                fp.write(b"\0")

        return offset
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/jdriscoll/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)
Example #21
0
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/jdriscoll/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)
Example #22
0
def _save(im, fp, filename):

    try:
        rawmode, prefix, photo, format, bits, extra = SAVE_INFO[im.mode]
    except KeyError:
        raise IOError("cannot write mode %s as TIFF" % im.mode)

    ifd = ImageFileDirectory(prefix)

    compression = im.encoderinfo.get('compression',
                                     im.info.get('compression', 'raw'))

    libtiff = WRITE_LIBTIFF or compression != 'raw'

    # required for color libtiff images
    ifd[PLANAR_CONFIGURATION] = getattr(im, '_planar_configuration', 1)

    # -- multi-page -- skip TIFF header on subsequent pages
    if not libtiff and fp.tell() == 0:
        # tiff header (write via IFD to get everything right)
        # PIL always starts the first IFD at offset 8
        fp.write(ifd.prefix + ifd.o16(42) + ifd.o32(8))

    ifd[IMAGEWIDTH] = im.size[0]
    ifd[IMAGELENGTH] = im.size[1]

    # write any arbitrary tags passed in as an ImageFileDirectory
    info = im.encoderinfo.get("tiffinfo", {})
    if Image.DEBUG:
        print("Tiffinfo Keys: %s" % info.keys)
    keys = list(info.keys())
    for key in keys:
        ifd[key] = info.get(key)
        try:
            ifd.tagtype[key] = info.tagtype[key]
        except:
            pass  # might not be an IFD, Might not have populated type

    # additions written by Greg Couch, [email protected]
    # inspired by image-sig posting from Kevin Cazabon, [email protected]
    if hasattr(im, 'tag'):
        # preserve tags from original TIFF image file
        for key in (RESOLUTION_UNIT, X_RESOLUTION, Y_RESOLUTION,
                    IPTC_NAA_CHUNK, PHOTOSHOP_CHUNK, XMP):
            if key in im.tag:
                ifd[key] = im.tag[key]
            ifd.tagtype[key] = im.tag.tagtype.get(key, None)

        # preserve ICC profile (should also work when saving other formats
        # which support profiles as TIFF) -- 2008-06-06 Florian Hoech
        if "icc_profile" in im.info:
            ifd[ICCPROFILE] = im.info["icc_profile"]

    if "description" in im.encoderinfo:
        ifd[IMAGEDESCRIPTION] = im.encoderinfo["description"]
    if "resolution" in im.encoderinfo:
        ifd[X_RESOLUTION] = ifd[Y_RESOLUTION] \
            = _cvt_res(im.encoderinfo["resolution"])
    if "x resolution" in im.encoderinfo:
        ifd[X_RESOLUTION] = _cvt_res(im.encoderinfo["x resolution"])
    if "y resolution" in im.encoderinfo:
        ifd[Y_RESOLUTION] = _cvt_res(im.encoderinfo["y resolution"])
    if "resolution unit" in im.encoderinfo:
        unit = im.encoderinfo["resolution unit"]
        if unit == "inch":
            ifd[RESOLUTION_UNIT] = 2
        elif unit == "cm" or unit == "centimeter":
            ifd[RESOLUTION_UNIT] = 3
        else:
            ifd[RESOLUTION_UNIT] = 1
    if "software" in im.encoderinfo:
        ifd[SOFTWARE] = im.encoderinfo["software"]
    if "date time" in im.encoderinfo:
        ifd[DATE_TIME] = im.encoderinfo["date time"]
    if "artist" in im.encoderinfo:
        ifd[ARTIST] = im.encoderinfo["artist"]
    if "copyright" in im.encoderinfo:
        ifd[COPYRIGHT] = im.encoderinfo["copyright"]

    dpi = im.encoderinfo.get("dpi")
    if dpi:
        ifd[RESOLUTION_UNIT] = 2
        ifd[X_RESOLUTION] = _cvt_res(dpi[0])
        ifd[Y_RESOLUTION] = _cvt_res(dpi[1])

    if bits != (1, ):
        ifd[BITSPERSAMPLE] = bits
        if len(bits) != 1:
            ifd[SAMPLESPERPIXEL] = len(bits)
    if extra is not None:
        ifd[EXTRASAMPLES] = extra
    if format != 1:
        ifd[SAMPLEFORMAT] = format

    ifd[PHOTOMETRIC_INTERPRETATION] = photo

    if im.mode == "P":
        lut = im.im.getpalette("RGB", "RGB;L")
        ifd[COLORMAP] = tuple(i8(v) * 256 for v in lut)

    # data orientation
    stride = len(bits) * ((im.size[0] * bits[0] + 7) // 8)
    ifd[ROWSPERSTRIP] = im.size[1]
    ifd[STRIPBYTECOUNTS] = stride * im.size[1]
    ifd[STRIPOFFSETS] = 0  # this is adjusted by IFD writer
    # no compression by default:
    ifd[COMPRESSION] = COMPRESSION_INFO_REV.get(compression, 1)

    if libtiff:
        if Image.DEBUG:
            print("Saving using libtiff encoder")
            print(ifd.items())
        _fp = 0
        if hasattr(fp, "fileno"):
            fp.seek(0)
            _fp = os.dup(fp.fileno())

        # ICC Profile crashes.
        blocklist = [STRIPOFFSETS, STRIPBYTECOUNTS, ROWSPERSTRIP, ICCPROFILE]
        atts = {}
        # bits per sample is a single short in the tiff directory, not a list.
        atts[BITSPERSAMPLE] = bits[0]
        # Merge the ones that we have with (optional) more bits from
        # the original file, e.g x,y resolution so that we can
        # save(load('')) == original file.
        for k, v in itertools.chain(ifd.items(),
                                    getattr(im, 'ifd', {}).items()):
            if k not in atts and k not in blocklist:
                if type(v[0]) == tuple and len(v) > 1:
                    # A tuple of more than one rational tuples
                    # flatten to floats,
                    # following tiffcp.c->cpTag->TIFF_RATIONAL
                    atts[k] = [float(elt[0]) / float(elt[1]) for elt in v]
                    continue
                if type(v[0]) == tuple and len(v) == 1:
                    # A tuple of one rational tuples
                    # flatten to floats,
                    # following tiffcp.c->cpTag->TIFF_RATIONAL
                    atts[k] = float(v[0][0]) / float(v[0][1])
                    continue
                if type(v) == tuple and len(v) > 2:
                    # List of ints?
                    if type(v[0]) in (int, float):
                        atts[k] = list(v)
                    continue
                if type(v) == tuple and len(v) == 2:
                    # one rational tuple
                    # flatten to float,
                    # following tiffcp.c->cpTag->TIFF_RATIONAL
                    atts[k] = float(v[0]) / float(v[1])
                    continue
                if type(v) == tuple and len(v) == 1:
                    v = v[0]
                    # drop through
                if isStringType(v):
                    atts[k] = bytes(v.encode('ascii', 'replace')) + b"\0"
                    continue
                else:
                    # int or similar
                    atts[k] = v

        if Image.DEBUG:
            print(atts)

        # libtiff always expects the bytes in native order.
        # we're storing image byte order. So, if the rawmode
        # contains I;16, we need to convert from native to image
        # byte order.
        if im.mode in ('I;16B', 'I;16'):
            rawmode = 'I;16N'

        a = (rawmode, compression, _fp, filename, atts)
        # print (im.mode, compression, a, im.encoderconfig)
        e = Image._getencoder(im.mode, 'libtiff', a, im.encoderconfig)
        e.setimage(im.im, (0, 0) + im.size)
        while True:
            # undone, change to self.decodermaxblock:
            l, s, d = e.encode(16 * 1024)
            if not _fp:
                fp.write(d)
            if s:
                break
        if s < 0:
            raise IOError("encoder error %d when writing image file" % s)

    else:
        offset = ifd.save(fp)

        ImageFile._save(im, fp, [("raw", (0, 0) + im.size, offset,
                                  (rawmode, stride, 1))])

    # -- helper for multi-page save --
    if "_debug_multipage" in im.encoderinfo:
        # just to access o32 and o16 (using correct byte order)
        im._debug_multipage = ifd
Example #23
0
    def save(self, fp):

        o16 = self.o16
        o32 = self.o32

        fp.write(o16(len(self.tags)))

        # always write in ascending tag order
        tags = sorted(self.tags.items())

        directory = []
        append = directory.append

        offset = fp.tell() + len(self.tags) * 12 + 4

        stripoffsets = None

        # pass 1: convert tags to binary format
        for tag, value in tags:

            typ = None

            if tag in self.tagtype:
                typ = self.tagtype[tag]

            if Image.DEBUG:
                print("Tag %s, Type: %s, Value: %s" % (tag, typ, value))

            if typ == 1:
                # byte data
                if isinstance(value, tuple):
                    data = value = value[-1]
                else:
                    data = value
            elif typ == 7:
                # untyped data
                data = value = b"".join(value)
            elif typ in (11, 12):
                # float value
                tmap = {11: 'f', 12: 'd'}
                if not isinstance(value, tuple):
                    value = (value,)
                a = array.array(tmap[typ], value)
                if self.prefix != native_prefix:
                    a.byteswap()
                data = a.tostring()
            elif isStringType(value[0]):
                # string data
                if isinstance(value, tuple):
                    value = value[-1]
                typ = 2
                # was b'\0'.join(str), which led to \x00a\x00b sorts
                # of strings which I don't see in in the wild tiffs
                # and doesn't match the tiff spec: 8-bit byte that
                # contains a 7-bit ASCII code; the last byte must be
                # NUL (binary zero). Also, I don't think this was well
                # exercised before.
                data = value = b"" + value.encode('ascii', 'replace') + b"\0"
            else:
                # integer data
                if tag == STRIPOFFSETS:
                    stripoffsets = len(directory)
                    typ = 4  # to avoid catch-22
                elif tag in (X_RESOLUTION, Y_RESOLUTION) or typ == 5:
                    # identify rational data fields
                    typ = 5
                    if isinstance(value[0], tuple):
                        # long name for flatten
                        value = tuple(itertools.chain.from_iterable(value))
                elif not typ:
                    typ = 3
                    for v in value:
                        if v >= 65536:
                            typ = 4
                if typ == 3:
                    data = b"".join(map(o16, value))
                else:
                    data = b"".join(map(o32, value))

            if Image.DEBUG:
                from PIL import TiffTags
                tagname = TiffTags.TAGS.get(tag, "unknown")
                typname = TiffTags.TYPES.get(typ, "unknown")
                print("save: %s (%d)" % (tagname, tag), end=' ')
                print("- type: %s (%d)" % (typname, typ), end=' ')
                if tag in (COLORMAP, IPTC_NAA_CHUNK, PHOTOSHOP_CHUNK,
                           ICCPROFILE, XMP):
                    size = len(data)
                    print("- value: <table: %d bytes>" % size)
                else:
                    print("- value:", value)

            # figure out if data fits into the directory
            if len(data) == 4:
                append((tag, typ, len(value), data, b""))
            elif len(data) < 4:
                append((tag, typ, len(value), data + (4-len(data))*b"\0", b""))
            else:
                count = len(value)
                if typ == 5:
                    count = count // 2        # adjust for rational data field

                append((tag, typ, count, o32(offset), data))
                offset += len(data)
                if offset & 1:
                    offset += 1  # word padding

        # update strip offset data to point beyond auxiliary data
        if stripoffsets is not None:
            tag, typ, count, value, data = directory[stripoffsets]
            assert not data, "multistrip support not yet implemented"
            value = o32(self.i32(value) + offset)
            directory[stripoffsets] = tag, typ, count, value, data

        # pass 2: write directory to file
        for tag, typ, count, value, data in directory:
            if Image.DEBUG > 1:
                print(tag, typ, count, repr(value), repr(data))
            fp.write(o16(tag) + o16(typ) + o32(count) + value)

        # -- overwrite here for multi-page --
        fp.write(b"\0\0\0\0")  # end of directory

        # pass 3: write auxiliary data to file
        for tag, typ, count, value, data in directory:
            fp.write(data)
            if len(data) & 1:
                fp.write(b"\0")

        return offset
Example #24
0
def _save(im, fp, filename):

    try:
        rawmode, prefix, photo, format, bits, extra = SAVE_INFO[im.mode]
    except KeyError:
        raise IOError("cannot write mode %s as TIFF" % im.mode)

    ifd = ImageFileDirectory(prefix)

    compression = im.encoderinfo.get('compression', im.info.get('compression',
                                     'raw'))

    libtiff = WRITE_LIBTIFF or compression != 'raw'

    # required for color libtiff images
    ifd[PLANAR_CONFIGURATION] = getattr(im, '_planar_configuration', 1)

    # -- multi-page -- skip TIFF header on subsequent pages
    if not libtiff and fp.tell() == 0:
        # tiff header (write via IFD to get everything right)
        # PIL always starts the first IFD at offset 8
        fp.write(ifd.prefix + ifd.o16(42) + ifd.o32(8))

    ifd[IMAGEWIDTH] = im.size[0]
    ifd[IMAGELENGTH] = im.size[1]

    # write any arbitrary tags passed in as an ImageFileDirectory
    info = im.encoderinfo.get("tiffinfo", {})
    if Image.DEBUG:
        print("Tiffinfo Keys: %s" % info.keys)
    keys = list(info.keys())
    for key in keys:
        ifd[key] = info.get(key)
        try:
            ifd.tagtype[key] = info.tagtype[key]
        except:
            pass  # might not be an IFD, Might not have populated type

    # additions written by Greg Couch, [email protected]
    # inspired by image-sig posting from Kevin Cazabon, [email protected]
    if hasattr(im, 'tag'):
        # preserve tags from original TIFF image file
        for key in (RESOLUTION_UNIT, X_RESOLUTION, Y_RESOLUTION,
                    IPTC_NAA_CHUNK, PHOTOSHOP_CHUNK, XMP):
            if key in im.tag:
                ifd[key] = im.tag[key]
            ifd.tagtype[key] = im.tag.tagtype.get(key, None)

        # preserve ICC profile (should also work when saving other formats
        # which support profiles as TIFF) -- 2008-06-06 Florian Hoech
        if "icc_profile" in im.info:
            ifd[ICCPROFILE] = im.info["icc_profile"]

    for key, name, cvt in [
            (IMAGEDESCRIPTION, "description", lambda x: x),
            (X_RESOLUTION, "resolution", _cvt_res),
            (Y_RESOLUTION, "resolution", _cvt_res),
            (X_RESOLUTION, "x_resolution", _cvt_res),
            (Y_RESOLUTION, "y_resolution", _cvt_res),
            (RESOLUTION_UNIT, "resolution_unit",
             lambda x: {"inch": 2, "cm": 3, "centimeter": 3}.get(x, 1)),
            (SOFTWARE, "software", lambda x: x),
            (DATE_TIME, "date_time", lambda x: x),
            (ARTIST, "artist", lambda x: x),
            (COPYRIGHT, "copyright", lambda x: x)]:
        name_with_spaces = name.replace("_", " ")
        if "_" in name and name_with_spaces in im.encoderinfo:
            warnings.warn("%r is deprecated; use %r instead" %
                          (name_with_spaces, name), DeprecationWarning)
            ifd[key] = cvt(im.encoderinfo[name.replace("_", " ")])
        if name in im.encoderinfo:
            ifd[key] = cvt(im.encoderinfo[name])

    dpi = im.encoderinfo.get("dpi")
    if dpi:
        ifd[RESOLUTION_UNIT] = 2
        ifd[X_RESOLUTION] = _cvt_res(dpi[0])
        ifd[Y_RESOLUTION] = _cvt_res(dpi[1])

    if bits != (1,):
        ifd[BITSPERSAMPLE] = bits
        if len(bits) != 1:
            ifd[SAMPLESPERPIXEL] = len(bits)
    if extra is not None:
        ifd[EXTRASAMPLES] = extra
    if format != 1:
        ifd[SAMPLEFORMAT] = format

    ifd[PHOTOMETRIC_INTERPRETATION] = photo

    if im.mode == "P":
        lut = im.im.getpalette("RGB", "RGB;L")
        ifd[COLORMAP] = tuple(i8(v) * 256 for v in lut)

    # data orientation
    stride = len(bits) * ((im.size[0]*bits[0]+7)//8)
    ifd[ROWSPERSTRIP] = im.size[1]
    ifd[STRIPBYTECOUNTS] = stride * im.size[1]
    ifd[STRIPOFFSETS] = 0  # this is adjusted by IFD writer
    # no compression by default:
    ifd[COMPRESSION] = COMPRESSION_INFO_REV.get(compression, 1)

    if libtiff:
        if Image.DEBUG:
            print("Saving using libtiff encoder")
            print(ifd.items())
        _fp = 0
        if hasattr(fp, "fileno"):
            try:
                fp.seek(0)
                _fp = os.dup(fp.fileno())
            except io.UnsupportedOperation:
                pass

        # ICC Profile crashes.
        blocklist = [STRIPOFFSETS, STRIPBYTECOUNTS, ROWSPERSTRIP, ICCPROFILE]
        atts = {}
        # bits per sample is a single short in the tiff directory, not a list.
        atts[BITSPERSAMPLE] = bits[0]
        # Merge the ones that we have with (optional) more bits from
        # the original file, e.g x,y resolution so that we can
        # save(load('')) == original file.
        for k, v in itertools.chain(ifd.items(),
                                    getattr(im, 'ifd', {}).items()):
            if k not in atts and k not in blocklist:
                if type(v[0]) == tuple and len(v) > 1:
                    # A tuple of more than one rational tuples
                    # flatten to floats,
                    # following tiffcp.c->cpTag->TIFF_RATIONAL
                    atts[k] = [float(elt[0])/float(elt[1]) for elt in v]
                    continue
                if type(v[0]) == tuple and len(v) == 1:
                    # A tuple of one rational tuples
                    # flatten to floats,
                    # following tiffcp.c->cpTag->TIFF_RATIONAL
                    atts[k] = float(v[0][0])/float(v[0][1])
                    continue
                if (type(v) == tuple and
                        (len(v) > 2 or
                            (len(v) == 2 and v[1] == 0))):
                    # List of ints?
                    # Avoid divide by zero in next if-clause
                    if type(v[0]) in (int, float):
                        atts[k] = list(v)
                    continue
                if type(v) == tuple and len(v) == 2:
                    # one rational tuple
                    # flatten to float,
                    # following tiffcp.c->cpTag->TIFF_RATIONAL
                    atts[k] = float(v[0])/float(v[1])
                    continue
                if type(v) == tuple and len(v) == 1:
                    v = v[0]
                    # drop through
                if isStringType(v):
                    atts[k] = bytes(v.encode('ascii', 'replace')) + b"\0"
                    continue
                else:
                    # int or similar
                    atts[k] = v

        if Image.DEBUG:
            print(atts)

        # libtiff always expects the bytes in native order.
        # we're storing image byte order. So, if the rawmode
        # contains I;16, we need to convert from native to image
        # byte order.
        if im.mode in ('I;16B', 'I;16'):
            rawmode = 'I;16N'

        a = (rawmode, compression, _fp, filename, atts)
        # print (im.mode, compression, a, im.encoderconfig)
        e = Image._getencoder(im.mode, 'libtiff', a, im.encoderconfig)
        e.setimage(im.im, (0, 0)+im.size)
        while True:
            # undone, change to self.decodermaxblock:
            l, s, d = e.encode(16*1024)
            if not _fp:
                fp.write(d)
            if s:
                break
        if s < 0:
            raise IOError("encoder error %d when writing image file" % s)

    else:
        offset = ifd.save(fp)

        ImageFile._save(im, fp, [
            ("raw", (0, 0)+im.size, offset, (rawmode, stride, 1))
            ])

    # -- helper for multi-page save --
    if "_debug_multipage" in im.encoderinfo:
        # just to access o32 and o16 (using correct byte order)
        im._debug_multipage = ifd
Example #25
0
def _color(color, mode):
    if isStringType(color):
        from PIL import ImageColor
        color = ImageColor.getcolor(color, mode)
    return color
Example #26
0
def _save(im, fp, filename):

    try:
        rawmode, prefix, photo, format, bits, extra = SAVE_INFO[im.mode]
    except KeyError:
        raise IOError("cannot write mode %s as TIFF" % im.mode)

    ifd = ImageFileDirectory(prefix)

    compression = im.encoderinfo.get('compression',im.info.get('compression','raw'))
    libtiff = compression in ["tiff_ccitt", "group3", "group4",
                              "tiff_jpeg", "tiff_adobe_deflate",
                              "tiff_thunderscan", "tiff_deflate",
                              "tiff_sgilog", "tiff_sgilog24",
                              "tiff_raw_16"]

    # -- multi-page -- skip TIFF header on subsequent pages
    if not libtiff and fp.tell() == 0:
        # tiff header (write via IFD to get everything right)
        # PIL always starts the first IFD at offset 8
        fp.write(ifd.prefix + ifd.o16(42) + ifd.o32(8))

    ifd[IMAGEWIDTH] = im.size[0]
    ifd[IMAGELENGTH] = im.size[1]

    # additions written by Greg Couch, [email protected]
    # inspired by image-sig posting from Kevin Cazabon, [email protected]
    if hasattr(im, 'tag'):
        # preserve tags from original TIFF image file
        for key in (RESOLUTION_UNIT, X_RESOLUTION, Y_RESOLUTION):
            if key in im.tag.tagdata:
                ifd[key] = im.tag.tagdata.get(key)
        # preserve some more tags from original TIFF image file
        # -- 2008-06-06 Florian Hoech
        ifd.tagtype = im.tag.tagtype
        for key in (IPTC_NAA_CHUNK, PHOTOSHOP_CHUNK, XMP):
            if key in im.tag:
                ifd[key] = im.tag[key]
        # preserve ICC profile (should also work when saving other formats
        # which support profiles as TIFF) -- 2008-06-06 Florian Hoech
        if "icc_profile" in im.info:
            ifd[ICCPROFILE] = im.info["icc_profile"]
    if "description" in im.encoderinfo:
        ifd[IMAGEDESCRIPTION] = im.encoderinfo["description"]
    if "resolution" in im.encoderinfo:
        ifd[X_RESOLUTION] = ifd[Y_RESOLUTION] \
                                = _cvt_res(im.encoderinfo["resolution"])
    if "x resolution" in im.encoderinfo:
        ifd[X_RESOLUTION] = _cvt_res(im.encoderinfo["x resolution"])
    if "y resolution" in im.encoderinfo:
        ifd[Y_RESOLUTION] = _cvt_res(im.encoderinfo["y resolution"])
    if "resolution unit" in im.encoderinfo:
        unit = im.encoderinfo["resolution unit"]
        if unit == "inch":
            ifd[RESOLUTION_UNIT] = 2
        elif unit == "cm" or unit == "centimeter":
            ifd[RESOLUTION_UNIT] = 3
        else:
            ifd[RESOLUTION_UNIT] = 1
    if "software" in im.encoderinfo:
        ifd[SOFTWARE] = im.encoderinfo["software"]
    if "date time" in im.encoderinfo:
        ifd[DATE_TIME] = im.encoderinfo["date time"]
    if "artist" in im.encoderinfo:
        ifd[ARTIST] = im.encoderinfo["artist"]
    if "copyright" in im.encoderinfo:
        ifd[COPYRIGHT] = im.encoderinfo["copyright"]

    dpi = im.encoderinfo.get("dpi")
    if dpi:
        ifd[RESOLUTION_UNIT] = 2
        ifd[X_RESOLUTION] = _cvt_res(dpi[0])
        ifd[Y_RESOLUTION] = _cvt_res(dpi[1])

    if bits != (1,):
        ifd[BITSPERSAMPLE] = bits
        if len(bits) != 1:
            ifd[SAMPLESPERPIXEL] = len(bits)
    if extra is not None:
        ifd[EXTRASAMPLES] = extra
    if format != 1:
        ifd[SAMPLEFORMAT] = format

    ifd[PHOTOMETRIC_INTERPRETATION] = photo

    if im.mode == "P":
        lut = im.im.getpalette("RGB", "RGB;L")
        ifd[COLORMAP] = tuple(i8(v) * 256 for v in lut)

    # data orientation
    stride = len(bits) * ((im.size[0]*bits[0]+7)//8)
    ifd[ROWSPERSTRIP] = im.size[1]
    ifd[STRIPBYTECOUNTS] = stride * im.size[1]
    ifd[STRIPOFFSETS] = 0 # this is adjusted by IFD writer
    ifd[COMPRESSION] = COMPRESSION_INFO_REV.get(compression,1) # no compression by default

    if libtiff:
        if Image.DEBUG:
            print ("Saving using libtiff encoder")
            print (ifd.items())
        _fp = 0
        if hasattr(fp, "fileno"):
            fp.seek(0)
            _fp = os.dup(fp.fileno())

        blocklist =  [STRIPOFFSETS, STRIPBYTECOUNTS, ROWSPERSTRIP, ICCPROFILE] # ICC Profile crashes.
        atts={}
        # Merge the ones that we have with (optional) more bits from
        # the original file, e.g x,y resolution so that we can
        # save(load('')) == original file.
        for k,v in itertools.chain(ifd.items(), getattr(im, 'ifd', {}).items()):
            if k not in atts and k not in blocklist:
                if type(v[0]) == tuple and len(v) > 1:
                    # A tuple of more than one rational tuples
                    # flatten to floats, following tiffcp.c->cpTag->TIFF_RATIONAL
                    atts[k] = [float(elt[0])/float(elt[1]) for elt in v]
                    continue
                if type(v[0]) == tuple and len(v) == 1:
                    # A tuple of one rational tuples
                    # flatten to floats, following tiffcp.c->cpTag->TIFF_RATIONAL
                    atts[k] = float(v[0][0])/float(v[0][1])
                    continue
                if type(v) == tuple and len(v) > 2:
                    # List of ints?
                    # BitsPerSample is one example, I get (8,8,8)
                    # UNDONE
                    continue
                if type(v) == tuple and len(v) == 2:
                    # one rational tuple
                    # flatten to float, following tiffcp.c->cpTag->TIFF_RATIONAL
                    atts[k] = float(v[0])/float(v[1])
                    continue
                if type(v) == tuple and len(v) == 1:
                    v = v[0]
                    # drop through
                if isStringType(v):
                    atts[k] = bytes(v.encode('ascii', 'replace')) + b"\0"
                    continue
                else:
                    # int or similar
                    atts[k] = v

        if Image.DEBUG:
            print (atts)

        # libtiff always returns the bytes in native order.
        # we're expecting image byte order. So, if the rawmode
        # contains I;16, we need to convert from native to image
        # byte order.
        if im.mode in ('I;16B', 'I;16'):
            rawmode = 'I;16N'

        a = (rawmode, compression, _fp, filename, atts)
        # print (im.mode, compression, a, im.encoderconfig)
        e = Image._getencoder(im.mode, compression, a, im.encoderconfig)
        e.setimage(im.im, (0,0)+im.size)
        while 1:
            l, s, d = e.encode(16*1024) # undone, change to self.decodermaxblock
            if not _fp:
                fp.write(d)
            if s:
                break
        if s < 0:
            raise IOError("encoder error %d when writing image file" % s)

    else:
        offset = ifd.save(fp)

        ImageFile._save(im, fp, [
            ("raw", (0,0)+im.size, offset, (rawmode, stride, 1))
            ])


    # -- helper for multi-page save --
    if "_debug_multipage" in im.encoderinfo:
        #just to access o32 and o16 (using correct byte order)
        im._debug_multipage = ifd
Example #27
0
def _color(color, mode):
    if isStringType(color):
        from PIL import ImageColor
        color = ImageColor.getcolor(color, mode)
    return color