Login

django image bundle template tag library

Author:
amitu
Posted:
June 11, 2007
Language:
Python
Version:
.96
Score:
2 (after 2 ratings)

Django ImageBundle tag, an implementation of image bundle by google: http://code.google.com/p/google-web-toolkit/wiki/ImageBundleDesign. Here is how to use it: http://www.djangosnippets.org/snippets/278/

  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
134
135
from django.template import Library, Node, TemplateSyntaxError, \
    resolve_variable, VariableDoesNotExist
from django.newforms.util import flatatt
from django.conf import settings
import Image, md5
from mx.Misc.OrderedMapping import OrderedMapping 

register = Library()

# img tag # {{{
class ImgNode(Node):
    def __init__(self, parts):
        self.params = self.clean(parts)

    def clean(self, parts):
        """ validates the content, and returns a dict """
        d = {}
        for part in parts:
            try:
                name, value = part.split("=", 1)
            except ValueError:
                raise TemplateSyntaxError, "Syntax Error, part: %s" % part
            d[name] = value
        if "src" not in d:
            raise TemplateSyntaxError, "SRC attribute is mandatory"
        return d

    def get(self, key, context, default=""):
        if not key in self.params: return default
        try:
            return resolve_variable(str(self.params[key]), context)
        except VariableDoesNotExist:
            return str(self.params[key]) 
        except Exception, e: print "big problem", e, "while key=", key

    def digest(self, context):
        """ returns a dict with varaibles resolved """
        d = {}
        for key, value in self.params.iteritems():
            d[key] = self.get(key, context)
        return d

    def render(self, context):
        d = dict(self.digest(context))
        return """<img src="/static2/clear.gif" style="background-image:url(%(src)s); background-position:%(pos-x)s %(pos-y)s; width:%(width)s; height:%(height)s">""" % d
    
#@register.tag()
def img(parser, token):
    return ImgNode(token.split_contents()[1:])
img = register.tag(img) 
# }}}

# imgbundle tag # {{{
class ImgBundleNode(Node):
    def __init__(self, nodelist):
        self.nodelist = nodelist
    def render(self, context):
        # the main magic happens here
        imgs = {}
        names = []
        for imgnode in self.nodelist.get_nodes_by_type(ImgNode):
            src = imgnode.get("src", context)
            imgs.setdefault(src, [])
            imgs[src].append(imgnode)
            names.append("%s:%s:%s" % (src, imgnode.params.get("width",""), imgnode.params.get("height","")))
        names = "".join(names)
        img_name = "/static2/%s.jpg" % md5.new(names).hexdigest()
        current_x = 0
        max_y = 0
        final_images = OrderedMapping()
        for src, nodelist in imgs.iteritems():
            # as of now we only support images that are in /static/
            m = Image.open(settings.SETTINGS_FILE_FOLDER.joinpath('static').joinpath(src.split("/")[-1])).convert("RGB")
            for n in nodelist:
                n.params["src"], n.params["pos-y"] = img_name, 0
                # there are 4 cases: 
                if not n.params.get("width") and not n.params.get("height"):
                    # neither width nor height is set
                    w, h = m.size
                elif not n.params.get("width") and n.params.get("height"):
                    # width not given, but height given
                    w, h = ((m.size[0] * 1.0) / m.size[1]) * int(n.params["height"][1:-3]), int(n.params["height"][1:-3])
                elif n.params.get("width") and not n.params.get("height"):
                    w, h = int(n.params["width"][1:-3]), ((m.size[1] * 1.0) / m.size[0]) * int(n.params["width"][1:-3])
                else:
                    w, h = int(n.params["width"][1:-3]), int(n.params["height"][1:-3])
                if final_images.has_key("%s:%s:%s" % (src, w, h)):
                    i, pos_x = final_images["%s:%s:%s" % (src, w, h)]
                else:
                    new_image = m.resize((w, h), Image.ANTIALIAS)
                    final_images["%s:%s:%s" % (src, w, h)] = (new_image, current_x)
                    pos_x = current_x
                    current_x += new_image.size[0]
                    if max_y < new_image.size[1]: max_y = new_image.size[1]
                n.params["width"], n.params["height"], n.params["pos-x"] = (w, h, -pos_x)
        if not settings.SETTINGS_FILE_FOLDER.joinpath('static').joinpath(img_name.split("/")[-1]).exists():
            n = Image.new("RGB", (current_x, max_y))
            print final_images, current_x
            for k in final_images.keys():
                i, p = final_images[k]
                n.paste(i, (p, 0, p + i.size[0], i.size[1])) 
            n.save(settings.SETTINGS_FILE_FOLDER.joinpath('static').joinpath(img_name.split("/")[-1]))
        return self.nodelist.render(context)

#@register.tag()
def imgbundle(parser, token):
    """
        For optimizing page load in browser, the number of http requests to 
        server should be minimized. Javascripts can be bundled, or inlined in
        the html page, and so can be CSS, but there is no way to include images.
        Further, in a typical page, there are a lot of small images, icons for
        example, that lead to a lot of http request by browser. 

        It is a well known optimization to bundle all the images into one, in 
        this case, we join the images end to end horizontally, and create a 
        single image, that will be fetched by the browser, and we will use CSS
        to make sure they line up.

        Put 
            {% imgbundle %}
        at the beginning of the page, and 
            {% endimgbundle %}
        near the bottom. 
        
        Instead of using <img>, use, {% img %} tag, which takes the same 
        parameters as <img> html tag. 

        Also in CSS, instead of using url(), use {% cssimg %}.
    """
    nodelist = parser.parse(('endimgbundle', ))
    parser.delete_first_token()
    return ImgBundleNode(nodelist)

imgbundle = register.tag(imgbundle) 
# }}}

More like this

  1. Template tag - list punctuation for a list of items by shapiromatron 10 months, 3 weeks ago
  2. JSONRequestMiddleware adds a .json() method to your HttpRequests by cdcarter 11 months 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, 7 months ago

Comments

Please login first before commenting.