index.cgi
author Tero Marttila <terom@fixme.fi>
Tue, 05 May 2009 16:33:59 +0300
changeset 2 50d95ac19dfc
parent 1 71c7382994c4
child 3 ac063212bd67
permissions -rwxr-xr-x
negative line spacing
#!/usr/bin/python2.5
import cgi, cgitb

cgitb.enable()

from PIL import Image, ImageDraw, ImageFont, ImageEnhance
from cStringIO import StringIO
import random


# settings
text = [
    "aalto",
    "unive",
    "rsity"
]

random_chars = [ '"', '!', '?' ]

line_colors = [
    "#0469af",
    "#fbc614",
    "#e1313b",
]

fonts = {
    'dejavu-sans-bold':     "/usr/share/fonts/truetype/ttf-dejavu/DejaVuSans-Bold.ttf",
    'helvetica':            "HELR65W.TTF",
}

font_name = 'helvetica'
font_size = 30

def randomize (seq) :
    """
        Returns the given sequence in random order as a list
    """
    
    # copy
    l = list(seq)
    
    # rearrange
    random.shuffle(l)

    return l

def build_data (text, chars, line_colors) :
    """
        Returns a matrix of (text, color) tuples representing the data to render

        [ [ (str, str) ] ]

            text        - list of lines
            chars       - list of random chars to interpse
            line_colors - list of colors to draw the chars in
    """

    data = []
    
    for line, char, color in zip(text, chars, line_colors) :
        # pick position to place char
        pos = random.randint(1, len(line) - 1)
        
        # split into three parts
        data.append([
                (line[:pos], "#000000"),
                (char, color),
                (line[pos:], "#000000"),
            ])
    
    return data

def load_font (font_name, font_size) :
    """
        Load a font by name
    """
    
    # load font
    font_path = fonts[font_name]
    font = ImageFont.truetype(font_path, font_size)
    
    return font

def render_img (data, font, background_color="#ffffff", line_spacing=0) :
    """
        Render the data (as from build_data) as an image, using the given PIL.ImageFont, and return the PIL Image object
    """

    img_width = img_height = 0

    img_data = []
    
    # compute image width/height
    for segments in data :
        line_width = line_height = 0
        
        # build a new list of segments with additional info
        line_segments = []
        
        for seg_text, seg_color in segments :
            # compute rendered text size
            seg_width, seg_height = font.getsize(seg_text)
            
            # update line_*
            line_width += seg_width
            line_height = max(line_height, seg_height)
            
            # build the new segments list
            line_segments.append((seg_text, seg_color, seg_width))
        
        # update img_*
        img_width = max(img_width, line_width)
        img_height += line_height
        img_data.append((line_segments, line_height))

    # calculate height needed for line spacing
    img_height += (len(img_data) - 1) * line_spacing

    # create image
    img = Image.new("RGB", (img_width, img_height), background_color)
    draw = ImageDraw.Draw(img)

    # draw text
    img_y = 0
    for segments, line_height in img_data :
        img_x = 0
        
        # draw each segment build above, incremeing along img_x
        for seg_text, seg_color, seg_width in segments :
            draw.text((img_x, img_y), seg_text, font=font, fill=seg_color)

            img_x += seg_width
        
        img_y += line_height + line_spacing
    
    return img

def effect_smooth (img, factor) :
    """
        De-sharpen the image by the given factor
    """

    return ImageEnhance.Sharpness(img).enhance(factor)

def build_png (img) :
    """
        Write the given PIL.Image as a string, returning the raw binary data
    """

    # render PNG output
    buf = StringIO()
    img.save(buf, "png")
    data = buf.getvalue()

    return data

def response (type, data) :
    """
        Write out the HTTP Response
    """

    from sys import stdout

    stdout.write("Content-Type: %s\r\n" % type)
    stdout.write("Content-Length: %d\r\n" % len(data))
    stdout.write("\r\n")
    stdout.write(data)

def main () :
    data = build_data(text, randomize(random_chars), line_colors)
    font = load_font(font_name, font_size)

    img = render_img(data, font, line_spacing=-10)
    img = effect_smooth(img, 0.6)
    png_data = build_png(img)

    response("image/png", png_data)

if __name__ == '__main__' :
    main()