Coverage for /var/srv/projects/api.amasfac.comuna18.com/tmp/venv/lib/python3.9/site-packages/PIL/TiffImagePlugin.py: 18%
1073 statements
« prev ^ index » next coverage.py v6.4.4, created at 2023-07-17 14:22 -0600
« prev ^ index » next coverage.py v6.4.4, created at 2023-07-17 14:22 -0600
1#
2# The Python Imaging Library.
3# $Id$
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# 2003-05-19 fl Check FILLORDER tag
31# 2003-09-26 fl Added RGBa support
32# 2004-02-24 fl Added DPI support; fixed rational write support
33# 2005-02-07 fl Added workaround for broken Corel Draw 10 files
34# 2006-01-09 fl Added support for float/double tags (from Russell Nelson)
35#
36# Copyright (c) 1997-2006 by Secret Labs AB. All rights reserved.
37# Copyright (c) 1995-1997 by Fredrik Lundh
38#
39# See the README file for information on usage and redistribution.
40#
41import io
42import itertools
43import logging
44import math
45import os
46import struct
47import warnings
48from collections.abc import MutableMapping
49from fractions import Fraction
50from numbers import Number, Rational
52from . import Image, ImageFile, ImageOps, ImagePalette, TiffTags
53from ._binary import i16be as i16
54from ._binary import i32be as i32
55from ._binary import o8
56from .TiffTags import TYPES
58logger = logging.getLogger(__name__)
60# Set these to true to force use of libtiff for reading or writing.
61READ_LIBTIFF = False
62WRITE_LIBTIFF = False
63IFD_LEGACY_API = True
64STRIP_SIZE = 65536
66II = b"II" # little-endian (Intel style)
67MM = b"MM" # big-endian (Motorola style)
69#
70# --------------------------------------------------------------------
71# Read TIFF files
73# a few tag names, just to make the code below a bit more readable
74IMAGEWIDTH = 256
75IMAGELENGTH = 257
76BITSPERSAMPLE = 258
77COMPRESSION = 259
78PHOTOMETRIC_INTERPRETATION = 262
79FILLORDER = 266
80IMAGEDESCRIPTION = 270
81STRIPOFFSETS = 273
82SAMPLESPERPIXEL = 277
83ROWSPERSTRIP = 278
84STRIPBYTECOUNTS = 279
85X_RESOLUTION = 282
86Y_RESOLUTION = 283
87PLANAR_CONFIGURATION = 284
88RESOLUTION_UNIT = 296
89TRANSFERFUNCTION = 301
90SOFTWARE = 305
91DATE_TIME = 306
92ARTIST = 315
93PREDICTOR = 317
94COLORMAP = 320
95TILEWIDTH = 322
96TILELENGTH = 323
97TILEOFFSETS = 324
98TILEBYTECOUNTS = 325
99SUBIFD = 330
100EXTRASAMPLES = 338
101SAMPLEFORMAT = 339
102JPEGTABLES = 347
103YCBCRSUBSAMPLING = 530
104REFERENCEBLACKWHITE = 532
105COPYRIGHT = 33432
106IPTC_NAA_CHUNK = 33723 # newsphoto properties
107PHOTOSHOP_CHUNK = 34377 # photoshop properties
108ICCPROFILE = 34675
109EXIFIFD = 34665
110XMP = 700
111JPEGQUALITY = 65537 # pseudo-tag by libtiff
113# https://github.com/imagej/ImageJA/blob/master/src/main/java/ij/io/TiffDecoder.java
114IMAGEJ_META_DATA_BYTE_COUNTS = 50838
115IMAGEJ_META_DATA = 50839
117COMPRESSION_INFO = {
118 # Compression => pil compression name
119 1: "raw",
120 2: "tiff_ccitt",
121 3: "group3",
122 4: "group4",
123 5: "tiff_lzw",
124 6: "tiff_jpeg", # obsolete
125 7: "jpeg",
126 8: "tiff_adobe_deflate",
127 32771: "tiff_raw_16", # 16-bit padding
128 32773: "packbits",
129 32809: "tiff_thunderscan",
130 32946: "tiff_deflate",
131 34676: "tiff_sgilog",
132 34677: "tiff_sgilog24",
133 34925: "lzma",
134 50000: "zstd",
135 50001: "webp",
136}
138COMPRESSION_INFO_REV = {v: k for k, v in COMPRESSION_INFO.items()}
140OPEN_INFO = {
141 # (ByteOrder, PhotoInterpretation, SampleFormat, FillOrder, BitsPerSample,
142 # ExtraSamples) => mode, rawmode
143 (II, 0, (1,), 1, (1,), ()): ("1", "1;I"),
144 (MM, 0, (1,), 1, (1,), ()): ("1", "1;I"),
145 (II, 0, (1,), 2, (1,), ()): ("1", "1;IR"),
146 (MM, 0, (1,), 2, (1,), ()): ("1", "1;IR"),
147 (II, 1, (1,), 1, (1,), ()): ("1", "1"),
148 (MM, 1, (1,), 1, (1,), ()): ("1", "1"),
149 (II, 1, (1,), 2, (1,), ()): ("1", "1;R"),
150 (MM, 1, (1,), 2, (1,), ()): ("1", "1;R"),
151 (II, 0, (1,), 1, (2,), ()): ("L", "L;2I"),
152 (MM, 0, (1,), 1, (2,), ()): ("L", "L;2I"),
153 (II, 0, (1,), 2, (2,), ()): ("L", "L;2IR"),
154 (MM, 0, (1,), 2, (2,), ()): ("L", "L;2IR"),
155 (II, 1, (1,), 1, (2,), ()): ("L", "L;2"),
156 (MM, 1, (1,), 1, (2,), ()): ("L", "L;2"),
157 (II, 1, (1,), 2, (2,), ()): ("L", "L;2R"),
158 (MM, 1, (1,), 2, (2,), ()): ("L", "L;2R"),
159 (II, 0, (1,), 1, (4,), ()): ("L", "L;4I"),
160 (MM, 0, (1,), 1, (4,), ()): ("L", "L;4I"),
161 (II, 0, (1,), 2, (4,), ()): ("L", "L;4IR"),
162 (MM, 0, (1,), 2, (4,), ()): ("L", "L;4IR"),
163 (II, 1, (1,), 1, (4,), ()): ("L", "L;4"),
164 (MM, 1, (1,), 1, (4,), ()): ("L", "L;4"),
165 (II, 1, (1,), 2, (4,), ()): ("L", "L;4R"),
166 (MM, 1, (1,), 2, (4,), ()): ("L", "L;4R"),
167 (II, 0, (1,), 1, (8,), ()): ("L", "L;I"),
168 (MM, 0, (1,), 1, (8,), ()): ("L", "L;I"),
169 (II, 0, (1,), 2, (8,), ()): ("L", "L;IR"),
170 (MM, 0, (1,), 2, (8,), ()): ("L", "L;IR"),
171 (II, 1, (1,), 1, (8,), ()): ("L", "L"),
172 (MM, 1, (1,), 1, (8,), ()): ("L", "L"),
173 (II, 1, (1,), 2, (8,), ()): ("L", "L;R"),
174 (MM, 1, (1,), 2, (8,), ()): ("L", "L;R"),
175 (II, 1, (1,), 1, (12,), ()): ("I;16", "I;12"),
176 (II, 1, (1,), 1, (16,), ()): ("I;16", "I;16"),
177 (MM, 1, (1,), 1, (16,), ()): ("I;16B", "I;16B"),
178 (II, 1, (1,), 2, (16,), ()): ("I;16", "I;16R"),
179 (II, 1, (2,), 1, (16,), ()): ("I", "I;16S"),
180 (MM, 1, (2,), 1, (16,), ()): ("I", "I;16BS"),
181 (II, 0, (3,), 1, (32,), ()): ("F", "F;32F"),
182 (MM, 0, (3,), 1, (32,), ()): ("F", "F;32BF"),
183 (II, 1, (1,), 1, (32,), ()): ("I", "I;32N"),
184 (II, 1, (2,), 1, (32,), ()): ("I", "I;32S"),
185 (MM, 1, (2,), 1, (32,), ()): ("I", "I;32BS"),
186 (II, 1, (3,), 1, (32,), ()): ("F", "F;32F"),
187 (MM, 1, (3,), 1, (32,), ()): ("F", "F;32BF"),
188 (II, 1, (1,), 1, (8, 8), (2,)): ("LA", "LA"),
189 (MM, 1, (1,), 1, (8, 8), (2,)): ("LA", "LA"),
190 (II, 2, (1,), 1, (8, 8, 8), ()): ("RGB", "RGB"),
191 (MM, 2, (1,), 1, (8, 8, 8), ()): ("RGB", "RGB"),
192 (II, 2, (1,), 2, (8, 8, 8), ()): ("RGB", "RGB;R"),
193 (MM, 2, (1,), 2, (8, 8, 8), ()): ("RGB", "RGB;R"),
194 (II, 2, (1,), 1, (8, 8, 8, 8), ()): ("RGBA", "RGBA"), # missing ExtraSamples
195 (MM, 2, (1,), 1, (8, 8, 8, 8), ()): ("RGBA", "RGBA"), # missing ExtraSamples
196 (II, 2, (1,), 1, (8, 8, 8, 8), (0,)): ("RGBX", "RGBX"),
197 (MM, 2, (1,), 1, (8, 8, 8, 8), (0,)): ("RGBX", "RGBX"),
198 (II, 2, (1,), 1, (8, 8, 8, 8, 8), (0, 0)): ("RGBX", "RGBXX"),
199 (MM, 2, (1,), 1, (8, 8, 8, 8, 8), (0, 0)): ("RGBX", "RGBXX"),
200 (II, 2, (1,), 1, (8, 8, 8, 8, 8, 8), (0, 0, 0)): ("RGBX", "RGBXXX"),
201 (MM, 2, (1,), 1, (8, 8, 8, 8, 8, 8), (0, 0, 0)): ("RGBX", "RGBXXX"),
202 (II, 2, (1,), 1, (8, 8, 8, 8), (1,)): ("RGBA", "RGBa"),
203 (MM, 2, (1,), 1, (8, 8, 8, 8), (1,)): ("RGBA", "RGBa"),
204 (II, 2, (1,), 1, (8, 8, 8, 8, 8), (1, 0)): ("RGBA", "RGBaX"),
205 (MM, 2, (1,), 1, (8, 8, 8, 8, 8), (1, 0)): ("RGBA", "RGBaX"),
206 (II, 2, (1,), 1, (8, 8, 8, 8, 8, 8), (1, 0, 0)): ("RGBA", "RGBaXX"),
207 (MM, 2, (1,), 1, (8, 8, 8, 8, 8, 8), (1, 0, 0)): ("RGBA", "RGBaXX"),
208 (II, 2, (1,), 1, (8, 8, 8, 8), (2,)): ("RGBA", "RGBA"),
209 (MM, 2, (1,), 1, (8, 8, 8, 8), (2,)): ("RGBA", "RGBA"),
210 (II, 2, (1,), 1, (8, 8, 8, 8, 8), (2, 0)): ("RGBA", "RGBAX"),
211 (MM, 2, (1,), 1, (8, 8, 8, 8, 8), (2, 0)): ("RGBA", "RGBAX"),
212 (II, 2, (1,), 1, (8, 8, 8, 8, 8, 8), (2, 0, 0)): ("RGBA", "RGBAXX"),
213 (MM, 2, (1,), 1, (8, 8, 8, 8, 8, 8), (2, 0, 0)): ("RGBA", "RGBAXX"),
214 (II, 2, (1,), 1, (8, 8, 8, 8), (999,)): ("RGBA", "RGBA"), # Corel Draw 10
215 (MM, 2, (1,), 1, (8, 8, 8, 8), (999,)): ("RGBA", "RGBA"), # Corel Draw 10
216 (II, 2, (1,), 1, (16, 16, 16), ()): ("RGB", "RGB;16L"),
217 (MM, 2, (1,), 1, (16, 16, 16), ()): ("RGB", "RGB;16B"),
218 (II, 2, (1,), 1, (16, 16, 16, 16), ()): ("RGBA", "RGBA;16L"),
219 (MM, 2, (1,), 1, (16, 16, 16, 16), ()): ("RGBA", "RGBA;16B"),
220 (II, 2, (1,), 1, (16, 16, 16, 16), (0,)): ("RGBX", "RGBX;16L"),
221 (MM, 2, (1,), 1, (16, 16, 16, 16), (0,)): ("RGBX", "RGBX;16B"),
222 (II, 2, (1,), 1, (16, 16, 16, 16), (1,)): ("RGBA", "RGBa;16L"),
223 (MM, 2, (1,), 1, (16, 16, 16, 16), (1,)): ("RGBA", "RGBa;16B"),
224 (II, 2, (1,), 1, (16, 16, 16, 16), (2,)): ("RGBA", "RGBA;16L"),
225 (MM, 2, (1,), 1, (16, 16, 16, 16), (2,)): ("RGBA", "RGBA;16B"),
226 (II, 3, (1,), 1, (1,), ()): ("P", "P;1"),
227 (MM, 3, (1,), 1, (1,), ()): ("P", "P;1"),
228 (II, 3, (1,), 2, (1,), ()): ("P", "P;1R"),
229 (MM, 3, (1,), 2, (1,), ()): ("P", "P;1R"),
230 (II, 3, (1,), 1, (2,), ()): ("P", "P;2"),
231 (MM, 3, (1,), 1, (2,), ()): ("P", "P;2"),
232 (II, 3, (1,), 2, (2,), ()): ("P", "P;2R"),
233 (MM, 3, (1,), 2, (2,), ()): ("P", "P;2R"),
234 (II, 3, (1,), 1, (4,), ()): ("P", "P;4"),
235 (MM, 3, (1,), 1, (4,), ()): ("P", "P;4"),
236 (II, 3, (1,), 2, (4,), ()): ("P", "P;4R"),
237 (MM, 3, (1,), 2, (4,), ()): ("P", "P;4R"),
238 (II, 3, (1,), 1, (8,), ()): ("P", "P"),
239 (MM, 3, (1,), 1, (8,), ()): ("P", "P"),
240 (II, 3, (1,), 1, (8, 8), (2,)): ("PA", "PA"),
241 (MM, 3, (1,), 1, (8, 8), (2,)): ("PA", "PA"),
242 (II, 3, (1,), 2, (8,), ()): ("P", "P;R"),
243 (MM, 3, (1,), 2, (8,), ()): ("P", "P;R"),
244 (II, 5, (1,), 1, (8, 8, 8, 8), ()): ("CMYK", "CMYK"),
245 (MM, 5, (1,), 1, (8, 8, 8, 8), ()): ("CMYK", "CMYK"),
246 (II, 5, (1,), 1, (8, 8, 8, 8, 8), (0,)): ("CMYK", "CMYKX"),
247 (MM, 5, (1,), 1, (8, 8, 8, 8, 8), (0,)): ("CMYK", "CMYKX"),
248 (II, 5, (1,), 1, (8, 8, 8, 8, 8, 8), (0, 0)): ("CMYK", "CMYKXX"),
249 (MM, 5, (1,), 1, (8, 8, 8, 8, 8, 8), (0, 0)): ("CMYK", "CMYKXX"),
250 (II, 5, (1,), 1, (16, 16, 16, 16), ()): ("CMYK", "CMYK;16L"),
251 # JPEG compressed images handled by LibTiff and auto-converted to RGBX
252 # Minimal Baseline TIFF requires YCbCr images to have 3 SamplesPerPixel
253 (II, 6, (1,), 1, (8, 8, 8), ()): ("RGB", "RGBX"),
254 (MM, 6, (1,), 1, (8, 8, 8), ()): ("RGB", "RGBX"),
255 (II, 8, (1,), 1, (8, 8, 8), ()): ("LAB", "LAB"),
256 (MM, 8, (1,), 1, (8, 8, 8), ()): ("LAB", "LAB"),
257}
259PREFIXES = [
260 b"MM\x00\x2A", # Valid TIFF header with big-endian byte order
261 b"II\x2A\x00", # Valid TIFF header with little-endian byte order
262 b"MM\x2A\x00", # Invalid TIFF header, assume big-endian
263 b"II\x00\x2A", # Invalid TIFF header, assume little-endian
264 b"MM\x00\x2B", # BigTIFF with big-endian byte order
265 b"II\x2B\x00", # BigTIFF with little-endian byte order
266]
269def _accept(prefix):
270 return prefix[:4] in PREFIXES
273def _limit_rational(val, max_val):
274 inv = abs(val) > 1
275 n_d = IFDRational(1 / val if inv else val).limit_rational(max_val)
276 return n_d[::-1] if inv else n_d
279def _limit_signed_rational(val, max_val, min_val):
280 frac = Fraction(val)
281 n_d = frac.numerator, frac.denominator
283 if min(n_d) < min_val:
284 n_d = _limit_rational(val, abs(min_val))
286 if max(n_d) > max_val:
287 val = Fraction(*n_d)
288 n_d = _limit_rational(val, max_val)
290 return n_d
293##
294# Wrapper for TIFF IFDs.
296_load_dispatch = {}
297_write_dispatch = {}
300class IFDRational(Rational):
301 """Implements a rational class where 0/0 is a legal value to match
302 the in the wild use of exif rationals.
304 e.g., DigitalZoomRatio - 0.00/0.00 indicates that no digital zoom was used
305 """
307 """ If the denominator is 0, store this as a float('nan'), otherwise store
308 as a fractions.Fraction(). Delegate as appropriate
310 """
312 __slots__ = ("_numerator", "_denominator", "_val")
314 def __init__(self, value, denominator=1):
315 """
316 :param value: either an integer numerator, a
317 float/rational/other number, or an IFDRational
318 :param denominator: Optional integer denominator
319 """
320 if isinstance(value, IFDRational):
321 self._numerator = value.numerator
322 self._denominator = value.denominator
323 self._val = value._val
324 return
326 if isinstance(value, Fraction):
327 self._numerator = value.numerator
328 self._denominator = value.denominator
329 else:
330 self._numerator = value
331 self._denominator = denominator
333 if denominator == 0:
334 self._val = float("nan")
335 elif denominator == 1:
336 self._val = Fraction(value)
337 else:
338 self._val = Fraction(value, denominator)
340 @property
341 def numerator(self):
342 return self._numerator
344 @property
345 def denominator(self):
346 return self._denominator
348 def limit_rational(self, max_denominator):
349 """
351 :param max_denominator: Integer, the maximum denominator value
352 :returns: Tuple of (numerator, denominator)
353 """
355 if self.denominator == 0:
356 return self.numerator, self.denominator
358 f = self._val.limit_denominator(max_denominator)
359 return f.numerator, f.denominator
361 def __repr__(self):
362 return str(float(self._val))
364 def __hash__(self):
365 return self._val.__hash__()
367 def __eq__(self, other):
368 val = self._val
369 if isinstance(other, IFDRational):
370 other = other._val
371 if isinstance(other, float):
372 val = float(val)
373 return val == other
375 def __getstate__(self):
376 return [self._val, self._numerator, self._denominator]
378 def __setstate__(self, state):
379 IFDRational.__init__(self, 0)
380 _val, _numerator, _denominator = state
381 self._val = _val
382 self._numerator = _numerator
383 self._denominator = _denominator
385 def _delegate(op):
386 def delegate(self, *args):
387 return getattr(self._val, op)(*args)
389 return delegate
391 """ a = ['add','radd', 'sub', 'rsub', 'mul', 'rmul',
392 'truediv', 'rtruediv', 'floordiv', 'rfloordiv',
393 'mod','rmod', 'pow','rpow', 'pos', 'neg',
394 'abs', 'trunc', 'lt', 'gt', 'le', 'ge', 'bool',
395 'ceil', 'floor', 'round']
396 print("\n".join("__%s__ = _delegate('__%s__')" % (s,s) for s in a))
397 """
399 __add__ = _delegate("__add__")
400 __radd__ = _delegate("__radd__")
401 __sub__ = _delegate("__sub__")
402 __rsub__ = _delegate("__rsub__")
403 __mul__ = _delegate("__mul__")
404 __rmul__ = _delegate("__rmul__")
405 __truediv__ = _delegate("__truediv__")
406 __rtruediv__ = _delegate("__rtruediv__")
407 __floordiv__ = _delegate("__floordiv__")
408 __rfloordiv__ = _delegate("__rfloordiv__")
409 __mod__ = _delegate("__mod__")
410 __rmod__ = _delegate("__rmod__")
411 __pow__ = _delegate("__pow__")
412 __rpow__ = _delegate("__rpow__")
413 __pos__ = _delegate("__pos__")
414 __neg__ = _delegate("__neg__")
415 __abs__ = _delegate("__abs__")
416 __trunc__ = _delegate("__trunc__")
417 __lt__ = _delegate("__lt__")
418 __gt__ = _delegate("__gt__")
419 __le__ = _delegate("__le__")
420 __ge__ = _delegate("__ge__")
421 __bool__ = _delegate("__bool__")
422 __ceil__ = _delegate("__ceil__")
423 __floor__ = _delegate("__floor__")
424 __round__ = _delegate("__round__")
427class ImageFileDirectory_v2(MutableMapping):
428 """This class represents a TIFF tag directory. To speed things up, we
429 don't decode tags unless they're asked for.
431 Exposes a dictionary interface of the tags in the directory::
433 ifd = ImageFileDirectory_v2()
434 ifd[key] = 'Some Data'
435 ifd.tagtype[key] = TiffTags.ASCII
436 print(ifd[key])
437 'Some Data'
439 Individual values are returned as the strings or numbers, sequences are
440 returned as tuples of the values.
442 The tiff metadata type of each item is stored in a dictionary of
443 tag types in
444 :attr:`~PIL.TiffImagePlugin.ImageFileDirectory_v2.tagtype`. The types
445 are read from a tiff file, guessed from the type added, or added
446 manually.
448 Data Structures:
450 * ``self.tagtype = {}``
452 * Key: numerical TIFF tag number
453 * Value: integer corresponding to the data type from
454 :py:data:`.TiffTags.TYPES`
456 .. versionadded:: 3.0.0
458 'Internal' data structures:
460 * ``self._tags_v2 = {}``
462 * Key: numerical TIFF tag number
463 * Value: decoded data, as tuple for multiple values
465 * ``self._tagdata = {}``
467 * Key: numerical TIFF tag number
468 * Value: undecoded byte string from file
470 * ``self._tags_v1 = {}``
472 * Key: numerical TIFF tag number
473 * Value: decoded data in the v1 format
475 Tags will be found in the private attributes ``self._tagdata``, and in
476 ``self._tags_v2`` once decoded.
478 ``self.legacy_api`` is a value for internal use, and shouldn't be changed
479 from outside code. In cooperation with
480 :py:class:`~PIL.TiffImagePlugin.ImageFileDirectory_v1`, if ``legacy_api``
481 is true, then decoded tags will be populated into both ``_tags_v1`` and
482 ``_tags_v2``. ``_tags_v2`` will be used if this IFD is used in the TIFF
483 save routine. Tags should be read from ``_tags_v1`` if
484 ``legacy_api == true``.
486 """
488 def __init__(self, ifh=b"II\052\0\0\0\0\0", prefix=None, group=None):
489 """Initialize an ImageFileDirectory.
491 To construct an ImageFileDirectory from a real file, pass the 8-byte
492 magic header to the constructor. To only set the endianness, pass it
493 as the 'prefix' keyword argument.
495 :param ifh: One of the accepted magic headers (cf. PREFIXES); also sets
496 endianness.
497 :param prefix: Override the endianness of the file.
498 """
499 if not _accept(ifh):
500 raise SyntaxError(f"not a TIFF file (header {repr(ifh)} not valid)")
501 self._prefix = prefix if prefix is not None else ifh[:2]
502 if self._prefix == MM:
503 self._endian = ">"
504 elif self._prefix == II:
505 self._endian = "<"
506 else:
507 raise SyntaxError("not a TIFF IFD")
508 self._bigtiff = ifh[2] == 43
509 self.group = group
510 self.tagtype = {}
511 """ Dictionary of tag types """
512 self.reset()
513 (self.next,) = (
514 self._unpack("Q", ifh[8:]) if self._bigtiff else self._unpack("L", ifh[4:])
515 )
516 self._legacy_api = False
518 prefix = property(lambda self: self._prefix) 518 ↛ exitline 518 didn't run the lambda on line 518
519 offset = property(lambda self: self._offset) 519 ↛ exitline 519 didn't run the lambda on line 519
520 legacy_api = property(lambda self: self._legacy_api) 520 ↛ exitline 520 didn't run the lambda on line 520
522 @legacy_api.setter
523 def legacy_api(self, value):
524 raise Exception("Not allowing setting of legacy api")
526 def reset(self):
527 self._tags_v1 = {} # will remain empty if legacy_api is false
528 self._tags_v2 = {} # main tag storage
529 self._tagdata = {}
530 self.tagtype = {} # added 2008-06-05 by Florian Hoech
531 self._next = None
532 self._offset = None
534 def __str__(self):
535 return str(dict(self))
537 def named(self):
538 """
539 :returns: dict of name|key: value
541 Returns the complete tag dictionary, with named tags where possible.
542 """
543 return {
544 TiffTags.lookup(code, self.group).name: value
545 for code, value in self.items()
546 }
548 def __len__(self):
549 return len(set(self._tagdata) | set(self._tags_v2))
551 def __getitem__(self, tag):
552 if tag not in self._tags_v2: # unpack on the fly
553 data = self._tagdata[tag]
554 typ = self.tagtype[tag]
555 size, handler = self._load_dispatch[typ]
556 self[tag] = handler(self, data, self.legacy_api) # check type
557 val = self._tags_v2[tag]
558 if self.legacy_api and not isinstance(val, (tuple, bytes)):
559 val = (val,)
560 return val
562 def __contains__(self, tag):
563 return tag in self._tags_v2 or tag in self._tagdata
565 def __setitem__(self, tag, value):
566 self._setitem(tag, value, self.legacy_api)
568 def _setitem(self, tag, value, legacy_api):
569 basetypes = (Number, bytes, str)
571 info = TiffTags.lookup(tag, self.group)
572 values = [value] if isinstance(value, basetypes) else value
574 if tag not in self.tagtype:
575 if info.type:
576 self.tagtype[tag] = info.type
577 else:
578 self.tagtype[tag] = TiffTags.UNDEFINED
579 if all(isinstance(v, IFDRational) for v in values):
580 self.tagtype[tag] = (
581 TiffTags.RATIONAL
582 if all(v >= 0 for v in values)
583 else TiffTags.SIGNED_RATIONAL
584 )
585 elif all(isinstance(v, int) for v in values):
586 if all(0 <= v < 2**16 for v in values):
587 self.tagtype[tag] = TiffTags.SHORT
588 elif all(-(2**15) < v < 2**15 for v in values):
589 self.tagtype[tag] = TiffTags.SIGNED_SHORT
590 else:
591 self.tagtype[tag] = (
592 TiffTags.LONG
593 if all(v >= 0 for v in values)
594 else TiffTags.SIGNED_LONG
595 )
596 elif all(isinstance(v, float) for v in values):
597 self.tagtype[tag] = TiffTags.DOUBLE
598 elif all(isinstance(v, str) for v in values):
599 self.tagtype[tag] = TiffTags.ASCII
600 elif all(isinstance(v, bytes) for v in values):
601 self.tagtype[tag] = TiffTags.BYTE
603 if self.tagtype[tag] == TiffTags.UNDEFINED:
604 values = [
605 v.encode("ascii", "replace") if isinstance(v, str) else v
606 for v in values
607 ]
608 elif self.tagtype[tag] == TiffTags.RATIONAL:
609 values = [float(v) if isinstance(v, int) else v for v in values]
611 is_ifd = self.tagtype[tag] == TiffTags.LONG and isinstance(values, dict)
612 if not is_ifd:
613 values = tuple(info.cvt_enum(value) for value in values)
615 dest = self._tags_v1 if legacy_api else self._tags_v2
617 # Three branches:
618 # Spec'd length == 1, Actual length 1, store as element
619 # Spec'd length == 1, Actual > 1, Warn and truncate. Formerly barfed.
620 # No Spec, Actual length 1, Formerly (<4.2) returned a 1 element tuple.
621 # Don't mess with the legacy api, since it's frozen.
622 if not is_ifd and (
623 (info.length == 1)
624 or self.tagtype[tag] == TiffTags.BYTE
625 or (info.length is None and len(values) == 1 and not legacy_api)
626 ):
627 # Don't mess with the legacy api, since it's frozen.
628 if legacy_api and self.tagtype[tag] in [
629 TiffTags.RATIONAL,
630 TiffTags.SIGNED_RATIONAL,
631 ]: # rationals
632 values = (values,)
633 try:
634 (dest[tag],) = values
635 except ValueError:
636 # We've got a builtin tag with 1 expected entry
637 warnings.warn(
638 f"Metadata Warning, tag {tag} had too many entries: "
639 f"{len(values)}, expected 1"
640 )
641 dest[tag] = values[0]
643 else:
644 # Spec'd length > 1 or undefined
645 # Unspec'd, and length > 1
646 dest[tag] = values
648 def __delitem__(self, tag):
649 self._tags_v2.pop(tag, None)
650 self._tags_v1.pop(tag, None)
651 self._tagdata.pop(tag, None)
653 def __iter__(self):
654 return iter(set(self._tagdata) | set(self._tags_v2))
656 def _unpack(self, fmt, data):
657 return struct.unpack(self._endian + fmt, data)
659 def _pack(self, fmt, *values):
660 return struct.pack(self._endian + fmt, *values)
662 def _register_loader(idx, size):
663 def decorator(func):
664 from .TiffTags import TYPES
666 if func.__name__.startswith("load_"): 666 ↛ 668line 666 didn't jump to line 668, because the condition on line 666 was never false
667 TYPES[idx] = func.__name__[5:].replace("_", " ")
668 _load_dispatch[idx] = size, func # noqa: F821
669 return func
671 return decorator
673 def _register_writer(idx):
674 def decorator(func):
675 _write_dispatch[idx] = func # noqa: F821
676 return func
678 return decorator
680 def _register_basic(idx_fmt_name):
681 from .TiffTags import TYPES
683 idx, fmt, name = idx_fmt_name
684 TYPES[idx] = name
685 size = struct.calcsize("=" + fmt)
686 _load_dispatch[idx] = ( # noqa: F821 686 ↛ exitline 686 didn't jump to the function exit
687 size,
688 lambda self, data, legacy_api=True: (
689 self._unpack(f"{len(data) // size}{fmt}", data)
690 ),
691 )
692 _write_dispatch[idx] = lambda self, *values: ( # noqa: F821 692 ↛ exitline 692 didn't run the lambda on line 692
693 b"".join(self._pack(fmt, value) for value in values)
694 )
696 list(
697 map(
698 _register_basic,
699 [
700 (TiffTags.SHORT, "H", "short"),
701 (TiffTags.LONG, "L", "long"),
702 (TiffTags.SIGNED_BYTE, "b", "signed byte"),
703 (TiffTags.SIGNED_SHORT, "h", "signed short"),
704 (TiffTags.SIGNED_LONG, "l", "signed long"),
705 (TiffTags.FLOAT, "f", "float"),
706 (TiffTags.DOUBLE, "d", "double"),
707 (TiffTags.IFD, "L", "long"),
708 (TiffTags.LONG8, "Q", "long8"),
709 ],
710 )
711 )
713 @_register_loader(1, 1) # Basic type, except for the legacy API.
714 def load_byte(self, data, legacy_api=True):
715 return data
717 @_register_writer(1) # Basic type, except for the legacy API.
718 def write_byte(self, data):
719 return data
721 @_register_loader(2, 1)
722 def load_string(self, data, legacy_api=True):
723 if data.endswith(b"\0"):
724 data = data[:-1]
725 return data.decode("latin-1", "replace")
727 @_register_writer(2)
728 def write_string(self, value):
729 # remerge of https://github.com/python-pillow/Pillow/pull/1416
730 return b"" + value.encode("ascii", "replace") + b"\0"
732 @_register_loader(5, 8)
733 def load_rational(self, data, legacy_api=True):
734 vals = self._unpack(f"{len(data) // 4}L", data)
736 def combine(a, b):
737 return (a, b) if legacy_api else IFDRational(a, b)
739 return tuple(combine(num, denom) for num, denom in zip(vals[::2], vals[1::2]))
741 @_register_writer(5)
742 def write_rational(self, *values):
743 return b"".join(
744 self._pack("2L", *_limit_rational(frac, 2**32 - 1)) for frac in values
745 )
747 @_register_loader(7, 1)
748 def load_undefined(self, data, legacy_api=True):
749 return data
751 @_register_writer(7)
752 def write_undefined(self, value):
753 return value
755 @_register_loader(10, 8)
756 def load_signed_rational(self, data, legacy_api=True):
757 vals = self._unpack(f"{len(data) // 4}l", data)
759 def combine(a, b):
760 return (a, b) if legacy_api else IFDRational(a, b)
762 return tuple(combine(num, denom) for num, denom in zip(vals[::2], vals[1::2]))
764 @_register_writer(10)
765 def write_signed_rational(self, *values):
766 return b"".join(
767 self._pack("2l", *_limit_signed_rational(frac, 2**31 - 1, -(2**31)))
768 for frac in values
769 )
771 def _ensure_read(self, fp, size):
772 ret = fp.read(size)
773 if len(ret) != size:
774 raise OSError(
775 "Corrupt EXIF data. "
776 f"Expecting to read {size} bytes but only got {len(ret)}. "
777 )
778 return ret
780 def load(self, fp):
782 self.reset()
783 self._offset = fp.tell()
785 try:
786 tag_count = (
787 self._unpack("Q", self._ensure_read(fp, 8))
788 if self._bigtiff
789 else self._unpack("H", self._ensure_read(fp, 2))
790 )[0]
791 for i in range(tag_count):
792 tag, typ, count, data = (
793 self._unpack("HHQ8s", self._ensure_read(fp, 20))
794 if self._bigtiff
795 else self._unpack("HHL4s", self._ensure_read(fp, 12))
796 )
798 tagname = TiffTags.lookup(tag, self.group).name
799 typname = TYPES.get(typ, "unknown")
800 msg = f"tag: {tagname} ({tag}) - type: {typname} ({typ})"
802 try:
803 unit_size, handler = self._load_dispatch[typ]
804 except KeyError:
805 logger.debug(msg + f" - unsupported type {typ}")
806 continue # ignore unsupported type
807 size = count * unit_size
808 if size > (8 if self._bigtiff else 4):
809 here = fp.tell()
810 (offset,) = self._unpack("Q" if self._bigtiff else "L", data)
811 msg += f" Tag Location: {here} - Data Location: {offset}"
812 fp.seek(offset)
813 data = ImageFile._safe_read(fp, size)
814 fp.seek(here)
815 else:
816 data = data[:size]
818 if len(data) != size:
819 warnings.warn(
820 "Possibly corrupt EXIF data. "
821 f"Expecting to read {size} bytes but only got {len(data)}."
822 f" Skipping tag {tag}"
823 )
824 logger.debug(msg)
825 continue
827 if not data:
828 logger.debug(msg)
829 continue
831 self._tagdata[tag] = data
832 self.tagtype[tag] = typ
834 msg += " - value: " + (
835 "<table: %d bytes>" % size if size > 32 else repr(data)
836 )
837 logger.debug(msg)
839 (self.next,) = (
840 self._unpack("Q", self._ensure_read(fp, 8))
841 if self._bigtiff
842 else self._unpack("L", self._ensure_read(fp, 4))
843 )
844 except OSError as msg:
845 warnings.warn(str(msg))
846 return
848 def tobytes(self, offset=0):
849 # FIXME What about tagdata?
850 result = self._pack("H", len(self._tags_v2))
852 entries = []
853 offset = offset + len(result) + len(self._tags_v2) * 12 + 4
854 stripoffsets = None
856 # pass 1: convert tags to binary format
857 # always write tags in ascending order
858 for tag, value in sorted(self._tags_v2.items()):
859 if tag == STRIPOFFSETS:
860 stripoffsets = len(entries)
861 typ = self.tagtype.get(tag)
862 logger.debug(f"Tag {tag}, Type: {typ}, Value: {repr(value)}")
863 is_ifd = typ == TiffTags.LONG and isinstance(value, dict)
864 if is_ifd:
865 if self._endian == "<":
866 ifh = b"II\x2A\x00\x08\x00\x00\x00"
867 else:
868 ifh = b"MM\x00\x2A\x00\x00\x00\x08"
869 ifd = ImageFileDirectory_v2(ifh, group=tag)
870 values = self._tags_v2[tag]
871 for ifd_tag, ifd_value in values.items():
872 ifd[ifd_tag] = ifd_value
873 data = ifd.tobytes(offset)
874 else:
875 values = value if isinstance(value, tuple) else (value,)
876 data = self._write_dispatch[typ](self, *values)
878 tagname = TiffTags.lookup(tag, self.group).name
879 typname = "ifd" if is_ifd else TYPES.get(typ, "unknown")
880 msg = f"save: {tagname} ({tag}) - type: {typname} ({typ})"
881 msg += " - value: " + (
882 "<table: %d bytes>" % len(data) if len(data) >= 16 else str(values)
883 )
884 logger.debug(msg)
886 # count is sum of lengths for string and arbitrary data
887 if is_ifd:
888 count = 1
889 elif typ in [TiffTags.BYTE, TiffTags.ASCII, TiffTags.UNDEFINED]:
890 count = len(data)
891 else:
892 count = len(values)
893 # figure out if data fits into the entry
894 if len(data) <= 4:
895 entries.append((tag, typ, count, data.ljust(4, b"\0"), b""))
896 else:
897 entries.append((tag, typ, count, self._pack("L", offset), data))
898 offset += (len(data) + 1) // 2 * 2 # pad to word
900 # update strip offset data to point beyond auxiliary data
901 if stripoffsets is not None:
902 tag, typ, count, value, data = entries[stripoffsets]
903 if data:
904 raise NotImplementedError("multistrip support not yet implemented")
905 value = self._pack("L", self._unpack("L", value)[0] + offset)
906 entries[stripoffsets] = tag, typ, count, value, data
908 # pass 2: write entries to file
909 for tag, typ, count, value, data in entries:
910 logger.debug(f"{tag} {typ} {count} {repr(value)} {repr(data)}")
911 result += self._pack("HHL4s", tag, typ, count, value)
913 # -- overwrite here for multi-page --
914 result += b"\0\0\0\0" # end of entries
916 # pass 3: write auxiliary data to file
917 for tag, typ, count, value, data in entries:
918 result += data
919 if len(data) & 1:
920 result += b"\0"
922 return result
924 def save(self, fp):
926 if fp.tell() == 0: # skip TIFF header on subsequent pages
927 # tiff header -- PIL always starts the first IFD at offset 8
928 fp.write(self._prefix + self._pack("HL", 42, 8))
930 offset = fp.tell()
931 result = self.tobytes(offset)
932 fp.write(result)
933 return offset + len(result)
936ImageFileDirectory_v2._load_dispatch = _load_dispatch
937ImageFileDirectory_v2._write_dispatch = _write_dispatch
938for idx, name in TYPES.items():
939 name = name.replace(" ", "_")
940 setattr(ImageFileDirectory_v2, "load_" + name, _load_dispatch[idx][1])
941 setattr(ImageFileDirectory_v2, "write_" + name, _write_dispatch[idx])
942del _load_dispatch, _write_dispatch, idx, name
945# Legacy ImageFileDirectory support.
946class ImageFileDirectory_v1(ImageFileDirectory_v2):
947 """This class represents the **legacy** interface to a TIFF tag directory.
949 Exposes a dictionary interface of the tags in the directory::
951 ifd = ImageFileDirectory_v1()
952 ifd[key] = 'Some Data'
953 ifd.tagtype[key] = TiffTags.ASCII
954 print(ifd[key])
955 ('Some Data',)
957 Also contains a dictionary of tag types as read from the tiff image file,
958 :attr:`~PIL.TiffImagePlugin.ImageFileDirectory_v1.tagtype`.
960 Values are returned as a tuple.
962 .. deprecated:: 3.0.0
963 """
965 def __init__(self, *args, **kwargs):
966 super().__init__(*args, **kwargs)
967 self._legacy_api = True
969 tags = property(lambda self: self._tags_v1) 969 ↛ exitline 969 didn't run the lambda on line 969
970 tagdata = property(lambda self: self._tagdata) 970 ↛ exitline 970 didn't run the lambda on line 970
972 # defined in ImageFileDirectory_v2
973 tagtype: dict
974 """Dictionary of tag types"""
976 @classmethod
977 def from_v2(cls, original):
978 """Returns an
979 :py:class:`~PIL.TiffImagePlugin.ImageFileDirectory_v1`
980 instance with the same data as is contained in the original
981 :py:class:`~PIL.TiffImagePlugin.ImageFileDirectory_v2`
982 instance.
984 :returns: :py:class:`~PIL.TiffImagePlugin.ImageFileDirectory_v1`
986 """
988 ifd = cls(prefix=original.prefix)
989 ifd._tagdata = original._tagdata
990 ifd.tagtype = original.tagtype
991 ifd.next = original.next # an indicator for multipage tiffs
992 return ifd
994 def to_v2(self):
995 """Returns an
996 :py:class:`~PIL.TiffImagePlugin.ImageFileDirectory_v2`
997 instance with the same data as is contained in the original
998 :py:class:`~PIL.TiffImagePlugin.ImageFileDirectory_v1`
999 instance.
1001 :returns: :py:class:`~PIL.TiffImagePlugin.ImageFileDirectory_v2`
1003 """
1005 ifd = ImageFileDirectory_v2(prefix=self.prefix)
1006 ifd._tagdata = dict(self._tagdata)
1007 ifd.tagtype = dict(self.tagtype)
1008 ifd._tags_v2 = dict(self._tags_v2)
1009 return ifd
1011 def __contains__(self, tag):
1012 return tag in self._tags_v1 or tag in self._tagdata
1014 def __len__(self):
1015 return len(set(self._tagdata) | set(self._tags_v1))
1017 def __iter__(self):
1018 return iter(set(self._tagdata) | set(self._tags_v1))
1020 def __setitem__(self, tag, value):
1021 for legacy_api in (False, True):
1022 self._setitem(tag, value, legacy_api)
1024 def __getitem__(self, tag):
1025 if tag not in self._tags_v1: # unpack on the fly
1026 data = self._tagdata[tag]
1027 typ = self.tagtype[tag]
1028 size, handler = self._load_dispatch[typ]
1029 for legacy in (False, True):
1030 self._setitem(tag, handler(self, data, legacy), legacy)
1031 val = self._tags_v1[tag]
1032 if not isinstance(val, (tuple, bytes)):
1033 val = (val,)
1034 return val
1037# undone -- switch this pointer when IFD_LEGACY_API == False
1038ImageFileDirectory = ImageFileDirectory_v1
1041##
1042# Image plugin for TIFF files.
1045class TiffImageFile(ImageFile.ImageFile):
1047 format = "TIFF"
1048 format_description = "Adobe TIFF"
1049 _close_exclusive_fp_after_loading = False
1051 def __init__(self, fp=None, filename=None):
1052 self.tag_v2 = None
1053 """ Image file directory (tag dictionary) """
1055 self.tag = None
1056 """ Legacy tag entries """
1058 super().__init__(fp, filename)
1060 def _open(self):
1061 """Open the first image in a TIFF file"""
1063 # Header
1064 ifh = self.fp.read(8)
1065 if ifh[2] == 43:
1066 ifh += self.fp.read(8)
1068 self.tag_v2 = ImageFileDirectory_v2(ifh)
1070 # legacy IFD entries will be filled in later
1071 self.ifd = None
1073 # setup frame pointers
1074 self.__first = self.__next = self.tag_v2.next
1075 self.__frame = -1
1076 self._fp = self.fp
1077 self._frame_pos = []
1078 self._n_frames = None
1080 logger.debug("*** TiffImageFile._open ***")
1081 logger.debug(f"- __first: {self.__first}")
1082 logger.debug(f"- ifh: {repr(ifh)}") # Use repr to avoid str(bytes)
1084 # and load the first frame
1085 self._seek(0)
1087 @property
1088 def n_frames(self):
1089 if self._n_frames is None:
1090 current = self.tell()
1091 self._seek(len(self._frame_pos))
1092 while self._n_frames is None:
1093 self._seek(self.tell() + 1)
1094 self.seek(current)
1095 return self._n_frames
1097 def seek(self, frame):
1098 """Select a given frame as current image"""
1099 if not self._seek_check(frame):
1100 return
1101 self._seek(frame)
1102 # Create a new core image object on second and
1103 # subsequent frames in the image. Image may be
1104 # different size/mode.
1105 Image._decompression_bomb_check(self.size)
1106 self.im = Image.core.new(self.mode, self.size)
1108 def _seek(self, frame):
1109 self.fp = self._fp
1111 # reset buffered io handle in case fp
1112 # was passed to libtiff, invalidating the buffer
1113 self.fp.tell()
1115 while len(self._frame_pos) <= frame:
1116 if not self.__next:
1117 raise EOFError("no more images in TIFF file")
1118 logger.debug(
1119 f"Seeking to frame {frame}, on frame {self.__frame}, "
1120 f"__next {self.__next}, location: {self.fp.tell()}"
1121 )
1122 self.fp.seek(self.__next)
1123 self._frame_pos.append(self.__next)
1124 logger.debug("Loading tags, location: %s" % self.fp.tell())
1125 self.tag_v2.load(self.fp)
1126 if self.tag_v2.next in self._frame_pos:
1127 # This IFD has already been processed
1128 # Declare this to be the end of the image
1129 self.__next = 0
1130 else:
1131 self.__next = self.tag_v2.next
1132 if self.__next == 0:
1133 self._n_frames = frame + 1
1134 if len(self._frame_pos) == 1:
1135 self.is_animated = self.__next != 0
1136 self.__frame += 1
1137 self.fp.seek(self._frame_pos[frame])
1138 self.tag_v2.load(self.fp)
1139 self._reload_exif()
1140 # fill the legacy tag/ifd entries
1141 self.tag = self.ifd = ImageFileDirectory_v1.from_v2(self.tag_v2)
1142 self.__frame = frame
1143 self._setup()
1145 def tell(self):
1146 """Return the current frame number"""
1147 return self.__frame
1149 def getxmp(self):
1150 """
1151 Returns a dictionary containing the XMP tags.
1152 Requires defusedxml to be installed.
1154 :returns: XMP tags in a dictionary.
1155 """
1156 return self._getxmp(self.tag_v2[700]) if 700 in self.tag_v2 else {}
1158 def get_photoshop_blocks(self):
1159 """
1160 Returns a dictionary of Photoshop "Image Resource Blocks".
1161 The keys are the image resource ID. For more information, see
1162 https://www.adobe.com/devnet-apps/photoshop/fileformatashtml/#50577409_pgfId-1037727
1164 :returns: Photoshop "Image Resource Blocks" in a dictionary.
1165 """
1166 blocks = {}
1167 val = self.tag_v2.get(0x8649)
1168 if val:
1169 while val[:4] == b"8BIM":
1170 id = i16(val[4:6])
1171 n = math.ceil((val[6] + 1) / 2) * 2
1172 size = i32(val[6 + n : 10 + n])
1173 data = val[10 + n : 10 + n + size]
1174 blocks[id] = {"data": data}
1176 val = val[math.ceil((10 + n + size) / 2) * 2 :]
1177 return blocks
1179 def load(self):
1180 if self.tile and self.use_load_libtiff:
1181 return self._load_libtiff()
1182 return super().load()
1184 def load_end(self):
1185 if self._tile_orientation:
1186 method = {
1187 2: Image.Transpose.FLIP_LEFT_RIGHT,
1188 3: Image.Transpose.ROTATE_180,
1189 4: Image.Transpose.FLIP_TOP_BOTTOM,
1190 5: Image.Transpose.TRANSPOSE,
1191 6: Image.Transpose.ROTATE_270,
1192 7: Image.Transpose.TRANSVERSE,
1193 8: Image.Transpose.ROTATE_90,
1194 }.get(self._tile_orientation)
1195 if method is not None:
1196 self.im = self.im.transpose(method)
1197 self._size = self.im.size
1199 # allow closing if we're on the first frame, there's no next
1200 # This is the ImageFile.load path only, libtiff specific below.
1201 if not self.is_animated:
1202 self._close_exclusive_fp_after_loading = True
1204 # reset buffered io handle in case fp
1205 # was passed to libtiff, invalidating the buffer
1206 self.fp.tell()
1208 # load IFD data from fp before it is closed
1209 exif = self.getexif()
1210 for key in TiffTags.TAGS_V2_GROUPS.keys():
1211 if key not in exif:
1212 continue
1213 exif.get_ifd(key)
1215 def _load_libtiff(self):
1216 """Overload method triggered when we detect a compressed tiff
1217 Calls out to libtiff"""
1219 Image.Image.load(self)
1221 self.load_prepare()
1223 if not len(self.tile) == 1:
1224 raise OSError("Not exactly one tile")
1226 # (self._compression, (extents tuple),
1227 # 0, (rawmode, self._compression, fp))
1228 extents = self.tile[0][1]
1229 args = list(self.tile[0][3])
1231 # To be nice on memory footprint, if there's a
1232 # file descriptor, use that instead of reading
1233 # into a string in python.
1234 # libtiff closes the file descriptor, so pass in a dup.
1235 try:
1236 fp = hasattr(self.fp, "fileno") and os.dup(self.fp.fileno())
1237 # flush the file descriptor, prevents error on pypy 2.4+
1238 # should also eliminate the need for fp.tell
1239 # in _seek
1240 if hasattr(self.fp, "flush"):
1241 self.fp.flush()
1242 except OSError:
1243 # io.BytesIO have a fileno, but returns an OSError if
1244 # it doesn't use a file descriptor.
1245 fp = False
1247 if fp:
1248 args[2] = fp
1250 decoder = Image._getdecoder(
1251 self.mode, "libtiff", tuple(args), self.decoderconfig
1252 )
1253 try:
1254 decoder.setimage(self.im, extents)
1255 except ValueError as e:
1256 raise OSError("Couldn't set the image") from e
1258 close_self_fp = self._exclusive_fp and not self.is_animated
1259 if hasattr(self.fp, "getvalue"):
1260 # We've got a stringio like thing passed in. Yay for all in memory.
1261 # The decoder needs the entire file in one shot, so there's not
1262 # a lot we can do here other than give it the entire file.
1263 # unless we could do something like get the address of the
1264 # underlying string for stringio.
1265 #
1266 # Rearranging for supporting byteio items, since they have a fileno
1267 # that returns an OSError if there's no underlying fp. Easier to
1268 # deal with here by reordering.
1269 logger.debug("have getvalue. just sending in a string from getvalue")
1270 n, err = decoder.decode(self.fp.getvalue())
1271 elif fp:
1272 # we've got a actual file on disk, pass in the fp.
1273 logger.debug("have fileno, calling fileno version of the decoder.")
1274 if not close_self_fp:
1275 self.fp.seek(0)
1276 # 4 bytes, otherwise the trace might error out
1277 n, err = decoder.decode(b"fpfp")
1278 else:
1279 # we have something else.
1280 logger.debug("don't have fileno or getvalue. just reading")
1281 self.fp.seek(0)
1282 # UNDONE -- so much for that buffer size thing.
1283 n, err = decoder.decode(self.fp.read())
1285 if fp:
1286 try:
1287 os.close(fp)
1288 except OSError:
1289 pass
1291 self.tile = []
1292 self.readonly = 0
1294 self.load_end()
1296 # libtiff closed the fp in a, we need to close self.fp, if possible
1297 if close_self_fp:
1298 self.fp.close()
1299 self.fp = None # might be shared
1301 if err < 0:
1302 raise OSError(err)
1304 return Image.Image.load(self)
1306 def _setup(self):
1307 """Setup this image object based on current tags"""
1309 if 0xBC01 in self.tag_v2:
1310 raise OSError("Windows Media Photo files not yet supported")
1312 # extract relevant tags
1313 self._compression = COMPRESSION_INFO[self.tag_v2.get(COMPRESSION, 1)]
1314 self._planar_configuration = self.tag_v2.get(PLANAR_CONFIGURATION, 1)
1316 # photometric is a required tag, but not everyone is reading
1317 # the specification
1318 photo = self.tag_v2.get(PHOTOMETRIC_INTERPRETATION, 0)
1320 # old style jpeg compression images most certainly are YCbCr
1321 if self._compression == "tiff_jpeg":
1322 photo = 6
1324 fillorder = self.tag_v2.get(FILLORDER, 1)
1326 logger.debug("*** Summary ***")
1327 logger.debug(f"- compression: {self._compression}")
1328 logger.debug(f"- photometric_interpretation: {photo}")
1329 logger.debug(f"- planar_configuration: {self._planar_configuration}")
1330 logger.debug(f"- fill_order: {fillorder}")
1331 logger.debug(f"- YCbCr subsampling: {self.tag.get(530)}")
1333 # size
1334 xsize = int(self.tag_v2.get(IMAGEWIDTH))
1335 ysize = int(self.tag_v2.get(IMAGELENGTH))
1336 self._size = xsize, ysize
1338 logger.debug(f"- size: {self.size}")
1340 sample_format = self.tag_v2.get(SAMPLEFORMAT, (1,))
1341 if len(sample_format) > 1 and max(sample_format) == min(sample_format) == 1:
1342 # SAMPLEFORMAT is properly per band, so an RGB image will
1343 # be (1,1,1). But, we don't support per band pixel types,
1344 # and anything more than one band is a uint8. So, just
1345 # take the first element. Revisit this if adding support
1346 # for more exotic images.
1347 sample_format = (1,)
1349 bps_tuple = self.tag_v2.get(BITSPERSAMPLE, (1,))
1350 extra_tuple = self.tag_v2.get(EXTRASAMPLES, ())
1351 if photo in (2, 6, 8): # RGB, YCbCr, LAB
1352 bps_count = 3
1353 elif photo == 5: # CMYK
1354 bps_count = 4
1355 else:
1356 bps_count = 1
1357 bps_count += len(extra_tuple)
1358 bps_actual_count = len(bps_tuple)
1359 samples_per_pixel = self.tag_v2.get(
1360 SAMPLESPERPIXEL,
1361 3 if self._compression == "tiff_jpeg" and photo in (2, 6) else 1,
1362 )
1363 if samples_per_pixel < bps_actual_count:
1364 # If a file has more values in bps_tuple than expected,
1365 # remove the excess.
1366 bps_tuple = bps_tuple[:samples_per_pixel]
1367 elif samples_per_pixel > bps_actual_count and bps_actual_count == 1:
1368 # If a file has only one value in bps_tuple, when it should have more,
1369 # presume it is the same number of bits for all of the samples.
1370 bps_tuple = bps_tuple * samples_per_pixel
1372 if len(bps_tuple) != samples_per_pixel:
1373 raise SyntaxError("unknown data organization")
1375 # mode: check photometric interpretation and bits per pixel
1376 key = (
1377 self.tag_v2.prefix,
1378 photo,
1379 sample_format,
1380 fillorder,
1381 bps_tuple,
1382 extra_tuple,
1383 )
1384 logger.debug(f"format key: {key}")
1385 try:
1386 self.mode, rawmode = OPEN_INFO[key]
1387 except KeyError as e:
1388 logger.debug("- unsupported format")
1389 raise SyntaxError("unknown pixel mode") from e
1391 logger.debug(f"- raw mode: {rawmode}")
1392 logger.debug(f"- pil mode: {self.mode}")
1394 self.info["compression"] = self._compression
1396 xres = self.tag_v2.get(X_RESOLUTION, 1)
1397 yres = self.tag_v2.get(Y_RESOLUTION, 1)
1399 if xres and yres:
1400 resunit = self.tag_v2.get(RESOLUTION_UNIT)
1401 if resunit == 2: # dots per inch
1402 self.info["dpi"] = (xres, yres)
1403 elif resunit == 3: # dots per centimeter. convert to dpi
1404 self.info["dpi"] = (xres * 2.54, yres * 2.54)
1405 elif resunit is None: # used to default to 1, but now 2)
1406 self.info["dpi"] = (xres, yres)
1407 # For backward compatibility,
1408 # we also preserve the old behavior
1409 self.info["resolution"] = xres, yres
1410 else: # No absolute unit of measurement
1411 self.info["resolution"] = xres, yres
1413 # build tile descriptors
1414 x = y = layer = 0
1415 self.tile = []
1416 self.use_load_libtiff = READ_LIBTIFF or self._compression != "raw"
1417 if self.use_load_libtiff:
1418 # Decoder expects entire file as one tile.
1419 # There's a buffer size limit in load (64k)
1420 # so large g4 images will fail if we use that
1421 # function.
1422 #
1423 # Setup the one tile for the whole image, then
1424 # use the _load_libtiff function.
1426 # libtiff handles the fillmode for us, so 1;IR should
1427 # actually be 1;I. Including the R double reverses the
1428 # bits, so stripes of the image are reversed. See
1429 # https://github.com/python-pillow/Pillow/issues/279
1430 if fillorder == 2:
1431 # Replace fillorder with fillorder=1
1432 key = key[:3] + (1,) + key[4:]
1433 logger.debug(f"format key: {key}")
1434 # this should always work, since all the
1435 # fillorder==2 modes have a corresponding
1436 # fillorder=1 mode
1437 self.mode, rawmode = OPEN_INFO[key]
1438 # libtiff always returns the bytes in native order.
1439 # we're expecting image byte order. So, if the rawmode
1440 # contains I;16, we need to convert from native to image
1441 # byte order.
1442 if rawmode == "I;16":
1443 rawmode = "I;16N"
1444 if ";16B" in rawmode:
1445 rawmode = rawmode.replace(";16B", ";16N")
1446 if ";16L" in rawmode:
1447 rawmode = rawmode.replace(";16L", ";16N")
1449 # YCbCr images with new jpeg compression with pixels in one plane
1450 # unpacked straight into RGB values
1451 if (
1452 photo == 6
1453 and self._compression == "jpeg"
1454 and self._planar_configuration == 1
1455 ):
1456 rawmode = "RGB"
1458 # Offset in the tile tuple is 0, we go from 0,0 to
1459 # w,h, and we only do this once -- eds
1460 a = (rawmode, self._compression, False, self.tag_v2.offset)
1461 self.tile.append(("libtiff", (0, 0, xsize, ysize), 0, a))
1463 elif STRIPOFFSETS in self.tag_v2 or TILEOFFSETS in self.tag_v2:
1464 # striped image
1465 if STRIPOFFSETS in self.tag_v2:
1466 offsets = self.tag_v2[STRIPOFFSETS]
1467 h = self.tag_v2.get(ROWSPERSTRIP, ysize)
1468 w = self.size[0]
1469 else:
1470 # tiled image
1471 offsets = self.tag_v2[TILEOFFSETS]
1472 w = self.tag_v2.get(322)
1473 h = self.tag_v2.get(323)
1475 for offset in offsets:
1476 if x + w > xsize:
1477 stride = w * sum(bps_tuple) / 8 # bytes per line
1478 else:
1479 stride = 0
1481 tile_rawmode = rawmode
1482 if self._planar_configuration == 2:
1483 # each band on it's own layer
1484 tile_rawmode = rawmode[layer]
1485 # adjust stride width accordingly
1486 stride /= bps_count
1488 a = (tile_rawmode, int(stride), 1)
1489 self.tile.append(
1490 (
1491 self._compression,
1492 (x, y, min(x + w, xsize), min(y + h, ysize)),
1493 offset,
1494 a,
1495 )
1496 )
1497 x = x + w
1498 if x >= self.size[0]:
1499 x, y = 0, y + h
1500 if y >= self.size[1]:
1501 x = y = 0
1502 layer += 1
1503 else:
1504 logger.debug("- unsupported data organization")
1505 raise SyntaxError("unknown data organization")
1507 # Fix up info.
1508 if ICCPROFILE in self.tag_v2:
1509 self.info["icc_profile"] = self.tag_v2[ICCPROFILE]
1511 # fixup palette descriptor
1513 if self.mode in ["P", "PA"]:
1514 palette = [o8(b // 256) for b in self.tag_v2[COLORMAP]]
1515 self.palette = ImagePalette.raw("RGB;L", b"".join(palette))
1517 self._tile_orientation = self.tag_v2.get(0x0112)
1520#
1521# --------------------------------------------------------------------
1522# Write TIFF files
1524# little endian is default except for image modes with
1525# explicit big endian byte-order
1527SAVE_INFO = {
1528 # mode => rawmode, byteorder, photometrics,
1529 # sampleformat, bitspersample, extra
1530 "1": ("1", II, 1, 1, (1,), None),
1531 "L": ("L", II, 1, 1, (8,), None),
1532 "LA": ("LA", II, 1, 1, (8, 8), 2),
1533 "P": ("P", II, 3, 1, (8,), None),
1534 "PA": ("PA", II, 3, 1, (8, 8), 2),
1535 "I": ("I;32S", II, 1, 2, (32,), None),
1536 "I;16": ("I;16", II, 1, 1, (16,), None),
1537 "I;16S": ("I;16S", II, 1, 2, (16,), None),
1538 "F": ("F;32F", II, 1, 3, (32,), None),
1539 "RGB": ("RGB", II, 2, 1, (8, 8, 8), None),
1540 "RGBX": ("RGBX", II, 2, 1, (8, 8, 8, 8), 0),
1541 "RGBA": ("RGBA", II, 2, 1, (8, 8, 8, 8), 2),
1542 "CMYK": ("CMYK", II, 5, 1, (8, 8, 8, 8), None),
1543 "YCbCr": ("YCbCr", II, 6, 1, (8, 8, 8), None),
1544 "LAB": ("LAB", II, 8, 1, (8, 8, 8), None),
1545 "I;32BS": ("I;32BS", MM, 1, 2, (32,), None),
1546 "I;16B": ("I;16B", MM, 1, 1, (16,), None),
1547 "I;16BS": ("I;16BS", MM, 1, 2, (16,), None),
1548 "F;32BF": ("F;32BF", MM, 1, 3, (32,), None),
1549}
1552def _save(im, fp, filename):
1554 try:
1555 rawmode, prefix, photo, format, bits, extra = SAVE_INFO[im.mode]
1556 except KeyError as e:
1557 raise OSError(f"cannot write mode {im.mode} as TIFF") from e
1559 ifd = ImageFileDirectory_v2(prefix=prefix)
1561 encoderinfo = im.encoderinfo
1562 encoderconfig = im.encoderconfig
1563 try:
1564 compression = encoderinfo["compression"]
1565 except KeyError:
1566 compression = im.info.get("compression")
1567 if isinstance(compression, int):
1568 # compression value may be from BMP. Ignore it
1569 compression = None
1570 if compression is None:
1571 compression = "raw"
1572 elif compression == "tiff_jpeg":
1573 # OJPEG is obsolete, so use new-style JPEG compression instead
1574 compression = "jpeg"
1575 elif compression == "tiff_deflate":
1576 compression = "tiff_adobe_deflate"
1578 libtiff = WRITE_LIBTIFF or compression != "raw"
1580 # required for color libtiff images
1581 ifd[PLANAR_CONFIGURATION] = 1
1583 ifd[IMAGEWIDTH] = im.size[0]
1584 ifd[IMAGELENGTH] = im.size[1]
1586 # write any arbitrary tags passed in as an ImageFileDirectory
1587 if "tiffinfo" in encoderinfo:
1588 info = encoderinfo["tiffinfo"]
1589 elif "exif" in encoderinfo:
1590 info = encoderinfo["exif"]
1591 if isinstance(info, bytes):
1592 exif = Image.Exif()
1593 exif.load(info)
1594 info = exif
1595 else:
1596 info = {}
1597 logger.debug("Tiffinfo Keys: %s" % list(info))
1598 if isinstance(info, ImageFileDirectory_v1):
1599 info = info.to_v2()
1600 for key in info:
1601 if isinstance(info, Image.Exif) and key in TiffTags.TAGS_V2_GROUPS.keys():
1602 ifd[key] = info.get_ifd(key)
1603 else:
1604 ifd[key] = info.get(key)
1605 try:
1606 ifd.tagtype[key] = info.tagtype[key]
1607 except Exception:
1608 pass # might not be an IFD. Might not have populated type
1610 # additions written by Greg Couch, gregc@cgl.ucsf.edu
1611 # inspired by image-sig posting from Kevin Cazabon, kcazabon@home.com
1612 if hasattr(im, "tag_v2"):
1613 # preserve tags from original TIFF image file
1614 for key in (
1615 RESOLUTION_UNIT,
1616 X_RESOLUTION,
1617 Y_RESOLUTION,
1618 IPTC_NAA_CHUNK,
1619 PHOTOSHOP_CHUNK,
1620 XMP,
1621 ):
1622 if key in im.tag_v2:
1623 ifd[key] = im.tag_v2[key]
1624 ifd.tagtype[key] = im.tag_v2.tagtype[key]
1626 # preserve ICC profile (should also work when saving other formats
1627 # which support profiles as TIFF) -- 2008-06-06 Florian Hoech
1628 icc = encoderinfo.get("icc_profile", im.info.get("icc_profile"))
1629 if icc:
1630 ifd[ICCPROFILE] = icc
1632 for key, name in [
1633 (IMAGEDESCRIPTION, "description"),
1634 (X_RESOLUTION, "resolution"),
1635 (Y_RESOLUTION, "resolution"),
1636 (X_RESOLUTION, "x_resolution"),
1637 (Y_RESOLUTION, "y_resolution"),
1638 (RESOLUTION_UNIT, "resolution_unit"),
1639 (SOFTWARE, "software"),
1640 (DATE_TIME, "date_time"),
1641 (ARTIST, "artist"),
1642 (COPYRIGHT, "copyright"),
1643 ]:
1644 if name in encoderinfo:
1645 ifd[key] = encoderinfo[name]
1647 dpi = encoderinfo.get("dpi")
1648 if dpi:
1649 ifd[RESOLUTION_UNIT] = 2
1650 ifd[X_RESOLUTION] = dpi[0]
1651 ifd[Y_RESOLUTION] = dpi[1]
1653 if bits != (1,):
1654 ifd[BITSPERSAMPLE] = bits
1655 if len(bits) != 1:
1656 ifd[SAMPLESPERPIXEL] = len(bits)
1657 if extra is not None:
1658 ifd[EXTRASAMPLES] = extra
1659 if format != 1:
1660 ifd[SAMPLEFORMAT] = format
1662 if PHOTOMETRIC_INTERPRETATION not in ifd:
1663 ifd[PHOTOMETRIC_INTERPRETATION] = photo
1664 elif im.mode in ("1", "L") and ifd[PHOTOMETRIC_INTERPRETATION] == 0:
1665 if im.mode == "1":
1666 inverted_im = im.copy()
1667 px = inverted_im.load()
1668 for y in range(inverted_im.height):
1669 for x in range(inverted_im.width):
1670 px[x, y] = 0 if px[x, y] == 255 else 255
1671 im = inverted_im
1672 else:
1673 im = ImageOps.invert(im)
1675 if im.mode in ["P", "PA"]:
1676 lut = im.im.getpalette("RGB", "RGB;L")
1677 colormap = []
1678 colors = len(lut) // 3
1679 for i in range(3):
1680 colormap += [v * 256 for v in lut[colors * i : colors * (i + 1)]]
1681 colormap += [0] * (256 - colors)
1682 ifd[COLORMAP] = colormap
1683 # data orientation
1684 stride = len(bits) * ((im.size[0] * bits[0] + 7) // 8)
1685 # aim for given strip size (64 KB by default) when using libtiff writer
1686 if libtiff:
1687 rows_per_strip = 1 if stride == 0 else min(STRIP_SIZE // stride, im.size[1])
1688 # JPEG encoder expects multiple of 8 rows
1689 if compression == "jpeg":
1690 rows_per_strip = min(((rows_per_strip + 7) // 8) * 8, im.size[1])
1691 else:
1692 rows_per_strip = im.size[1]
1693 if rows_per_strip == 0:
1694 rows_per_strip = 1
1695 strip_byte_counts = 1 if stride == 0 else stride * rows_per_strip
1696 strips_per_image = (im.size[1] + rows_per_strip - 1) // rows_per_strip
1697 ifd[ROWSPERSTRIP] = rows_per_strip
1698 if strip_byte_counts >= 2**16:
1699 ifd.tagtype[STRIPBYTECOUNTS] = TiffTags.LONG
1700 ifd[STRIPBYTECOUNTS] = (strip_byte_counts,) * (strips_per_image - 1) + (
1701 stride * im.size[1] - strip_byte_counts * (strips_per_image - 1),
1702 )
1703 ifd[STRIPOFFSETS] = tuple(
1704 range(0, strip_byte_counts * strips_per_image, strip_byte_counts)
1705 ) # this is adjusted by IFD writer
1706 # no compression by default:
1707 ifd[COMPRESSION] = COMPRESSION_INFO_REV.get(compression, 1)
1709 if im.mode == "YCbCr":
1710 for tag, value in {
1711 YCBCRSUBSAMPLING: (1, 1),
1712 REFERENCEBLACKWHITE: (0, 255, 128, 255, 128, 255),
1713 }.items():
1714 ifd.setdefault(tag, value)
1716 blocklist = [TILEWIDTH, TILELENGTH, TILEOFFSETS, TILEBYTECOUNTS]
1717 if libtiff:
1718 if "quality" in encoderinfo:
1719 quality = encoderinfo["quality"]
1720 if not isinstance(quality, int) or quality < 0 or quality > 100:
1721 raise ValueError("Invalid quality setting")
1722 if compression != "jpeg":
1723 raise ValueError(
1724 "quality setting only supported for 'jpeg' compression"
1725 )
1726 ifd[JPEGQUALITY] = quality
1728 logger.debug("Saving using libtiff encoder")
1729 logger.debug("Items: %s" % sorted(ifd.items()))
1730 _fp = 0
1731 if hasattr(fp, "fileno"):
1732 try:
1733 fp.seek(0)
1734 _fp = os.dup(fp.fileno())
1735 except io.UnsupportedOperation:
1736 pass
1738 # optional types for non core tags
1739 types = {}
1740 # STRIPOFFSETS and STRIPBYTECOUNTS are added by the library
1741 # based on the data in the strip.
1742 # The other tags expect arrays with a certain length (fixed or depending on
1743 # BITSPERSAMPLE, etc), passing arrays with a different length will result in
1744 # segfaults. Block these tags until we add extra validation.
1745 # SUBIFD may also cause a segfault.
1746 blocklist += [
1747 REFERENCEBLACKWHITE,
1748 STRIPBYTECOUNTS,
1749 STRIPOFFSETS,
1750 TRANSFERFUNCTION,
1751 SUBIFD,
1752 ]
1754 # bits per sample is a single short in the tiff directory, not a list.
1755 atts = {BITSPERSAMPLE: bits[0]}
1756 # Merge the ones that we have with (optional) more bits from
1757 # the original file, e.g x,y resolution so that we can
1758 # save(load('')) == original file.
1759 legacy_ifd = {}
1760 if hasattr(im, "tag"):
1761 legacy_ifd = im.tag.to_v2()
1763 # SAMPLEFORMAT is determined by the image format and should not be copied
1764 # from legacy_ifd.
1765 supplied_tags = {**getattr(im, "tag_v2", {}), **legacy_ifd}
1766 if SAMPLEFORMAT in supplied_tags:
1767 del supplied_tags[SAMPLEFORMAT]
1769 for tag, value in itertools.chain(ifd.items(), supplied_tags.items()):
1770 # Libtiff can only process certain core items without adding
1771 # them to the custom dictionary.
1772 # Custom items are supported for int, float, unicode, string and byte
1773 # values. Other types and tuples require a tagtype.
1774 if tag not in TiffTags.LIBTIFF_CORE:
1775 if not Image.core.libtiff_support_custom_tags:
1776 continue
1778 if tag in ifd.tagtype:
1779 types[tag] = ifd.tagtype[tag]
1780 elif not (isinstance(value, (int, float, str, bytes))):
1781 continue
1782 else:
1783 type = TiffTags.lookup(tag).type
1784 if type:
1785 types[tag] = type
1786 if tag not in atts and tag not in blocklist:
1787 if isinstance(value, str):
1788 atts[tag] = value.encode("ascii", "replace") + b"\0"
1789 elif isinstance(value, IFDRational):
1790 atts[tag] = float(value)
1791 else:
1792 atts[tag] = value
1794 if SAMPLEFORMAT in atts and len(atts[SAMPLEFORMAT]) == 1:
1795 atts[SAMPLEFORMAT] = atts[SAMPLEFORMAT][0]
1797 logger.debug("Converted items: %s" % sorted(atts.items()))
1799 # libtiff always expects the bytes in native order.
1800 # we're storing image byte order. So, if the rawmode
1801 # contains I;16, we need to convert from native to image
1802 # byte order.
1803 if im.mode in ("I;16B", "I;16"):
1804 rawmode = "I;16N"
1806 # Pass tags as sorted list so that the tags are set in a fixed order.
1807 # This is required by libtiff for some tags. For example, the JPEGQUALITY
1808 # pseudo tag requires that the COMPRESS tag was already set.
1809 tags = list(atts.items())
1810 tags.sort()
1811 a = (rawmode, compression, _fp, filename, tags, types)
1812 e = Image._getencoder(im.mode, "libtiff", a, encoderconfig)
1813 e.setimage(im.im, (0, 0) + im.size)
1814 while True:
1815 # undone, change to self.decodermaxblock:
1816 l, s, d = e.encode(16 * 1024)
1817 if not _fp:
1818 fp.write(d)
1819 if s:
1820 break
1821 if s < 0:
1822 raise OSError(f"encoder error {s} when writing image file")
1824 else:
1825 for tag in blocklist:
1826 del ifd[tag]
1827 offset = ifd.save(fp)
1829 ImageFile._save(
1830 im, fp, [("raw", (0, 0) + im.size, offset, (rawmode, stride, 1))]
1831 )
1833 # -- helper for multi-page save --
1834 if "_debug_multipage" in encoderinfo:
1835 # just to access o32 and o16 (using correct byte order)
1836 im._debug_multipage = ifd
1839class AppendingTiffWriter:
1840 fieldSizes = [
1841 0, # None
1842 1, # byte
1843 1, # ascii
1844 2, # short
1845 4, # long
1846 8, # rational
1847 1, # sbyte
1848 1, # undefined
1849 2, # sshort
1850 4, # slong
1851 8, # srational
1852 4, # float
1853 8, # double
1854 ]
1856 # StripOffsets = 273
1857 # FreeOffsets = 288
1858 # TileOffsets = 324
1859 # JPEGQTables = 519
1860 # JPEGDCTables = 520
1861 # JPEGACTables = 521
1862 Tags = {273, 288, 324, 519, 520, 521}
1864 def __init__(self, fn, new=False):
1865 if hasattr(fn, "read"):
1866 self.f = fn
1867 self.close_fp = False
1868 else:
1869 self.name = fn
1870 self.close_fp = True
1871 try:
1872 self.f = open(fn, "w+b" if new else "r+b")
1873 except OSError:
1874 self.f = open(fn, "w+b")
1875 self.beginning = self.f.tell()
1876 self.setup()
1878 def setup(self):
1879 # Reset everything.
1880 self.f.seek(self.beginning, os.SEEK_SET)
1882 self.whereToWriteNewIFDOffset = None
1883 self.offsetOfNewPage = 0
1885 self.IIMM = iimm = self.f.read(4)
1886 if not iimm:
1887 # empty file - first page
1888 self.isFirst = True
1889 return
1891 self.isFirst = False
1892 if iimm == b"II\x2a\x00":
1893 self.setEndian("<")
1894 elif iimm == b"MM\x00\x2a":
1895 self.setEndian(">")
1896 else:
1897 raise RuntimeError("Invalid TIFF file header")
1899 self.skipIFDs()
1900 self.goToEnd()
1902 def finalize(self):
1903 if self.isFirst:
1904 return
1906 # fix offsets
1907 self.f.seek(self.offsetOfNewPage)
1909 iimm = self.f.read(4)
1910 if not iimm:
1911 # raise RuntimeError("nothing written into new page")
1912 # Make it easy to finish a frame without committing to a new one.
1913 return
1915 if iimm != self.IIMM:
1916 raise RuntimeError("IIMM of new page doesn't match IIMM of first page")
1918 ifd_offset = self.readLong()
1919 ifd_offset += self.offsetOfNewPage
1920 self.f.seek(self.whereToWriteNewIFDOffset)
1921 self.writeLong(ifd_offset)
1922 self.f.seek(ifd_offset)
1923 self.fixIFD()
1925 def newFrame(self):
1926 # Call this to finish a frame.
1927 self.finalize()
1928 self.setup()
1930 def __enter__(self):
1931 return self
1933 def __exit__(self, exc_type, exc_value, traceback):
1934 if self.close_fp:
1935 self.close()
1936 return False
1938 def tell(self):
1939 return self.f.tell() - self.offsetOfNewPage
1941 def seek(self, offset, whence=io.SEEK_SET):
1942 if whence == os.SEEK_SET:
1943 offset += self.offsetOfNewPage
1945 self.f.seek(offset, whence)
1946 return self.tell()
1948 def goToEnd(self):
1949 self.f.seek(0, os.SEEK_END)
1950 pos = self.f.tell()
1952 # pad to 16 byte boundary
1953 pad_bytes = 16 - pos % 16
1954 if 0 < pad_bytes < 16:
1955 self.f.write(bytes(pad_bytes))
1956 self.offsetOfNewPage = self.f.tell()
1958 def setEndian(self, endian):
1959 self.endian = endian
1960 self.longFmt = self.endian + "L"
1961 self.shortFmt = self.endian + "H"
1962 self.tagFormat = self.endian + "HHL"
1964 def skipIFDs(self):
1965 while True:
1966 ifd_offset = self.readLong()
1967 if ifd_offset == 0:
1968 self.whereToWriteNewIFDOffset = self.f.tell() - 4
1969 break
1971 self.f.seek(ifd_offset)
1972 num_tags = self.readShort()
1973 self.f.seek(num_tags * 12, os.SEEK_CUR)
1975 def write(self, data):
1976 return self.f.write(data)
1978 def readShort(self):
1979 (value,) = struct.unpack(self.shortFmt, self.f.read(2))
1980 return value
1982 def readLong(self):
1983 (value,) = struct.unpack(self.longFmt, self.f.read(4))
1984 return value
1986 def rewriteLastShortToLong(self, value):
1987 self.f.seek(-2, os.SEEK_CUR)
1988 bytes_written = self.f.write(struct.pack(self.longFmt, value))
1989 if bytes_written is not None and bytes_written != 4:
1990 raise RuntimeError(f"wrote only {bytes_written} bytes but wanted 4")
1992 def rewriteLastShort(self, value):
1993 self.f.seek(-2, os.SEEK_CUR)
1994 bytes_written = self.f.write(struct.pack(self.shortFmt, value))
1995 if bytes_written is not None and bytes_written != 2:
1996 raise RuntimeError(f"wrote only {bytes_written} bytes but wanted 2")
1998 def rewriteLastLong(self, value):
1999 self.f.seek(-4, os.SEEK_CUR)
2000 bytes_written = self.f.write(struct.pack(self.longFmt, value))
2001 if bytes_written is not None and bytes_written != 4:
2002 raise RuntimeError(f"wrote only {bytes_written} bytes but wanted 4")
2004 def writeShort(self, value):
2005 bytes_written = self.f.write(struct.pack(self.shortFmt, value))
2006 if bytes_written is not None and bytes_written != 2:
2007 raise RuntimeError(f"wrote only {bytes_written} bytes but wanted 2")
2009 def writeLong(self, value):
2010 bytes_written = self.f.write(struct.pack(self.longFmt, value))
2011 if bytes_written is not None and bytes_written != 4:
2012 raise RuntimeError(f"wrote only {bytes_written} bytes but wanted 4")
2014 def close(self):
2015 self.finalize()
2016 self.f.close()
2018 def fixIFD(self):
2019 num_tags = self.readShort()
2021 for i in range(num_tags):
2022 tag, field_type, count = struct.unpack(self.tagFormat, self.f.read(8))
2024 field_size = self.fieldSizes[field_type]
2025 total_size = field_size * count
2026 is_local = total_size <= 4
2027 if not is_local:
2028 offset = self.readLong()
2029 offset += self.offsetOfNewPage
2030 self.rewriteLastLong(offset)
2032 if tag in self.Tags:
2033 cur_pos = self.f.tell()
2035 if is_local:
2036 self.fixOffsets(
2037 count, isShort=(field_size == 2), isLong=(field_size == 4)
2038 )
2039 self.f.seek(cur_pos + 4)
2040 else:
2041 self.f.seek(offset)
2042 self.fixOffsets(
2043 count, isShort=(field_size == 2), isLong=(field_size == 4)
2044 )
2045 self.f.seek(cur_pos)
2047 offset = cur_pos = None
2049 elif is_local:
2050 # skip the locally stored value that is not an offset
2051 self.f.seek(4, os.SEEK_CUR)
2053 def fixOffsets(self, count, isShort=False, isLong=False):
2054 if not isShort and not isLong:
2055 raise RuntimeError("offset is neither short nor long")
2057 for i in range(count):
2058 offset = self.readShort() if isShort else self.readLong()
2059 offset += self.offsetOfNewPage
2060 if isShort and offset >= 65536:
2061 # offset is now too large - we must convert shorts to longs
2062 if count != 1:
2063 raise RuntimeError("not implemented") # XXX TODO
2065 # simple case - the offset is just one and therefore it is
2066 # local (not referenced with another offset)
2067 self.rewriteLastShortToLong(offset)
2068 self.f.seek(-10, os.SEEK_CUR)
2069 self.writeShort(TiffTags.LONG) # rewrite the type to LONG
2070 self.f.seek(8, os.SEEK_CUR)
2071 elif isShort:
2072 self.rewriteLastShort(offset)
2073 else:
2074 self.rewriteLastLong(offset)
2077def _save_all(im, fp, filename):
2078 encoderinfo = im.encoderinfo.copy()
2079 encoderconfig = im.encoderconfig
2080 append_images = list(encoderinfo.get("append_images", []))
2081 if not hasattr(im, "n_frames") and not append_images:
2082 return _save(im, fp, filename)
2084 cur_idx = im.tell()
2085 try:
2086 with AppendingTiffWriter(fp) as tf:
2087 for ims in [im] + append_images:
2088 ims.encoderinfo = encoderinfo
2089 ims.encoderconfig = encoderconfig
2090 if not hasattr(ims, "n_frames"):
2091 nfr = 1
2092 else:
2093 nfr = ims.n_frames
2095 for idx in range(nfr):
2096 ims.seek(idx)
2097 ims.load()
2098 _save(ims, tf, filename)
2099 tf.newFrame()
2100 finally:
2101 im.seek(cur_idx)
2104#
2105# --------------------------------------------------------------------
2106# Register
2108Image.register_open(TiffImageFile.format, TiffImageFile, _accept)
2109Image.register_save(TiffImageFile.format, _save)
2110Image.register_save_all(TiffImageFile.format, _save_all)
2112Image.register_extensions(TiffImageFile.format, [".tif", ".tiff"])
2114Image.register_mime(TiffImageFile.format, "image/tiff")