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