@@ -962,13 +962,16 @@ def tobytes(self, offset: int = 0) -> bytes:
962962 result = self ._pack ("Q" if self ._bigtiff else "H" , len (self ._tags_v2 ))
963963
964964 entries : list [tuple [int , int , int , bytes , bytes ]] = []
965- offset += len (result ) + len (self ._tags_v2 ) * (20 if self ._bigtiff else 12 ) + 4
965+
966+ fmt = "Q" if self ._bigtiff else "L"
967+ fmt_size = 8 if self ._bigtiff else 4
968+ offset += (
969+ len (result ) + len (self ._tags_v2 ) * (20 if self ._bigtiff else 12 ) + fmt_size
970+ )
966971 stripoffsets = None
967972
968973 # pass 1: convert tags to binary format
969974 # always write tags in ascending order
970- fmt = "Q" if self ._bigtiff else "L"
971- fmt_size = 8 if self ._bigtiff else 4
972975 for tag , value in sorted (self ._tags_v2 .items ()):
973976 if tag == STRIPOFFSETS :
974977 stripoffsets = len (entries )
@@ -1024,7 +1027,7 @@ def tobytes(self, offset: int = 0) -> bytes:
10241027 )
10251028
10261029 # -- overwrite here for multi-page --
1027- result += b" \0 \0 \0 \0 " # end of entries
1030+ result += self . _pack ( fmt , 0 ) # end of entries
10281031
10291032 # pass 3: write auxiliary data to file
10301033 for tag , typ , count , value , data in entries :
@@ -2044,20 +2047,21 @@ def setup(self) -> None:
20442047 self .offsetOfNewPage = 0
20452048
20462049 self .IIMM = iimm = self .f .read (4 )
2050+ self ._bigtiff = b"\x2B " in iimm
20472051 if not iimm :
20482052 # empty file - first page
20492053 self .isFirst = True
20502054 return
20512055
20522056 self .isFirst = False
2053- if iimm == b"II\x2a \x00 " :
2054- self .setEndian ("<" )
2055- elif iimm == b"MM\x00 \x2a " :
2056- self .setEndian (">" )
2057- else :
2057+ if iimm not in PREFIXES :
20582058 msg = "Invalid TIFF file header"
20592059 raise RuntimeError (msg )
20602060
2061+ self .setEndian ("<" if iimm .startswith (II ) else ">" )
2062+
2063+ if self ._bigtiff :
2064+ self .f .seek (4 , os .SEEK_CUR )
20612065 self .skipIFDs ()
20622066 self .goToEnd ()
20632067
@@ -2077,11 +2081,13 @@ def finalize(self) -> None:
20772081 msg = "IIMM of new page doesn't match IIMM of first page"
20782082 raise RuntimeError (msg )
20792083
2080- ifd_offset = self .readLong ()
2084+ if self ._bigtiff :
2085+ self .f .seek (4 , os .SEEK_CUR )
2086+ ifd_offset = self ._read (8 if self ._bigtiff else 4 )
20812087 ifd_offset += self .offsetOfNewPage
20822088 assert self .whereToWriteNewIFDOffset is not None
20832089 self .f .seek (self .whereToWriteNewIFDOffset )
2084- self .writeLong (ifd_offset )
2090+ self ._write (ifd_offset , 8 if self . _bigtiff else 4 )
20852091 self .f .seek (ifd_offset )
20862092 self .fixIFD ()
20872093
@@ -2127,18 +2133,20 @@ def setEndian(self, endian: str) -> None:
21272133 self .endian = endian
21282134 self .longFmt = f"{ self .endian } L"
21292135 self .shortFmt = f"{ self .endian } H"
2130- self .tagFormat = f"{ self .endian } HHL"
2136+ self .tagFormat = f"{ self .endian } HH" + ( "Q" if self . _bigtiff else "L" )
21312137
21322138 def skipIFDs (self ) -> None :
21332139 while True :
2134- ifd_offset = self .readLong ( )
2140+ ifd_offset = self ._read ( 8 if self . _bigtiff else 4 )
21352141 if ifd_offset == 0 :
2136- self .whereToWriteNewIFDOffset = self .f .tell () - 4
2142+ self .whereToWriteNewIFDOffset = self .f .tell () - (
2143+ 8 if self ._bigtiff else 4
2144+ )
21372145 break
21382146
21392147 self .f .seek (ifd_offset )
2140- num_tags = self .readShort ( )
2141- self .f .seek (num_tags * 12 , os .SEEK_CUR )
2148+ num_tags = self ._read ( 8 if self . _bigtiff else 2 )
2149+ self .f .seek (num_tags * ( 20 if self . _bigtiff else 12 ) , os .SEEK_CUR )
21422150
21432151 def write (self , data : Buffer , / ) -> int :
21442152 return self .f .write (data )
@@ -2168,81 +2176,99 @@ def _verify_bytes_written(bytes_written: int | None, expected: int) -> None:
21682176 msg = f"wrote only { bytes_written } bytes but wanted { expected } "
21692177 raise RuntimeError (msg )
21702178
2171- def rewriteLastShortToLong (self , value : int ) -> None :
2172- self .f .seek (- 2 , os .SEEK_CUR )
2173- bytes_written = self .f .write (struct .pack (self .longFmt , value ))
2174- self ._verify_bytes_written (bytes_written , 4 )
2175-
2176- def _rewriteLast (self , value : int , field_size : int ) -> None :
2179+ def _rewriteLast (
2180+ self , value : int , field_size : int , new_field_size : int = 0
2181+ ) -> None :
21772182 self .f .seek (- field_size , os .SEEK_CUR )
2183+ if not new_field_size :
2184+ new_field_size = field_size
21782185 bytes_written = self .f .write (
2179- struct .pack (self .endian + self ._fmt (field_size ), value )
2186+ struct .pack (self .endian + self ._fmt (new_field_size ), value )
21802187 )
2181- self ._verify_bytes_written (bytes_written , field_size )
2188+ self ._verify_bytes_written (bytes_written , new_field_size )
2189+
2190+ def rewriteLastShortToLong (self , value : int ) -> None :
2191+ self ._rewriteLast (value , 2 , 4 )
21822192
21832193 def rewriteLastShort (self , value : int ) -> None :
21842194 return self ._rewriteLast (value , 2 )
21852195
21862196 def rewriteLastLong (self , value : int ) -> None :
21872197 return self ._rewriteLast (value , 4 )
21882198
2199+ def _write (self , value : int , field_size : int ) -> None :
2200+ bytes_written = self .f .write (
2201+ struct .pack (self .endian + self ._fmt (field_size ), value )
2202+ )
2203+ self ._verify_bytes_written (bytes_written , field_size )
2204+
21892205 def writeShort (self , value : int ) -> None :
2190- bytes_written = self .f .write (struct .pack (self .shortFmt , value ))
2191- self ._verify_bytes_written (bytes_written , 2 )
2206+ self ._write (value , 2 )
21922207
21932208 def writeLong (self , value : int ) -> None :
2194- bytes_written = self .f .write (struct .pack (self .longFmt , value ))
2195- self ._verify_bytes_written (bytes_written , 4 )
2209+ self ._write (value , 4 )
21962210
21972211 def close (self ) -> None :
21982212 self .finalize ()
21992213 if self .close_fp :
22002214 self .f .close ()
22012215
22022216 def fixIFD (self ) -> None :
2203- num_tags = self .readShort ( )
2217+ num_tags = self ._read ( 8 if self . _bigtiff else 2 )
22042218
22052219 for i in range (num_tags ):
2206- tag , field_type , count = struct .unpack (self .tagFormat , self .f .read (8 ))
2220+ tag , field_type , count = struct .unpack (
2221+ self .tagFormat , self .f .read (12 if self ._bigtiff else 8 )
2222+ )
22072223
22082224 field_size = self .fieldSizes [field_type ]
22092225 total_size = field_size * count
2210- is_local = total_size <= 4
2226+ fmt_size = 8 if self ._bigtiff else 4
2227+ is_local = total_size <= fmt_size
22112228 if not is_local :
2212- offset = self .readLong ( ) + self .offsetOfNewPage
2213- self .rewriteLastLong (offset )
2229+ offset = self ._read ( fmt_size ) + self .offsetOfNewPage
2230+ self ._rewriteLast (offset , fmt_size )
22142231
22152232 if tag in self .Tags :
22162233 cur_pos = self .f .tell ()
22172234
22182235 if is_local :
22192236 self ._fixOffsets (count , field_size )
2220- self .f .seek (cur_pos + 4 )
2237+ self .f .seek (cur_pos + fmt_size )
22212238 else :
22222239 self .f .seek (offset )
22232240 self ._fixOffsets (count , field_size )
22242241 self .f .seek (cur_pos )
22252242
22262243 elif is_local :
22272244 # skip the locally stored value that is not an offset
2228- self .f .seek (4 , os .SEEK_CUR )
2245+ self .f .seek (fmt_size , os .SEEK_CUR )
22292246
22302247 def _fixOffsets (self , count : int , field_size : int ) -> None :
22312248 for i in range (count ):
22322249 offset = self ._read (field_size )
22332250 offset += self .offsetOfNewPage
2234- if field_size == 2 and offset >= 65536 :
2235- # offset is now too large - we must convert shorts to longs
2251+
2252+ new_field_size = 0
2253+ if self ._bigtiff and field_size in (2 , 4 ) and offset >= 2 ** 32 :
2254+ # offset is now too large - we must convert long to long8
2255+ new_field_size = 8
2256+ elif field_size == 2 and offset >= 2 ** 16 :
2257+ # offset is now too large - we must convert short to long
2258+ new_field_size = 4
2259+ if new_field_size :
22362260 if count != 1 :
22372261 msg = "not implemented"
22382262 raise RuntimeError (msg ) # XXX TODO
22392263
22402264 # simple case - the offset is just one and therefore it is
22412265 # local (not referenced with another offset)
2242- self .rewriteLastShortToLong (offset )
2243- self .f .seek (- 10 , os .SEEK_CUR )
2244- self .writeShort (TiffTags .LONG ) # rewrite the type to LONG
2245- self .f .seek (8 , os .SEEK_CUR )
2266+ self ._rewriteLast (offset , field_size , new_field_size )
2267+ # Move back past the new offset, past 'count', and before 'field_type'
2268+ rewind = - new_field_size - 4 - 2
2269+ self .f .seek (rewind , os .SEEK_CUR )
2270+ self .writeShort (new_field_size ) # rewrite the type
2271+ self .f .seek (2 - rewind , os .SEEK_CUR )
22462272 else :
22472273 self ._rewriteLast (offset , field_size )
22482274
0 commit comments