~ubuntu-branches/ubuntu/precise/lazygal/precise

« back to all changes in this revision

Viewing changes to lazygal/pyexiv2api.py

  • Committer: Bazaar Package Importer
  • Author(s): Michal Čihař
  • Date: 2011-03-21 15:55:53 UTC
  • mfrom: (1.1.6 upstream) (2.1.10 sid)
  • Revision ID: james.westby@ubuntu.com-20110321155553-2qmag1i39n54hjv3
Tags: 0.6-2
* Update home page.
* Suggest gstreamer for video support.
* Require python 2.6 or newer (Closes: #619032).

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# Lazygal, a lazy satic web gallery generator.
 
2
# Copyright (C) 2011 Alexandre Rossi <alexandre.rossi@gmail.com>
 
3
#
 
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.
 
8
#
 
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.
 
13
#
 
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.
 
17
 
 
18
"""
 
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
 
21
latest API.
 
22
"""
 
23
 
 
24
 
 
25
import sys
 
26
import datetime
 
27
 
 
28
import pyexiv2
 
29
import Image as PILImage
 
30
 
 
31
 
 
32
def decode_exif_user_comment(raw, imgpath):
 
33
    """
 
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
 
36
    of pyexiv2.
 
37
    """
 
38
    # This field can contain charset information
 
39
    if raw.startswith('charset='):
 
40
        tokens = raw.split(' ')
 
41
        csetfield = tokens[0]
 
42
        text = ' '.join(tokens[1:])
 
43
        ignore, cset = csetfield.split('=')
 
44
        cset = cset.strip('"')
 
45
    else:
 
46
        cset = None
 
47
        text = raw
 
48
 
 
49
    if cset == 'Unicode':
 
50
        encoding = None
 
51
        # Starting from 0.20, exiv2 converts unicode comments to UTF-8
 
52
        try:
 
53
            text.decode('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]
 
58
            if endianess == 'MM':
 
59
                encoding = 'utf-16be'
 
60
            elif endianess == 'II':
 
61
                encoding = 'utf-16le'
 
62
            else:
 
63
                raise ValueError
 
64
        else:
 
65
            encoding = 'utf-8'
 
66
    elif cset == 'Ascii':
 
67
        encoding = 'ascii'
 
68
    elif cset == 'Jis':
 
69
        encoding = 'shift_jis'
 
70
    else:
 
71
        # Fallback to utf-8 as this is mostly the default for Linux
 
72
        # distributions.
 
73
        encoding = 'utf-8'
 
74
 
 
75
    # Return the decoded string according to the found encoding.
 
76
    try:
 
77
        return text.decode(encoding)
 
78
    except UnicodeDecodeError:
 
79
        return text.decode(encoding, 'replace')
 
80
 
 
81
 
 
82
# This is required at import time for inheritances below.
 
83
if 'ImageMetadata' in dir(pyexiv2):
 
84
    Pyexiv2ImageMetadata = pyexiv2.ImageMetadata
 
85
else:
 
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
 
90
 
 
91
 
 
92
class _ImageMetadata_0_2_2(Pyexiv2ImageMetadata):
 
93
 
 
94
    def __init__(self, imgpath):
 
95
        super(_ImageMetadata_0_2_2, self).__init__(imgpath)
 
96
        self.imgpath = imgpath
 
97
 
 
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)
 
102
        return tag
 
103
 
 
104
 
 
105
class _ImageMetadata_0_2(_ImageMetadata_0_2_2):
 
106
 
 
107
    def get_jpeg_comment(self):
 
108
        # comment appeared in pyexiv2 0.2.2, so use PIL
 
109
        im = PILImage.open(self.imgpath)
 
110
        try:
 
111
            return im.app['COM'].strip('\x00')
 
112
        except KeyError:
 
113
            return ''
 
114
 
 
115
    comment = property(get_jpeg_comment)
 
116
 
 
117
 
 
118
class _ImageTag_0_1(object):
 
119
 
 
120
    def __init__(self, tag, imgpath, md, key):
 
121
        self._tag = tag
 
122
        self._imgpath = imgpath
 
123
        self._metadata = md
 
124
        self._key = key
 
125
 
 
126
    def __getattr__(self, name):
 
127
        if name == 'value':
 
128
            return self.get_value()
 
129
        elif name == 'raw_value':
 
130
            return self._tag
 
131
        else:
 
132
            raise AttributeError
 
133
 
 
134
    def __str__(self):
 
135
        return str(self._tag)
 
136
 
 
137
    def get_exif_date(self):
 
138
        '''
 
139
        Parses date from EXIF information.
 
140
        '''
 
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))
 
147
 
 
148
    def get_int(self):
 
149
        return int(self._tag)
 
150
 
 
151
    def get_interpreted_value(self):
 
152
        return self._metadata.interpretedExifValue(self._key)
 
153
 
 
154
    def get_decoded_utf8(self):
 
155
        return self.get_interpreted_value().decode('utf-8')
 
156
 
 
157
    def get_decoded_exif_user_comment(self):
 
158
        return decode_exif_user_comment(self._tag, self._imgpath)
 
159
 
 
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',
 
171
    }
 
172
 
 
173
    def get_value(self):
 
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)
 
178
        else:
 
179
            return self._tag
 
180
 
 
181
 
 
182
class _ImageMetadata_0_1(object):
 
183
 
 
184
    def __init__(self, imgpath):
 
185
        self.imgpath = imgpath
 
186
        self._metadata = pyexiv2.Image(self.imgpath.encode(sys.getfilesystemencoding()))
 
187
 
 
188
    def __getitem__(self, key):
 
189
        return _ImageTag_0_1(self._metadata[key], self.imgpath,
 
190
                             self._metadata, key)
 
191
 
 
192
    def __setitem__(self, key, value):
 
193
        self._metadata[key] = value
 
194
 
 
195
    def __delitem__(self, key):
 
196
        del self._metadata[key]
 
197
 
 
198
    def read(self):
 
199
        self._metadata.readMetadata()
 
200
 
 
201
    def write(self):
 
202
        self._metadata.writeMetadata()
 
203
 
 
204
    def __try_copy_tag_to(self, tag_key, dest_imgtags):
 
205
        try:
 
206
            dest_imgtags._metadata[tag_key] = self[tag_key]
 
207
        except (ValueError, TypeError):
 
208
            pass
 
209
 
 
210
    def copy(self, dest_imgtags):
 
211
        try:
 
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)
 
220
 
 
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)
 
224
 
 
225
    def get_exif_keys(self): return self._metadata.exifKeys()
 
226
    exif_keys = property(get_exif_keys)
 
227
 
 
228
    def get_iptc_keys(self): return self._metadata.iptcKeys()
 
229
    iptc_keys = property(get_iptc_keys)
 
230
 
 
231
 
 
232
if 'ImageMetadata' in dir(pyexiv2):
 
233
    if 'comment' in dir(pyexiv2.ImageMetadata):
 
234
        # pyexiv2 (>= 0.2.2)
 
235
        if pyexiv2.version_info >= (0, 3, 0):
 
236
            ImageMetadata = pyexiv2.ImageMetadata
 
237
        else:
 
238
            ImageMetadata = _ImageMetadata_0_2_2
 
239
    else:
 
240
        # pyexiv2 (>= 0.2, << 0.2.2)
 
241
        ImageMetadata = _ImageMetadata_0_2
 
242
elif 'Image' in dir(pyexiv2):
 
243
    # pyexiv2 (<< 0.2)
 
244
    ImageMetadata = _ImageMetadata_0_1
 
245
else:
 
246
    raise ImportError('Unrecognized pyexiv2 version.')
 
247
 
 
248
 
 
249
# vim: ts=4 sw=4 expandtab