Back
Mar 4, 2011

Css sprite generation

I've created this small sprite to create css sprites. It glues images from directory directory into single file and generates corresponding css.

#!/usr/bin/python

import os
import os.path as p
import Image


css_inline_item_template = '''
%(selector)s {
    background: url(%(url)s) no-repeat -%(xpos)spx -%(ypos)spx;
    width: %(width)spx;
    height: %(height)spx;
}
'''

css_base_template = '''
%(base_selector)s {
    background-image: url(%(url)s);
    background-repeat: no-repeat;
}
'''

css_base_item_template = '''
%(selector)s {
    background-position: -%(xpos)spx -%(ypos)spx;
    width: %(width)spx;
    height: %(height)spx;
}
'''


def generate_sprite(images, margin=10):
    def _basename(path):
        return p.splitext(p.basename(path))[0]

    data = []

    master_height = max(*[i.size[1] for i in images])
    master_width = sum([i.size[0] for i in images]) + margin * (len(images) - 1)

    sprite = Image.new(
        mode='RGBA',
        size=(master_width, master_height),
        color=(0,0,0,0))  # fully transparent

    location = 0
    for image in images:
        if 'transparency' in image.info:
            raise Exception('Incorrect transparency mode: ' + image.filename)

        sprite.paste(image,(location, 0))
        data.append({
            'xpos': location,
            'ypos': 0,
            'dir': _basename(p.dirname(image.filename)),
            'file': _basename(image.filename),
            'width': image.size[0],
            'height': image.size[1],
        })
        location += image.size[0] + margin

    return sprite, data


def render_css(base_template, item_template, css_data, sprite_url, base_selector, selector):
    base_info = {
        'base_selector': base_selector,
        'url': sprite_url,
    }
    base_css = base_template % base_info

    items_css = '\n'.join([
        item_template % dict([('selector', selector % image_data)]
            + image_data.items()
            + base_info.items())
        for image_data in css_data])

    return base_css + items_css


def create_sprite_and_css(images_dir, css_output, sprite_output,
        selector='.%(file)s', base_selector='.sprite', url=None, inline=False):
    images = [Image.open(p.join(images_dir, filename))
                for filename in os.listdir(images_dir)
                if not filename.startswith('.')]

    sprite, css_data = generate_sprite(images)
    sprite.save(sprite_output)

    sprite_url = url or p.basename(sprite_output)
    if inline:
        css = render_css('', css_inline_item_template, css_data, sprite_url, base_selector, selector)
    else:
        css = render_css(css_base_template, css_base_item_template, css_data, sprite_url, base_selector, selector)

    css_file = open(css_output, 'w')
    css_file.write(css)
    css_file.close()


if __name__ == '__main__':
    from optparse import OptionParser

    usage = "usage: %prog [options] images_dir css_output sprite_output"
    parser = OptionParser(usage=usage)
    parser.add_option("-i", "--inline", action="store_true", dest="inline", default=False,
                      help="When set, image url will be put in css rule for every item.")
    parser.add_option("-s", "--selector", dest="selector", default='.%(file)s',
                      help="Selector template. You can use dir, file, width, height here.")
    parser.add_option("-S", "--base-selector", dest="base_selector", default='.sprite',
                      help="Base selector template for base css rule (when -i is not set).")
    parser.add_option("-u", "--url", dest="url", default='',
                      help="Css rule Image url template.")

    (options, args) = parser.parse_args()

    if len(args) < 3:
        print 'You must provide all 3 arguments.'
    else:
        images_dir, css_output, sprite_output = [p.realpath(arg) for arg in args]
        create_sprite_and_css(images_dir, css_output, sprite_output,
                selector=options.selector,
                base_selector=options.base_selector,
                inline=options.inline,
                url=options.url)

Update

This article is pretty old. Now it's better to use something integrated to your build process, like grunt or webassets.

Subscribe for the news and updates

More thoughts
Jun 8, 2022Technology
How to Use MongoDB in Python: Gearheart`s Experience

In this article, we have prepared a quick tutorial on how to use MongoDB in Python and listed top ORM.

Feb 12, 2020Technology
5 Best Payment Gateways For 2020

We reviewed the best payment gateways in 2020. Here’s our comparison of their features, advantages, and disadvantages.

May 10, 2018Technology
How to Build a Cloud-Based Leads Management System for Universities

Lead management is an important part of the marketing strategy of every company of any size. Besides automating various business processes, privately-held organizations should consider implementing an IT solution that would help them manage their leads. So, how should you make a web-based leads management system for a University in order to significantly increase sales?

May 26, 2017Technology
Tutorial: Django User Registration and Authentication

In this beginners friends article I'll explain how to make authentication with Google account on your Django site and how to make authentication for you REST API.

Dec 1, 2016Technology
How to Use Django & PostgreSQL for Full Text Search

For any project there may be a need to use a database full-text search. We expect high speed and relevant results from this search. When we face such problem, we usually think about Solr, ElasticSearch, Sphinx, AWS CloudSearch, etc. But in this article we will talk about PostgreSQL. Starting from version 8.3, a full-text search support in PostgreSQL is available. Let's look at how it is implemented in the DBMS itself.

Aug 31, 2016Technology
Angular vs React Comparison

In this article, we will compare two most popular JS Libraries (Angular vs React). Both of them were created by professionals and have been used in famous big projects.