diff -r ef2c1ffdca8f -r 63e89dc2d6f1 degal/exif.py --- a/degal/exif.py Sat Jun 13 18:34:55 2009 +0300 +++ b/degal/exif.py Sat Jun 13 19:21:12 2009 +0300 @@ -34,7 +34,7 @@ """ # store - self.buf = buffer(obj, offset, size) + self.buf = buffer(obj, *(arg for arg in (offset, size) if arg is not None)) self.offset = offset self.size = size self.prefix = struct_prefix @@ -107,7 +107,7 @@ Represents a single Tag in an IFD """ - def __init__ (self, offset, tag, type, count, value_ref) : + def __init__ (self, offset, tag, type, count, data_raw) : """ Build a Tag with the given binary items from the IFD entry """ @@ -116,14 +116,14 @@ self.tag = tag self.type = type self.count = count - self.value_ref = value_ref + self.data_raw = data_raw # lookup the type for this tag self.type_data = exif_data.FIELD_TYPES.get(type) # unpack it if self.type_data : - self.type_format, self.type_name = self.type_data + self.type_format, self.type_name, self.type_func = self.type_data # lookup the tag data for this tag self.tag_data = exif_data.EXIF_TAGS.get(tag) @@ -131,7 +131,7 @@ # unpack it if self.tag_data : # the EXIF tag name - self.tag_name = tag_data[0] + self.tag_name = self.tag_data[0] # the optional value formatting specification if len(self.tag_data) > 1 : @@ -152,6 +152,19 @@ else : return None + def process_values (self, raw_values) : + """ + Process the given raw values unpacked from the file. + """ + + if self.type_data and self.type_func : + # use the filter func + return self.type_func(raw_values) + + else : + # nada, just leave them + return raw_values + def readable_value (self, value) : """ Convert the given value for this tag into a human-readable string. @@ -161,7 +174,7 @@ if self.tag_data and self.tag_value_spec : # map it - return exif_data.tag_value(self.tag_value_spec, value) + return exif_data.map_value(self.tag_value_spec, value) else : # nope... @@ -199,10 +212,10 @@ # read each tag for offset in self.iter_offsets(self.count, IFD_ENTRY_SIZE, 0x02) : # read the tag data - tag, type, count, value_ref = self.pread_struct(offset, 'HHII') + tag, type, count, data_raw = self.pread_struct(offset, 'HHI4s') # yield the new Tag - yield Tag(offset, tag, type, count, value_ref) + yield Tag(offset, tag, type, count, data_raw) class EXIF (Buffer) : """ @@ -231,8 +244,8 @@ offset = self.pread_item(0x04, 'I') while offset : - # create and read the IFD - ifd = IFD(self, offset=offset) + # create and read the IFD, operating on the right sub-buffer + ifd = IFD(self.buf, offset=offset) # yield it yield ifd @@ -242,32 +255,67 @@ __iter__ = iter_ifds - def tag_values (self, tag) : + def tag_data_info (self, tag) : + """ + Calculate the location, format and size of the given tag's data. + + Returns a (fmt, offset, size) tuple. + """ + # unknown tag? + if not tag.type_data : + return None + + # data format + if len(tag.type_format) == 1 : + # let struct handle the count + fmt = "%d%s" % (tag.count, tag.type_format) + + else : + # handle the count ourselves + fmt = tag.type_format * tag.count + + # size of the data + size = self.item_size(fmt) + + # inline or external? + if size > 0x04 : + # point at the external data + offset = self.unpack_item('I', tag.data_raw) + + else : + # point at the inline data + offset = tag.offset + 0x08 + + return fmt, offset, size + + def tag_values_raw (self, tag) : """ Get the raw values for the given tag as a tuple. Returns None if the tag could not be recognized. """ - # unknown tag? - if not tag.type_data : - return None - - # size of the data - data_size = tag.count * self.item_size(tag.type_format) + # find the data + data_info = self.tag_data_info(tag) - # inline or external? - if data_size > 0x04 : - # point at the external data - offset = self.unpack_item('I', tag.value_ref) - - else : - # point at the inline data - offset = tag.offset + 0x08 + # not found? + if not data_info : + return None + + # unpack + data_fmt, data_offset, data_size = data_info # read values - return self.pread_struct(offset, "%d%s" % (tag.count, tag.type_format)) + return self.pread_struct(data_offset, data_fmt) + def tag_values (self, tag) : + """ + Gets the processed values for the given tag as a list. + """ + + # read + process + return tag.process_values(self.tag_values_raw(tag)) + def tag_value (self, tag) : """ Return the human-readable string value for the given tag. @@ -301,7 +349,7 @@ offset = file.tell() # mmap the region for the EXIF data - buffer = mmap_region(file, length) + buffer = mmap_buffer(file, length) # read byte-order header byte_order = file.read(2) @@ -349,7 +397,7 @@ raise Exception("Not a JPEG marker: %x%x" % (marker_byte, marker_type)) # special cases for no data - if marker_byte in JPEG_NOSIZE_MARKERS : + if marker_type in JPEG_NOSIZE_MARKERS : size = 0 else : @@ -393,7 +441,7 @@ # look for APP1 marker (0xE1) with EXIF signature elif marker == JPEG_EXIF_MARKER and file.read(len(JPEG_EXIF_HEADER)) == JPEG_EXIF_HEADER: # skipped the initial Exif marker signature - return size - JPEG_EXIF_HEADER + return size - len(JPEG_EXIF_HEADER) # nothing return None @@ -409,7 +457,7 @@ size = jpeg_find_exif(file) # not found? - if not res : + if not size : # nothing return @@ -450,22 +498,36 @@ Dump all tags from the given EXIF object to stdout """ - print "EXIF offset=%d, size=%d:" % (exif.offset, exif.size) + print "EXIF offset=%#08x, size=%d:" % (exif.offset, exif.size) for i, ifd in enumerate(exif.iter_ifds()) : - print "\tIFD %d, offset=%d, size=%d, count=%d, next=%d:" % (i, ifd.offset, ifd.size, ifd.count, ifd.next_offset) + print "\tIFD:%d offset=%#04x(%#08x), count=%d, next=%d:" % ( + i, + ifd.offset, ifd.offset + exif.offset, + ifd.count, + ifd.next_offset + ) - for i, tag in enumerate(exif.iter_tags()) : - print "\t\tTag %d, offset=%d, tag=%d/%s, type=%d/%s, count=%d:" % ( + for i, tag in enumerate(ifd.iter_tags()) : + data_info = exif.tag_data_info(tag) + + if data_info : + data_fmt, data_offset, data_size = data_info + + else : + data_fmt = data_offset = data_size = None + + print "\t\tTag:%d offset=%#04x(%#08x), tag=%d/%s, type=%d/%s, count=%d, fmt=%s, offset=%#04x, size=%s:" % ( i, - tag.offset, - tag.code, tag.name or '???', + tag.offset, tag.offset + exif.offset, + tag.tag, tag.name or '???', tag.type, tag.type_name if tag.type_data else '???', tag.count, + data_fmt, data_offset, data_size, ) for i, value in enumerate(exif.tag_values(tag)) : - print "\t\t\t%02d: %s" % (i, tag.readable_value(value)) + print "\t\t\t%02d: %r -> %s" % (i, value, tag.readable_value(value)) def main (path) : """