comparison data/plugin/macro/MoinImage.py @ 263:448a086d840c

MoinImage: simplify API
author Thomas Waldmann <tw AT waldmann-edv DOT de>
date Sat, 13 Sep 2008 20:21:20 +0200
parents c605998e1123
children b7e7be4340b0
comparison
equal deleted inserted replaced
262:c605998e1123 263:448a086d840c
5 Features: 5 Features:
6 * rendering (and caching) of thumbnails/webnails/originals 6 * rendering (and caching) of thumbnails/webnails/originals
7 * gives ready-to-use image urls 7 * gives ready-to-use image urls
8 * auto-rotation based on EXIF information 8 * auto-rotation based on EXIF information
9 * determines creation time from EXIF or file system 9 * determines creation time from EXIF or file system
10 * access to some EXIF data (cached) 10 * easy access to some EXIF data (cached)
11 11
12 Requires PIL and ExifTags libs. 12 Requires PIL and ExifTags libs.
13 13
14 @copyright: 2008 MoinMoin:ThomasWaldmann, 14 @copyright: 2008 MoinMoin:ThomasWaldmann,
15 2008 MoinMoin:ReimarBauer 15 2008 MoinMoin:ReimarBauer
16 @license: GNU GPL, see COPYING for details. 16 @license: GNU GPL, see COPYING for details.
17 """ 17 """
18 18
19 # default width, height
20 WEBNAIL_SIZE = (640, 480)
21 THUMBNAIL_SIZE = (128, 128)
22
23 # we don't process and cache all EXIF infos, but just these:
24 EXIF_CACHED = set(['DateTimeOriginal', 'TimeZoneOffset', 'Orientation', ])
25
26 import os, time 19 import os, time
27 import StringIO 20 import StringIO
28 21
29 from MoinMoin import caching, wikiutil 22 from MoinMoin import caching, wikiutil
30 from MoinMoin.action import AttachFile, cache 23 from MoinMoin.action import AttachFile, cache
38 import ExifTags 31 import ExifTags
39 except ImportError: 32 except ImportError:
40 ExifTags = None 33 ExifTags = None
41 34
42 class MoinImage(object): 35 class MoinImage(object):
36 # predefined sizes (width, height) - use them if you like:
37 WEBNAIL_SIZE = (640, 480)
38 THUMBNAIL_SIZE = (128, 128)
39
40 # we don't process and cache all EXIF infos, but just these:
41 EXIF_CACHED = set(['DateTimeOriginal', 'TimeZoneOffset', 'Orientation', ])
42
43 def __init__(self, request, 43 def __init__(self, request,
44 name, # PageName/AttachName for now, later this is just the item name 44 item_name, # PageName/AttachName for now, later this is just the item name
45 description=u'', # we just store this, but we do not use it in MoinImage 45 description=u'', # we just store this, but we do not use it in MoinImage
46 webnail_size=WEBNAIL_SIZE,
47 thumbnail_size=THUMBNAIL_SIZE,
48 ): 46 ):
49 self.request = request 47 self.request = request
50 self.pagename, self.attachname = name.rsplit('/', 1) 48 self.pagename, self.attachname = item_name.rsplit('/', 1)
51 self.description = description 49 self.description = description
52 self.webnail_size = webnail_size
53 self.thumbnail_size = thumbnail_size
54 50
55 # cached property values: 51 # cached property values:
56 self.__filename = None 52 self.__filename = None
57 self.__content_type = None 53 self.__content_type = None
58 self.__cache_key = None 54 self.__cache_key = None
74 70
75 def _get_image(self): 71 def _get_image(self):
76 if self.__image is None and Image is not None: 72 if self.__image is None and Image is not None:
77 self.__image = Image.open(self._filename) 73 self.__image = Image.open(self._filename)
78 return self.__image 74 return self.__image
79 image = property(_get_image) 75 image = property(_get_image) # the original image (PIL Image object) or None
80 76
81 def _get_cache_key(self): 77 def _get_cache_key(self):
82 if self.__cache_key is None: 78 if self.__cache_key is None:
83 self.__cache_key = cache.key(self.request, itemname=self.pagename, attachname=self.attachname) 79 self.__cache_key = cache.key(self.request, itemname=self.pagename, attachname=self.attachname)
84 return self.__cache_key 80 return self.__cache_key
95 try: 91 try:
96 exif_data = {} 92 exif_data = {}
97 if self.image is not None: # we have PIL 93 if self.image is not None: # we have PIL
98 for tag, value in self.image._getexif().items(): 94 for tag, value in self.image._getexif().items():
99 tag_name = ExifTags.TAGS.get(tag) 95 tag_name = ExifTags.TAGS.get(tag)
100 if tag_name in EXIF_CACHED: 96 if tag_name in self.EXIF_CACHED:
101 exif_data[tag_name] = value 97 exif_data[tag_name] = value
102 try: 98 try:
103 time_str = exif_data['DateTimeOriginal'] 99 time_str = exif_data['DateTimeOriginal']
104 tm = time.strptime(time_str, "%Y:%m:%d %H:%M:%S") 100 tm = time.strptime(time_str, "%Y:%m:%d %H:%M:%S")
105 t = time.mktime(tm) 101 t = time.mktime(tm)
118 exif_cache.update(exif_data) 114 exif_cache.update(exif_data)
119 self.__exif = exif_data 115 self.__exif = exif_data
120 else: 116 else:
121 self.__exif = exif_cache.content() 117 self.__exif = exif_cache.content()
122 return self.__exif 118 return self.__exif
123 exif = property(_get_exif_data) 119 exif = property(_get_exif_data) # dict of preprocessed EXIF data (string -> value)
124 120
125 def _get_ctime(self): 121 def _get_ctime(self):
126 """ return creation time of image (either from EXIF or file date) as UNIX timestamp """ 122 """ return creation time of image (either from EXIF or file date) as UNIX timestamp """
127 if self.__ctime is None: 123 if self.__ctime is None:
128 try: 124 try:
179 buf.close() 175 buf.close()
180 else: # XXX what to do without PIL? 176 else: # XXX what to do without PIL?
181 data = '' 177 data = ''
182 return content_type, data 178 return content_type, data
183 179
184 def _cache_url(self, size=None): 180 def url(self, size=None):
185 """ return a cache url for a rendering of this image with specified size, 181 """ return a cache url for a rendering of this image with specified size -
186 make sure the cache contains that rendering. 182 the code automatically makes sure that the cache contains that rendering.
183 If size is None, it gives a url for the full image size rendering.
184 Otherwise, size has to be a tuple (w, h) - if you like, you can use
185 these class level constant sizes:
186 WEBNAIL_SIZE - medium size, one of those likely to fit in a browser window
187 THUMBNAIL_SIZE - small size, for showing collections
187 """ 188 """
188 request = self.request 189 request = self.request
189 content_type = self.content_type 190 content_type = self.content_type
190 if size is None: 191 if size is None:
191 size_str = 'orig' 192 size_str = 'orig'
193 size_str = '%d_%d' % size 194 size_str = '%d_%d' % size
194 key = '%s_%s_%s' % (content_type.replace('/', '_'), size_str, self._cache_key) 195 key = '%s_%s_%s' % (content_type.replace('/', '_'), size_str, self._cache_key)
195 if not cache.exists(request, key): 196 if not cache.exists(request, key):
196 content_type, data = self._transform(size=size, content_type=content_type) 197 content_type, data = self._transform(size=size, content_type=content_type)
197 cache.put(request, key, data, content_type=content_type) 198 cache.put(request, key, data, content_type=content_type)
198 return cache.url(self.request, key) 199 return cache.url(request, key)
199
200 def webnail_url(self):
201 """ return url of webnail of this image """
202 return self._cache_url(self.webnail_size)
203
204 def thumbnail_url(self):
205 """ return url of thumbnail of this image """
206 return self._cache_url(self.thumbnail_size)
207
208 def fullsize_url(self):
209 """ return url of non-resized, but maybe transposed original image """
210 return self._cache_url() # keep size as is
211 200
212 201
213 def macro_MoinImage(macro, itemname=wikiutil.required_arg(unicode), desc=u''): 202 def macro_MoinImage(macro, itemname=wikiutil.required_arg(unicode), desc=u''):
214 """ Embed an Image into a wiki page - 203 """ Embed an Image into a wiki page -
215 currently more for testing MoinImage class than for practical use: 204 currently more for testing MoinImage class than for practical use:
219 request = macro.request 208 request = macro.request
220 fmt = macro.formatter 209 fmt = macro.formatter
221 thispagename = fmt.page.page_name 210 thispagename = fmt.page.page_name
222 pagename, fname = AttachFile.absoluteName(itemname, thispagename) 211 pagename, fname = AttachFile.absoluteName(itemname, thispagename)
223 img = MoinImage(request, itemname, desc) 212 img = MoinImage(request, itemname, desc)
224 213 return (fmt.image(src=img.url(img.THUMBNAIL_SIZE), alt=img.description) +
225 src = img.fullsize_url()
226 alt = fmt.text(img.description)
227 return (fmt.image(src=src, alt=alt) +
228 fmt.text(time.asctime(time.gmtime(img.ctime))) + 214 fmt.text(time.asctime(time.gmtime(img.ctime))) +
229 ' ' + 215 ' ' +
230 fmt.text(img.description) + 216 fmt.text(img.description) +
231 '---' + 217 '---' +
232 fmt.text(repr(img.exif))) 218 fmt.text(repr(img.exif)))