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.info.get("compression", "raw")
    libtiff = compression in ["tiff_ccitt", "group3", "group4", "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 im.tag.tagdata.has_key(key):
                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 im.tag.has_key(key):
                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 im.info.has_key("icc_profile"):
            ifd[ICCPROFILE] = im.info["icc_profile"]
    if im.encoderinfo.has_key("description"):
        ifd[IMAGEDESCRIPTION] = im.encoderinfo["description"]
    if im.encoderinfo.has_key("resolution"):
        ifd[X_RESOLUTION] = ifd[Y_RESOLUTION] = _cvt_res(im.encoderinfo["resolution"])
    if im.encoderinfo.has_key("x resolution"):
        ifd[X_RESOLUTION] = _cvt_res(im.encoderinfo["x resolution"])
    if im.encoderinfo.has_key("y resolution"):
        ifd[Y_RESOLUTION] = _cvt_res(im.encoderinfo["y resolution"])
    if im.encoderinfo.has_key("resolution unit"):
        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 im.encoderinfo.has_key("software"):
        ifd[SOFTWARE] = im.encoderinfo["software"]
    if im.encoderinfo.has_key("date time"):
        ifd[DATE_TIME] = im.encoderinfo["date time"]
    if im.encoderinfo.has_key("artist"):
        ifd[ARTIST] = im.encoderinfo["artist"]
    if im.encoderinfo.has_key("copyright"):
        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(map(lambda v: ord(v) * 256, 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 = dict([(k, v) for (k, (v,)) in ifd.items() if k not in blocklist])
        try:
            # pull in more bits from the original file, e.g x,y resolution
            # so that we can save(load('')) == original file.
            for k, v in 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) == 1:
                        # int or similar
                        atts[k] = v[0]
                        continue
                    if type(v) == str:
                        atts[k] = v
                        continue

        except:
            # if we don't have an ifd here, just punt.
            pass
        if Image.DEBUG:
            print atts
        a = (rawmode, compression, _fp, filename, atts)
        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 im.encoderinfo.has_key("_debug_multipage"):
        # just to access o32 and o16 (using correct byte order)
        im._debug_multipage = ifd
Example #2
0
def _save(im, fp, tile):
    "Helper to save image based on tile list"

    im.load()
    if not hasattr(im, "encoderconfig"):
        im.encoderconfig = ()
    tile.sort(_tilesort)
    # FIXME: make MAXBLOCK a configuration parameter
    bufsize = max(MAXBLOCK, im.size[0] * 4)  # see RawEncode.c
    try:
        raise AttributeError(
        )  # ugly! workaround to keep file operations from happening in the C code.
        # due to msvcrt / mingw errors with passing file handles around that I
        # do not care about dealing with, at all.
        # proper fix involves:
        # h=get_osfhandle(f.fileno())
        # On the Mingw side: fd=open_osfhandle(h); FILE *f=fdopen(fd)
        # See: http://mail.python.org/pipermail/python-list/2007-March/603414.html
        fh = fp.fileno()
        fp.flush()
    except AttributeError:
        # compress to Python file-compatible object
        for e, b, o, a in tile:
            e = Image._getencoder(im.mode, e, a, im.encoderconfig)
            if o > 0:
                fp.seek(o, 0)
            e.setimage(im.im, b)
            while 1:
                l, s, d = e.encode(bufsize)
                fp.write(d)
                if s:
                    break
            if s < 0:
                raise IOError("encoder error %d when writing image file" % s)
    else:
        # slight speedup: compress to real file object
        for e, b, o, a in tile:
            e = Image._getencoder(im.mode, e, a, im.encoderconfig)
            if o > 0:
                fp.seek(o, 0)
            e.setimage(im.im, b)
            s = e.encode_to_file(fh, bufsize)
            if s < 0:
                raise IOError("encoder error %d when writing image file" % s)
    try:
        fp.flush()
    except:
        pass
Example #3
0
def _save(im, fp, tile):
    "Helper to save image based on tile list"

    im.load()
    if not hasattr(im, "encoderconfig"):
        im.encoderconfig = ()
    tile.sort(_tilesort)
    # FIXME: make MAXBLOCK a configuration parameter
    bufsize = max(MAXBLOCK, im.size[0] * 4)  # see RawEncode.c
    try:
        fh = fp.fileno()
        fp.flush()
    except AttributeError:
        # compress to Python file-compatible object
        for e, b, o, a in tile:
            e = Image._getencoder(im.mode, e, a, im.encoderconfig)
            if o > 0:
                fp.seek(o, 0)
            e.setimage(im.im, b)
            while 1:
                l, s, d = e.encode(bufsize)
                fp.write(d)
                if s:
                    break
            if s < 0:
                raise IOError("encoder error %d when writing image file" % s)
    else:
        # slight speedup: compress to real file object
        for e, b, o, a in tile:
            e = Image._getencoder(im.mode, e, a, im.encoderconfig)
            if o > 0:
                fp.seek(o, 0)
            e.setimage(im.im, b)
            s = e.encode_to_file(fh, bufsize)
            if s < 0:
                raise IOError("encoder error %d when writing image file" % s)
    try:
        fp.flush()
    except:
        pass
Example #4
0
def _save(im, fp, tile):
    "Helper to save image based on tile list"

    im.load()
    if not hasattr(im, "encoderconfig"):
        im.encoderconfig = ()
    tile.sort(key=_tilesort)
    # FIXME: make MAXBLOCK a configuration parameter
    bufsize = max(MAXBLOCK, im.size[0] * 4) # see RawEncode.c
    try:
        fh = fp.fileno()
        fp.flush()
    except AttributeError:
        # compress to Python file-compatible object
        for e, b, o, a in tile:
            e = Image._getencoder(im.mode, e, a, im.encoderconfig)
            if o > 0:
                fp.seek(o, 0)
            e.setimage(im.im, b)
            while True:
                l, s, d = e.encode(bufsize)
                fp.write(d)
                if s:
                    break
            if s < 0:
                raise IOError("encoder error {0} when writing image file".format(s))
    else:
        # slight speedup: compress to real file object
        for e, b, o, a in tile:
            e = Image._getencoder(im.mode, e, a, im.encoderconfig)
            if o > 0:
                fp.seek(o, 0)
            e.setimage(im.im, b)
            s = e.encode_to_file(fh, bufsize)
            if s < 0:
                raise IOError("encoder error {0} when writing image file".format(s))
    try:
        fp.flush()
    except:
        pass