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

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 

51 

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 

57 

58logger = logging.getLogger(__name__) 

59 

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 

65 

66II = b"II" # little-endian (Intel style) 

67MM = b"MM" # big-endian (Motorola style) 

68 

69# 

70# -------------------------------------------------------------------- 

71# Read TIFF files 

72 

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 

112 

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 

116 

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} 

137 

138COMPRESSION_INFO_REV = {v: k for k, v in COMPRESSION_INFO.items()} 

139 

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} 

258 

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] 

267 

268 

269def _accept(prefix): 

270 return prefix[:4] in PREFIXES 

271 

272 

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 

277 

278 

279def _limit_signed_rational(val, max_val, min_val): 

280 frac = Fraction(val) 

281 n_d = frac.numerator, frac.denominator 

282 

283 if min(n_d) < min_val: 

284 n_d = _limit_rational(val, abs(min_val)) 

285 

286 if max(n_d) > max_val: 

287 val = Fraction(*n_d) 

288 n_d = _limit_rational(val, max_val) 

289 

290 return n_d 

291 

292 

293## 

294# Wrapper for TIFF IFDs. 

295 

296_load_dispatch = {} 

297_write_dispatch = {} 

298 

299 

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. 

303 

304 e.g., DigitalZoomRatio - 0.00/0.00 indicates that no digital zoom was used 

305 """ 

306 

307 """ If the denominator is 0, store this as a float('nan'), otherwise store 

308 as a fractions.Fraction(). Delegate as appropriate 

309 

310 """ 

311 

312 __slots__ = ("_numerator", "_denominator", "_val") 

313 

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 

325 

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 

332 

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) 

339 

340 @property 

341 def numerator(self): 

342 return self._numerator 

343 

344 @property 

345 def denominator(self): 

346 return self._denominator 

347 

348 def limit_rational(self, max_denominator): 

349 """ 

350 

351 :param max_denominator: Integer, the maximum denominator value 

352 :returns: Tuple of (numerator, denominator) 

353 """ 

354 

355 if self.denominator == 0: 

356 return self.numerator, self.denominator 

357 

358 f = self._val.limit_denominator(max_denominator) 

359 return f.numerator, f.denominator 

360 

361 def __repr__(self): 

362 return str(float(self._val)) 

363 

364 def __hash__(self): 

365 return self._val.__hash__() 

366 

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 

374 

375 def __getstate__(self): 

376 return [self._val, self._numerator, self._denominator] 

377 

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 

384 

385 def _delegate(op): 

386 def delegate(self, *args): 

387 return getattr(self._val, op)(*args) 

388 

389 return delegate 

390 

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

398 

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__") 

425 

426 

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. 

430 

431 Exposes a dictionary interface of the tags in the directory:: 

432 

433 ifd = ImageFileDirectory_v2() 

434 ifd[key] = 'Some Data' 

435 ifd.tagtype[key] = TiffTags.ASCII 

436 print(ifd[key]) 

437 'Some Data' 

438 

439 Individual values are returned as the strings or numbers, sequences are 

440 returned as tuples of the values. 

441 

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. 

447 

448 Data Structures: 

449 

450 * ``self.tagtype = {}`` 

451 

452 * Key: numerical TIFF tag number 

453 * Value: integer corresponding to the data type from 

454 :py:data:`.TiffTags.TYPES` 

455 

456 .. versionadded:: 3.0.0 

457 

458 'Internal' data structures: 

459 

460 * ``self._tags_v2 = {}`` 

461 

462 * Key: numerical TIFF tag number 

463 * Value: decoded data, as tuple for multiple values 

464 

465 * ``self._tagdata = {}`` 

466 

467 * Key: numerical TIFF tag number 

468 * Value: undecoded byte string from file 

469 

470 * ``self._tags_v1 = {}`` 

471 

472 * Key: numerical TIFF tag number 

473 * Value: decoded data in the v1 format 

474 

475 Tags will be found in the private attributes ``self._tagdata``, and in 

476 ``self._tags_v2`` once decoded. 

477 

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

485 

486 """ 

487 

488 def __init__(self, ifh=b"II\052\0\0\0\0\0", prefix=None, group=None): 

489 """Initialize an ImageFileDirectory. 

490 

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. 

494 

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 

517 

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

521 

522 @legacy_api.setter 

523 def legacy_api(self, value): 

524 raise Exception("Not allowing setting of legacy api") 

525 

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 

533 

534 def __str__(self): 

535 return str(dict(self)) 

536 

537 def named(self): 

538 """ 

539 :returns: dict of name|key: value 

540 

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 } 

547 

548 def __len__(self): 

549 return len(set(self._tagdata) | set(self._tags_v2)) 

550 

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 

561 

562 def __contains__(self, tag): 

563 return tag in self._tags_v2 or tag in self._tagdata 

564 

565 def __setitem__(self, tag, value): 

566 self._setitem(tag, value, self.legacy_api) 

567 

568 def _setitem(self, tag, value, legacy_api): 

569 basetypes = (Number, bytes, str) 

570 

571 info = TiffTags.lookup(tag, self.group) 

572 values = [value] if isinstance(value, basetypes) else value 

573 

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 

602 

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] 

610 

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) 

614 

615 dest = self._tags_v1 if legacy_api else self._tags_v2 

616 

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] 

642 

643 else: 

644 # Spec'd length > 1 or undefined 

645 # Unspec'd, and length > 1 

646 dest[tag] = values 

647 

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) 

652 

653 def __iter__(self): 

654 return iter(set(self._tagdata) | set(self._tags_v2)) 

655 

656 def _unpack(self, fmt, data): 

657 return struct.unpack(self._endian + fmt, data) 

658 

659 def _pack(self, fmt, *values): 

660 return struct.pack(self._endian + fmt, *values) 

661 

662 def _register_loader(idx, size): 

663 def decorator(func): 

664 from .TiffTags import TYPES 

665 

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 

670 

671 return decorator 

672 

673 def _register_writer(idx): 

674 def decorator(func): 

675 _write_dispatch[idx] = func # noqa: F821 

676 return func 

677 

678 return decorator 

679 

680 def _register_basic(idx_fmt_name): 

681 from .TiffTags import TYPES 

682 

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 ) 

695 

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 ) 

712 

713 @_register_loader(1, 1) # Basic type, except for the legacy API. 

714 def load_byte(self, data, legacy_api=True): 

715 return data 

716 

717 @_register_writer(1) # Basic type, except for the legacy API. 

718 def write_byte(self, data): 

719 return data 

720 

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

726 

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" 

731 

732 @_register_loader(5, 8) 

733 def load_rational(self, data, legacy_api=True): 

734 vals = self._unpack(f"{len(data) // 4}L", data) 

735 

736 def combine(a, b): 

737 return (a, b) if legacy_api else IFDRational(a, b) 

738 

739 return tuple(combine(num, denom) for num, denom in zip(vals[::2], vals[1::2])) 

740 

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 ) 

746 

747 @_register_loader(7, 1) 

748 def load_undefined(self, data, legacy_api=True): 

749 return data 

750 

751 @_register_writer(7) 

752 def write_undefined(self, value): 

753 return value 

754 

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) 

758 

759 def combine(a, b): 

760 return (a, b) if legacy_api else IFDRational(a, b) 

761 

762 return tuple(combine(num, denom) for num, denom in zip(vals[::2], vals[1::2])) 

763 

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 ) 

770 

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 

779 

780 def load(self, fp): 

781 

782 self.reset() 

783 self._offset = fp.tell() 

784 

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 ) 

797 

798 tagname = TiffTags.lookup(tag, self.group).name 

799 typname = TYPES.get(typ, "unknown") 

800 msg = f"tag: {tagname} ({tag}) - type: {typname} ({typ})" 

801 

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] 

817 

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 

826 

827 if not data: 

828 logger.debug(msg) 

829 continue 

830 

831 self._tagdata[tag] = data 

832 self.tagtype[tag] = typ 

833 

834 msg += " - value: " + ( 

835 "<table: %d bytes>" % size if size > 32 else repr(data) 

836 ) 

837 logger.debug(msg) 

838 

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 

847 

848 def tobytes(self, offset=0): 

849 # FIXME What about tagdata? 

850 result = self._pack("H", len(self._tags_v2)) 

851 

852 entries = [] 

853 offset = offset + len(result) + len(self._tags_v2) * 12 + 4 

854 stripoffsets = None 

855 

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) 

877 

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) 

885 

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 

899 

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 

907 

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) 

912 

913 # -- overwrite here for multi-page -- 

914 result += b"\0\0\0\0" # end of entries 

915 

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" 

921 

922 return result 

923 

924 def save(self, fp): 

925 

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

929 

930 offset = fp.tell() 

931 result = self.tobytes(offset) 

932 fp.write(result) 

933 return offset + len(result) 

934 

935 

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 

943 

944 

945# Legacy ImageFileDirectory support. 

946class ImageFileDirectory_v1(ImageFileDirectory_v2): 

947 """This class represents the **legacy** interface to a TIFF tag directory. 

948 

949 Exposes a dictionary interface of the tags in the directory:: 

950 

951 ifd = ImageFileDirectory_v1() 

952 ifd[key] = 'Some Data' 

953 ifd.tagtype[key] = TiffTags.ASCII 

954 print(ifd[key]) 

955 ('Some Data',) 

956 

957 Also contains a dictionary of tag types as read from the tiff image file, 

958 :attr:`~PIL.TiffImagePlugin.ImageFileDirectory_v1.tagtype`. 

959 

960 Values are returned as a tuple. 

961 

962 .. deprecated:: 3.0.0 

963 """ 

964 

965 def __init__(self, *args, **kwargs): 

966 super().__init__(*args, **kwargs) 

967 self._legacy_api = True 

968 

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

971 

972 # defined in ImageFileDirectory_v2 

973 tagtype: dict 

974 """Dictionary of tag types""" 

975 

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. 

983 

984 :returns: :py:class:`~PIL.TiffImagePlugin.ImageFileDirectory_v1` 

985 

986 """ 

987 

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 

993 

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. 

1000 

1001 :returns: :py:class:`~PIL.TiffImagePlugin.ImageFileDirectory_v2` 

1002 

1003 """ 

1004 

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 

1010 

1011 def __contains__(self, tag): 

1012 return tag in self._tags_v1 or tag in self._tagdata 

1013 

1014 def __len__(self): 

1015 return len(set(self._tagdata) | set(self._tags_v1)) 

1016 

1017 def __iter__(self): 

1018 return iter(set(self._tagdata) | set(self._tags_v1)) 

1019 

1020 def __setitem__(self, tag, value): 

1021 for legacy_api in (False, True): 

1022 self._setitem(tag, value, legacy_api) 

1023 

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 

1035 

1036 

1037# undone -- switch this pointer when IFD_LEGACY_API == False 

1038ImageFileDirectory = ImageFileDirectory_v1 

1039 

1040 

1041## 

1042# Image plugin for TIFF files. 

1043 

1044 

1045class TiffImageFile(ImageFile.ImageFile): 

1046 

1047 format = "TIFF" 

1048 format_description = "Adobe TIFF" 

1049 _close_exclusive_fp_after_loading = False 

1050 

1051 def __init__(self, fp=None, filename=None): 

1052 self.tag_v2 = None 

1053 """ Image file directory (tag dictionary) """ 

1054 

1055 self.tag = None 

1056 """ Legacy tag entries """ 

1057 

1058 super().__init__(fp, filename) 

1059 

1060 def _open(self): 

1061 """Open the first image in a TIFF file""" 

1062 

1063 # Header 

1064 ifh = self.fp.read(8) 

1065 if ifh[2] == 43: 

1066 ifh += self.fp.read(8) 

1067 

1068 self.tag_v2 = ImageFileDirectory_v2(ifh) 

1069 

1070 # legacy IFD entries will be filled in later 

1071 self.ifd = None 

1072 

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 

1079 

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) 

1083 

1084 # and load the first frame 

1085 self._seek(0) 

1086 

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 

1096 

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) 

1107 

1108 def _seek(self, frame): 

1109 self.fp = self._fp 

1110 

1111 # reset buffered io handle in case fp 

1112 # was passed to libtiff, invalidating the buffer 

1113 self.fp.tell() 

1114 

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

1144 

1145 def tell(self): 

1146 """Return the current frame number""" 

1147 return self.__frame 

1148 

1149 def getxmp(self): 

1150 """ 

1151 Returns a dictionary containing the XMP tags. 

1152 Requires defusedxml to be installed. 

1153 

1154 :returns: XMP tags in a dictionary. 

1155 """ 

1156 return self._getxmp(self.tag_v2[700]) if 700 in self.tag_v2 else {} 

1157 

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 

1163 

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} 

1175 

1176 val = val[math.ceil((10 + n + size) / 2) * 2 :] 

1177 return blocks 

1178 

1179 def load(self): 

1180 if self.tile and self.use_load_libtiff: 

1181 return self._load_libtiff() 

1182 return super().load() 

1183 

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 

1198 

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 

1203 

1204 # reset buffered io handle in case fp 

1205 # was passed to libtiff, invalidating the buffer 

1206 self.fp.tell() 

1207 

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) 

1214 

1215 def _load_libtiff(self): 

1216 """Overload method triggered when we detect a compressed tiff 

1217 Calls out to libtiff""" 

1218 

1219 Image.Image.load(self) 

1220 

1221 self.load_prepare() 

1222 

1223 if not len(self.tile) == 1: 

1224 raise OSError("Not exactly one tile") 

1225 

1226 # (self._compression, (extents tuple), 

1227 # 0, (rawmode, self._compression, fp)) 

1228 extents = self.tile[0][1] 

1229 args = list(self.tile[0][3]) 

1230 

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 

1246 

1247 if fp: 

1248 args[2] = fp 

1249 

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 

1257 

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

1284 

1285 if fp: 

1286 try: 

1287 os.close(fp) 

1288 except OSError: 

1289 pass 

1290 

1291 self.tile = [] 

1292 self.readonly = 0 

1293 

1294 self.load_end() 

1295 

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 

1300 

1301 if err < 0: 

1302 raise OSError(err) 

1303 

1304 return Image.Image.load(self) 

1305 

1306 def _setup(self): 

1307 """Setup this image object based on current tags""" 

1308 

1309 if 0xBC01 in self.tag_v2: 

1310 raise OSError("Windows Media Photo files not yet supported") 

1311 

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) 

1315 

1316 # photometric is a required tag, but not everyone is reading 

1317 # the specification 

1318 photo = self.tag_v2.get(PHOTOMETRIC_INTERPRETATION, 0) 

1319 

1320 # old style jpeg compression images most certainly are YCbCr 

1321 if self._compression == "tiff_jpeg": 

1322 photo = 6 

1323 

1324 fillorder = self.tag_v2.get(FILLORDER, 1) 

1325 

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)}") 

1332 

1333 # size 

1334 xsize = int(self.tag_v2.get(IMAGEWIDTH)) 

1335 ysize = int(self.tag_v2.get(IMAGELENGTH)) 

1336 self._size = xsize, ysize 

1337 

1338 logger.debug(f"- size: {self.size}") 

1339 

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

1348 

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 

1371 

1372 if len(bps_tuple) != samples_per_pixel: 

1373 raise SyntaxError("unknown data organization") 

1374 

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 

1390 

1391 logger.debug(f"- raw mode: {rawmode}") 

1392 logger.debug(f"- pil mode: {self.mode}") 

1393 

1394 self.info["compression"] = self._compression 

1395 

1396 xres = self.tag_v2.get(X_RESOLUTION, 1) 

1397 yres = self.tag_v2.get(Y_RESOLUTION, 1) 

1398 

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 

1412 

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. 

1425 

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

1448 

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" 

1457 

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

1462 

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) 

1474 

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 

1480 

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 

1487 

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

1506 

1507 # Fix up info. 

1508 if ICCPROFILE in self.tag_v2: 

1509 self.info["icc_profile"] = self.tag_v2[ICCPROFILE] 

1510 

1511 # fixup palette descriptor 

1512 

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

1516 

1517 self._tile_orientation = self.tag_v2.get(0x0112) 

1518 

1519 

1520# 

1521# -------------------------------------------------------------------- 

1522# Write TIFF files 

1523 

1524# little endian is default except for image modes with 

1525# explicit big endian byte-order 

1526 

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} 

1550 

1551 

1552def _save(im, fp, filename): 

1553 

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 

1558 

1559 ifd = ImageFileDirectory_v2(prefix=prefix) 

1560 

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" 

1577 

1578 libtiff = WRITE_LIBTIFF or compression != "raw" 

1579 

1580 # required for color libtiff images 

1581 ifd[PLANAR_CONFIGURATION] = 1 

1582 

1583 ifd[IMAGEWIDTH] = im.size[0] 

1584 ifd[IMAGELENGTH] = im.size[1] 

1585 

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 

1609 

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] 

1625 

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 

1631 

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] 

1646 

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] 

1652 

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 

1661 

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) 

1674 

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) 

1708 

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) 

1715 

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 

1727 

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 

1737 

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 ] 

1753 

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

1762 

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] 

1768 

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 

1777 

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 

1793 

1794 if SAMPLEFORMAT in atts and len(atts[SAMPLEFORMAT]) == 1: 

1795 atts[SAMPLEFORMAT] = atts[SAMPLEFORMAT][0] 

1796 

1797 logger.debug("Converted items: %s" % sorted(atts.items())) 

1798 

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" 

1805 

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

1823 

1824 else: 

1825 for tag in blocklist: 

1826 del ifd[tag] 

1827 offset = ifd.save(fp) 

1828 

1829 ImageFile._save( 

1830 im, fp, [("raw", (0, 0) + im.size, offset, (rawmode, stride, 1))] 

1831 ) 

1832 

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 

1837 

1838 

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 ] 

1855 

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} 

1863 

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

1877 

1878 def setup(self): 

1879 # Reset everything. 

1880 self.f.seek(self.beginning, os.SEEK_SET) 

1881 

1882 self.whereToWriteNewIFDOffset = None 

1883 self.offsetOfNewPage = 0 

1884 

1885 self.IIMM = iimm = self.f.read(4) 

1886 if not iimm: 

1887 # empty file - first page 

1888 self.isFirst = True 

1889 return 

1890 

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

1898 

1899 self.skipIFDs() 

1900 self.goToEnd() 

1901 

1902 def finalize(self): 

1903 if self.isFirst: 

1904 return 

1905 

1906 # fix offsets 

1907 self.f.seek(self.offsetOfNewPage) 

1908 

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 

1914 

1915 if iimm != self.IIMM: 

1916 raise RuntimeError("IIMM of new page doesn't match IIMM of first page") 

1917 

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

1924 

1925 def newFrame(self): 

1926 # Call this to finish a frame. 

1927 self.finalize() 

1928 self.setup() 

1929 

1930 def __enter__(self): 

1931 return self 

1932 

1933 def __exit__(self, exc_type, exc_value, traceback): 

1934 if self.close_fp: 

1935 self.close() 

1936 return False 

1937 

1938 def tell(self): 

1939 return self.f.tell() - self.offsetOfNewPage 

1940 

1941 def seek(self, offset, whence=io.SEEK_SET): 

1942 if whence == os.SEEK_SET: 

1943 offset += self.offsetOfNewPage 

1944 

1945 self.f.seek(offset, whence) 

1946 return self.tell() 

1947 

1948 def goToEnd(self): 

1949 self.f.seek(0, os.SEEK_END) 

1950 pos = self.f.tell() 

1951 

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

1957 

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" 

1963 

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 

1970 

1971 self.f.seek(ifd_offset) 

1972 num_tags = self.readShort() 

1973 self.f.seek(num_tags * 12, os.SEEK_CUR) 

1974 

1975 def write(self, data): 

1976 return self.f.write(data) 

1977 

1978 def readShort(self): 

1979 (value,) = struct.unpack(self.shortFmt, self.f.read(2)) 

1980 return value 

1981 

1982 def readLong(self): 

1983 (value,) = struct.unpack(self.longFmt, self.f.read(4)) 

1984 return value 

1985 

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

1991 

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

1997 

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

2003 

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

2008 

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

2013 

2014 def close(self): 

2015 self.finalize() 

2016 self.f.close() 

2017 

2018 def fixIFD(self): 

2019 num_tags = self.readShort() 

2020 

2021 for i in range(num_tags): 

2022 tag, field_type, count = struct.unpack(self.tagFormat, self.f.read(8)) 

2023 

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) 

2031 

2032 if tag in self.Tags: 

2033 cur_pos = self.f.tell() 

2034 

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) 

2046 

2047 offset = cur_pos = None 

2048 

2049 elif is_local: 

2050 # skip the locally stored value that is not an offset 

2051 self.f.seek(4, os.SEEK_CUR) 

2052 

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

2056 

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 

2064 

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) 

2075 

2076 

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) 

2083 

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 

2094 

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) 

2102 

2103 

2104# 

2105# -------------------------------------------------------------------- 

2106# Register 

2107 

2108Image.register_open(TiffImageFile.format, TiffImageFile, _accept) 

2109Image.register_save(TiffImageFile.format, _save) 

2110Image.register_save_all(TiffImageFile.format, _save_all) 

2111 

2112Image.register_extensions(TiffImageFile.format, [".tif", ".tiff"]) 

2113 

2114Image.register_mime(TiffImageFile.format, "image/tiff")