Ticket #961: image_tags.py

File image_tags.py, 6.6 KB (added by Nebojša Đorđević - nesh <nesh@…>, 9 years ago)

img filter implementation

Line 
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
15import os, struct
16from django.utils.translation import gettext
17from django.conf.settings import DEBUG
18
19def _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#
27try:
28    import Image
29    HAS_PIL = True
30except ImportError:
31    HAS_PIL = False
32
33def _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
78from contrib.utils import set_cached_file, get_cached_file
79
80def _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
94def 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
105def remove_thumbnail(field):
106    th = thumbnail_file(field)
107    if th:
108        os.unlink(th)
109#
110
111def _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
152from django.core.template import TemplateSyntaxError, TokenParser, Library
153register = Library()
154
155@register.filter()
156def img(value, args=''):
157    """ Create ``<img>`` tag with all required parameters.
158
159If image dimension is set and dimension is different from original
160image size and *thumbnail*\ [#]_ option is not ``False``
161it 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#
Back to Top