[Python-modules-commits] [python-exif] 09/13: Import python-exif_1.1.0.orig.tar.gz
Wolfgang Borgert
debacle at moszumanska.debian.org
Mon Oct 6 22:17:18 UTC 2014
This is an automated email from the git hooks/post-receive script.
debacle pushed a commit to branch master
in repository python-exif.
commit 21b49cea1497d71319eafc193815a92d698484a6
Author: W. Martin Borgert <debacle at debian.org>
Date: Tue Oct 7 00:12:06 2014 +0200
Import python-exif_1.1.0.orig.tar.gz
---
EXIF.py | 219 +++++++++++++++++++++++++++++++++++++++++++++++-------------
README.md | 41 ++++++++++++
changes.txt | 24 ++++++-
3 files changed, 237 insertions(+), 47 deletions(-)
diff --git a/EXIF.py b/EXIF.py
index ed4192a..a188154 100755
--- a/EXIF.py
+++ b/EXIF.py
@@ -1,8 +1,10 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
-# Library to extract EXIF information from digital camera image files
-# http://sourceforge.net/projects/exif-py/
+#
+# Library to extract EXIF information from digital camera image files.
+# https://github.com/ianare/exif-py
+#
#
# VERSION 1.1.0
#
@@ -22,7 +24,6 @@
#
# These 2 are useful when you are retrieving a large list of images
#
-#
# To return an error on invalid tags,
# pass the -s or --strict argument, or as
# tags = EXIF.process_file(f, strict=True)
@@ -48,7 +49,7 @@
# 'EXIF DateTimeOriginal', 'Image Orientation', 'MakerNote FocusMode'
#
# Copyright (c) 2002-2007 Gene Cash All rights reserved
-# Copyright (c) 2007-2008 Ianaré Sévi All rights reserved
+# Copyright (c) 2007-2012 Ianaré Sévi All rights reserved
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
@@ -102,7 +103,7 @@ def make_string_uc(seq):
seq = seq[8:]
# Of course, this is only correct if ASCII, and the standard explicitly
# allows JIS and Unicode.
- return make_string(seq)
+ return make_string( make_string(seq) )
# field type descriptions as (length, abbreviation, full name) tuples
FIELD_TYPES = (
@@ -171,9 +172,9 @@ EXIF_TAGS = {
3: 'Rotated 180',
4: 'Mirrored vertical',
5: 'Mirrored horizontal then rotated 90 CCW',
- 6: 'Rotated 90 CW',
+ 6: 'Rotated 90 CCW',
7: 'Mirrored horizontal then rotated 90 CW',
- 8: 'Rotated 90 CCW'}),
+ 8: 'Rotated 90 CW'}),
0x0115: ('SamplesPerPixel', ),
0x0116: ('RowsPerStrip', ),
0x0117: ('StripByteCounts', ),
@@ -251,40 +252,54 @@ EXIF_TAGS = {
2: 'CenterWeightedAverage',
3: 'Spot',
4: 'MultiSpot',
- 5: 'Pattern'}),
+ 5: 'Pattern',
+ 6: 'Partial',
+ 255: 'other'}),
0x9208: ('LightSource',
{0: 'Unknown',
1: 'Daylight',
2: 'Fluorescent',
- 3: 'Tungsten',
- 9: 'Fine Weather',
- 10: 'Flash',
+ 3: 'Tungsten (incandescent light)',
+ 4: 'Flash',
+ 9: 'Fine weather',
+ 10: 'Cloudy weather',
11: 'Shade',
- 12: 'Daylight Fluorescent',
- 13: 'Day White Fluorescent',
- 14: 'Cool White Fluorescent',
- 15: 'White Fluorescent',
- 17: 'Standard Light A',
- 18: 'Standard Light B',
- 19: 'Standard Light C',
+ 12: 'Daylight fluorescent (D 5700 - 7100K)',
+ 13: 'Day white fluorescent (N 4600 - 5400K)',
+ 14: 'Cool white fluorescent (W 3900 - 4500K)',
+ 15: 'White fluorescent (WW 3200 - 3700K)',
+ 17: 'Standard light A',
+ 18: 'Standard light B',
+ 19: 'Standard light C',
20: 'D55',
21: 'D65',
22: 'D75',
- 255: 'Other'}),
+ 23: 'D50',
+ 24: 'ISO studio tungsten',
+ 255: 'other light source',}),
0x9209: ('Flash',
- {0: 'No',
- 1: 'Fired',
- 5: 'Fired (?)', # no return sensed
- 7: 'Fired (!)', # return sensed
- 9: 'Fill Fired',
- 13: 'Fill Fired (?)',
- 15: 'Fill Fired (!)',
- 16: 'Off',
- 24: 'Auto Off',
- 25: 'Auto Fired',
- 29: 'Auto Fired (?)',
- 31: 'Auto Fired (!)',
- 32: 'Not Available'}),
+ {0: 'Flash did not fire',
+ 1: 'Flash fired',
+ 5: 'Strobe return light not detected',
+ 7: 'Strobe return light detected',
+ 9: 'Flash fired, compulsory flash mode',
+ 13: 'Flash fired, compulsory flash mode, return light not detected',
+ 15: 'Flash fired, compulsory flash mode, return light detected',
+ 16: 'Flash did not fire, compulsory flash mode',
+ 24: 'Flash did not fire, auto mode',
+ 25: 'Flash fired, auto mode',
+ 29: 'Flash fired, auto mode, return light not detected',
+ 31: 'Flash fired, auto mode, return light detected',
+ 32: 'No flash function',
+ 65: 'Flash fired, red-eye reduction mode',
+ 69: 'Flash fired, red-eye reduction mode, return light not detected',
+ 71: 'Flash fired, red-eye reduction mode, return light detected',
+ 73: 'Flash fired, compulsory flash mode, red-eye reduction mode',
+ 77: 'Flash fired, compulsory flash mode, red-eye reduction mode, return light not detected',
+ 79: 'Flash fired, compulsory flash mode, red-eye reduction mode, return light detected',
+ 89: 'Flash fired, auto mode, red-eye reduction mode',
+ 93: 'Flash fired, auto mode, return light not detected, red-eye reduction mode',
+ 95: 'Flash fired, auto mode, return light detected, red-eye reduction mode'}),
0x920A: ('FocalLength', ),
0x9214: ('SubjectArea', ),
0x927C: ('MakerNote', ),
@@ -410,7 +425,10 @@ GPS_TAGS = {
0x0018: ('GPSDestBearing', ),
0x0019: ('GPSDestDistanceRef', ),
0x001A: ('GPSDestDistance', ),
+ 0x001B: ('GPSProcessingMethod', ),
+ 0x001C: ('GPSAreaInformation', ),
0x001D: ('GPSDate', ),
+ 0x001E: ('GPSDifferential', ),
}
# Ignore these tags when quick processing
@@ -1231,10 +1249,17 @@ class IFD_Tag:
return self.printable
def __repr__(self):
- return '(0x%04X) %s=%s @ %d' % (self.tag,
+ try:
+ s= '(0x%04X) %s=%s @ %d' % (self.tag,
FIELD_TYPES[self.field_type][2],
self.printable,
self.field_offset)
+ except:
+ s= '(%s) %s=%s @ %s' % (str(self.tag),
+ FIELD_TYPES[self.field_type][2],
+ self.printable,
+ str(self.field_offset))
+ return s
# class that handles an EXIF header
class EXIF_header:
@@ -1283,7 +1308,11 @@ class EXIF_header:
# return pointer to next IFD
def next_IFD(self, ifd):
entries=self.s2n(ifd, 2)
- return self.s2n(ifd+2+12*entries, 4)
+ next_ifd = self.s2n(ifd+2+12*entries, 4)
+ if next_ifd == ifd:
+ return 0
+ else:
+ return next_ifd
# return list of IFDs in header
def list_IFDs(self):
@@ -1348,14 +1377,15 @@ class EXIF_header:
# special case: null-terminated ASCII string
# XXX investigate
# sometimes gets too big to fit in int value
- if count != 0 and count < (2**31):
- self.file.seek(self.offset + offset)
- values = self.file.read(count)
- #print values
- # Drop any garbage after a null.
- values = values.split('\x00', 1)[0]
- else:
- values = ''
+ if count != 0: # and count < (2**31): # 2E31 is hardware dependant. --gd
+ try:
+ self.file.seek(self.offset + offset)
+ values = self.file.read(count)
+ #print values
+ # Drop any garbage after a null.
+ values = values.split('\x00', 1)[0]
+ except OverflowError:
+ values = ''
else:
values = []
signed = (field_type in [6, 8, 9, 10])
@@ -1567,7 +1597,8 @@ class EXIF_header:
dict=MAKERNOTE_CANON_TAGS)
for i in (('MakerNote Tag 0x0001', MAKERNOTE_CANON_TAG_0x001),
('MakerNote Tag 0x0004', MAKERNOTE_CANON_TAG_0x004)):
- self.canon_decode_tag(self.tags[i[0]].values, i[1])
+ if i[0] in self.tags:
+ self.canon_decode_tag(self.tags[i[0]].values, i[1])
return
@@ -1613,26 +1644,124 @@ def process_file(f, stop_tag='UNDEF', details=True, strict=False, debug=False):
offset = 0
elif data[0:2] == '\xFF\xD8':
# it's a JPEG file
+ if debug: print "JPEG format recognized data[0:2] == '0xFFD8'."
+ base = 2
while data[2] == '\xFF' and data[6:10] in ('JFIF', 'JFXX', 'OLYM', 'Phot'):
+ if debug: print "data[2] == 0xxFF data[3]==%x and data[6:10] = %s"%(ord(data[3]),data[6:10])
length = ord(data[4])*256+ord(data[5])
+ if debug: print "Length offset is",length
f.read(length-8)
# fake an EXIF beginning of file
+ # I don't think this is used. --gd
data = '\xFF\x00'+f.read(10)
fake_exif = 1
- if data[2] == '\xFF' and data[6:10] == 'Exif':
+ if base>2:
+ if debug: print "added to base "
+ base = base + length + 4 -2
+ else:
+ if debug: print "added to zero "
+ base = length + 4
+ if debug: print "Set segment base to",base
+
+ # Big ugly patch to deal with APP2 (or other) data coming before APP1
+ f.seek(0)
+ data = f.read(base+4000) # in theory, this could be insufficient since 64K is the maximum size--gd
+ # base = 2
+ while 1:
+ if debug: print "Segment base 0x%X" % base
+ if data[base:base+2]=='\xFF\xE1':
+ # APP1
+ if debug: print "APP1 at base",hex(base)
+ if debug: print "Length",hex(ord(data[base+2])), hex(ord(data[base+3]))
+ if debug: print "Code",data[base+4:base+8]
+ if data[base+4:base+8] == "Exif":
+ if debug: print "Decrement base by",2,"to get to pre-segment header (for compatibility with later code)"
+ base = base-2
+ break
+ if debug: print "Increment base by",ord(data[base+2])*256+ord(data[base+3])+2
+ base=base+ord(data[base+2])*256+ord(data[base+3])+2
+ elif data[base:base+2]=='\xFF\xE0':
+ # APP0
+ if debug: print "APP0 at base",hex(base)
+ if debug: print "Length",hex(ord(data[base+2])), hex(ord(data[base+3]))
+ if debug: print "Code",data[base+4:base+8]
+ if debug: print "Increment base by",ord(data[base+2])*256+ord(data[base+3])+2
+ base=base+ord(data[base+2])*256+ord(data[base+3])+2
+ elif data[base:base+2]=='\xFF\xE2':
+ # APP2
+ if debug: print "APP2 at base",hex(base)
+ if debug: print "Length",hex(ord(data[base+2])), hex(ord(data[base+3]))
+ if debug: print "Code",data[base+4:base+8]
+ if debug: print "Increment base by",ord(data[base+2])*256+ord(data[base+3])+2
+ base=base+ord(data[base+2])*256+ord(data[base+3])+2
+ elif data[base:base+2]=='\xFF\xEE':
+ # APP14
+ if debug: print "APP14 Adobe segment at base",hex(base)
+ if debug: print "Length",hex(ord(data[base+2])), hex(ord(data[base+3]))
+ if debug: print "Code",data[base+4:base+8]
+ if debug: print "Increment base by",ord(data[base+2])*256+ord(data[base+3])+2
+ print "There is useful EXIF-like data here, but we have no parser for it."
+ base=base+ord(data[base+2])*256+ord(data[base+3])+2
+ elif data[base:base+2]=='\xFF\xDB':
+ if debug: print "JPEG image data at base",hex(base),"No more segments are expected."
+ # sys.exit(0)
+ break
+ elif data[base:base+2]=='\xFF\xD8':
+ # APP12
+ if debug: print "FFD8 segment at base",hex(base)
+ if debug: print "Got",hex(ord(data[base])), hex(ord(data[base+1])),"and", data[4+base:10+base], "instead."
+ if debug: print "Length",hex(ord(data[base+2])), hex(ord(data[base+3]))
+ if debug: print "Code",data[base+4:base+8]
+ if debug: print "Increment base by",ord(data[base+2])*256+ord(data[base+3])+2
+ base=base+ord(data[base+2])*256+ord(data[base+3])+2
+ elif data[base:base+2]=='\xFF\xEC':
+ # APP12
+ if debug: print "APP12 XMP (Ducky) or Pictureinfo segment at base",hex(base)
+ if debug: print "Got",hex(ord(data[base])), hex(ord(data[base+1])),"and", data[4+base:10+base], "instead."
+ if debug: print "Length",hex(ord(data[base+2])), hex(ord(data[base+3]))
+ if debug: print "Code",data[base+4:base+8]
+ if debug: print "Increment base by",ord(data[base+2])*256+ord(data[base+3])+2
+ print "There is useful EXIF-like data here (quality, comment, copyright), but we have no parser for it."
+ base=base+ord(data[base+2])*256+ord(data[base+3])+2
+ else:
+ try:
+ if debug: print "Unexpected/unhandled segment type or file content."
+ if debug: print "Got",hex(ord(data[base])), hex(ord(data[base+1])),"and", data[4+base:10+base], "instead."
+ if debug: print "Increment base by",ord(data[base+2])*256+ord(data[base+3])+2
+ except: pass
+ try: base=base+ord(data[base+2])*256+ord(data[base+3])+2
+ except: pass
+
+ f.seek(base+12)
+ if data[2+base] == '\xFF' and data[6+base:10+base] == 'Exif':
# detected EXIF header
offset = f.tell()
endian = f.read(1)
+ #HACK TEST: endian = 'M'
+ elif data[2+base] == '\xFF' and data[6+base:10+base+1] == 'Ducky':
+ # detected Ducky header.
+ if debug: print "EXIF-like header (normally 0xFF and code):",hex(ord(data[2+base])) , "and", data[6+base:10+base+1]
+ offset = f.tell()
+ endian = f.read(1)
+ elif data[2+base] == '\xFF' and data[6+base:10+base+1] == 'Adobe':
+ # detected APP14 (Adobe)
+ if debug: print "EXIF-like header (normally 0xFF and code):",hex(ord(data[2+base])) , "and", data[6+base:10+base+1]
+ offset = f.tell()
+ endian = f.read(1)
else:
# no EXIF information
+ if debug: print "No EXIF header expected data[2+base]==0xFF and data[6+base:10+base]===Exif (or Duck)"
+ if debug: print " but got",hex(ord(data[2+base])) , "and", data[6+base:10+base+1]
return {}
else:
# file format not recognized
+ if debug: print "file format not recognized"
return {}
# deal with the EXIF info we found
if debug:
- print {'I': 'Intel', 'M': 'Motorola'}[endian], 'format'
+ print "Endian format is ",endian
+ print {'I': 'Intel', 'M': 'Motorola', '\x01':'Adobe Ducky', 'd':'XMP/Adobe unknown' }[endian], 'format'
hdr = EXIF_header(f, endian, offset, fake_exif, strict, debug)
ifd_list = hdr.list_IFDs()
ctr = 0
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..4efd005
--- /dev/null
+++ b/README.md
@@ -0,0 +1,41 @@
+exif-py
+=======
+
+Python library to extract EXIF data from tiff and jpeg files.
+
+Originally written by Gene Cash / Thierry Bousch.
+
+##Usage
+
+###Command line
+Get all tags
+~~~
+$ ./EXIF.py image.jpg
+~~~
+
+Show command line options
+~~~
+$ ./EXIF.py
+~~~
+
+###Within python script
+~~~
+[python]
+import EXIF
+
+# Open image file for reading (binary mode)
+f = open(path_name, 'rb')
+
+# Get all tags
+tags = EXIF.process_file(f)
+
+# Get tags, but not MakerNote
+tags = EXIF.process_file(f, details=False)
+
+# Stop processing the file after reaching a tag (faster processing)
+tag = "DateTimeOriginal"
+tags = EXIF.process_file(f, details=False, stop_tag=tag)
+
+# Return an error on invalid tags
+tags = EXIF.process_file(f, strict=True)
+~~~
\ No newline at end of file
diff --git a/changes.txt b/changes.txt
index 05440b8..d1b18e6 100644
--- a/changes.txt
+++ b/changes.txt
@@ -1,5 +1,20 @@
~ EXIF.py Changelog ~
+2012-11-30 - Gregory Dudek (date of merge).
+Patches and changes:
+ Overflow error fixes added (related to 2**31 size)
+ GPS tags added.
+
+2012-09-26 - Ianaré Sévi
+Merge patches:
+ Add GPS tags
+ Add better endian debug info
+
+2012-06-13 - Ianaré Sévi
+Merge patches:
+ Support malformed last IFD by fhats
+ Light source, Flash and Metering mode dictionaries update by gryfik
+
2008-07-31 - Ianaré Sévi
Wikipedia Commons hunt for suitable test case images,
testing new code additions.
@@ -84,10 +99,12 @@ Fixed subtle bug when faking an EXIF header, which affected maker notes
using relative offsets, and a fix for Nikon D100.
1999-08-21 TB Last update by Thierry Bousch to his code.
+
2002-01-17 CEC Discovered code on web.
Commented everything.
Made small code improvements.
Reformatted for readability.
+
2002-01-19 CEC Added ability to read TIFFs and JFIF-format JPEGs.
Added ability to extract JPEG formatted thumbnail.
Added ability to read GPS IFD (not tested).
@@ -95,17 +112,20 @@ using relative offsets, and a fix for Nikon D100.
tag name.
Factored into library returning dictionary of IFDs plus
thumbnail, if any.
+
2002-01-20 CEC Added MakerNote processing logic.
Added Olympus MakerNote.
Converted data structure to single-level dictionary, avoiding
tag name collisions by prefixing with IFD name. This makes
it much easier to use.
2002-01-23 CEC Trimmed nulls from end of string values.
+
2002-01-25 CEC Discovered JPEG thumbnail in Olympus TIFF MakerNote.
+
2002-01-26 CEC Added ability to extract TIFF thumbnails.
Added Nikon, Fujifilm, Casio MakerNotes.
+
2003-11-30 CEC Fixed problem with canon_decode_tag() not creating an
IFD_Tag() object.
+
2004-02-15 CEC Finally fixed bit shift warning by converting Y to 0L.
-
-~ EOF ~
--
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/python-modules/packages/python-exif.git
More information about the Python-modules-commits
mailing list