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