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
Dec 13, 2022Technology
How to create a timelapse video from frames

We’ll tell you how to create a video timelapse from a sequence of snapshots and provide customers with video playlists optimized for browser playback.

Jun 1, 2018Technology
Site search organization: basic concepts

Now it's time to get acquainted with Elasticsearch. This NoSQL database is used to store logs, analyze information and - most importantly - search.

Aug 25, 2017Technology
How to Upload Files With Django

File upload works differently from simple form inputs, which can be somewhat troublesome for beginners. Here I'll show you how to handle uploads with ease.

Feb 28, 2017Technology
How to write an API in Django

There is such a term as Remote Procedure Call (RPC). In other words, by using this technology, programs can call functions on remote computers. There are many ways to implement RPC.

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.

Feb 18, 2010Technology
Business logic in models

In my recent project there was a lot of data business logic, so I had to organize this code somehow. In this article I'll describe a few hints on how to it.