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 22, 2024Technology
Python and the Point Rush in DeFi

This article demonstrates how to use Python to automate yield calculations in decentralized finance (DeFi), focusing on the Renzo and Pendle platforms. It guides readers through estimating potential rewards based on factors like token prices, liquidity, and reward distribution rules, emphasizing the importance of regular data updates and informed decision-making in DeFi investments.

Aug 18, 2022Technology
5 Best Practices of RESTful API Design to Keep Your Users Happy

Dive into our guide to RESTful API best practices

Jun 27, 2018Technology
How to Work With Legacy Code: Code Refactoring Techniques

In this article we'll review general approach to working with the best kind of projects - the ones with old untested and undocumented spaghetti code and a tight schedule. We'll review anger management techniques, coping mechanisms and some refactoring tips that might come in handy.

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?

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, 2010Technology
Composing multiple views in Django

In UNIX way, each view should solve single task. This is good idea, but sometimes we need to mix logic of different views on same page. Filter, sort, paginate, or, for example, add comment on product page. In this article I'll show how we can mix such multiple views.