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