degal/lib/EXIF.py
changeset 98 d7d98c4479ab
parent 54 cc007b6ab972
equal deleted inserted replaced
97:92c20f8b297f 98:d7d98c4479ab
    79 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
    79 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
    80 #
    80 #
    81 #
    81 #
    82 # ----- See 'changes.txt' file for all contributors and changes ----- #
    82 # ----- See 'changes.txt' file for all contributors and changes ----- #
    83 #
    83 #
    84 
    84 import mmap
    85 
    85 
    86 # Don't throw an exception when given an out of range character.
    86 # Don't throw an exception when given an out of range character.
    87 def make_string(seq):
    87 def make_string(seq):
    88     str = ''
    88     str = ''
    89     for c in seq:
    89     for c in seq:
  1238 
  1238 
  1239 # class that handles an EXIF header
  1239 # class that handles an EXIF header
  1240 class EXIF_header:
  1240 class EXIF_header:
  1241     def __init__(self, file, endian, offset, fake_exif, strict, debug=0):
  1241     def __init__(self, file, endian, offset, fake_exif, strict, debug=0):
  1242         self.file = file
  1242         self.file = file
       
  1243         self.mmap = mmap.mmap(file.fileno(), 0, access=mmap.ACCESS_READ)
  1243         self.endian = endian
  1244         self.endian = endian
  1244         self.offset = offset
  1245         self.offset = offset
  1245         self.fake_exif = fake_exif
  1246         self.fake_exif = fake_exif
  1246         self.strict = strict
  1247         self.strict = strict
  1247         self.debug = debug
  1248         self.debug = debug
  1248         self.tags = {}
  1249         self.tags = {}
       
  1250     
       
  1251     def pread(self, reloffset, length):
       
  1252         """Read <length> bytes from self.file at relative offset <offset>"""
       
  1253         
       
  1254         offset = self.offset + reloffset
       
  1255 
       
  1256         return self.mmap[offset:offset + length]
  1249 
  1257 
  1250     # convert slice to integer, based on sign and endian flags
  1258     # convert slice to integer, based on sign and endian flags
  1251     # usually this offset is assumed to be relative to the beginning of the
  1259     # usually this offset is assumed to be relative to the beginning of the
  1252     # start of the EXIF information.  For some cameras that use relative tags,
  1260     # start of the EXIF information.  For some cameras that use relative tags,
  1253     # this offset may be relative to some other starting point.
  1261     # this offset may be relative to some other starting point.
  1254     def s2n(self, offset, length, signed=0):
  1262     def s2n(self, offset, length, signed=0):
  1255         self.file.seek(self.offset+offset)
  1263         slice=self.pread(offset, length)
  1256         slice=self.file.read(length)
       
  1257         if self.endian == 'I':
  1264         if self.endian == 'I':
  1258             val=s2n_intel(slice)
  1265             val=s2n_intel(slice)
  1259         else:
  1266         else:
  1260             val=s2n_motorola(slice)
  1267             val=s2n_motorola(slice)
  1261         # Sign extension ?
  1268         # Sign extension ?
  1347                 if field_type == 2:
  1354                 if field_type == 2:
  1348                     # special case: null-terminated ASCII string
  1355                     # special case: null-terminated ASCII string
  1349                     # XXX investigate
  1356                     # XXX investigate
  1350                     # sometimes gets too big to fit in int value
  1357                     # sometimes gets too big to fit in int value
  1351                     if count != 0 and count < (2**31):
  1358                     if count != 0 and count < (2**31):
  1352                         self.file.seek(self.offset + offset)
  1359                         values = self.pread(offset, count)
  1353                         values = self.file.read(count)
       
  1354                         #print values
  1360                         #print values
  1355                         # Drop any garbage after a null.
  1361                         # Drop any garbage after a null.
  1356                         values = values.split('\x00', 1)[0]
  1362                         values = values.split('\x00', 1)[0]
  1357                     else:
  1363                     else:
  1358                         values = ''
  1364                         values = ''
  1424         if self.endian == 'M':
  1430         if self.endian == 'M':
  1425             tiff = 'MM\x00*\x00\x00\x00\x08'
  1431             tiff = 'MM\x00*\x00\x00\x00\x08'
  1426         else:
  1432         else:
  1427             tiff = 'II*\x00\x08\x00\x00\x00'
  1433             tiff = 'II*\x00\x08\x00\x00\x00'
  1428         # ... plus thumbnail IFD data plus a null "next IFD" pointer
  1434         # ... plus thumbnail IFD data plus a null "next IFD" pointer
  1429         self.file.seek(self.offset+thumb_ifd)
  1435         tiff += self.pread(thumb_ifd, entries*12+2)+'\x00\x00\x00\x00'
  1430         tiff += self.file.read(entries*12+2)+'\x00\x00\x00\x00'
       
  1431 
  1436 
  1432         # fix up large value offset pointers into data area
  1437         # fix up large value offset pointers into data area
  1433         for i in range(entries):
  1438         for i in range(entries):
  1434             entry = thumb_ifd + 2 + 12 * i
  1439             entry = thumb_ifd + 2 + 12 * i
  1435             tag = self.s2n(entry, 2)
  1440             tag = self.s2n(entry, 2)
  1452                 # remember strip offsets location
  1457                 # remember strip offsets location
  1453                 if tag == 0x0111:
  1458                 if tag == 0x0111:
  1454                     strip_off = newoff
  1459                     strip_off = newoff
  1455                     strip_len = 4
  1460                     strip_len = 4
  1456                 # get original data and store it
  1461                 # get original data and store it
  1457                 self.file.seek(self.offset + oldoff)
  1462                 tiff += self.pread(oldoff, count * typelen)
  1458                 tiff += self.file.read(count * typelen)
       
  1459 
  1463 
  1460         # add pixel strips and update strip offset info
  1464         # add pixel strips and update strip offset info
  1461         old_offsets = self.tags['Thumbnail StripOffsets'].values
  1465         old_offsets = self.tags['Thumbnail StripOffsets'].values
  1462         old_counts = self.tags['Thumbnail StripByteCounts'].values
  1466         old_counts = self.tags['Thumbnail StripByteCounts'].values
  1463         for i in range(len(old_offsets)):
  1467         for i in range(len(old_offsets)):
  1464             # update offset pointer (more nasty "strings are immutable" crap)
  1468             # update offset pointer (more nasty "strings are immutable" crap)
  1465             offset = self.n2s(len(tiff), strip_len)
  1469             offset = self.n2s(len(tiff), strip_len)
  1466             tiff = tiff[:strip_off] + offset + tiff[strip_off + strip_len:]
  1470             tiff = tiff[:strip_off] + offset + tiff[strip_off + strip_len:]
  1467             strip_off += strip_len
  1471             strip_off += strip_len
  1468             # add pixel strip to end
  1472             # add pixel strip to end
  1469             self.file.seek(self.offset + old_offsets[i])
  1473             tiff += self.pread(old_offsets[i], old_counts[i])
  1470             tiff += self.file.read(old_counts[i])
       
  1471 
  1474 
  1472         self.tags['TIFFThumbnail'] = tiff
  1475         self.tags['TIFFThumbnail'] = tiff
  1473 
  1476 
  1474     # decode all the camera-specific MakerNote formats
  1477     # decode all the camera-specific MakerNote formats
  1475 
  1478