summaryrefslogtreecommitdiff
path: root/thumbnail/action/Thumbnail.py
diff options
context:
space:
mode:
Diffstat (limited to 'thumbnail/action/Thumbnail.py')
-rw-r--r--thumbnail/action/Thumbnail.py140
1 files changed, 140 insertions, 0 deletions
diff --git a/thumbnail/action/Thumbnail.py b/thumbnail/action/Thumbnail.py
new file mode 100644
index 0000000..3ff1f42
--- /dev/null
+++ b/thumbnail/action/Thumbnail.py
@@ -0,0 +1,140 @@
1import os
2import hashlib
3from PIL import Image, ImageOps
4from PIL.ImageFileIO import ImageFileIO
5
6from MoinMoin import log
7from MoinMoin import wikiutil
8from MoinMoin.Page import Page
9from MoinMoin.action import AttachFile
10from MoinMoin.caching import CacheEntry
11
12
13logging = log.getLogger(__name__)
14
15
16def crop(img, width, height):
17 src_width, src_height = img.size
18 src_ratio = float(src_width) / float(src_height)
19 dst_width, dst_height = int(width), int(height)
20 dst_ratio = float(dst_width) / float(dst_height)
21
22 if dst_ratio < src_ratio:
23 crop_height = src_height
24 crop_width = crop_height * dst_ratio
25 x_offset = float(src_width - crop_width) / 2
26 y_offset = 0
27 else:
28 crop_width = src_width
29 crop_height = crop_width / dst_ratio
30 x_offset = 0
31 y_offset = float(src_height - crop_height) / 3
32
33 return img.crop((
34 x_offset,
35 y_offset,
36 x_offset + int(crop_width),
37 y_offset + int(crop_height)
38 ))
39
40
41def thumbnail(img, long_side):
42 long_side = int(long_side)
43 width, height = [float(d) for d in img.size]
44
45 if height > width:
46 width = (width / height) * long_side
47 height = long_side
48 else:
49 height = (height / width) * long_side
50 width = long_side
51
52 img.thumbnail((width, height), Image.ANTIALIAS)
53 return img
54
55
56def thumbnail_constrain(img, size, dimension):
57 size = int(size)
58 width, height = [float(d) for d in img.size]
59
60 if dimension.lower() == "h":
61 height, width = size, ((width * size) / height)
62 elif dimension.lower() == "w":
63 width, height = size, ((height * size) / width)
64 else:
65 raise Exception("Must contrain valid dimension")
66
67 img.thumbnail((int(width), int(height)), Image.ANTIALIAS)
68 return img
69
70
71def get_cache_key(request, filename):
72 ops = hashlib.md5(":".join(request.values.getlist("do")))
73
74 key = filename.split(".")
75 key.insert(0, "thumbnail")
76 key.insert(-1, ops.hexdigest()[:8])
77
78 return ".".join(key)
79
80
81def execute(pagename, request):
82 _ = request.getText
83
84 if not request.user.may.read(pagename):
85 return _('You are not allowed to view attachments of this page.')
86
87 page = Page(request, pagename)
88 pagename, filename, fpath = AttachFile._access_file(pagename, request)
89
90 if not filename:
91 request.status_code = 404
92 return
93
94 cache = CacheEntry(request, page,
95 get_cache_key(request, filename), scope="item")
96
97 if cache.exists() and (cache.mtime() >= os.path.getmtime(fpath)):
98 logging.info("Using cache for %s", fpath)
99 cache.open(mode="r")
100 request.write(cache.read())
101 cache.close()
102 return
103
104 action_map = {
105 'ds': ImageOps.grayscale,
106 'cr': crop,
107 'th': thumbnail,
108 'tc': thumbnail_constrain,
109 }
110
111 with open(fpath) as fp:
112 img = Image.open(fp)
113
114 for action in request.values.getlist("do"):
115 action = action.split(":")
116 args = action[1].split(",") if len(action) > 1 else []
117
118 try:
119 img = action_map[action[0]](img, *args)
120 except Exception, e:
121 request.status_code = 400
122 request.write("Error: {}".format(e))
123 return
124
125 mt = wikiutil.MimeType(filename=filename)
126 request.headers['Content-Type'] = mt.content_type()
127 data = img.tostring('jpeg', img.mode)
128
129 try:
130 cache.lock("w")
131 cache.open(mode="w")
132 cache.write(data)
133 cache.close()
134 except Exception, e:
135 request.status_code = 500
136 request.write("Error: {}".format(e))
137 return
138 finally:
139 cache.unlock()
140 request.write(data)