Login

Image inlining template tag lib

Author:
rbeck
Posted:
November 17, 2010
Language:
Python
Version:
1.2
Score:
0 (after 2 ratings)

A custom templatetag for inlining image in the browser. The principe is to base64 encode the image and avoid a http request. There is a cache handling, you just have to specify a writable directory.

An example of the utilisation (template part): http://djangosnippets.org/snippets/2267/

The explication on http://raphaelbeck.wordpress.com/2010/11/14/make-inline-images-to-improve-performance-with-django-template-tags/

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
# -*- coding: utf-8 -*-
"""
    django templates tags for images
    @author: R. BECK
    @version: 0.1
"""

#from __future__ import with_statement #Python 2.5

import base64
import os
import Image
from re import compile as compile_re
try:
    from cStringIO import StringIO
except ImportError:
    from StringIO  import StringIO

from django.conf     import settings
from django.template import TemplateSyntaxError, Node, Library, Template

register = Library()

TEMPLATE_DEBUG = settings.TEMPLATE_DEBUG
MEDIA_ROOT     = settings.MEDIA_ROOT
CACHE_DIR      = "/tmp"

templatevar_re = compile_re("\{\{(.+)\}\}")
media_re       = compile_re("\{\{MEDIA_ROOT|MEDIA_URL\}\}")


class EmbeddedImgNode(Node):
    """Image node parser for rendering html inline base64 image"""
    
    def __init__(self, attributes):
        self.attrs = {}
        attrs = self.attrs
        for attr_value in attributes:
            try:
                attr, value = attr_value.split('=', 1)
            except ValueError, val_err:
                raise TemplateSyntaxError(u"Syntax Error :", val_err)
            attrs[attr] = value

        src = attrs.get('src')
        if not src:
            raise TemplateSyntaxError(u"You have to specify a non-empty src \
                                        attribute")


    def _encode_img(self, file_path):
        """Returns image base64 string representation and makes a cache file"""
        filename   = file_path.rpartition(os.sep)[2]
        need_cache = True
        content    = ""

        cache_file = "%s_cache" % os.path.join(CACHE_DIR, filename)

        try:
            with open(cache_file, 'r') as cached_file:
                content = cached_file.read()
            need_cache = False
        except IOError:
            pass

        if need_cache:
            try:
                image = open(file_path, 'r')
                out   = StringIO()
                base64.encode(image, out)
                content = out.getvalue().replace('\n', '')
            except IOError:
                pass
            else:
                try:
                    with open(cache_file, 'w+') as cached_file:
                        cached_file.write(content)
                except IOError:
                    pass
        return content


    def _render_img(self):
        """Prepare image attributes"""
        attrs = self.attrs
        attrs_get = attrs.get
        src    = attrs_get('src')
        height = attrs_get('height')
        width  = attrs_get('width')

        if media_re.search(src):
            src = src.replace('{{MEDIA_ROOT}}', '') \
                     .replace('{{MEDIA_URL}}', '')
         
        src = src.replace('"', '')
        src = os.path.join(MEDIA_ROOT, src)

        try:
            img     = Image.open(src)
            _width, _height    = img.size
            _format = 'image/%s' % img.format

            if not height:
                attrs['height'] = '%spx' % _height

            if not width:
                attrs['width'] = '%spx' % _width

            b64encoded = self._encode_img(img.filename)

            attrs['src'] = "data:%s;base64,%s" % (_format, b64encoded)
        except IOError:
            attrs['src'] = ""


    def render(self, context):
        self._render_img()

        attrs  = self.attrs
        search = templatevar_re.search

        for k, v in attrs.iteritems():
            if search(v):
                attrs[k] = Template(v).render(context)

        return """<img %s />""" % ' '.join(['%s=%s' % (k, v if v else '""')
                                             for k, v in attrs.iteritems()])



@register.tag(name="embedded_img")
def do_embedded_img(parser, token):
    return EmbeddedImgNode(token.split_contents()[1:])

More like this

  1. Template tag - list punctuation for a list of items by shapiromatron 10 months, 2 weeks ago
  2. JSONRequestMiddleware adds a .json() method to your HttpRequests by cdcarter 10 months, 3 weeks ago
  3. Serializer factory with Django Rest Framework by julio 1 year, 5 months ago
  4. Image compression before saving the new model / work with JPG, PNG by Schleidens 1 year, 6 months ago
  5. Help text hyperlinks by sa2812 1 year, 6 months ago

Comments

Please login first before commenting.