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