|  |  | @@ -15,7 +15,7 @@ import struct | 
		
	
		
			
			|  |  |  | # CRW: https://web.archive.org/web/20081230095207/http://xyrion.org/ciff/CIFFspecV1R04.pdf | 
		
	
		
			
			|  |  |  | # CR2: https://web.archive.org/web/20230404015346/http://lclevy.free.fr/cr2/ | 
		
	
		
			
			|  |  |  | # JPEG: https://www.w3.org/Graphics/JPEG/itu-t81.pdf | 
		
	
		
			
			|  |  |  | # JFIF: http://www.w3.org/Graphics/JPEG/jfif3.pdf | 
		
	
		
			
			|  |  |  | # JFIF: https://www.w3.org/Graphics/JPEG/jfif3.pdf | 
		
	
		
			
			|  |  |  | # EXIF: https://www.cipa.jp/std/documents/e/DC-X008-Translation-2019-E.pdf | 
		
	
		
			
			|  |  |  | # | 
		
	
		
			
			|  |  |  | # Exif Tags: | 
		
	
	
		
			
				|  |  | @@ -407,11 +407,7 @@ class CanonMakerNote(enum.IntEnum): | 
		
	
		
			
			|  |  |  | PictureStylePC = 0x4009 | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | canonmakernotehandlers = { | 
		
	
		
			
			|  |  |  | CanonMakerNote.FirmwareVersion: lambda fh, endian, res, off: | 
		
	
		
			
			|  |  |  | tuple(res.split(b'\x00', 1)), | 
		
	
		
			
			|  |  |  | CanonMakerNote.Lens: lambda fh, endian, res, off: | 
		
	
		
			
			|  |  |  | res.split(b'\x00', 1)[0], | 
		
	
		
			
			|  |  |  | } | 
		
	
		
			
			|  |  |  | } | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | # Needed by Exif | 
		
	
		
			
			|  |  |  | class TIFFResolutionUnit(enum.IntEnum): | 
		
	
	
		
			
				|  |  | @@ -892,10 +888,16 @@ def tiff_bytes(endian, data): | 
		
	
		
			
			|  |  |  | return map(ord, data) | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | def tiff_ascii(endian, data): | 
		
	
		
			
			|  |  |  | if data[-1] != '\x00': | 
		
	
		
			
			|  |  |  | return data	# XXX - Canon MakerNote requires this. | 
		
	
		
			
			|  |  |  | #raise ValueError, 'string does not terminate with NUL' | 
		
	
		
			
			|  |  |  | return data[:-1] | 
		
	
		
			
			|  |  |  | if data[-2:] == b'\x00\x00': | 
		
	
		
			
			|  |  |  | # multipl strings | 
		
	
		
			
			|  |  |  | return [ x.decode('ASCII') for x in data[:-2].split(b'\x00') ] | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | if data[-1] != 0: | 
		
	
		
			
			|  |  |  | #print('d:', repr(data)) | 
		
	
		
			
			|  |  |  | #raise ValueError('string does not terminate with NUL') | 
		
	
		
			
			|  |  |  | return data | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | return data[:-1].decode('ascii') | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | def tiff_short(endian, data): | 
		
	
		
			
			|  |  |  | return struct.unpack(endian + 'H' * (len(data) // 2), data) | 
		
	
	
		
			
				|  |  | @@ -922,10 +924,15 @@ tifftypes = { | 
		
	
		
			
			|  |  |  | 4: (tiff_long, 4), | 
		
	
		
			
			|  |  |  | 5: (tiff_rational, 8), | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | #6: sbyte | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | # Exif Types | 
		
	
		
			
			|  |  |  | 7: (lambda x, y: y, 1),	# byte string | 
		
	
		
			
			|  |  |  | #8: sshort | 
		
	
		
			
			|  |  |  | 9: (tiff_slong, 4), | 
		
	
		
			
			|  |  |  | 10: (tiff_srational, 8), | 
		
	
		
			
			|  |  |  | #11: single float | 
		
	
		
			
			|  |  |  | #12: double float | 
		
	
		
			
			|  |  |  | } | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | TIFF_IFD_CNT = 'H' | 
		
	
	
		
			
				|  |  | @@ -942,6 +949,7 @@ def tiff_ifd(fh, endian, off): | 
		
	
		
			
			|  |  |  | for i in range(cnt): | 
		
	
		
			
			|  |  |  | tag, ttype, length, valoff = entries[i * | 
		
	
		
			
			|  |  |  | TIFF_IFD_ENTRY_CNT:(i + 1) * TIFF_IFD_ENTRY_CNT] | 
		
	
		
			
			|  |  |  | #print('t:', repr(tag), repr(ttype), repr(length), repr(valoff)) | 
		
	
		
			
			|  |  |  | typefun, typequantum = tifftypes[ttype] | 
		
	
		
			
			|  |  |  | blength = length * typequantum | 
		
	
		
			
			|  |  |  | if blength <= 4: | 
		
	
	
		
			
				|  |  | @@ -1021,26 +1029,6 @@ def getendian(val): | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | return endian | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | class fileoff: | 
		
	
		
			
			|  |  |  | '''A wrapper around a file object that pretends it | 
		
	
		
			
			|  |  |  | starts at off. | 
		
	
		
			
			|  |  |  | ''' | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | def __init__(self, fh, off): | 
		
	
		
			
			|  |  |  | self.fh = fh | 
		
	
		
			
			|  |  |  | self.off = off | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | def seek(self, arg, whence=0): | 
		
	
		
			
			|  |  |  | if whence == 0: | 
		
	
		
			
			|  |  |  | arg += self.off | 
		
	
		
			
			|  |  |  | return self.fh.seek(arg, whence) | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | def read(self, *args): | 
		
	
		
			
			|  |  |  | return self.fh.read(*args) | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | def tell(self): | 
		
	
		
			
			|  |  |  | return self.th.tell() - self.off | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | def idcrw(fh): | 
		
	
		
			
			|  |  |  | fh.seek(0) | 
		
	
		
			
			|  |  |  | isjpeg = False | 
		
	
	
		
			
				|  |  | @@ -1059,6 +1047,7 @@ def idcrw(fh): | 
		
	
		
			
			|  |  |  | while True: | 
		
	
		
			
			|  |  |  | fh.seek(pos) | 
		
	
		
			
			|  |  |  | data = fh.read(10) | 
		
	
		
			
			|  |  |  | marklen = int.from_bytes(data[2:4], 'big') | 
		
	
		
			
			|  |  |  | if data == b'': | 
		
	
		
			
			|  |  |  | raise ValueError('unexpected end of file') | 
		
	
		
			
			|  |  |  | 
 | 
		
	
	
		
			
				|  |  | @@ -1068,14 +1057,14 @@ def idcrw(fh): | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | if data[:2] != b'\xff\xe1' or data[4:10] != b'Exif\x00\x00': | 
		
	
		
			
			|  |  |  | # Skip over marker | 
		
	
		
			
			|  |  |  | pos += 2 + int.from_bytes(data[2:4], 'big') | 
		
	
		
			
			|  |  |  | pos += 2 + marklen | 
		
	
		
			
			|  |  |  | continue | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | # required due to coverage bug | 
		
	
		
			
			|  |  |  | if True: #pragma: no cover | 
		
	
		
			
			|  |  |  | break | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | fh = fileoff(fh, fh.tell()) | 
		
	
		
			
			|  |  |  | fh = BytesIO(fh.read(marklen - 8)) | 
		
	
		
			
			|  |  |  | endian = getendian(fh.read(2)) | 
		
	
		
			
			|  |  |  | isjpeg = True | 
		
	
		
			
			|  |  |  | 
 | 
		
	
	
		
			
				|  |  | @@ -1235,6 +1224,7 @@ class _TestCRW(unittest.TestCase): | 
		
	
		
			
			|  |  |  | self.assertEqual(ci[0][TIFFTag.ExifIFDPointer][ExifTag.ExposureTime][0], Fraction(1, 200)) | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | #print(repr(ci)) | 
		
	
		
			
			|  |  |  | #print(repr(ci[0][TIFFTag.ExifIFDPointer][ExifTag.MakerNote][CanonMakerNote.SerialInfo])) | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | def test_jpegexif(self): | 
		
	
		
			
			|  |  |  | with open(self.fixtures / 'exif.jpeg', 'rb') as fp: | 
		
	
	
		
			
				|  |  | @@ -1242,5 +1232,7 @@ class _TestCRW(unittest.TestCase): | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | self.assertEqual(ci[0][TIFFTag.ExifIFDPointer][ExifTag.ISOSpeedRatings][0], 100) | 
		
	
		
			
			|  |  |  | self.assertEqual(ci[0][TIFFTag.ExifIFDPointer][ExifTag.UserComment], b'UNICODE\x00' + 'abc123สวัสดี'.encode('utf-16-be')) | 
		
	
		
			
			|  |  |  | self.assertEqual(ci[0][TIFFTag.ExifIFDPointer][ExifTag.LensMake], b'Random Lens Maker\x00') | 
		
	
		
			
			|  |  |  | self.assertEqual(ci[0][TIFFTag.ImageDescription], b'Some comment\x00') | 
		
	
		
			
			|  |  |  | self.assertEqual(ci[0][TIFFTag.ExifIFDPointer][ExifTag.LensMake], 'Random Lens Maker') | 
		
	
		
			
			|  |  |  | self.assertEqual(ci[0][TIFFTag.ImageDescription], 'Some comment') | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | #print(repr(ci)) |