home *** CD-ROM | disk | FTP | other *** search
/ Enter 2004 April / enter-2004-04.iso / files / EVE_1424_100181.exe / TiffImagePlugin.py < prev    next >
Encoding:
Python Source  |  2004-04-20  |  21.2 KB  |  677 lines

  1. #
  2. # The Python Imaging Library.
  3. # $Id: //modules/pil/PIL/TiffImagePlugin.py#5 $
  4. #
  5. # TIFF file handling
  6. #
  7. # TIFF is a flexible, if somewhat aged, image file format originally
  8. # defined by Aldus.  Although TIFF supports a wide variety of pixel
  9. # layouts and compression methods, the name doesn't really stand for
  10. # "thousands of incompatible file formats," it just feels that way.
  11. #
  12. # To read TIFF data from a stream, the stream must be seekable.  For
  13. # progressive decoding, make sure to use TIFF files where the tag
  14. # directory is placed first in the file.
  15. #
  16. # History:
  17. # 1995-09-01 fl   Created
  18. # 1996-05-04 fl   Handle JPEGTABLES tag
  19. # 1996-05-18 fl   Fixed COLORMAP support
  20. # 1997-01-05 fl   Fixed PREDICTOR support
  21. # 1997-08-27 fl   Added support for rational tags (from Perry Stoll)
  22. # 1998-01-10 fl   Fixed seek/tell (from Jan Blom)
  23. # 1998-07-15 fl   Use private names for internal variables
  24. # 1999-06-13 fl   Rewritten for PIL 1.0 (1.0)
  25. # 2000-10-11 fl   Additional fixes for Python 2.0 (1.1)
  26. # 2001-04-17 fl   Fixed rewind support (seek to frame 0) (1.2)
  27. # 2001-05-12 fl   Added write support for more tags (from Greg Couch) (1.3)
  28. # 2001-12-18 fl   Added workaround for broken Matrox library
  29. # 2002-01-18 fl   Don't mess up if photometric tag is missing (D. Alan Stewart)
  30. #
  31. # Copyright (c) 1997-2002 by Secret Labs AB
  32. # Copyright (c) 1995-1997 by Fredrik Lundh
  33. #
  34. # See the README file for information on usage and redistribution.
  35. #
  36.  
  37. __version__ = "1.3.2"
  38.  
  39. import Image, ImageFile
  40. import ImagePalette
  41.  
  42. import string
  43.  
  44. #
  45. # --------------------------------------------------------------------
  46. # Read TIFF files
  47.  
  48. def il16(c,o=0):
  49.     return ord(c[o]) + (ord(c[o+1])<<8)
  50. def il32(c,o=0):
  51.     return ord(c[o]) + (ord(c[o+1])<<8) + (ord(c[o+2])<<16) + (ord(c[o+3])<<24)
  52. def ol16(i):
  53.     return chr(i&255) + chr(i>>8&255)
  54. def ol32(i):
  55.     return chr(i&255) + chr(i>>8&255) + chr(i>>16&255) + chr(i>>24&255)
  56.  
  57. def ib16(c,o=0):
  58.     return ord(c[o+1]) + (ord(c[o])<<8)
  59. def ib32(c,o=0):
  60.     return ord(c[o+3]) + (ord(c[o+2])<<8) + (ord(c[o+1])<<16) + (ord(c[o])<<24)
  61.  
  62. # a few tag names, just to make the code below a bit more readable
  63. IMAGEWIDTH = 256
  64. IMAGELENGTH = 257
  65. BITSPERSAMPLE = 258
  66. COMPRESSION = 259
  67. PHOTOMETRIC_INTERPRETATION = 262
  68. IMAGEDESCRIPTION = 270
  69. STRIPOFFSETS = 273
  70. SAMPLESPERPIXEL = 277
  71. ROWSPERSTRIP = 278
  72. STRIPBYTECOUNTS = 279
  73. X_RESOLUTION = 282
  74. Y_RESOLUTION = 283
  75. PLANAR_CONFIGURATION = 284
  76. RESOLUTION_UNIT = 296
  77. SOFTWARE = 305
  78. DATE_TIME = 306
  79. ARTIST = 315
  80. PREDICTOR = 317
  81. COLORMAP = 320
  82. EXTRASAMPLES = 338
  83. SAMPLEFORMAT = 339
  84. JPEGTABLES = 347
  85. COPYRIGHT = 33432
  86. IPTC_NAA_CHUNK = 33723 # newsphoto properties
  87. PHOTOSHOP_CHUNK = 34377 # photoshop properties
  88.  
  89. COMPRESSION_INFO = {
  90.     # Compression => pil compression name
  91.     1: "raw",
  92.     2: "tiff_ccitt",
  93.     3: "group3",
  94.     4: "group4",
  95.     5: "tiff_lzw",
  96.     6: "tiff_jpeg", # obsolete
  97.     7: "jpeg",
  98.     32771: "tiff_raw_16", # 16-bit padding
  99.     32773: "packbits"
  100. }
  101.  
  102. OPEN_INFO = {
  103.     # Photometric Interpretation, SampleFormat, BitsPerSample, ExtraSamples =>
  104.     # mode, rawmode
  105.     (0, 1, (1,), ()): ("1", "1;I"),
  106.     (0, 1, (8,), ()): ("L", "L;I"),
  107.     (1, 1, (1,), ()): ("1", "1"),
  108.     (1, 1, (8,), ()): ("L", "L"),
  109.     (1, 2, (16,), ()): ("I;16", "I;16"),
  110.     (1, 2, (32,), ()): ("I", "I;32S"),
  111.     (1, 3, (32,), ()): ("F", "F;32F"),
  112.     (2, 1, (8,8,8), ()): ("RGB", "RGB"),
  113.     (2, 1, (8,8,8,8), (0,)): ("RGBX", "RGBX"),
  114.     (2, 1, (8,8,8,8), (2,)): ("RGBA", "RGBA"),
  115.     (3, 1, (1,), ()): ("P", "P;1"),
  116.     (3, 1, (2,), ()): ("P", "P;2"),
  117.     (3, 1, (4,), ()): ("P", "P;4"),
  118.     (3, 1, (8,), ()): ("P", "P"),
  119.     (5, 1, (8,8,8,8), ()): ("CMYK", "CMYK"),
  120.     (6, 1, (8,8,8), ()): ("YCbCr", "YCbCr"),
  121.     (8, 1, (8,8,8), ()): ("LAB", "LAB"),
  122. }
  123.  
  124. PREFIXES = ["MM\000\052", "II\052\000"]
  125.  
  126. def _accept(prefix):
  127.     return prefix[:4] in PREFIXES
  128.  
  129.  
  130. class ImageFileDirectory:
  131.  
  132.     # represents a TIFF tag directory.  to speed things up,
  133.     # we don't decode tags unless they're asked for.
  134.  
  135.     def __init__(self, prefix="II"):
  136.         assert prefix in ("MM", "II")
  137.         self.prefix = prefix
  138.         if prefix == "MM":
  139.             self.i16, self.i32 = ib16, ib32
  140.             # FIXME: save doesn't yet support big-endian mode...
  141.         else:
  142.             self.i16, self.i32 = il16, il32
  143.             self.o16, self.o32 = ol16, ol32
  144.         self.reset()
  145.  
  146.     def reset(self):
  147.         self.tags = {}
  148.         self.tagdata = {}
  149.         self.next = None
  150.  
  151.     # dictionary API (sort of)
  152.  
  153.     def __len__(self):
  154.         return max(len(self.tagdata), len(self.tags))
  155.  
  156.     def __getitem__(self, tag):
  157.         try:
  158.             return self.tags[tag]
  159.         except KeyError:
  160.             type, data = self.tagdata[tag] # unpack on the fly
  161.             size, handler = self.load_dispatch[type]
  162.             self.tags[tag] = data = handler(self, data)
  163.             del self.tagdata[tag]
  164.             return data
  165.  
  166.     def get(self, tag, default=None):
  167.         try:
  168.             return self[tag]
  169.         except KeyError:
  170.             return default
  171.  
  172.     def getscalar(self, tag, default=None):
  173.         try:
  174.             value = self[tag]
  175.             if len(value) != 1:
  176.                 if tag == SAMPLEFORMAT:
  177.                     # work around broken (?) matrox library
  178.                     # (from Ted Wright, via Bob Klimek)
  179.                     raise KeyError # use default
  180.                 raise ValueError, "not a scalar"
  181.             return value[0]
  182.         except KeyError:
  183.             if default is None:
  184.                 raise
  185.             return default
  186.  
  187.     def has_key(self, tag):
  188.         return self.tags.has_key(tag) or self.tagdata.has_key(tag)
  189.  
  190.     def __setitem__(self, tag, value):
  191.         if type(value) is not type(()):
  192.             value = (value,)
  193.         self.tags[tag] = value
  194.  
  195.     # load primitives
  196.  
  197.     load_dispatch = {}
  198.  
  199.     def load_byte(self, data):
  200.         l = []
  201.         for i in range(len(data)):
  202.             l.append(ord(data[i]))
  203.         return tuple(l)
  204.     load_dispatch[1] = (1, load_byte)
  205.  
  206.     def load_string(self, data):
  207.         if data[-1:] == '\0':
  208.             data = data[:-1]
  209.         return data
  210.     load_dispatch[2] = (1, load_string)
  211.  
  212.     def load_short(self, data):
  213.         l = []
  214.         for i in range(0, len(data), 2):
  215.             l.append(self.i16(data, i))
  216.         return tuple(l)
  217.     load_dispatch[3] = (2, load_short)
  218.  
  219.     def load_long(self, data):
  220.         l = []
  221.         for i in range(0, len(data), 4):
  222.             l.append(self.i32(data, i))
  223.         return tuple(l)
  224.     load_dispatch[4] = (4, load_long)
  225.  
  226.     def load_rational(self, data):
  227.         l = []
  228.         for i in range(0, len(data), 8):
  229.             l.append((self.i32(data, i), self.i32(data, i+4)))
  230.         return tuple(l)
  231.     load_dispatch[5] = (8, load_rational)
  232.  
  233.     def load_undefined(self, data):
  234.         # Untyped data
  235.         return data
  236.     load_dispatch[7] = (1, load_undefined)
  237.  
  238.     def load(self, fp):
  239.         # load tag dictionary
  240.  
  241.         self.reset()
  242.  
  243.         i16 = self.i16
  244.         i32 = self.i32
  245.  
  246.         for i in range(i16(fp.read(2))):
  247.  
  248.             ifd = fp.read(12)
  249.  
  250.             tag, typ = i16(ifd), i16(ifd, 2)
  251.  
  252.             if Image.DEBUG:
  253.                 import TiffTags
  254.                 tagname = TiffTags.TAGS.get(tag, "unknown")
  255.                 typname = TiffTags.TYPES.get(typ, "unknown")
  256.                 print "tag: %s (%d)" % (tagname, tag),
  257.                 print "- type: %s (%d)" % (typname, typ),
  258.  
  259.             try:
  260.                 dispatch = self.load_dispatch[typ]
  261.             except KeyError:
  262.                 if Image.DEBUG:
  263.                     print "- unsupported type", typ
  264.                 continue # ignore unsupported type
  265.  
  266.             size, handler = dispatch
  267.  
  268.             size = size * i32(ifd, 4)
  269.  
  270.             # Get and expand tag value
  271.             if size > 4:
  272.                 here = fp.tell()
  273.                 fp.seek(i32(ifd, 8))
  274.                 data = fp.read(size)
  275.                 fp.seek(here)
  276.             else:
  277.                 data = ifd[8:8+size]
  278.  
  279.             if len(data) != size:
  280.                 raise IOError, "not enough data"
  281.  
  282.             self.tagdata[tag] = typ, data
  283.  
  284.             if Image.DEBUG:
  285.                 if tag in (COLORMAP, IPTC_NAA_CHUNK, PHOTOSHOP_CHUNK):
  286.                     print "- value: <table: %d bytes>" % size
  287.                 else:
  288.                     print "- value:", self[tag]
  289.  
  290.         self.next = i32(fp.read(4))
  291.  
  292.     # save primitives
  293.  
  294.     def save(self, fp):
  295.  
  296.         o16 = self.o16
  297.         o32 = self.o32
  298.  
  299.         fp.write(o16(len(self.tags)))
  300.  
  301.         # always write in ascending tag order
  302.         tags = self.tags.items()
  303.         tags.sort()
  304.  
  305.         directory = []
  306.         append = directory.append
  307.  
  308.         offset = fp.tell() + len(self.tags) * 12 + 4
  309.  
  310.         stripoffsets = None
  311.  
  312.         # pass 1: convert tags to binary format
  313.         for tag, value in tags:
  314.  
  315.             if Image.DEBUG:
  316.                 import TiffTags
  317.                 tagname = TiffTags.TAGS.get(tag, "unknown")
  318.                 print "save: %s (%d)" % (tagname, tag),
  319.                 print "- value:", value
  320.  
  321.             if type(value[0]) is type(""):
  322.                 # string data
  323.                 typ = 2
  324.                 data = value = string.join(value, "\0") + "\0"
  325.  
  326.             else:
  327.                 # integer data
  328.                 if tag == STRIPOFFSETS:
  329.                     stripoffsets = len(directory)
  330.                     typ = 4 # to avoid catch-22
  331.                 elif tag in (X_RESOLUTION, Y_RESOLUTION):
  332.                     # identify rational data fields
  333.                     typ = 5
  334.                 else:
  335.                     typ = 3
  336.                     for v in value:
  337.                         if v >= 65536:
  338.                             typ = 4
  339.                 if typ == 3:
  340.                     data = string.join(map(o16, value), "")
  341.                 else:
  342.                     data = string.join(map(o32, value), "")
  343.  
  344.             # figure out if data fits into the directory
  345.             if len(data) == 4:
  346.                 append((tag, typ, len(value), data, ""))
  347.             elif len(data) < 4:
  348.                 append((tag, typ, len(value), data + (4-len(data))*"\0", ""))
  349.             else:
  350.                 count = len(value)
  351.                 if typ == 5:
  352.                     count = count / 2        # adjust for rational data field
  353.                 append((tag, typ, count, o32(offset), data))
  354.                 offset = offset + len(data)
  355.                 if offset & 1:
  356.                     offset = offset + 1 # word padding
  357.  
  358.         # update strip offset data to point beyond auxiliary data
  359.         if stripoffsets is not None:
  360.             tag, typ, count, value, data = directory[stripoffsets]
  361.             assert not data, "multistrip support not yet implemented"
  362.             value = o32(self.i32(value) + offset)
  363.             directory[stripoffsets] = tag, typ, count, value, data
  364.  
  365.         # pass 2: write directory to file
  366.         for tag, typ, count, value, data in directory:
  367.             if Image.DEBUG > 1:
  368.                 print tag, typ, count, repr(value), repr(data)
  369.             fp.write(o16(tag) + o16(typ) + o32(count) + value)
  370.         fp.write("\0\0\0\0") # end of directory
  371.  
  372.         # pass 3: write auxiliary data to file
  373.         for tag, typ, count, value, data in directory:
  374.             fp.write(data)
  375.             if len(data) & 1:
  376.                 fp.write("\0")
  377.  
  378.         return offset
  379.  
  380. class TiffImageFile(ImageFile.ImageFile):
  381.  
  382.     format = "TIFF"
  383.     format_description = "Adobe TIFF"
  384.  
  385.     def _open(self):
  386.         "Open the first image in a TIFF file"
  387.  
  388.         # Header
  389.         ifh = self.fp.read(8)
  390.  
  391.         if ifh[:4] not in PREFIXES:
  392.             raise SyntaxError, "not a TIFF file"
  393.  
  394.         # image file directory (tag dictionary)
  395.         self.tag = self.ifd = ImageFileDirectory(ifh[:2])
  396.  
  397.         # setup frame pointers
  398.         self.__first = self.__next = self.ifd.i32(ifh, 4)
  399.         self.__frame = -1
  400.         self.__fp = self.fp
  401.  
  402.         # and load the first frame
  403.         self._seek(0)
  404.  
  405.     def seek(self, frame):
  406.         "Select a given frame as current image"
  407.  
  408.         if frame < 0:
  409.             frame = 0
  410.         self._seek(frame)
  411.  
  412.     def tell(self):
  413.         "Return the current frame number"
  414.  
  415.         return self._tell()
  416.  
  417.     def _seek(self, frame):
  418.  
  419.         self.fp = self.__fp
  420.         if frame < self.__frame:
  421.             # rewind file
  422.             self.__frame = -1
  423.             self.__next = self.__first
  424.         while self.__frame < frame:
  425.             if not self.__next:
  426.                 raise EOFError, "no more images in TIFF file"
  427.             self.fp.seek(self.__next)
  428.             self.tag.load(self.fp)
  429.             self.__next = self.tag.next
  430.             self.__frame = self.__frame + 1
  431.         self._setup()
  432.  
  433.     def _tell(self):
  434.  
  435.         return self.__frame
  436.  
  437.     def _decoder(self, rawmode, layer):
  438.         "Setup decoder contexts"
  439.  
  440.         args = None
  441.         if rawmode == "RGB" and self._planar_configuration == 2:
  442.             rawmode = rawmode[layer]
  443.         if self._compression == "raw":
  444.             args = (rawmode, 0, 1)
  445.         if self._compression in ["packbits", "tiff_lzw", "jpeg"]:
  446.             args = rawmode
  447.             if self._compression == "jpeg" and self.tag.has_key(JPEGTABLES):
  448.                 # Hack to handle abbreviated JPEG headers
  449.                 self.tile_prefix = self.tag[JPEGTABLES]
  450.             elif self._compression == "tiff_lzw" and self.tag.has_key(317):
  451.                 # Section 14: Differencing Predictor
  452.                 self.decoderconfig = (self.tag[PREDICTOR][0],)
  453.  
  454.         return args
  455.  
  456.     def _setup(self):
  457.         "Setup this image object based on current tags"
  458.  
  459.         # extract relevant tags
  460.         self._compression = COMPRESSION_INFO[self.tag.getscalar(
  461.             COMPRESSION, 1
  462.             )]
  463.         self._planar_configuration = self.tag.getscalar(
  464.             PLANAR_CONFIGURATION, 1
  465.             )
  466.  
  467.         # photometric is a required tag, but not everyone is reading
  468.         # the specification
  469.         photo = self.tag.getscalar(PHOTOMETRIC_INTERPRETATION, 0)
  470.  
  471.         if Image.DEBUG:
  472.             print "*** Summary ***"
  473.             print "- compression:", self._compression
  474.             print "- photometric_interpretation:", photo
  475.             print "- planar_configuration:", self._planar_configuration
  476.  
  477.         # size
  478.         xsize = self.tag.getscalar(IMAGEWIDTH)
  479.         ysize = self.tag.getscalar(IMAGELENGTH)
  480.         self.size = xsize, ysize
  481.  
  482.         if Image.DEBUG:
  483.             print "- size:", self.size
  484.  
  485.         format = self.tag.getscalar(SAMPLEFORMAT, 1)
  486.  
  487.         # mode: check photometric interpretation and bits per pixel
  488.         key = (
  489.             photo, format,
  490.             self.tag.get(BITSPERSAMPLE, (1,)),
  491.             self.tag.get(EXTRASAMPLES, ())
  492.             )
  493.         if Image.DEBUG:
  494.             print "format key:", key
  495.         try:
  496.             self.mode, rawmode = OPEN_INFO[key]
  497.         except KeyError:
  498.             if Image.DEBUG:
  499.                 print "- unsupported format"
  500.             raise SyntaxError, "unknown pixel mode"
  501.  
  502.         if Image.DEBUG:
  503.             print "- raw mode:", rawmode
  504.             print "- pil mode:", self.mode
  505.  
  506.         self.info["compression"] = self._compression
  507.  
  508.         # build tile descriptors
  509.         x = y = l = 0
  510.         self.tile = []
  511.         if self.tag.has_key(STRIPOFFSETS):
  512.             # striped image
  513.             h = self.tag.getscalar(ROWSPERSTRIP, ysize)
  514.             w = self.size[0]
  515.             a = None
  516.             for o in self.tag[STRIPOFFSETS]:
  517.                 if not a:
  518.                     a = self._decoder(rawmode, l)
  519.                 self.tile.append(
  520.                     (self._compression,
  521.                     (0, min(y, ysize), w, min(y+h, ysize)),
  522.                     o, a))
  523.                 y = y + h
  524.                 if y >= self.size[1]:
  525.                     x = y = 0
  526.                     l = l + 1
  527.                     a = None
  528.         elif self.tag.has_key(324):
  529.             # tiled image
  530.             w = self.tag.getscalar(322)
  531.             h = self.tag.getscalar(323)
  532.             a = None
  533.             for o in self.tag[324]:
  534.                 if not a:
  535.                     a = self._decoder(rawmode, l)
  536.                 # FIXME: this doesn't work if the image size
  537.                 # is not a multiple of the tile size...
  538.                 self.tile.append(
  539.                     (self._compression,
  540.                     (x, y, x+w, y+h),
  541.                     o, a))
  542.                 x = x + w
  543.                 if x >= self.size[0]:
  544.                     x, y = 0, y + h
  545.                     if y >= self.size[1]:
  546.                         x = y = 0
  547.                         l = l + 1
  548.                         a = None
  549.         else:
  550.             if Image.DEBUG:
  551.                 print "- unsupported data organization"
  552.             raise SyntaxError, "unknown data organization"
  553.  
  554.         # fixup palette descriptor
  555.         if self.mode == "P":
  556.             palette = map(lambda a: chr(a / 256), self.tag[COLORMAP])
  557.             self.palette = ImagePalette.raw("RGB;L", string.join(palette, ""))
  558.  
  559. #
  560. # --------------------------------------------------------------------
  561. # Write TIFF files
  562.  
  563. # little endian is default
  564.  
  565. SAVE_INFO = {
  566.     # mode => rawmode, photometrics, sampleformat, bitspersample, extra
  567.     "1": ("1", 1, 1, (1,), None),
  568.     "L": ("L", 1, 1, (8,), None),
  569.     "P": ("P", 3, 1, (8,), None),
  570.     "I": ("I;32S", 1, 2, (32,), None),
  571.     "I;16": ("I;16", 1, 2, (16,), None),
  572.     "F": ("F;32F", 1, 3, (32,), None),
  573.     "RGB": ("RGB", 2, 1, (8,8,8), None),
  574.     "RGBX": ("RGBX", 2, 1, (8,8,8,8), 0),
  575.     "RGBA": ("RGBA", 2, 1, (8,8,8,8), 2),
  576.     "CMYK": ("CMYK", 5, 1, (8,8,8,8), None),
  577.     "YCbCr": ("YCbCr", 6, 1, (8,8,8), None),
  578.     "LAB": ("LAB", 8, 1, (8,8,8), None),
  579. }
  580.  
  581. def _cvt_res(value):
  582.     # convert value to TIFF rational number -- (numerator, denominator)
  583.     if type(value) in (type([]), type(())):
  584.         assert(len(value) % 2 == 0)
  585.         return value
  586.     if type(value) == type(1):
  587.         return (value, 1)
  588.     value = float(value)
  589.     return (int(value * 65536), 65536)
  590.  
  591. def _save(im, fp, filename):
  592.  
  593.     try:
  594.         rawmode, photo, format, bits, extra = SAVE_INFO[im.mode]
  595.     except KeyError:
  596.         raise IOError, "cannot write mode %s as TIFF" % im.mode
  597.  
  598.     ifd = ImageFileDirectory()
  599.  
  600.     # tiff header (write via IFD to get everything right)
  601.     fp.write(ifd.prefix + ifd.o16(42) + ifd.o32(8))
  602.  
  603.     ifd[IMAGEWIDTH] = im.size[0]
  604.     ifd[IMAGELENGTH] = im.size[1]
  605.  
  606.     # additions written by Greg Couch, gregc@cgl.ucsf.edu
  607.     # inspired by image-sig posting from Kevin Cazabon, kcazabon@home.com
  608.     if hasattr(im, 'tag'):
  609.         # preserve tags from original TIFF image file
  610.         for key in (RESOLUTION_UNIT, X_RESOLUTION, Y_RESOLUTION):
  611.             if im.tag.tagdata.has_key(key):
  612.                 ifd[key] = im.tag.tagdata.get(key)
  613.     if im.encoderinfo.has_key("description"):
  614.         ifd[IMAGEDESCRIPTION] = im.encoderinfo["description"]
  615.     if im.encoderinfo.has_key("resolution"):
  616.         ifd[X_RESOLUTION] = ifd[Y_RESOLUTION] \
  617.                                 = _cvt_res(im.encoderinfo["resolution"])
  618.     if im.encoderinfo.has_key("x resolution"):
  619.         ifd[X_RESOLUTION] = _cvt_res(im.encoderinfo["x resolution"])
  620.     if im.encoderinfo.has_key("y resolution"):
  621.         ifd[Y_RESOLUTION] = _cvt_res(im.encoderinfo["y resolution"])
  622.     if im.encoderinfo.has_key("resolution unit"):
  623.         unit = im.encoderinfo["resolution unit"]
  624.         if unit == "inch":
  625.             ifd[RESOLUTION_UNIT] = 2
  626.         elif unit == "cm" or unit == "centimeter":
  627.             ifd[RESOLUTION_UNIT] = 3
  628.         else:
  629.             ifd[RESOLUTION_UNIT] = 1
  630.     if im.encoderinfo.has_key("software"):
  631.         ifd[SOFTWARE] = im.encoderinfo["software"]
  632.     if im.encoderinfo.has_key("date time"):
  633.         ifd[DATE_TIME] = im.encoderinfo["date time"]
  634.     if im.encoderinfo.has_key("artist"):
  635.         ifd[ARTIST] = im.encoderinfo["artist"]
  636.     if im.encoderinfo.has_key("copyright"):
  637.         ifd[COPYRIGHT] = im.encoderinfo["copyright"]
  638.  
  639.     if bits != (1,):
  640.         ifd[BITSPERSAMPLE] = bits
  641.         if len(bits) != 1:
  642.             ifd[SAMPLESPERPIXEL] = len(bits)
  643.     if extra is not None:
  644.         ifd[EXTRASAMPLES] = extra
  645.     if format != 1:
  646.         ifd[SAMPLEFORMAT] = format
  647.  
  648.     ifd[PHOTOMETRIC_INTERPRETATION] = photo
  649.  
  650.     if im.mode == "P":
  651.         lut = im.im.getpalette("RGB", "RGB;L")
  652.         ifd[COLORMAP] = tuple(map(lambda v: ord(v) * 256, lut))
  653.  
  654.     # data orientation
  655.     stride = len(bits) * ((im.size[0]*bits[0]+7)/8)
  656.     ifd[ROWSPERSTRIP] = im.size[1]
  657.     ifd[STRIPBYTECOUNTS] = stride * im.size[1]
  658.     ifd[STRIPOFFSETS] = 0 # this is adjusted by IFD writer
  659.  
  660.     offset = ifd.save(fp)
  661.  
  662.     ImageFile._save(im, fp, [
  663.         ("raw", (0,0)+im.size, offset, (rawmode, stride, 1))
  664.         ])
  665.  
  666. #
  667. # --------------------------------------------------------------------
  668. # Register
  669.  
  670. Image.register_open("TIFF", TiffImageFile, _accept)
  671. Image.register_save("TIFF", _save)
  672.  
  673. Image.register_extension("TIFF", ".tif")
  674. Image.register_extension("TIFF", ".tiff")
  675.  
  676. Image.register_mime("TIFF", "image/tiff")
  677.