1 | # -*- coding: utf-8 -*-
|
---|
2 | # vim:enc=utf8:fenc=utf8:ts=4:sts=4:sw=4:sta:et:nofen:si:ai:nowrap
|
---|
3 | # (C) Djordjevic Nebojsa <nesh@studioquattro.co.yu> 2005
|
---|
4 | # You're welcome to redistribute this software under the
|
---|
5 | # terms of the GNU Library General Public Licence version 2.0
|
---|
6 | # or, at your option, any higher version.
|
---|
7 | #
|
---|
8 | # You can read the complete GNU LGPL in the file COPYING
|
---|
9 | # which should come along with this software, or visit
|
---|
10 | # the Free Software Foundation's WEB site http://www.fsf.org
|
---|
11 | #
|
---|
12 |
|
---|
13 | """ image related filters """
|
---|
14 |
|
---|
15 | import os, struct
|
---|
16 | from django.utils.translation import gettext
|
---|
17 | from django.conf.settings import DEBUG
|
---|
18 |
|
---|
19 | def _path(src):
|
---|
20 | from django.conf.settings import MEDIA_ROOT, MEDIA_URL
|
---|
21 |
|
---|
22 | if src.startswith(MEDIA_URL):
|
---|
23 | return os.path.join(MEDIA_ROOT, src[len(MEDIA_URL):])
|
---|
24 | else:
|
---|
25 | return os.path.join(MEDIA_ROOT, src)
|
---|
26 | #
|
---|
27 | try:
|
---|
28 | import Image
|
---|
29 | HAS_PIL = True
|
---|
30 | except ImportError:
|
---|
31 | HAS_PIL = False
|
---|
32 |
|
---|
33 | def _no_pil_image_size(fname):
|
---|
34 | """
|
---|
35 | Determine the image type of FNAME and return its size.
|
---|
36 | from draco
|
---|
37 | """
|
---|
38 | try:
|
---|
39 | filehandle = file(fname, 'rb')
|
---|
40 | except IOError:
|
---|
41 | return None
|
---|
42 | head = filehandle.read(24)
|
---|
43 | if len(head) != 24:
|
---|
44 | return
|
---|
45 | if head[:4] == '\x89PNG':
|
---|
46 | # PNG
|
---|
47 | check = struct.unpack('>i', head[4:8])[0]
|
---|
48 | if check != 0x0d0a1a0a:
|
---|
49 | return
|
---|
50 | width, height = struct.unpack('>ii', head[16:24])
|
---|
51 | elif head[:6] in ('GIF87a', 'GIF89a'):
|
---|
52 | # GIF
|
---|
53 | width, height = struct.unpack('<HH', head[6:10])
|
---|
54 | elif head[:4] == '\xff\xd8\xff\xe0' and head[6:10] == 'JFIF':
|
---|
55 | # JPEG
|
---|
56 | try:
|
---|
57 | filehandle.seek(0) # Read 0xff next
|
---|
58 | size = 2
|
---|
59 | filetype = 0
|
---|
60 | while not 0xc0 <= filetype <= 0xcf:
|
---|
61 | filehandle.seek(size, 1)
|
---|
62 | byte = filehandle.read(1)
|
---|
63 | while ord(byte) == 0xff:
|
---|
64 | byte = filehandle.read(1)
|
---|
65 | filetype = ord(byte)
|
---|
66 | size = struct.unpack('>H', filehandle.read(2))[0] - 2
|
---|
67 | # We are at a SOFn block
|
---|
68 | filehandle.seek(1, 1) # Skip `precision' byte.
|
---|
69 | height, width = struct.unpack('>HH', filehandle.read(4))
|
---|
70 | except:
|
---|
71 | raise
|
---|
72 | return
|
---|
73 | else:
|
---|
74 | return
|
---|
75 | return width, height
|
---|
76 | #
|
---|
77 |
|
---|
78 | from contrib.utils import set_cached_file, get_cached_file
|
---|
79 |
|
---|
80 | def _image_size(path):
|
---|
81 | size = get_cached_file(path)
|
---|
82 | if size is None:
|
---|
83 | if HAS_PIL:
|
---|
84 | size = Image.open(path).size
|
---|
85 | else:
|
---|
86 | size = _no_pil_image_size(path)
|
---|
87 | set_cached_file(path, size)
|
---|
88 | #
|
---|
89 | return size
|
---|
90 | #
|
---|
91 |
|
---|
92 | _THUMB_DIR = 'thumb'
|
---|
93 |
|
---|
94 | def thumbnail_file(filename):
|
---|
95 | from django.conf.settings import MEDIA_ROOT
|
---|
96 | basedir = os.path.dirname(filename)
|
---|
97 | name = os.path.basename(filename)
|
---|
98 | fname, ext = os.path.splitext(name)
|
---|
99 | ret = os.path.join(MEDIA_ROOT, basedir, _THUMB_DIR, fname + '.jpg')
|
---|
100 | if os.path.isfile(ret):
|
---|
101 | return ret
|
---|
102 | return None
|
---|
103 | # thumbnail_file
|
---|
104 |
|
---|
105 | def remove_thumbnail(field):
|
---|
106 | th = thumbnail_file(field)
|
---|
107 | if th:
|
---|
108 | os.unlink(th)
|
---|
109 | #
|
---|
110 |
|
---|
111 | def _make_thumb(path, width, height):
|
---|
112 | if not HAS_PIL:
|
---|
113 | return path
|
---|
114 |
|
---|
115 | from django.conf.settings import MEDIA_ROOT
|
---|
116 | url = os.path.dirname(path)
|
---|
117 | path = _path(path)
|
---|
118 | # thumb dir
|
---|
119 | basedir = os.path.dirname(path)
|
---|
120 | thdir = os.path.join(MEDIA_ROOT, basedir, _THUMB_DIR)
|
---|
121 |
|
---|
122 | # create dir if not exists
|
---|
123 | if not os.path.isdir(thdir):
|
---|
124 | os.mkdir(thdir)
|
---|
125 |
|
---|
126 | name = os.path.basename(path)
|
---|
127 | fname, ext = os.path.splitext(name)
|
---|
128 | outfile = os.path.join(thdir, fname + '.jpg')
|
---|
129 |
|
---|
130 | if os.path.isfile(outfile):
|
---|
131 | # thumbnail already exists
|
---|
132 | return '%s/thumb/%s.jpg' % (url, fname)
|
---|
133 |
|
---|
134 | # make and save thumbnail
|
---|
135 | try:
|
---|
136 | im = Image.open(path)
|
---|
137 | w, h = im.size
|
---|
138 | if (h != height) or (w != width):
|
---|
139 | im.thumbnail((width, height), Image.ANTIALIAS)
|
---|
140 | im.save(outfile, "JPEG")
|
---|
141 | else:
|
---|
142 | return path
|
---|
143 | except:
|
---|
144 | return path
|
---|
145 |
|
---|
146 | return '%s/thumb/%s.jpg' % (url, fname)
|
---|
147 | # _make_thumb
|
---|
148 |
|
---|
149 | ##################################################
|
---|
150 | ## FILTER ##
|
---|
151 |
|
---|
152 | from django.core.template import TemplateSyntaxError, TokenParser, Library
|
---|
153 | register = Library()
|
---|
154 |
|
---|
155 | @register.filter()
|
---|
156 | def img(value, args=''):
|
---|
157 | """ Create ``<img>`` tag with all required parameters.
|
---|
158 |
|
---|
159 | If image dimension is set and dimension is different from original
|
---|
160 | image size and *thumbnail*\ [#]_ option is not ``False``
|
---|
161 | it will make required **thumbnail**\ [#]_ and return link to it.
|
---|
162 |
|
---|
163 | .. [#] default is ``True``.
|
---|
164 | .. [#] requires PIL_, if PIL_ is not found fails silently.
|
---|
165 | .. _PIL: http://www.pythonware.com/products/pil/
|
---|
166 | """
|
---|
167 |
|
---|
168 | kwargs = {}
|
---|
169 | if args:
|
---|
170 | for arg in args.split('|'):
|
---|
171 | arg = arg.strip()
|
---|
172 | if arg == '': continue
|
---|
173 | kw, val = arg.split('=', 1)
|
---|
174 | kw = kw.lower()
|
---|
175 | try:
|
---|
176 | val = int(val) # convert all ints
|
---|
177 | except ValueError:
|
---|
178 | pass
|
---|
179 | kwargs[kw] = val
|
---|
180 | # for
|
---|
181 | # if
|
---|
182 |
|
---|
183 | path = _path(value)
|
---|
184 |
|
---|
185 | # file exists?
|
---|
186 | if not value:
|
---|
187 | if DEBUG:
|
---|
188 | return '<div class="error"><p class="title">%s</p></div>' % gettext('no image')
|
---|
189 | else:
|
---|
190 | return ''
|
---|
191 | elif not os.path.isfile(path):
|
---|
192 | if DEBUG:
|
---|
193 | return '<div class="error"><p class="title">%s</p><p>%s</p></div>' \
|
---|
194 | % (gettext('no image'), gettext('image %s not found') % value)
|
---|
195 | else:
|
---|
196 | return ''
|
---|
197 |
|
---|
198 | # no alt, use value
|
---|
199 | if 'alt' not in kwargs:
|
---|
200 | kwargs['alt'] = value
|
---|
201 | #
|
---|
202 |
|
---|
203 | if kwargs.get('thumbnail', True) and HAS_PIL:
|
---|
204 | if ('width' in kwargs) or ('height' in kwargs):
|
---|
205 | size = _image_size(path)
|
---|
206 | if size is not None:
|
---|
207 | width, height = size
|
---|
208 | if 'width' in kwargs:
|
---|
209 | new_width = kwargs['width']
|
---|
210 | del kwargs['width']
|
---|
211 | else:
|
---|
212 | new_width = width
|
---|
213 | #
|
---|
214 | if 'height' in kwargs:
|
---|
215 | new_height = kwargs['height']
|
---|
216 | del kwargs['height']
|
---|
217 | else:
|
---|
218 | new_height = height
|
---|
219 | #
|
---|
220 | if (size is not None) and ((new_width != width) or (new_height != height)):
|
---|
221 | value = _make_thumb(value, new_width, new_height)
|
---|
222 | path = _path(value)
|
---|
223 | # if
|
---|
224 | # if
|
---|
225 | # if
|
---|
226 |
|
---|
227 | size = _image_size(path)
|
---|
228 | if size is not None:
|
---|
229 | w, h = size
|
---|
230 | kwargs['width'] = '%d' % w
|
---|
231 | kwargs['height'] = '%d' % h
|
---|
232 | #
|
---|
233 |
|
---|
234 | ret = ['src="%s"' % value] # TODO: escape
|
---|
235 | for k, v in kwargs.items():
|
---|
236 | ret.append('%s="%s"' % (k, v))
|
---|
237 | return '<img %s />' % ' '.join(ret)
|
---|
238 | #
|
---|