degal/exif.py
branchnew-exif
changeset 106 a4f605bd122c
parent 105 effae6f38749
child 107 2e2ef5c99985
equal deleted inserted replaced
105:effae6f38749 106:a4f605bd122c
   141             return self.tag_data.name
   141             return self.tag_data.name
   142 
   142 
   143         else :
   143         else :
   144             return None
   144             return None
   145     
   145     
       
   146     def is_subifd (self) :
       
   147         """
       
   148             Tests if this Tag is of a IFDTag type
       
   149         """
       
   150 
       
   151         return self.tag_data and isinstance(self.tag_data, exif_data.IFDTag)
       
   152     
       
   153     @lazy_load
       
   154     def subifd (self) :
       
   155         """
       
   156             Load the sub-IFD for this tag
       
   157         """
       
   158 
       
   159         # the tag_dict to use
       
   160         tag_dict = self.tag_data.ifd_tags or self.ifd.tag_dict
       
   161         
       
   162         # construct, return
       
   163         return self.ifd.exif._load_subifd(self, tag_dict)
       
   164 
   146     def process_values (self, raw_values) :
   165     def process_values (self, raw_values) :
   147         """
   166         """
   148             Process the given raw values unpacked from the file.
   167             Process the given raw values unpacked from the file.
   149         """
   168         """
   150 
   169 
   177 class IFD (Buffer) :
   196 class IFD (Buffer) :
   178     """
   197     """
   179         Represents an IFD (Image file directory) region in EXIF data.
   198         Represents an IFD (Image file directory) region in EXIF data.
   180     """
   199     """
   181 
   200 
   182     def __init__ (self, buffer, tag_dict, **buffer_opts) :
   201     def __init__ (self, exif, buffer, tag_dict, **buffer_opts) :
   183         """
   202         """
   184             Access the IFD data from the given bufferable object with given buffer opts.
   203             Access the IFD data from the given bufferable object with given buffer opts.
   185 
   204 
   186             This will read the `count` and `next_offset` values.
   205             This will read the `count` and `next_offset` values.
   187         """
   206         """
   188 
   207 
   189         # init
   208         # init
   190         super(IFD, self).__init__(buffer, **buffer_opts)
   209         super(IFD, self).__init__(buffer, **buffer_opts)
   191 
   210 
   192         # store
   211         # store
       
   212         self.exif = exif
   193         self.tag_dict = tag_dict
   213         self.tag_dict = tag_dict
   194         
   214         
   195         # read header
   215         # read header
   196         self.count = self.pread_item(0, 'H')
   216         self.count = self.pread_item(0, 'H')
   197 
   217 
   208         for offset in self.iter_offsets(self.count, IFD_ENTRY_SIZE, 0x02) :
   228         for offset in self.iter_offsets(self.count, IFD_ENTRY_SIZE, 0x02) :
   209             # read the tag data
   229             # read the tag data
   210             tag, type, count, data_raw = self.pread_struct(offset, 'HHI4s')
   230             tag, type, count, data_raw = self.pread_struct(offset, 'HHI4s')
   211             
   231             
   212             # yield the new Tag
   232             # yield the new Tag
   213             yield Tag(self, offset, tag, type, count, data_raw)
   233             yield Tag(self, self.offset + offset, tag, type, count, data_raw)
       
   234 
       
   235     def get_tags (self, filter=None) :
       
   236         """
       
   237             Yield a series of tag objects for this IFD and all sub-IFDs.
       
   238         """
       
   239         
       
   240         for tag in self.tags :
       
   241             if tag.is_subifd() :
       
   242                 # recurse
       
   243                 for subtag in tag.subifd.get_tags(filter=filter) :
       
   244                     yield subtag
       
   245             
       
   246             else :
       
   247                 # normal tag
       
   248                 yield tag
   214 
   249 
   215 class EXIF (Buffer) :
   250 class EXIF (Buffer) :
   216     """
   251     """
   217         Represents the EXIF data embedded in some image file in the form of a Region.
   252         Represents the EXIF data embedded in some image file in the form of a Region.
   218     """
   253     """
   219 
   254 
   220     def __init__ (self, buffer, tags=None, **buffer_opts) :
   255     def __init__ (self, buffer, **buffer_opts) :
   221         """
   256         """
   222             Access the EXIF data from the given bufferable object with the given buffer options.
   257             Access the EXIF data from the given bufferable object with the given buffer options.
   223 
       
   224             `tags`, if given, specifies that only the given named tags should be loaded.
       
   225         """
   258         """
   226 
   259 
   227         # init Buffer
   260         # init Buffer
   228         super(EXIF, self).__init__(buffer, **buffer_opts)
   261         super(EXIF, self).__init__(buffer, **buffer_opts)
   229 
   262 
   239         # starting offset
   272         # starting offset
   240         offset = self.pread_item(0x04, 'I')
   273         offset = self.pread_item(0x04, 'I')
   241 
   274 
   242         while offset :
   275         while offset :
   243             # create and read the IFD, operating on the right sub-buffer
   276             # create and read the IFD, operating on the right sub-buffer
   244             ifd = IFD(self.buf, exif_data.EXIF_TAGS, offset=offset)
   277             ifd = IFD(self, self.buf, exif_data.EXIF_TAGS, offset=offset)
   245 
   278 
   246             # yield it
   279             # yield it
   247             yield ifd
   280             yield ifd
   248 
   281 
   249             # skip to next offset
   282             # skip to next offset
   250             offset = ifd.next_offset
   283             offset = ifd.next_offset
   251     
   284     
   252     def iter_all_ifds (self) :
   285     def _load_subifd (self, tag, tag_dict) :
   253         """
   286         """
   254             Iterate over all of the IFDs contained within this EXIF, or within other IFDs.
   287             Creates and returns a sub-IFD for the given tag.
   255         """
   288         """
   256     
   289 
       
   290         # locate it
       
   291         offset, = self.tag_values_raw(tag)
       
   292 
       
   293         # construct the new IFD
       
   294         return IFD(self, self.buf, tag_dict, offset=offset)
       
   295 
   257     def tag_data_info (self, tag) :
   296     def tag_data_info (self, tag) :
   258         """
   297         """
   259             Calculate the location, format and size of the given tag's data.
   298             Calculate the location, format and size of the given tag's data.
   260 
   299 
   261             Returns a (fmt, offset, size) tuple.
   300             Returns a (fmt, offset, size) tuple.
   327         if not values :
   366         if not values :
   328             return ""
   367             return ""
   329 
   368 
   330         # return as comma-separated formatted string, yes
   369         # return as comma-separated formatted string, yes
   331         return tag.readable_value(values)
   370         return tag.readable_value(values)
       
   371     
       
   372     def get_main_tags (self, **opts) :
       
   373         """
       
   374             Get the tags for the main image's IFD as a dict.
       
   375         """
       
   376 
       
   377         if not self.ifds :
       
   378             # weird case
       
   379             raise Exception("No IFD for main image found")
       
   380 
       
   381         # the main IFD is always the first one
       
   382         main_ifd = self.ifds[0]
       
   383 
       
   384         # do it
       
   385         return dict((tag.name, self.tag_value(tag)) for tag in main_ifd.get_tags(**opts))
   332 
   386 
   333 # mapping from two-byte TIFF byte order marker to struct prefix
   387 # mapping from two-byte TIFF byte order marker to struct prefix
   334 TIFF_BYTE_ORDER = {
   388 TIFF_BYTE_ORDER = {
   335     'II': '<',
   389     'II': '<',
   336     'MM': '>',
   390     'MM': '>',
   490     file = open(path, 'rb')
   544     file = open(path, 'rb')
   491 
   545 
   492     # try and load it
   546     # try and load it
   493     return func(file, **opts)
   547     return func(file, **opts)
   494 
   548 
       
   549 def dump_tag (exif, i, tag, indent=2) :
       
   550     """
       
   551         Dump the given tag
       
   552     """
       
   553 
       
   554     data_info = exif.tag_data_info(tag)
       
   555 
       
   556     if data_info :
       
   557         data_fmt, data_offset, data_size = data_info
       
   558 
       
   559     else :
       
   560         data_fmt = data_offset = data_size = None
       
   561 
       
   562     print "%sTag:%d offset=%#04x(%#08x), tag=%d/%s, type=%d/%s, count=%d, fmt=%s, offset=%#04x, size=%s, is_subifd=%s:" % (
       
   563         '\t'*indent,
       
   564         i, 
       
   565         tag.offset, tag.offset + exif.offset,
       
   566         tag.tag, tag.name or '???',
       
   567         tag.type, tag.type_name if tag.type_data else '???',
       
   568         tag.count,
       
   569         data_fmt, data_offset, data_size,
       
   570         tag.is_subifd(),
       
   571     )
       
   572     
       
   573     if tag.is_subifd() :
       
   574         # recurse
       
   575         dump_ifd(exif, 0, tag.subifd, indent + 1)
       
   576 
       
   577     else :
       
   578         # dump each value
       
   579         values = exif.tag_values(tag)
       
   580         
       
   581         for i, value in enumerate(values) :
       
   582             print "%s\t%02d: %.120r" % ('\t'*indent, i, value)
       
   583         
       
   584         # and then the readable one
       
   585         print "%s\t->  %.120s" % ('\t'*indent, tag.readable_value(values), )
       
   586 
       
   587 
       
   588 def dump_ifd (exif, i, ifd, indent=1) :
       
   589     """
       
   590         Dump the given IFD, recursively
       
   591     """
       
   592 
       
   593     print "%sIFD:%d offset=%#04x(%#08x), count=%d, next=%d:" % (
       
   594         '\t'*indent,
       
   595         i, 
       
   596         ifd.offset, ifd.offset + exif.offset,
       
   597         ifd.count, 
       
   598         ifd.next_offset
       
   599     )
       
   600     
       
   601     for i, tag in enumerate(ifd.tags) :
       
   602         # dump
       
   603         dump_tag(exif, i, tag, indent + 1)
       
   604 
       
   605 
   495 def dump_exif (exif) :
   606 def dump_exif (exif) :
   496     """
   607     """
   497         Dump all tags from the given EXIF object to stdout
   608         Dump all tags from the given EXIF object to stdout
   498     """
   609     """
   499 
   610 
   500     print "EXIF offset=%#08x, size=%d:" % (exif.offset, exif.size)
   611     print "EXIF offset=%#08x, size=%d:" % (exif.offset, exif.size)
   501 
   612 
   502     for i, ifd in enumerate(exif.ifds) :
   613     for i, ifd in enumerate(exif.ifds) :
   503         print "\tIFD:%d offset=%#04x(%#08x), count=%d, next=%d:" % (
   614         # dump
   504             i, 
   615         dump_ifd(exif, i, ifd)
   505             ifd.offset, ifd.offset + exif.offset,
   616 
   506             ifd.count, 
   617 
   507             ifd.next_offset
   618 def list_tags (exif) :
   508         )
   619     """
   509         
   620         Print a neat listing of tags to stdout
   510         for i, tag in enumerate(ifd.tags) :
   621     """
   511             data_info = exif.tag_data_info(tag)
   622 
   512 
   623     for k, v in exif.get_main_tags().iteritems() :
   513             if data_info :
   624         print "%30s: %s" % (k, v)
   514                 data_fmt, data_offset, data_size = data_info
   625 
   515 
   626 def main (path, debug=False) :
   516             else :
       
   517                 data_fmt = data_offset = data_size = None
       
   518 
       
   519             print "\t\tTag:%d offset=%#04x(%#08x), tag=%d/%s, type=%d/%s, count=%d, fmt=%s, offset=%#04x, size=%s:" % (
       
   520                 i, 
       
   521                 tag.offset, tag.offset + exif.offset,
       
   522                 tag.tag, tag.name or '???',
       
   523                 tag.type, tag.type_name if tag.type_data else '???',
       
   524                 tag.count,
       
   525                 data_fmt, data_offset, data_size,
       
   526             )
       
   527 
       
   528             values = exif.tag_values(tag)
       
   529             
       
   530             for i, value in enumerate(values) :
       
   531                 print "\t\t\t%02d: %r" % (i, value)
       
   532 
       
   533             print "\t\t\t->  %s" % (tag.readable_value(values), )
       
   534 
       
   535 def main (path, quiet=False) :
       
   536     """
   627     """
   537         Load and dump EXIF data from the given path
   628         Load and dump EXIF data from the given path
   538     """
   629     """
   539     
   630     
   540     # try and load it
   631     # try and load it
   541     exif = load_path(path)
   632     exif = load_path(path)
   542 
   633 
   543     if not exif :
   634     if not exif :
   544         raise Exception("No EXIF data found")
   635         raise Exception("No EXIF data found")
   545     
   636     
   546     if not quiet :
   637     if debug :
   547         # dump it
   638         # dump it
   548         print "%s: " % path
   639         print "%s: " % path
   549         print
   640         print
   550 
   641 
   551         dump_exif(exif)
   642         dump_exif(exif)
       
   643     
       
   644     else :
       
   645         # list them
       
   646         list_tags(exif)
   552 
   647 
   553 if __name__ == '__main__' :
   648 if __name__ == '__main__' :
   554     from sys import argv
   649     from sys import argv
   555 
   650 
   556     main(argv[1], '-q' in argv)
   651     main(argv[1], '-d' in argv)
   557 
   652