diff -r effae6f38749 -r a4f605bd122c degal/exif.py --- a/degal/exif.py Sat Jun 13 20:59:53 2009 +0300 +++ b/degal/exif.py Sat Jun 13 22:22:04 2009 +0300 @@ -143,6 +143,25 @@ else : return None + def is_subifd (self) : + """ + Tests if this Tag is of a IFDTag type + """ + + return self.tag_data and isinstance(self.tag_data, exif_data.IFDTag) + + @lazy_load + def subifd (self) : + """ + Load the sub-IFD for this tag + """ + + # the tag_dict to use + tag_dict = self.tag_data.ifd_tags or self.ifd.tag_dict + + # construct, return + return self.ifd.exif._load_subifd(self, tag_dict) + def process_values (self, raw_values) : """ Process the given raw values unpacked from the file. @@ -179,7 +198,7 @@ Represents an IFD (Image file directory) region in EXIF data. """ - def __init__ (self, buffer, tag_dict, **buffer_opts) : + def __init__ (self, exif, buffer, tag_dict, **buffer_opts) : """ Access the IFD data from the given bufferable object with given buffer opts. @@ -190,6 +209,7 @@ super(IFD, self).__init__(buffer, **buffer_opts) # store + self.exif = exif self.tag_dict = tag_dict # read header @@ -210,18 +230,31 @@ tag, type, count, data_raw = self.pread_struct(offset, 'HHI4s') # yield the new Tag - yield Tag(self, offset, tag, type, count, data_raw) + yield Tag(self, self.offset + offset, tag, type, count, data_raw) + + def get_tags (self, filter=None) : + """ + Yield a series of tag objects for this IFD and all sub-IFDs. + """ + + for tag in self.tags : + if tag.is_subifd() : + # recurse + for subtag in tag.subifd.get_tags(filter=filter) : + yield subtag + + else : + # normal tag + yield tag class EXIF (Buffer) : """ Represents the EXIF data embedded in some image file in the form of a Region. """ - def __init__ (self, buffer, tags=None, **buffer_opts) : + def __init__ (self, buffer, **buffer_opts) : """ Access the EXIF data from the given bufferable object with the given buffer options. - - `tags`, if given, specifies that only the given named tags should be loaded. """ # init Buffer @@ -241,7 +274,7 @@ while offset : # create and read the IFD, operating on the right sub-buffer - ifd = IFD(self.buf, exif_data.EXIF_TAGS, offset=offset) + ifd = IFD(self, self.buf, exif_data.EXIF_TAGS, offset=offset) # yield it yield ifd @@ -249,11 +282,17 @@ # skip to next offset offset = ifd.next_offset - def iter_all_ifds (self) : + def _load_subifd (self, tag, tag_dict) : """ - Iterate over all of the IFDs contained within this EXIF, or within other IFDs. + Creates and returns a sub-IFD for the given tag. """ - + + # locate it + offset, = self.tag_values_raw(tag) + + # construct the new IFD + return IFD(self, self.buf, tag_dict, offset=offset) + def tag_data_info (self, tag) : """ Calculate the location, format and size of the given tag's data. @@ -329,6 +368,21 @@ # return as comma-separated formatted string, yes return tag.readable_value(values) + + def get_main_tags (self, **opts) : + """ + Get the tags for the main image's IFD as a dict. + """ + + if not self.ifds : + # weird case + raise Exception("No IFD for main image found") + + # the main IFD is always the first one + main_ifd = self.ifds[0] + + # do it + return dict((tag.name, self.tag_value(tag)) for tag in main_ifd.get_tags(**opts)) # mapping from two-byte TIFF byte order marker to struct prefix TIFF_BYTE_ORDER = { @@ -492,6 +546,63 @@ # try and load it return func(file, **opts) +def dump_tag (exif, i, tag, indent=2) : + """ + Dump the given tag + """ + + 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 "%sTag:%d offset=%#04x(%#08x), tag=%d/%s, type=%d/%s, count=%d, fmt=%s, offset=%#04x, size=%s, is_subifd=%s:" % ( + '\t'*indent, + i, + 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, + tag.is_subifd(), + ) + + if tag.is_subifd() : + # recurse + dump_ifd(exif, 0, tag.subifd, indent + 1) + + else : + # dump each value + values = exif.tag_values(tag) + + for i, value in enumerate(values) : + print "%s\t%02d: %.120r" % ('\t'*indent, i, value) + + # and then the readable one + print "%s\t-> %.120s" % ('\t'*indent, tag.readable_value(values), ) + + +def dump_ifd (exif, i, ifd, indent=1) : + """ + Dump the given IFD, recursively + """ + + print "%sIFD:%d offset=%#04x(%#08x), count=%d, next=%d:" % ( + '\t'*indent, + i, + ifd.offset, ifd.offset + exif.offset, + ifd.count, + ifd.next_offset + ) + + for i, tag in enumerate(ifd.tags) : + # dump + dump_tag(exif, i, tag, indent + 1) + + def dump_exif (exif) : """ Dump all tags from the given EXIF object to stdout @@ -500,39 +611,19 @@ print "EXIF offset=%#08x, size=%d:" % (exif.offset, exif.size) for i, ifd in enumerate(exif.ifds) : - 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(ifd.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 + # dump + dump_ifd(exif, i, ifd) - 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.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, - ) - values = exif.tag_values(tag) - - for i, value in enumerate(values) : - print "\t\t\t%02d: %r" % (i, value) +def list_tags (exif) : + """ + Print a neat listing of tags to stdout + """ - print "\t\t\t-> %s" % (tag.readable_value(values), ) + for k, v in exif.get_main_tags().iteritems() : + print "%30s: %s" % (k, v) -def main (path, quiet=False) : +def main (path, debug=False) : """ Load and dump EXIF data from the given path """ @@ -543,15 +634,19 @@ if not exif : raise Exception("No EXIF data found") - if not quiet : + if debug : # dump it print "%s: " % path print dump_exif(exif) + + else : + # list them + list_tags(exif) if __name__ == '__main__' : from sys import argv - main(argv[1], '-q' in argv) + main(argv[1], '-d' in argv)