1
# Lazygal, a lazy satic web gallery generator.
2
# Copyright (C) 2011 Alexandre Rossi <alexandre.rossi@gmail.com>
4
# This program is free software; you can redistribute it and/or modify
5
# it under the terms of the GNU General Public License as published by
6
# the Free Software Foundation; either version 2 of the License, or
7
# (at your option) any later version.
9
# This program is distributed in the hope that it will be useful,
10
# but WITHOUT ANY WARRANTY; without even the implied warranty of
11
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12
# GNU General Public License for more details.
14
# You should have received a copy of the GNU General Public License along
15
# with this program; if not, write to the Free Software Foundation, Inc.,
16
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19
This module provides a simple wrapper to the pyexiv2 API which changed between
20
version 0.1 and 0.2. The goal is to provide a wrapper compatible with the
29
import Image as PILImage
32
def decode_exif_user_comment(raw, imgpath):
34
Before pyexiv2 0.3, the EXIF user comment was not decoded to a unicode
35
string. This function does exactly that and is used for earlier versions
38
# This field can contain charset information
39
if raw.startswith('charset='):
40
tokens = raw.split(' ')
42
text = ' '.join(tokens[1:])
43
ignore, cset = csetfield.split('=')
44
cset = cset.strip('"')
51
# Starting from 0.20, exiv2 converts unicode comments to UTF-8
54
except UnicodeDecodeError:
55
# Decoding failed, maybe we can assume we are with exiv2 << 0.20
56
im = PILImage.open(imgpath)
57
endianess = im.app['APP1'][6:8]
60
elif endianess == 'II':
69
encoding = 'shift_jis'
71
# Fallback to utf-8 as this is mostly the default for Linux
75
# Return the decoded string according to the found encoding.
77
return text.decode(encoding)
78
except UnicodeDecodeError:
79
return text.decode(encoding, 'replace')
82
# This is required at import time for inheritances below.
83
if 'ImageMetadata' in dir(pyexiv2):
84
Pyexiv2ImageMetadata = pyexiv2.ImageMetadata
86
# This is for the interpreter to be happy, but if pyexiv2.ImageMetadata
87
# does not exist, we are with pyexiv2 << 0.2, and the old API, so we won't
88
# be using classes that inherit from Pyexiv2ImageMetadata.
89
Pyexiv2ImageMetadata = object
92
class _ImageMetadata_0_2_2(Pyexiv2ImageMetadata):
94
def __init__(self, imgpath):
95
super(_ImageMetadata_0_2_2, self).__init__(imgpath)
96
self.imgpath = imgpath
98
def __getitem__(self, key):
99
tag = super(_ImageMetadata_0_2_2, self).__getitem__(key)
100
if key == 'Exif.Photo.UserComment':
101
tag.value = decode_exif_user_comment(tag.value, self.imgpath)
105
class _ImageMetadata_0_2(_ImageMetadata_0_2_2):
107
def get_jpeg_comment(self):
108
# comment appeared in pyexiv2 0.2.2, so use PIL
109
im = PILImage.open(self.imgpath)
111
return im.app['COM'].strip('\x00')
115
comment = property(get_jpeg_comment)
118
class _ImageTag_0_1(object):
120
def __init__(self, tag, imgpath, md, key):
122
self._imgpath = imgpath
126
def __getattr__(self, name):
128
return self.get_value()
129
elif name == 'raw_value':
135
return str(self._tag)
137
def get_exif_date(self):
139
Parses date from EXIF information.
141
exif_date = str(self._metadata[self._key])
142
date, time = exif_date.split(' ')
143
year, month, day = date.split('-')
144
hour, minute, second = time.split(':')
145
return datetime.datetime(int(year), int(month), int(day),
146
int(hour), int(minute), int(second))
149
return int(self._tag)
151
def get_interpreted_value(self):
152
return self._metadata.interpretedExifValue(self._key)
154
def get_decoded_utf8(self):
155
return self.get_interpreted_value().decode('utf-8')
157
def get_decoded_exif_user_comment(self):
158
return decode_exif_user_comment(self._tag, self._imgpath)
160
TAG_PYTRANSLATORS = {
161
'Exif.Photo.DateTimeDigitized' : 'get_exif_date',
162
'Exif.Photo.DateTimeOriginal' : 'get_exif_date',
163
'Exif.Photo.UserComment' : 'get_decoded_exif_user_comment',
164
'Exif.Image.DateTime' : 'get_exif_date',
165
'Exif.Image.Orientation' : 'get_int',
166
'Exif.Pentax.LensType' : 'get_interpreted_value',
167
'Exif.Nikon3.Lens' : 'get_interpreted_value',
168
'Exif.Nikon3.LensType' : 'get_interpreted_value',
169
'Exif.Minolta.LensID' : 'get_interpreted_value',
170
'Exif.Photo.Flash' : 'get_decoded_utf8',
174
if self._key in _ImageTag_0_1.TAG_PYTRANSLATORS.keys():
175
translator = getattr(self.__class__,
176
_ImageTag_0_1.TAG_PYTRANSLATORS[self._key])
177
return translator(self)
182
class _ImageMetadata_0_1(object):
184
def __init__(self, imgpath):
185
self.imgpath = imgpath
186
self._metadata = pyexiv2.Image(self.imgpath.encode(sys.getfilesystemencoding()))
188
def __getitem__(self, key):
189
return _ImageTag_0_1(self._metadata[key], self.imgpath,
192
def __setitem__(self, key, value):
193
self._metadata[key] = value
195
def __delitem__(self, key):
196
del self._metadata[key]
199
self._metadata.readMetadata()
202
self._metadata.writeMetadata()
204
def __try_copy_tag_to(self, tag_key, dest_imgtags):
206
dest_imgtags._metadata[tag_key] = self[tag_key]
207
except (ValueError, TypeError):
210
def copy(self, dest_imgtags):
212
self._metadata.copyMetadataTo(dest_imgtags._metadata)
213
except (ValueError, TypeError):
214
# Sometimes pyexiv2 (<< 0.2) fails during the copy on a badly
215
# formatted tag, so we try a manual copy here for each tag.
216
for tag_key in self.exif_keys:
217
self.__try_copy_tag_to(tag_key, dest_imgtags)
218
for tag_key in self.iptc_keys:
219
self.__try_copy_tag_to(tag_key, dest_imgtags)
221
def get_comment(self): return self._metadata.getComment()
222
def set_comment(self, value): self._metadata.setComment(value)
223
comment = property(get_comment, set_comment)
225
def get_exif_keys(self): return self._metadata.exifKeys()
226
exif_keys = property(get_exif_keys)
228
def get_iptc_keys(self): return self._metadata.iptcKeys()
229
iptc_keys = property(get_iptc_keys)
232
if 'ImageMetadata' in dir(pyexiv2):
233
if 'comment' in dir(pyexiv2.ImageMetadata):
235
if pyexiv2.version_info >= (0, 3, 0):
236
ImageMetadata = pyexiv2.ImageMetadata
238
ImageMetadata = _ImageMetadata_0_2_2
240
# pyexiv2 (>= 0.2, << 0.2.2)
241
ImageMetadata = _ImageMetadata_0_2
242
elif 'Image' in dir(pyexiv2):
244
ImageMetadata = _ImageMetadata_0_1
246
raise ImportError('Unrecognized pyexiv2 version.')
249
# vim: ts=4 sw=4 expandtab