~ubuntu-branches/debian/wheezy/phatch/wheezy

« back to all changes in this revision

Viewing changes to phatch/other/TiffImagePlugin.py

  • Committer: Bazaar Package Importer
  • Author(s): Emilio Pozuelo Monfort
  • Date: 2008-02-13 23:48:47 UTC
  • Revision ID: james.westby@ubuntu.com-20080213234847-mp6vc4y88a9rz5qz
Tags: upstream-0.1
ImportĀ upstreamĀ versionĀ 0.1

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# Patched version to fix dpi problem, see the python image-sig mailing list:
 
2
# http://www.mail-archive.com/image-sig@python.org/msg01572.html
 
3
# Two patches: see line 580 & 732
 
4
 
 
5
# The Python Imaging Library (PIL) is
 
6
#
 
7
#    Copyright (c) 1997-2006 by Secret Labs AB
 
8
#    Copyright (c) 1995-2006 by Fredrik Lundh
 
9
#
 
10
# By obtaining, using, and/or copying this software and/or its associated 
 
11
# documentation, you agree that you have read, understood, and will comply with
 
12
# the following terms and conditions:
 
13
 
14
# Permission to use, copy, modify, and distribute this software and its 
 
15
# associated documentation for any purpose and without fee is hereby granted, 
 
16
# provided that the above copyright notice appears in all copies, and that both
 
17
# that copyright notice and this permission notice appear in supporting 
 
18
# documentation, and that the name of Secret Labs AB or the author not be used
 
19
# in advertising or publicity pertaining to distribution of the software
 
20
# without specific, written prior permission.
 
21
 
22
# SECRET LABS AB AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS 
 
23
# SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. 
 
24
# IN NO EVENT SHALL SECRET LABS AB OR THE AUTHOR BE LIABLE FOR ANY SPECIAL, 
 
25
# INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 
 
26
# LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE 
 
27
# OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 
 
28
# PERFORMANCE OF THIS SOFTWARE.
 
29
 
30
# http://www.pythonware.com/products/pil/license.htm
 
31
 
 
32
# The Python Imaging Library.
 
33
# $Id: TiffImagePlugin.py 2803 2006-07-31 19:18:57Z fredrik $
 
34
#
 
35
# TIFF file handling
 
36
#
 
37
# TIFF is a flexible, if somewhat aged, image file format originally
 
38
# defined by Aldus.  Although TIFF supports a wide variety of pixel
 
39
# layouts and compression methods, the name doesn't really stand for
 
40
# "thousands of incompatible file formats," it just feels that way.
 
41
#
 
42
# To read TIFF data from a stream, the stream must be seekable.  For
 
43
# progressive decoding, make sure to use TIFF files where the tag
 
44
# directory is placed first in the file.
 
45
#
 
46
# History:
 
47
# 1995-09-01 fl   Created
 
48
# 1996-05-04 fl   Handle JPEGTABLES tag
 
49
# 1996-05-18 fl   Fixed COLORMAP support
 
50
# 1997-01-05 fl   Fixed PREDICTOR support
 
51
# 1997-08-27 fl   Added support for rational tags (from Perry Stoll)
 
52
# 1998-01-10 fl   Fixed seek/tell (from Jan Blom)
 
53
# 1998-07-15 fl   Use private names for internal variables
 
54
# 1999-06-13 fl   Rewritten for PIL 1.0 (1.0)
 
55
# 2000-10-11 fl   Additional fixes for Python 2.0 (1.1)
 
56
# 2001-04-17 fl   Fixed rewind support (seek to frame 0) (1.2)
 
57
# 2001-05-12 fl   Added write support for more tags (from Greg Couch) (1.3)
 
58
# 2001-12-18 fl   Added workaround for broken Matrox library
 
59
# 2002-01-18 fl   Don't mess up if photometric tag is missing (D. Alan Stewart)
 
60
# 2003-05-19 fl   Check FILLORDER tag
 
61
# 2003-09-26 fl   Added RGBa support
 
62
# 2004-02-24 fl   Added DPI support; fixed rational write support
 
63
# 2005-02-07 fl   Added workaround for broken Corel Draw 10 files
 
64
# 2006-01-09 fl   Added support for float/double tags (from Russell Nelson)
 
65
#
 
66
# Copyright (c) 1997-2006 by Secret Labs AB.  All rights reserved.
 
67
# Copyright (c) 1995-1997 by Fredrik Lundh
 
68
#
 
69
# See the README file for information on usage and redistribution.
 
70
#
 
71
 
 
72
__version__ = "1.3.5"
 
73
 
 
74
import Image, ImageFile
 
75
import ImagePalette
 
76
 
 
77
import array, string, sys
 
78
 
 
79
try:
 
80
    if sys.byteorder == "little":
 
81
        byteorder = "II"
 
82
    else:
 
83
        byteorder = "MM"
 
84
except AttributeError:
 
85
    if ord(array.array("i",[1]).tostring()[0]):
 
86
        byteorder = "II"
 
87
    else:
 
88
        byteorder = "MM"
 
89
 
 
90
#
 
91
# --------------------------------------------------------------------
 
92
# Read TIFF files
 
93
 
 
94
def il16(c,o=0):
 
95
    return ord(c[o]) + (ord(c[o+1])<<8)
 
96
def il32(c,o=0):
 
97
    return ord(c[o]) + (ord(c[o+1])<<8) + (ord(c[o+2])<<16) + (ord(c[o+3])<<24)
 
98
def ol16(i):
 
99
    return chr(i&255) + chr(i>>8&255)
 
100
def ol32(i):
 
101
    return chr(i&255) + chr(i>>8&255) + chr(i>>16&255) + chr(i>>24&255)
 
102
 
 
103
def ib16(c,o=0):
 
104
    return ord(c[o+1]) + (ord(c[o])<<8)
 
105
def ib32(c,o=0):
 
106
    return ord(c[o+3]) + (ord(c[o+2])<<8) + (ord(c[o+1])<<16) + (ord(c[o])<<24)
 
107
 
 
108
# a few tag names, just to make the code below a bit more readable
 
109
IMAGEWIDTH = 256
 
110
IMAGELENGTH = 257
 
111
BITSPERSAMPLE = 258
 
112
COMPRESSION = 259
 
113
PHOTOMETRIC_INTERPRETATION = 262
 
114
FILLORDER = 266
 
115
IMAGEDESCRIPTION = 270
 
116
STRIPOFFSETS = 273
 
117
SAMPLESPERPIXEL = 277
 
118
ROWSPERSTRIP = 278
 
119
STRIPBYTECOUNTS = 279
 
120
X_RESOLUTION = 282
 
121
Y_RESOLUTION = 283
 
122
PLANAR_CONFIGURATION = 284
 
123
RESOLUTION_UNIT = 296
 
124
SOFTWARE = 305
 
125
DATE_TIME = 306
 
126
ARTIST = 315
 
127
PREDICTOR = 317
 
128
COLORMAP = 320
 
129
EXTRASAMPLES = 338
 
130
SAMPLEFORMAT = 339
 
131
JPEGTABLES = 347
 
132
COPYRIGHT = 33432
 
133
IPTC_NAA_CHUNK = 33723 # newsphoto properties
 
134
PHOTOSHOP_CHUNK = 34377 # photoshop properties
 
135
 
 
136
COMPRESSION_INFO = {
 
137
    # Compression => pil compression name
 
138
    1: "raw",
 
139
    2: "tiff_ccitt",
 
140
    3: "group3",
 
141
    4: "group4",
 
142
    5: "tiff_lzw",
 
143
    6: "tiff_jpeg", # obsolete
 
144
    7: "jpeg",
 
145
    32771: "tiff_raw_16", # 16-bit padding
 
146
    32773: "packbits"
 
147
}
 
148
 
 
149
OPEN_INFO = {
 
150
    # (PhotoInterpretation, SampleFormat, FillOrder, BitsPerSample,
 
151
    #  ExtraSamples) => mode, rawmode
 
152
    (0, 1, 1, (1,), ()): ("1", "1;I"),
 
153
    (0, 1, 2, (1,), ()): ("1", "1;IR"),
 
154
    (0, 1, 1, (8,), ()): ("L", "L;I"),
 
155
    (0, 1, 2, (8,), ()): ("L", "L;IR"),
 
156
    (1, 1, 1, (1,), ()): ("1", "1"),
 
157
    (1, 1, 2, (1,), ()): ("1", "1;R"),
 
158
    (1, 1, 1, (8,), ()): ("L", "L"),
 
159
    (1, 1, 1, (8,8), (2,)): ("LA", "LA"),
 
160
    (1, 1, 2, (8,), ()): ("L", "L;R"),
 
161
    (1, 1, 1, (16,), ()): ("I;16", "I;16"),
 
162
    (1, 2, 1, (16,), ()): ("I;16S", "I;16S"),
 
163
    (1, 2, 1, (32,), ()): ("I", "I;32S"),
 
164
    (1, 3, 1, (32,), ()): ("F", "F;32F"),
 
165
    (2, 1, 1, (8,8,8), ()): ("RGB", "RGB"),
 
166
    (2, 1, 2, (8,8,8), ()): ("RGB", "RGB;R"),
 
167
    (2, 1, 1, (8,8,8,8), (0,)): ("RGBX", "RGBX"),
 
168
    (2, 1, 1, (8,8,8,8), (1,)): ("RGBA", "RGBa"),
 
169
    (2, 1, 1, (8,8,8,8), (2,)): ("RGBA", "RGBA"),
 
170
    (2, 1, 1, (8,8,8,8), (999,)): ("RGBA", "RGBA"), # corel draw 10
 
171
    (3, 1, 1, (1,), ()): ("P", "P;1"),
 
172
    (3, 1, 2, (1,), ()): ("P", "P;1R"),
 
173
    (3, 1, 1, (2,), ()): ("P", "P;2"),
 
174
    (3, 1, 2, (2,), ()): ("P", "P;2R"),
 
175
    (3, 1, 1, (4,), ()): ("P", "P;4"),
 
176
    (3, 1, 2, (4,), ()): ("P", "P;4R"),
 
177
    (3, 1, 1, (8,), ()): ("P", "P"),
 
178
    (3, 1, 1, (8,8), (2,)): ("PA", "PA"),
 
179
    (3, 1, 2, (8,), ()): ("P", "P;R"),
 
180
    (5, 1, 1, (8,8,8,8), ()): ("CMYK", "CMYK"),
 
181
    (6, 1, 1, (8,8,8), ()): ("YCbCr", "YCbCr"),
 
182
    (8, 1, 1, (8,8,8), ()): ("LAB", "LAB"),
 
183
}
 
184
 
 
185
PREFIXES = ["MM\000\052", "II\052\000", "II\xBC\000"]
 
186
 
 
187
def _accept(prefix):
 
188
    return prefix[:4] in PREFIXES
 
189
 
 
190
##
 
191
# Wrapper for TIFF IFDs.
 
192
 
 
193
class ImageFileDirectory:
 
194
 
 
195
    # represents a TIFF tag directory.  to speed things up,
 
196
    # we don't decode tags unless they're asked for.
 
197
 
 
198
    def __init__(self, prefix="II"):
 
199
        self.prefix = prefix[:2]
 
200
        if self.prefix == "MM":
 
201
            self.i16, self.i32 = ib16, ib32
 
202
            # FIXME: save doesn't yet support big-endian mode...
 
203
        elif self.prefix == "II":
 
204
            self.i16, self.i32 = il16, il32
 
205
            self.o16, self.o32 = ol16, ol32
 
206
        else:
 
207
            raise SyntaxError("not a TIFF IFD")
 
208
        self.reset()
 
209
 
 
210
    def reset(self):
 
211
        self.tags = {}
 
212
        self.tagdata = {}
 
213
        self.next = None
 
214
 
 
215
    # dictionary API (sort of)
 
216
 
 
217
    def keys(self):
 
218
        return self.tagdata.keys() + self.tags.keys()
 
219
 
 
220
    def items(self):
 
221
        items = self.tags.items()
 
222
        for tag in self.tagdata.keys():
 
223
            items.append((tag, self[tag]))
 
224
        return items
 
225
 
 
226
    def __len__(self):
 
227
        return len(self.tagdata) + len(self.tags)
 
228
 
 
229
    def __getitem__(self, tag):
 
230
        try:
 
231
            return self.tags[tag]
 
232
        except KeyError:
 
233
            type, data = self.tagdata[tag] # unpack on the fly
 
234
            size, handler = self.load_dispatch[type]
 
235
            self.tags[tag] = data = handler(self, data)
 
236
            del self.tagdata[tag]
 
237
            return data
 
238
 
 
239
    def get(self, tag, default=None):
 
240
        try:
 
241
            return self[tag]
 
242
        except KeyError:
 
243
            return default
 
244
 
 
245
    def getscalar(self, tag, default=None):
 
246
        try:
 
247
            value = self[tag]
 
248
            if len(value) != 1:
 
249
                if tag == SAMPLEFORMAT:
 
250
                    # work around broken (?) matrox library
 
251
                    # (from Ted Wright, via Bob Klimek)
 
252
                    raise KeyError # use default
 
253
                raise ValueError, "not a scalar"
 
254
            return value[0]
 
255
        except KeyError:
 
256
            if default is None:
 
257
                raise
 
258
            return default
 
259
 
 
260
    def has_key(self, tag):
 
261
        return self.tags.has_key(tag) or self.tagdata.has_key(tag)
 
262
 
 
263
    def __setitem__(self, tag, value):
 
264
        if type(value) is not type(()):
 
265
            value = (value,)
 
266
        self.tags[tag] = value
 
267
 
 
268
    # load primitives
 
269
 
 
270
    load_dispatch = {}
 
271
 
 
272
    def load_byte(self, data):
 
273
        l = []
 
274
        for i in range(len(data)):
 
275
            l.append(ord(data[i]))
 
276
        return tuple(l)
 
277
    load_dispatch[1] = (1, load_byte)
 
278
 
 
279
    def load_string(self, data):
 
280
        if data[-1:] == '\0':
 
281
            data = data[:-1]
 
282
        return data
 
283
    load_dispatch[2] = (1, load_string)
 
284
 
 
285
    def load_short(self, data):
 
286
        l = []
 
287
        for i in range(0, len(data), 2):
 
288
            l.append(self.i16(data, i))
 
289
        return tuple(l)
 
290
    load_dispatch[3] = (2, load_short)
 
291
 
 
292
    def load_long(self, data):
 
293
        l = []
 
294
        for i in range(0, len(data), 4):
 
295
            l.append(self.i32(data, i))
 
296
        return tuple(l)
 
297
    load_dispatch[4] = (4, load_long)
 
298
 
 
299
    def load_rational(self, data):
 
300
        l = []
 
301
        for i in range(0, len(data), 8):
 
302
            l.append((self.i32(data, i), self.i32(data, i+4)))
 
303
        return tuple(l)
 
304
    load_dispatch[5] = (8, load_rational)
 
305
 
 
306
    def load_float(self, data):
 
307
        a = array.array("f", data)
 
308
        if self.prefix != byteorder:
 
309
            a.byteswap()
 
310
        return tuple(a)
 
311
    load_dispatch[11] = (4, load_float)
 
312
 
 
313
    def load_double(self, data):
 
314
        a = array.array("d", data)
 
315
        if self.prefix != byteorder:
 
316
            a.byteswap()
 
317
        return tuple(a)
 
318
    load_dispatch[12] = (8, load_double)
 
319
 
 
320
    def load_undefined(self, data):
 
321
        # Untyped data
 
322
        return data
 
323
    load_dispatch[7] = (1, load_undefined)
 
324
 
 
325
    def load(self, fp):
 
326
        # load tag dictionary
 
327
 
 
328
        self.reset()
 
329
 
 
330
        i16 = self.i16
 
331
        i32 = self.i32
 
332
 
 
333
        for i in range(i16(fp.read(2))):
 
334
 
 
335
            ifd = fp.read(12)
 
336
 
 
337
            tag, typ = i16(ifd), i16(ifd, 2)
 
338
 
 
339
            if Image.DEBUG:
 
340
                import TiffTags
 
341
                tagname = TiffTags.TAGS.get(tag, "unknown")
 
342
                typname = TiffTags.TYPES.get(typ, "unknown")
 
343
                print "tag: %s (%d)" % (tagname, tag),
 
344
                print "- type: %s (%d)" % (typname, typ),
 
345
 
 
346
            try:
 
347
                dispatch = self.load_dispatch[typ]
 
348
            except KeyError:
 
349
                if Image.DEBUG:
 
350
                    print "- unsupported type", typ
 
351
                continue # ignore unsupported type
 
352
 
 
353
            size, handler = dispatch
 
354
 
 
355
            size = size * i32(ifd, 4)
 
356
 
 
357
            # Get and expand tag value
 
358
            if size > 4:
 
359
                here = fp.tell()
 
360
                fp.seek(i32(ifd, 8))
 
361
                data = ImageFile._safe_read(fp, size)
 
362
                fp.seek(here)
 
363
            else:
 
364
                data = ifd[8:8+size]
 
365
 
 
366
            if len(data) != size:
 
367
                raise IOError, "not enough data"
 
368
 
 
369
            self.tagdata[tag] = typ, data
 
370
 
 
371
            if Image.DEBUG:
 
372
                if tag in (COLORMAP, IPTC_NAA_CHUNK, PHOTOSHOP_CHUNK):
 
373
                    print "- value: <table: %d bytes>" % size
 
374
                else:
 
375
                    print "- value:", self[tag]
 
376
 
 
377
        self.next = i32(fp.read(4))
 
378
 
 
379
    # save primitives
 
380
 
 
381
    def save(self, fp):
 
382
 
 
383
        o16 = self.o16
 
384
        o32 = self.o32
 
385
 
 
386
        fp.write(o16(len(self.tags)))
 
387
 
 
388
        # always write in ascending tag order
 
389
        tags = self.tags.items()
 
390
        tags.sort()
 
391
 
 
392
        directory = []
 
393
        append = directory.append
 
394
 
 
395
        offset = fp.tell() + len(self.tags) * 12 + 4
 
396
 
 
397
        stripoffsets = None
 
398
 
 
399
        # pass 1: convert tags to binary format
 
400
        for tag, value in tags:
 
401
 
 
402
            if Image.DEBUG:
 
403
                import TiffTags
 
404
                tagname = TiffTags.TAGS.get(tag, "unknown")
 
405
                print "save: %s (%d)" % (tagname, tag),
 
406
                print "- value:", value
 
407
 
 
408
            if type(value[0]) is type(""):
 
409
                # string data
 
410
                typ = 2
 
411
                data = value = string.join(value, "\0") + "\0"
 
412
 
 
413
            else:
 
414
                # integer data
 
415
                if tag == STRIPOFFSETS:
 
416
                    stripoffsets = len(directory)
 
417
                    typ = 4 # to avoid catch-22
 
418
                elif tag in (X_RESOLUTION, Y_RESOLUTION):
 
419
                    # identify rational data fields
 
420
                    typ = 5
 
421
                else:
 
422
                    typ = 3
 
423
                    for v in value:
 
424
                        if v >= 65536:
 
425
                            typ = 4
 
426
                if typ == 3:
 
427
                    data = string.join(map(o16, value), "")
 
428
                else:
 
429
                    data = string.join(map(o32, value), "")
 
430
 
 
431
            # figure out if data fits into the directory
 
432
            if len(data) == 4:
 
433
                append((tag, typ, len(value), data, ""))
 
434
            elif len(data) < 4:
 
435
                append((tag, typ, len(value), data + (4-len(data))*"\0", ""))
 
436
            else:
 
437
                count = len(value)
 
438
                if typ == 5:
 
439
                    count = count / 2        # adjust for rational data field
 
440
                append((tag, typ, count, o32(offset), data))
 
441
                offset = offset + len(data)
 
442
                if offset & 1:
 
443
                    offset = offset + 1 # word padding
 
444
 
 
445
        # update strip offset data to point beyond auxiliary data
 
446
        if stripoffsets is not None:
 
447
            tag, typ, count, value, data = directory[stripoffsets]
 
448
            assert not data, "multistrip support not yet implemented"
 
449
            value = o32(self.i32(value) + offset)
 
450
            directory[stripoffsets] = tag, typ, count, value, data
 
451
 
 
452
        # pass 2: write directory to file
 
453
        for tag, typ, count, value, data in directory:
 
454
            if Image.DEBUG > 1:
 
455
                print tag, typ, count, repr(value), repr(data)
 
456
            fp.write(o16(tag) + o16(typ) + o32(count) + value)
 
457
        fp.write("\0\0\0\0") # end of directory
 
458
 
 
459
        # pass 3: write auxiliary data to file
 
460
        for tag, typ, count, value, data in directory:
 
461
            fp.write(data)
 
462
            if len(data) & 1:
 
463
                fp.write("\0")
 
464
 
 
465
        return offset
 
466
 
 
467
##
 
468
# Image plugin for TIFF files.
 
469
 
 
470
class TiffImageFile(ImageFile.ImageFile):
 
471
 
 
472
    format = "TIFF"
 
473
    format_description = "Adobe TIFF"
 
474
 
 
475
    def _open(self):
 
476
        "Open the first image in a TIFF file"
 
477
 
 
478
        # Header
 
479
        ifh = self.fp.read(8)
 
480
 
 
481
        if ifh[:4] not in PREFIXES:
 
482
            raise SyntaxError, "not a TIFF file"
 
483
 
 
484
        # image file directory (tag dictionary)
 
485
        self.tag = self.ifd = ImageFileDirectory(ifh[:2])
 
486
 
 
487
        # setup frame pointers
 
488
        self.__first = self.__next = self.ifd.i32(ifh, 4)
 
489
        self.__frame = -1
 
490
        self.__fp = self.fp
 
491
 
 
492
        # and load the first frame
 
493
        self._seek(0)
 
494
 
 
495
    def seek(self, frame):
 
496
        "Select a given frame as current image"
 
497
 
 
498
        if frame < 0:
 
499
            frame = 0
 
500
        self._seek(frame)
 
501
 
 
502
    def tell(self):
 
503
        "Return the current frame number"
 
504
 
 
505
        return self._tell()
 
506
 
 
507
    def _seek(self, frame):
 
508
 
 
509
        self.fp = self.__fp
 
510
        if frame < self.__frame:
 
511
            # rewind file
 
512
            self.__frame = -1
 
513
            self.__next = self.__first
 
514
        while self.__frame < frame:
 
515
            if not self.__next:
 
516
                raise EOFError, "no more images in TIFF file"
 
517
            self.fp.seek(self.__next)
 
518
            self.tag.load(self.fp)
 
519
            self.__next = self.tag.next
 
520
            self.__frame = self.__frame + 1
 
521
        self._setup()
 
522
 
 
523
    def _tell(self):
 
524
 
 
525
        return self.__frame
 
526
 
 
527
    def _decoder(self, rawmode, layer):
 
528
        "Setup decoder contexts"
 
529
 
 
530
        args = None
 
531
        if rawmode == "RGB" and self._planar_configuration == 2:
 
532
            rawmode = rawmode[layer]
 
533
        compression = self._compression
 
534
        if compression == "raw":
 
535
            args = (rawmode, 0, 1)
 
536
        elif compression == "jpeg":
 
537
            args = rawmode, ""
 
538
            if self.tag.has_key(JPEGTABLES):
 
539
                # Hack to handle abbreviated JPEG headers
 
540
                self.tile_prefix = self.tag[JPEGTABLES]
 
541
        elif compression == "packbits":
 
542
            args = rawmode
 
543
        elif compression == "tiff_lzw":
 
544
            args = rawmode
 
545
            if self.tag.has_key(317):
 
546
                # Section 14: Differencing Predictor
 
547
                self.decoderconfig = (self.tag[PREDICTOR][0],)
 
548
 
 
549
        return args
 
550
 
 
551
    def _setup(self):
 
552
        "Setup this image object based on current tags"
 
553
 
 
554
        if self.tag.has_key(0xBC01):
 
555
            raise IOError, "Windows Media Photo files not yet supported"
 
556
 
 
557
        getscalar = self.tag.getscalar
 
558
 
 
559
        # extract relevant tags
 
560
        self._compression = COMPRESSION_INFO[getscalar(COMPRESSION, 1)]
 
561
        self._planar_configuration = getscalar(PLANAR_CONFIGURATION, 1)
 
562
 
 
563
        # photometric is a required tag, but not everyone is reading
 
564
        # the specification
 
565
        photo = getscalar(PHOTOMETRIC_INTERPRETATION, 0)
 
566
 
 
567
        fillorder = getscalar(FILLORDER, 1)
 
568
 
 
569
        if Image.DEBUG:
 
570
            print "*** Summary ***"
 
571
            print "- compression:", self._compression
 
572
            print "- photometric_interpretation:", photo
 
573
            print "- planar_configuration:", self._planar_configuration
 
574
            print "- fill_order:", fillorder
 
575
 
 
576
        # size
 
577
        xsize = getscalar(IMAGEWIDTH)
 
578
        ysize = getscalar(IMAGELENGTH)
 
579
        self.size = xsize, ysize
 
580
 
 
581
        if Image.DEBUG:
 
582
            print "- size:", self.size
 
583
 
 
584
        format = getscalar(SAMPLEFORMAT, 1)
 
585
 
 
586
        # mode: check photometric interpretation and bits per pixel
 
587
        key = (
 
588
            photo, format, fillorder,
 
589
            self.tag.get(BITSPERSAMPLE, (1,)),
 
590
            self.tag.get(EXTRASAMPLES, ())
 
591
            )
 
592
        if Image.DEBUG:
 
593
            print "format key:", key
 
594
        try:
 
595
            self.mode, rawmode = OPEN_INFO[key]
 
596
        except KeyError:
 
597
            if Image.DEBUG:
 
598
                print "- unsupported format"
 
599
            raise SyntaxError, "unknown pixel mode"
 
600
 
 
601
        if Image.DEBUG:
 
602
            print "- raw mode:", rawmode
 
603
            print "- pil mode:", self.mode
 
604
 
 
605
        self.info["compression"] = self._compression
 
606
 
 
607
        # BEGIN PATCH
 
608
        xres = getscalar(X_RESOLUTION, (1, 1))
 
609
        yres = getscalar(Y_RESOLUTION, (1, 1))
 
610
 
 
611
        if xres and yres:
 
612
            xres = xres[0] / (xres[1] or 1)
 
613
            yres = yres[0] / (yres[1] or 1)
 
614
            resunit = getscalar(RESOLUTION_UNIT, 1)
 
615
            if resunit == 2: # Inches
 
616
                self.info["dpi"] = xres, yres
 
617
            elif resunit == 3: # Centimeters
 
618
                self.info["dpi"] = xres * 2.54, yres * 2.54
 
619
            else: # No absolute unit of measurement.
 
620
                self.info["resolution"] = xres, yres
 
621
        # END PATCH
 
622
 
 
623
        # build tile descriptors
 
624
        x = y = l = 0
 
625
        self.tile = []
 
626
        if self.tag.has_key(STRIPOFFSETS):
 
627
            # striped image
 
628
            h = getscalar(ROWSPERSTRIP, ysize)
 
629
            w = self.size[0]
 
630
            a = None
 
631
            for o in self.tag[STRIPOFFSETS]:
 
632
                if not a:
 
633
                    a = self._decoder(rawmode, l)
 
634
                self.tile.append(
 
635
                    (self._compression,
 
636
                    (0, min(y, ysize), w, min(y+h, ysize)),
 
637
                    o, a))
 
638
                y = y + h
 
639
                if y >= self.size[1]:
 
640
                    x = y = 0
 
641
                    l = l + 1
 
642
                    a = None
 
643
        elif self.tag.has_key(324):
 
644
            # tiled image
 
645
            w = getscalar(322)
 
646
            h = getscalar(323)
 
647
            a = None
 
648
            for o in self.tag[324]:
 
649
                if not a:
 
650
                    a = self._decoder(rawmode, l)
 
651
                # FIXME: this doesn't work if the image size
 
652
                # is not a multiple of the tile size...
 
653
                self.tile.append(
 
654
                    (self._compression,
 
655
                    (x, y, x+w, y+h),
 
656
                    o, a))
 
657
                x = x + w
 
658
                if x >= self.size[0]:
 
659
                    x, y = 0, y + h
 
660
                    if y >= self.size[1]:
 
661
                        x = y = 0
 
662
                        l = l + 1
 
663
                        a = None
 
664
        else:
 
665
            if Image.DEBUG:
 
666
                print "- unsupported data organization"
 
667
            raise SyntaxError("unknown data organization")
 
668
 
 
669
        # fixup palette descriptor
 
670
        if self.mode == "P":
 
671
            palette = map(lambda a: chr(a / 256), self.tag[COLORMAP])
 
672
            self.palette = ImagePalette.raw("RGB;L", string.join(palette, ""))
 
673
 
 
674
#
 
675
# --------------------------------------------------------------------
 
676
# Write TIFF files
 
677
 
 
678
# little endian is default
 
679
 
 
680
SAVE_INFO = {
 
681
    # mode => rawmode, photometrics, sampleformat, bitspersample, extra
 
682
    "1": ("1", 1, 1, (1,), None),
 
683
    "L": ("L", 1, 1, (8,), None),
 
684
    "LA": ("LA", 1, 1, (8,8), 2),
 
685
    "P": ("P", 3, 1, (8,), None),
 
686
    "PA": ("PA", 3, 1, (8,8), 2),
 
687
    "I": ("I;32S", 1, 2, (32,), None),
 
688
    "I;16": ("I;16", 1, 1, (16,), None),
 
689
    "I;16S": ("I;16S", 1, 2, (16,), None),
 
690
    "F": ("F;32F", 1, 3, (32,), None),
 
691
    "RGB": ("RGB", 2, 1, (8,8,8), None),
 
692
    "RGBX": ("RGBX", 2, 1, (8,8,8,8), 0),
 
693
    "RGBA": ("RGBA", 2, 1, (8,8,8,8), 2),
 
694
    "CMYK": ("CMYK", 5, 1, (8,8,8,8), None),
 
695
    "YCbCr": ("YCbCr", 6, 1, (8,8,8), None),
 
696
    "LAB": ("LAB", 8, 1, (8,8,8), None),
 
697
}
 
698
 
 
699
def _cvt_res(value):
 
700
    # convert value to TIFF rational number -- (numerator, denominator)
 
701
    if type(value) in (type([]), type(())):
 
702
        assert(len(value) % 2 == 0)
 
703
        return value
 
704
    if type(value) == type(1):
 
705
        return (value, 1)
 
706
    value = float(value)
 
707
    return (int(value * 65536), 65536)
 
708
 
 
709
def _save(im, fp, filename):
 
710
 
 
711
    try:
 
712
        rawmode, photo, format, bits, extra = SAVE_INFO[im.mode]
 
713
    except KeyError:
 
714
        raise IOError, "cannot write mode %s as TIFF" % im.mode
 
715
 
 
716
    ifd = ImageFileDirectory()
 
717
 
 
718
    # tiff header (write via IFD to get everything right)
 
719
    fp.write(ifd.prefix + ifd.o16(42) + ifd.o32(8))
 
720
 
 
721
    ifd[IMAGEWIDTH] = im.size[0]
 
722
    ifd[IMAGELENGTH] = im.size[1]
 
723
 
 
724
    # additions written by Greg Couch, gregc@cgl.ucsf.edu
 
725
    # inspired by image-sig posting from Kevin Cazabon, kcazabon@home.com
 
726
    if hasattr(im, 'tag'):
 
727
        # preserve tags from original TIFF image file
 
728
        for key in (RESOLUTION_UNIT, X_RESOLUTION, Y_RESOLUTION):
 
729
            if im.tag.tagdata.has_key(key):
 
730
                ifd[key] = im.tag.tagdata.get(key)
 
731
    if im.encoderinfo.has_key("description"):
 
732
        ifd[IMAGEDESCRIPTION] = im.encoderinfo["description"]
 
733
    if im.encoderinfo.has_key("resolution"):
 
734
        ifd[X_RESOLUTION] = ifd[Y_RESOLUTION] \
 
735
                                = _cvt_res(im.encoderinfo["resolution"])
 
736
    if im.encoderinfo.has_key("x resolution"):
 
737
        ifd[X_RESOLUTION] = _cvt_res(im.encoderinfo["x resolution"])
 
738
    if im.encoderinfo.has_key("y resolution"):
 
739
        ifd[Y_RESOLUTION] = _cvt_res(im.encoderinfo["y resolution"])
 
740
    if im.encoderinfo.has_key("resolution unit"):
 
741
        unit = im.encoderinfo["resolution unit"]
 
742
        if unit == "inch":
 
743
            ifd[RESOLUTION_UNIT] = 2
 
744
        elif unit == "cm" or unit == "centimeter":
 
745
            ifd[RESOLUTION_UNIT] = 3
 
746
        else:
 
747
            ifd[RESOLUTION_UNIT] = 1
 
748
    if im.encoderinfo.has_key("software"):
 
749
        ifd[SOFTWARE] = im.encoderinfo["software"]
 
750
    if im.encoderinfo.has_key("date time"):
 
751
        ifd[DATE_TIME] = im.encoderinfo["date time"]
 
752
    if im.encoderinfo.has_key("artist"):
 
753
        ifd[ARTIST] = im.encoderinfo["artist"]
 
754
    if im.encoderinfo.has_key("copyright"):
 
755
        ifd[COPYRIGHT] = im.encoderinfo["copyright"]
 
756
 
 
757
    dpi = im.encoderinfo.get("dpi")
 
758
    if dpi:
 
759
        #BEGIN PATCH
 
760
        ifd[RESOLUTION_UNIT] = 2
 
761
        #END PATCH
 
762
        ifd[X_RESOLUTION] = _cvt_res(dpi[0])
 
763
        ifd[Y_RESOLUTION] = _cvt_res(dpi[1])
 
764
 
 
765
    if bits != (1,):
 
766
        ifd[BITSPERSAMPLE] = bits
 
767
        if len(bits) != 1:
 
768
            ifd[SAMPLESPERPIXEL] = len(bits)
 
769
    if extra is not None:
 
770
        ifd[EXTRASAMPLES] = extra
 
771
    if format != 1:
 
772
        ifd[SAMPLEFORMAT] = format
 
773
 
 
774
    ifd[PHOTOMETRIC_INTERPRETATION] = photo
 
775
 
 
776
    if im.mode == "P":
 
777
        lut = im.im.getpalette("RGB", "RGB;L")
 
778
        ifd[COLORMAP] = tuple(map(lambda v: ord(v) * 256, lut))
 
779
 
 
780
    # data orientation
 
781
    stride = len(bits) * ((im.size[0]*bits[0]+7)/8)
 
782
    ifd[ROWSPERSTRIP] = im.size[1]
 
783
    ifd[STRIPBYTECOUNTS] = stride * im.size[1]
 
784
    ifd[STRIPOFFSETS] = 0 # this is adjusted by IFD writer
 
785
    ifd[COMPRESSION] = 1 # no compression
 
786
 
 
787
    offset = ifd.save(fp)
 
788
 
 
789
    ImageFile._save(im, fp, [
 
790
        ("raw", (0,0)+im.size, offset, (rawmode, stride, 1))
 
791
        ])
 
792
 
 
793
#
 
794
# --------------------------------------------------------------------
 
795
# Register
 
796
 
 
797
Image.register_open("TIFF", TiffImageFile, _accept)
 
798
Image.register_save("TIFF", _save)
 
799
 
 
800
Image.register_extension("TIFF", ".tif")
 
801
Image.register_extension("TIFF", ".tiff")
 
802
 
 
803
Image.register_mime("TIFF", "image/tiff")