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 """""" % 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 , use, {% img %} tag, which takes the same parameters as 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) # }}}